001
오늘의 키워드: 제곧내
오늘부터 본격적으로 시각화를 배우기 시작했다. 데이터의 시각화를 QA/QC 직무와 연결해서 생각해보면, 품질 관리를 제대로 하려면 결국 데이터 분석이 뒷받침돼야 하고, 그 분석 결과로 사람들을 설득시키려면 눈에 잘 들어오는 형태로 보여줄 수 있어야 한다! 숫자를 잔뜩 늘어놓는 것보다 차트 하나가 훨씬 보기 편하니...
시각화보다 먼저 해야 할 건 전처리. 실무에서는 전처리를 할 때 가장 오래걸린다고 들었던 게 기억이 난다. 데이터 자체가 엉망이면 아무리 예쁘게 그려도 의미가 없다. 결국 전처리가 제대로 되어야? 시각화도 믿을 수 있는 것이 된다. 오늘 배웠던 내용들 잘 정리해서 나중에 필요할 때마다 찾아 쓸 수 있도록 해야겠다. 코딩 = 오픈북 😁
002
오늘 학습한 내용
1. 데이터 선택 & 필터링
| df['컬럼'] | 단일 컬럼 선택 | df['Age'] |
| df[['컬럼1', '컬럼2']] | 여러 컬럼 선택 (리스트로!) | df[['Age', 'Sex']] |
| df[조건] | 조건 필터링 | df[df['Age'] > 30] |
| df[조건1 & 조건2] | AND 조건 | df[(df['Age'] > 30) & (df['Sex'] == 'male')] |
| df[조건1 | 조건2] | OR 조건 | — |
| .isin([값1, 값2]) | 여러 값 중 하나인지 확인 | df[df['Pclass'].isin([1, 2])] |
| .isna() / .notna() | 결측치 여부 필터링 | df[df['Age'].notna()] |
| .loc[행, 열] | 이름 기반 선택. 슬라이싱 끝값 포함 | df.loc[0:4, 'Age'] |
| .iloc[행, 열] | 숫자 위치 기반 선택. 슬라이싱 끝값 제외 | df.iloc[0:4, 2] |
++ 조건이 많아서 코드가 길어지면 \로 줄바꿈
2. 데이터 집계 & 재구성
| .groupby('컬럼') | 특정 컬럼 기준으로 그룹 묶기 |
| .agg({'컬럼': '함수'}) | 그룹별로 컬럼마다 다른 집계 함수 적용 (max, mean, sum 등) |
| .sort_values('컬럼') | 컬럼 기준 정렬. ascending=False로 내림차순 |
| .sort_index() | 인덱스 기준 정렬 |
| .reset_index() | 인덱스 초기화 |
| .set_index('컬럼') | 특정 컬럼을 인덱스로 설정 (중복 없는 값 권장) |
| .pivot_table(index, columns, values, aggfunc) | 데이터를 재구성해서 요약·집계. 엑셀 피벗 테이블과 동일한 개념 |
# groupby + agg: 성별·요일별로 묶고 컬럼마다 다른 집계 적용
df.groupby(['sex', 'day']).agg({
'total_bill': 'max',
'tip': 'mean',
'size': 'sum'
})
# pivot_table
df.pivot_table(index='Date', columns='Category', values='Value', aggfunc='sum')
3. 데이터프레임 합치기
| pd.concat([df1, df2], axis=0) | 위아래 | 단순 연결. ignore_index=True로 인덱스 초기화 가능 |
| pd.concat([df1, df2], axis=1) | 좌우 | 단순 연결 |
| pd.merge(df1, df2, on='키', how='inner') | 좌우 | 공통 열 기준 병합. SQL JOIN과 동일 |
merge의 how 옵션: inner(교집합) / outer(합집합) / left(왼쪽 기준) / right(오른쪽 기준)
4. 데이터 탐색 메서드
| .head(n) / .tail(n) | 상위/하위 n행 확인 (기본 5행) |
| .info() | 컬럼명, 타입, 결측치 개수 요약 |
| .describe() | 숫자형 기본 통계 (평균, 표준편차, 사분위수 등) |
| .describe(include='all') | 범주형 포함 전체 통계 |
| .describe(percentiles=[.1, .9, .95]) | 백분위수 상세 지정 |
| .value_counts() | 빈도 확인 |
| .value_counts(normalize=True) | 비율 확인 (빈도보다 더 중요!) |
| .skew() | 왜도 |
| .kurtosis() | 첨도 |
| .corr() | 상관관계 매트릭스 |
★ 헷갈림 주의 🚨
5. 왜도 해석

| 평균 > 중위수 | 양수(+) | 봉우리 왼쪽, 꼬리 오른쪽 |
| 평균 < 중위수 | 음수(-) | 봉우리 오른쪽, 꼬리 왼쪽 |
| 평균 = 중위수 | 0 | 좌우 대칭 (정규분포) |
첨도 > 3이면 뾰족(데이터 몰림)
< 3이면 평평(데이터 퍼짐)
상관관계 절대값 기준: 0.7 이상 → 매우 강함 / 0.3~0.7 → 강함 / 0.1~0.3 → 중간 / 0.1 미만 → 약함
상관관계가 높다고 인과관계가 있는 건 아님!
6. 결측치 처리
결측치 형태: NaN, N/A, Null, 공백 등
0이나 9999처럼 생긴 건 결측치보다 이상치에 가까움
| .isnull().sum() | 컬럼별 결측치 개수. sum() 안에 넣는 게 아니라 뒤에 붙이는 것! |
| .isnull().sum() / len(df) * 100 | 결측치 비율 (개수보다 더 중요) |
| .dropna() | 결측치 있는 행 삭제 |
| .dropna(axis=1) | 결측치 있는 컬럼 삭제 |
| .dropna(subset=['컬럼']) | 특정 컬럼 기준으로만 행 삭제 |
| .fillna(값) | 결측치를 특정 값으로 대체 |
| .ffill() / .bfill() | 앞/뒤 값으로 채우기 (시계열 데이터에서 유용) |
+ dropna는 뭘 기준으로 지울지 잘 생각하고 써야 한다!
7. 결측치 대체 기준
| 정규분포에 가까운 연속형 | 평균값 .mean() |
| 이상치 많거나 치우친 분포 | 중위수 .median() |
| 범주형 변수 | 최빈값 .mode()[0] (mode()는 Series 반환이라 [0] 필요) |
# 결측치 비율 확인
(titanic.isnull().sum() / len(titanic) * 100).round(2)
# 대체 예시
titanic['Age'].fillna(titanic['Age'].median())
titanic['Embarked'].fillna(titanic['Embarked'].mode()[0])
8. 데이터 타입 변환
| .astype(int/float/str) | 기본 타입 변환. 결측치나 이상한 값 섞여 있으면 에러 남 |
| pd.to_numeric(df['컬럼'], errors='coerce') | 문자열 → 숫자 변환. errors='coerce'로 변환 불가능한 값은 NaN 처리 |
| .str.replace(',', '') | 콤마 등 문자 제거 후 변환할 때 전처리용 |
| pd.to_datetime(df['컬럼']) | 문자열 → 날짜 타입 변환. 대부분의 날짜 형식 자동 인식 |
실무에서 CSV 불러오면 숫자가 문자열로 읽히는 경우가 있어서 데이터 타입 변환이 필요할 때도 있다!
astype()은 결측치가 없는 깨끗한 데이터에만 쓰고, 실제 데이터엔 pd.to_numeric(errors='coerce')이 안전
# astype()은 깨끗한 데이터에만
df['score'].astype(float)
# 결측치·이상값 섞인 실제 데이터엔 이걸로
df['age_clean'] = pd.to_numeric(df['age'], errors='coerce')
# 콤마 포함된 숫자 (예: '50,000')
df['income_clean'] = df['income'].str.replace(',', '').astype(float)
# 날짜 변환
df['order_date'] = pd.to_datetime(df['order_date'])
9. 날짜 타입 변환 후 쓸 수 있는 것들
| .dt.year / .dt.month / .dt.day | 연/월/일 추출 |
| .dt.day_name() | 요일명 추출 |
| .dt.quarter | 분기 추출 |
| .dt.dayofweek | 요일 번호 (월=0, 일=6) |
| (날짜A - 날짜B).dt.days | 날짜 간 차이 (일 단위) |
10. 시각화 — Matplotlib 기초
라이브세션에서 Matplotlib 내용 들어가기 전에 VOD 강의로 가볍게 봤다.
▼ 주요 메서스 정리
| plt.plot(x, y) | 선 그래프 |
| plt.hist(data) | 히스토그램 (막대그래프 아님!) |
| plt.xlabel() / plt.ylabel() | 축 레이블 |
| plt.title() | 그래프 제목 |
| plt.legend() | 범례 표시 |
| plt.text(x, y, '텍스트') | 특정 좌표에 텍스트 추가 |
| plt.show() | 그래프 출력 |
| sns.heatmap(matrix, annot=True, cmap='coolwarm') | 히트맵 (상관관계 시각화에 유용) |
003
오늘의 코드카타
프로그래머스 24번 : 서울에서 김서방 찾기
문제 설명
String형 배열 seoul의 element중 "Kim"의 위치 x를 찾아, "김서방은 x에 있다"는 String을 반환하는 함수, solution을 완성하세요. seoul에 "Kim"은 오직 한 번만 나타나며 잘못된 값이 입력되는 경우는 없습니다.
제한 사항
- seoul은 길이 1 이상, 1000 이하인 배열입니다.
- seoul의 원소는 길이 1 이상, 20 이하인 문자열입니다.
- "Kim"은 반드시 seoul 안에 포함되어 있습니다.
입출력 예
| seoul | return |
| ["Jane", "Kim"] | "김서방은 1에 있다" |
▼ 내가 짠 코드
def solution(seoul):
for i in seoul:
if "Kim" in seoul:
answer = seoul.index("Kim")
return f"김서방은 {answer}에 있다"
어렵지 않게 짜긴 했다만... 굳이 for문을 안 써도 된다는 걸 다 짜고 알았다.
def solution(seoul):
return f"김서방은 {seoul.index('Kim')}에 있다"
원래는 코드카타 하루에 5~10개씩 풀었는데 20번 넘어가고부터는 어려워져서 하루에 하나씩이라도 풀자로 바뀜....^^
004
내일 학습할 것
- 오늘 배운 전처리 메서드들 하나씩 직접 타이핑하면서 손에 익히기
- 오늘 라이브세션 실습 문제 풀어보기
- Matplotlib이랑 Seaborn 활용해서 예제 데이터로 직접 그래프 만들어보고 커스텀해보기
'Today I Learned' 카테고리의 다른 글
| [내일배움캠프 QA/QC 6기] TIL #017 직접 해보기 (0) | 2026.06.04 |
|---|---|
| [내일배움캠프 QA/QC 6기] TIL #016 ✨데이터 시각화 (with Matplotlib)✨ (1) | 2026.06.02 |
| [내일배움캠프 QA/QC 트랙 6기 본캠프] TIL #014 🐼s (1) | 2026.05.29 |
| [내일배움캠프 QA/QC 트랙 6기 본캠프] TIL #013 데이터 분석 입문! (0) | 2026.05.28 |
| [내일배움캠프 QA/QC 트랙 6기 본캠프] TIL #012 (0) | 2026.05.27 |