001
오늘의 코드카타
41. 이상한 문자 만들기
문제 설명
문자열 s는 한 개 이상의 단어로 구성되어 있습니다. 각 단어는 하나 이상의 공백문자로 구분되어 있습니다. 각 단어의 짝수번째 알파벳은 대문자로, 홀수번째 알파벳은 소문자로 바꾼 문자열을 리턴하는 함수, solution을 완성하세요.
제한조건
- 문자열 전체의 짝/홀수 인덱스가 아니라, 단어(공백을 기준)별로 짝/홀수 인덱스를 판단해야합니다.
- 첫 번째 글자는 0번째 인덱스로 보아 짝수번째 알파벳으로 처리해야 합니다.
입출력 예
| s | return |
| "try hello world" | "TrY HeLlO WoRlD" |
입출력 예 설명
"try hello world"는 세 단어 "try", "hello", "world"로 구성되어 있습니다.
각 단어의 짝수번째 문자를 대문자로, 홀수번째 문자를 소문자로 바꾸면 "TrY", "HeLlO", "WoRlD"입니다.
따라서 "TrY HeLlO WoRlD" 를 리턴합니다.
지금까지 푼 것 중에 두번째로 오래 걸린 듯..
▼ 1차 시도 (실패)
def solution(s):
a = s.split() # ⚠️ 문제 구간
for i in range(len(a)): # 문자열 쪼갠 리스트의 길이만큼 반복
n = ''
for j in range(len(a[i])): # i번째 인덱스의 길이만큼 반복
if j % 2 == 0: # 짝수 조건
n += a[i][j].upper() # i번째 인덱스의 j번째 문자를 대문자로 변환해서 n에 누적
else: # 홀수 조건
n += a[i][j].lower() # i번째 인덱스의 j번째 문자를 소문자로 변환해서 n에 누적
a[i] = n # 리스트 a의 i번째 인덱스를 조건문 거쳐 만들어진 n으로
return ' '.join(a) # 쪼개진 리스트를 문자열로 변환

왜 실패하는 건지 도저히 모르겠어서~ 젬선생 찬스
🤖 曰...
프로그래머스 '이상한 문자 만들기' 문제 풀고 있구나! 이거 일부 테스트 케이스에서 실패 뜨는 이유가 있어.
문제 조건 중에 엄청 중요한 통곡의 벽이 하나 숨어있거든?
"각 단어는 하나 이상의 공백문자로 구분되어 있습니다."
이 말은 단어 사이에 공백이 2개, 3개일 수도 있고, 심지어 문자열 맨 앞이나 맨 뒤에 공백이 무더기로 들어있을 수도 있다는 뜻이야.
❌ 왜 실패가 떴을까?
네 코드에서 s.split()을 썼잖아? 파이썬에서 괄호 안에 아무것도 안 넣은 s.split()은 연속된 모든 공백을 싹 다 무시하고 단어만 딱 골라내서 리스트로 만들어.
예를 들어 입력이 "try hello" (공백 2개) 라면:
1. s.split() 결과: ['try', 'hello'] (공백 사라짐)변환 결과: ['TrY', 'HeLlO']' '.join() 결과: "TrY HeLlO" (공백이 1개로 줄어듦 ❌)
2. 원래 입력에 있던 공백 개수가 그대로 유지되어야 하는데, split() 때문에 공백이 다 1개짜리로 초기화돼서 실패(틀림)가 뜨는 거야 ㅇㅇ.
🛠️ 정답 코드로 고치기 (split(' ') 쓰기)
공백을 한 칸짜리 모양 그대로 유지하면서 쪼개려면 괄호 안에 ' ' (공백 한 칸)을 명시해 줘야 돼! 그러면 공백이 여러 개 있어도 빈 문자열('')로 리스트에 살아남아서 원래 공백 자리를 지켜줘.

' ' 면 한칸짜리 공백이잖아
근데 어떻게 두칸 세칸짜리 공백도 유지가 되는 거야?
🤖
이거 진짜 날카로운 질문이야 ㅇㅇ. 처음엔 다들 헷갈려 하는 게 당연해.
쉽게 이해할 수 있게 split()과 split(' ')이 문자열을 다루는 내부 방식을 비교해서 보여줄게.
💡 핵심은 "빈 문자열('')"을 남기느냐 마느냐의 차이
공백이 3칸 연속으로 있는 "A B"라는 문자열이 있다고 쳐보자. 컴파일러 눈에는 이게 "A" + " " + " " + " " + "B"로 보여.
1. split() (괄호 안에 아무것도 없을 때)
"야, 공백(스페이스, 탭, 줄바꿈)은 전부 그냥 '벽'이야. 연속으로 있든 말든 무조건 다 합쳐서 단어만 골라내!"
- "A" 발견 ➡️ 리스트에 넣음 ['A']
- 공백 3칸 연속 발견 ➡️ "어, 공백더미네? 싹 다 무시하고 다음 글자 나올 때까지 넘겨!"
- "B" 발견 ➡️ 리스트에 넣음 ['A', 'B']
- 결과: ['A', 'B'] ➡️ 나중에 ' '.join() 하면 중간 공백이 1칸으로 줄어듦.
2. split(' ') (공백 한 칸을 기준으로 지정했을 때)
"난 정확히 '공백 한 칸'을 만날 때마다 가위질을 할 거야. 그 사이에 아무것도 없어도 무조건 쪼개!"
여기서 가위질을 3번 세심하게 한다고 생각하면 편해.
- "A" 뒤에 첫 번째 공백 발견 ➡️ 싹둑! ➡️ 왼쪽의 "A"가 리스트에 들어감. ['A']
- 두 번째 공백 발견 ➡️ 싹둑! ➡️ 첫 번째 공백과 두 번째 공백 사이에 아무 글자도 없지? 그래서 빈 방인 "" (빈 문자열)을 리스트에 집어넣음. ['A', '']
- 세 번째 공백 발견 ➡️ 싹둑! ➡️ 두 번째와 세 번째 사이에도 아무것도 없으니 또 ""를 넣음. ['A', '', '']
- 마지막 "B" 발견 ➡️ 리스트에 넣음.
- 결과: ['A', '', '', 'B'] ➡️ 공백 개수만큼 빈 문자열('') 자리가 방을 차지하고 살아남음!
🔄 다시 합칠 때 (' '.join()) 어떻게 복원될까?
파이썬에서 ' '.join(리스트)은 리스트의 요소들 사이에 공백 한 칸(' ')을 끼워 넣으면서 하나로 합치는 기능이야.
방금 split(' ')으로 쪼개진 ['A', '', '', 'B']를 다시 합쳐보면:
1. "A"를 쓴다.
2. 사이에 공백 한 칸 주입 ➡️ "A "
3. 두 번째 요소인 "" (빈 방)을 합친다. ➡️ "A " (아무것도 안 더해짐)
4. 사이에 공백 한 칸 주입 ➡️ "A "
5. 세 번째 요소인 "" (빈 방)을 합친다. ➡️ "A "
6. 사이에 공백 한 칸 주입 ➡️ "A "
7. 마지막 "B"를 합친다. ➡️ "A B"
결과적으로 처음에 있던 공백 3칸이 완벽하게 그대로 유지되는 마법이 일어나 ㅇㅇ.
📜 요약
- split(): 공백이 몇 칸이든 다 무시하고 단어만 쏙 뽑아옴.
- split(' '): 공백 한 칸 단위로 칼같이 쪼개서, 연속된 공백 자리에 '' (빈 문자열)을 남겨둠 ➡️ 이 빈 문자열들이 자리를 지켜줘서 원래 공백 개수가 유지!

감사합니다.
▼ 2차 시도 (성공)
def solution(s):
a = s.split(' ') # 젬선생 피드백 반영
for i in range(len(a)):
n = ''
for j in range(len(a[i])):
if j % 2 == 0:
n += a[i][j].upper()
else:
n += a[i][j].lower()
a[i] = n
return ' '.join(a)
002
오늘 학습한 내용
[ 머신러닝의 이해와 라이브러리 활용 기초 ]
선형회귀분석 실습 : 몸무게로 키 예측해보기
강의 들으면서 실습해봤는데, 그냥 따라가기만 하니까 지금 내가 뭘 하고 있는 건지 뭘 구하고 있는 건지 잘 모르겠어서... 정리해봤다.
1. 데이터 준비 및 시각화
- weights, heights 리스트로 데이터프레임 만들고, Weight(x축) vs Height(y축) 산점도로 두 변수 관계를 먼저 눈으로 확인
weights = [87, 81, 82, 92, 79, 83, 88, 91, 85, 90]
heights = [180, 175, 178, 185, 172, 176, 182, 184, 179, 181]
body_df = pd.DataFrame({'Height': heights, 'Weight': weights})
plt.title('Height vs Weight')
plt.xlabel('Weight')
plt.ylabel('Height')
sns.scatterplot(data=body_df, x='Weight', y='Height')

2. 선형회귀 모델 학습
- sklearn의 LinearRegression으로 모델 만들고, X(독립변수=Weight)와 y(종속변수=Height)로 학습
- 학습된 모델에서 기울기(coef_)와 절편(intercept_)을 뽑아서 회귀식 형태로 출력
from sklearn.linear_model import LinearRegression
model_lr = LinearRegression()
X = body_df[['Weight']]
y = body_df[['Height']]
model_lr.fit(X = X, y = y)
w1 = model_lr.coef_[0][0]
w0 = model_lr.intercept_[0]
print('y = {}X + {}'.format(w1.round(2), w0.round(2)))
- 출력 : y = 0.87X + 104.36
3. 예측값/잔차/MSE 계산
- 학습한 회귀식(w1, w0)으로 예측값(pred)을 직접 만들고, 실제값과의 차이(error)와 그 제곱(error²)을 계산
- error² 평균을 직접 구해서 MSE(평균제곱오차) 계산
body_df['pred'] = body_df['Weight'] * w1 + w0
body_df['error'] = body_df['Height'] - body_df['pred']
body_df['error^2'] = body_df['error'] * body_df['error']
body_df['error^2'].sum()/len(body_df)

4. 회귀선 시각화
- 원본 산점도 위에 예측값(pred)으로 만든 회귀선을 빨간색으로 겹쳐 그려서, 실제 데이터와 모델이 얼마나 맞아떨어지는지 시각화
sns.scatterplot(data=body_df, x='Weight', y='Height')
sns.lineplot(data=body_df, x='Weight', y='pred', color='red')

5. sklearn 평가지표로 검증
- 직접 계산한 MSE 값이 맞는지, sklearn의 mean_squared_error로 다시 계산해서 비교 검증
- R²(결정계수)도 r2_score로 구해서 이 모델이 데이터를 얼마나 잘 설명하는지 확인
from sklearn.metrics import mean_squared_error
from sklearn.metrics import r2_score
y_true = body_df['Height']
y_pred = body_df['pred']
mean_squared_error(y_true, y_pred) # 1.1436123348017704
r2_score(y_true, y_pred) # 0.9235553252137854 (good.)
MSE는 낮을수록 좋은 것이나,
(MSE = 예측값과 실제값 차이를 제곱해서 평균낸 것, 0이면 완벽하게 맞춘 거고 클수록 많이 틀린 것)
낮을수록 좋다는 거랑 별개로, MSE 자체의 절대값이 크고 작은지는 데이터 단위/스케일에 따라 완전히 달라져서 분야마다 "이 정도면 괜찮다"는 기준이 달라짐!
R²은 0~1 사이로 정규화돼 있어서 "이 모델이 데이터를 몇 % 설명하는지" 직관적으로 비교 가능 (1에 가까울수록 좋음)
6. model.predict()로도 같은 결과 나오는지 재확인
- 직접 만든 pred 컬럼 대신, 모델의 predict() 메서드로 예측한 값(pred2)으로도 MSE를 구해서 두 방식이 같은 결과를 내는지 최종적으로 확인
y_pred2 = model_lr.predict(body_df[['Weight']])
mean_squared_error(y_true, y_pred2) # 1.1436123348017704, 앞에서 구한 거랑 같다.
실습하면서 느낀 점...
- 코드 한 줄 한 줄은 이해가 되는데, 다음으로 뭘 해야 하는지 감이 잘 안 잡힘 (혼자서 백지 상태에서 해보라고 하면 못 함...;)
- 필요한 메서드가 뭔지 바로바로 안 떠오름...(당연한 거겠지) 그냥 강의 보고 무지성으로 따라가게 됨
다짐
- 전체 흐름(데이터 준비 → 학습 → 예측 → 평가)을 먼저 그려보고, 그 다음 각 단계에 어떤 메서드가 들어가는지 채워나가는 식으로 공부하기
[ 기초 통계학 - 마무리 ]
1. 재현 가능성
같은 실험 반복했을 때 결과가 똑같이 나오는지가 연구 신뢰성 확보에 중요!!
- 실험 조건 완벽히 재현 어려움
- p해킹으로 p값 조작 가능 → 1종 오류 위험
- 유의수준 0.05 = 20번 중 1번은 틀려도 기각될 수 있다는 뜻
- 유의수준 낮추면 베타값(2종 오류) 커지는 트레이드오프 있음
2. p-해킹
유의미한 p값 나올 때까지 변수 바꿔가며 반복 분석하는 행위
XXX
- 여러 가설 반복 시도하다 p<0.05 나온 것만 보고
- 데이터 늘리다 우연히 좋은 시점에서 멈추기
- 결과 보고 가설을 사후에 다시 세우기
→ 가설은 미리 세우고 검증할 것, 탐색적 분석이면 본페로니 보정 등 사용
3. 선택적 보고
- 유의미한 결과만 보고하고 나머지는 숨기는 행위 → 분석 결과 왜곡, 신뢰성 저하
4. 자료 수집 중단 시점
- 언제 멈출지 미리 안 정하면 원하는 결과 나올 때까지 계속 모으게 됨 (예: 50명 → 결과 안 좋아서 100명까지 추가)
5. 데이터 탐색과 검증 분리
- 탐색해서 가설 세운 데이터 그대로 검증까지 하면 과적합 위험
→ 탐색용(train) / 검증용(test) 나누고, 검증 데이터는 맨 마지막에만 볼 것 (머신러닝에서는 학습, 검증, 평가 데이터로 나누는 게 일반적)
[ 아티클 스터디 ]
- 주제 : 확실히 알아두면 만사가 편해지는 머신러닝 10가지 알고리즘
- 아티클 링크 : https://yozm.wishket.com/magazine/detail/1931/
지금 배우고 있는 머신러닝의 알고리즘들 복습한다는 느낌으로 읽어봄
1. 지도학습 VS 비지도학습
- 지도학습: 정답(목표 변수)이 있는 데이터로 학습해서 예측.
- 비지도학습: 정답 없이 데이터 안에서 패턴 찾는 것.
2. 지도학습: 회귀 VS 분류
- 회귀: 연속적인 숫자값 예측 (가격, 온도 등) 대표적으로 선형회귀
- 분류: 정해진 범주 중 하나로 구분. 로지스틱 회귀(이진분류), 나이브 베이즈 등등
- KNN이나 트리 계열은 회귀/분류 둘 다 가능한 만능형
3. 베이스라인 모델들
1) 선형 회귀 / 로지스틱 회귀
- 종속변수-독립변수가 선형 관계일 때 쓰는 가장 기본적인 모델
- 단점: 데이터가 선형 관계 아니면 예측력 뚝 떨어짐
2) KNN (K-최근접 이웃)
- 거리 기반으로 작동, 별다른 가정 없이 직관적
- 다중분류에 간편하게 쓸 수 있고 작은 데이터셋에 적합
- 근데 데이터 커지면 느려지고 아웃라이어에 약함
3) 나이브 베이즈
- 조건부 확률 기반, 스팸 필터처럼 자연어 처리에 자주 쓰임 (딥러닝이 더 잘하지만 간단하게 할 때 이거 사용)
- 독립변수가 서로 독립적이면 경쟁력 있지만... 아니면 범용성 낮음
4. 트리 기반 모델들
1) 결정 트리
- 변수의 특정 기준점으로 데이터를 계속 쪼개가면서 예측
- 데이터 가정 필요 없고 이상치 영향도 거의 없음, 시각화도 직관적
- 트리가 깊어지면 오버피팅(훈련 데이터에만 과하게 맞춰지는 현상) 발생
2) 랜덤 포레스트
- 결정 트리의 오버피팅 문제를 완화하려고 트리 여러 개를 앙상블(여러 모델 결과를 합쳐서 예측)한 모델
- 이상치 영향 거의 없고 선형/비선형 둘 다 잘 작동
- 해석이 어렵고 학습 속도가 느림
3) XG부스트 / 라이트GBM
- 트리를 순차적으로 만들면서 이전 트리의 오차를 보완해가는 부스팅 방식
- 캐글 컴피티션에서 검증된 성능, 변수 많고 데이터 클수록 강함
- 라이트GBM은 변수 중요도까지 확인 가능해서 한 단계 더 진화한 버전
- 단점: 해석 어렵고 하이퍼파라미터 튜닝이 까다로움
결정 트리 → 랜덤 포레스트 → 부스팅 계열로 갈수록 예측력, 성능은 올라가나 해석이 어려워진다는 단점이...
5. 비지도학습
1) K-Means (군집화)
- 거리 기반으로 비슷한 데이터끼리 그룹 묶기
- 사용자가 K값(그룹 개수)을 직접 정해야 함, 자동으로 못 찾음
- 변수 스케일에 따라 결과가 달라질 수 있어서 스케일링(변수 단위/범위 맞춰주는 전처리) 필수
2) PCA (주성분 분석, 차원축소)
- 변수 개수는 줄이고 특성은 최대한 보존
- 다차원 데이터를 2차원으로 줄여서 시각화하기 좋고, 변수 간 높은 상관관계 문제도 해결
- 근데 기존 변수가 아니라 새로운 변수로 바뀌어서 해석이 좀 어려움, 정보 손실 불가피
'Today I Learned' 카테고리의 다른 글
| [내일배움캠프 QA/QC 6기] TIL #033 (0) | 2026.06.26 |
|---|---|
| [내일배움캠프 QA/QC 6기] TIL #032 (0) | 2026.06.25 |
| [내일배움캠프 QA/QC 6기] TIL #030 (0) | 2026.06.23 |
| [내일배움캠프 QA/QC 6기] TIL #029 (0) | 2026.06.22 |
| [내일배움캠프 QA/QC 6기] TIL #028 (0) | 2026.06.19 |