YOUR STUDY DESK
조금씩 풀고, 확실하게 이해하기.
5장부터 8장까지 순서대로. 답을 확인하고 직접 고쳐 보세요.
강의 실습과 제공된 정리 자료를 바탕으로 구성했습니다. 실제 시험 출제 문항을 예측하거나 보장하지 않습니다.
CHAPTER 05
데이터 파악하기 & 다루기 기초
불러오기→훑어보기→copy()→rename()→파생변수
① 데이터 파악 함수
| 코드 | 하는 일 |
|---|---|
pd.read_csv('파일.csv') | CSV 불러오기 |
df.head(n) / df.tail(n) | 앞 / 뒤 n행 (기본 5행) |
df.shape | (행, 열) 크기 — 괄호 없음 (어트리뷰트). shape[0]=행, shape[1]=열 |
df.info() | 열 이름, 자료형, non-null 개수 → 결측치 확인 |
df.describe() | 숫자형 요약통계 (count, mean, std, min, 25%, 50%, 75%, max) |
df.describe(include='all') | 문자형까지 포함 (unique, top, freq) |
describe()의 count < 전체 행 수 → 그 열에 결측치가 있다는 뜻 (실습1의
vmin).② 함수 · 메서드 · 어트리뷰트 구분
함수 (function)
sum([1,2,3]), pd.read_csv()
단독으로 호출
메서드 (method)
df.head()
객체.이름() — 괄호 있음
어트리뷰트 (attribute)
df.shape, df.columns
객체.이름 — 괄호 없음
③ 복사본 & 변수명 바꾸기
df_new = df.copy() # 원본 보호용 독립 복사본
df_new = df_new.rename(columns={'cty': 'city', 'hwy': 'highway'}) # {옛이름: 새이름}
copy() 이유: 원본은 그대로 보존하고 복사본만 수정하기 위해. rename은 결과를 다시 대입해야 반영됨.④ 파생변수 만들기
df['total'] = df['math'] + df['english'] + df['science']
df['mean'] = df['total'] / 3
import numpy as np
# 조건 2개 → np.where(조건, 참일 때, 거짓일 때)
df['result'] = np.where(df['mean'] >= 70, 'pass', 'review')
# 조건 3개 이상 → np.select
conditions = [df['mean'] >= 90, df['mean'] >= 80]
choices = ['A', 'B']
df['level'] = np.select(conditions, choices, default='C')
# 중첩 np.where 방식도 자주 나옴
df['level'] = np.where(df['mean'] >= 90, 'A',
np.where(df['mean'] >= 80, 'B', 'C'))
df['result'].value_counts() # 범주별 빈도
CHAPTER 06
데이터 가공 — 추출 · 정렬 · 요약 · 합치기
query (행)→[[ ]] (열)→sort_values→groupby + agg→merge / concat
6장의 핵심은 위 메서드를 점(.)으로 이어 붙이는 체이닝. 예:
df.query(...).groupby(...).agg(...).sort_values(...)① 행 추출 — query()
df.query("store == 'A관'") # 문자값은 따옴표 안에 따옴표
df.query("store != 'A관'") # 같지 않음
df.query("category == '음료' & rating >= 4.7") # 그리고: & 또는 and
df.query("rating >= 4.5 | quantity >= 2") # 또는: | 또는 or
df.query("payment in ['카드', '간편결제']") # 여러 값 중 하나
df.query("payment not in ['현금']") # 제외
② 열 선택
df['sales'] # 한 열 → Series
df[['store', 'menu', 'sales']] # 여러 열 → DataFrame (대괄호 2겹!)
df.drop(columns='menu') # 열 제거
③ 정렬
df.sort_values('sales') # 오름차순 (기본)
df.sort_values('sales', ascending=False) # 내림차순 → 상위 N개: .head(N)
df.sort_values(['store', 'sales'], ascending=[True, False])
④ 집단별 요약 — groupby() + agg() ★가장 중요
# 방법 1: 이름 붙여 요약 (권장) 새열이름=('원래열', '함수')
df.groupby('store', as_index=False).agg(
총매출=('sales', 'sum'),
평균평점=('rating', 'mean'),
주문건수=('sales', 'count')
)
# 방법 2: 한 열에 여러 통계
df.groupby('store')['sales'].agg(['mean', 'sum'])
# 여러 기준으로 묶기
df.groupby(['store', 'location']).agg(총매출=('sales', 'sum'))
| 요약 함수 | 의미 |
|---|---|
'mean' / 'median' | 평균 / 중앙값 |
'sum' / 'count' | 합계 / 개수(빈도) |
'min' / 'max' / 'std' | 최솟값 / 최댓값 / 표준편차 |
as_index=False → 묶은 열이 인덱스가 아닌 일반 열로 남음 (8장 그래프에 필수). .reset_index()도 같은 효과.⑤ 데이터 합치기
가로 합치기 — merge
기준 열(key)로 열을 붙임pd.merge(cafe, store, how='left', on='store')
세로 합치기 — concat
행을 이어 붙임pd.concat([cafe, event], ignore_index=True)
⑥ 기타
top = df.sort_values('sales', ascending=False).iloc[0] # 1위 행 자동 추출
top['store'], top['sales']
result.round(2) # 소수점 2자리
CHAPTER 07
데이터 정제 — 결측치 · 이상치
isna().sum()→dropna / fillna→규칙 기반 이상치→IQR 이상치→삭제·대체·유지+표시
① 결측치 확인
df.isna() # True/False 표
df.isna().sum() # ★ 열별 결측치 개수
df.columns[df.isna().sum() > 0] # 결측치 있는 열 이름
df['department'].value_counts(dropna=False) # NaN 포함 빈도
df['col'].notna() # 결측 아니면 True
② 결측치 제거 — dropna()
df_basic = df.dropna(subset=['study_hours', 'sleep_hours']) # ★ 필요한 열 기준만
len(df) - len(df_basic) # 제외된 행 수
그냥
dropna()가 위험한 이유: 분석에 쓰지 않는 열의 결측치 때문에 행 전체가 삭제 → 멀쩡한 데이터까지 버려지고 표본이 줄어 결과가 편향될 수 있음.③ 결측치 대체 — fillna()
df_app = df.copy()
df_app['study_hours'] = df_app['study_hours'].fillna(df_app['study_hours'].median()) # 중앙값
df_app['math'] = df_app['math'].fillna(df_app['math'].mean()) # 평균
df_app['department'] = df_app['department'].fillna(df_app['department'].mode()[0]) # 최빈값 ([0] 필수!)
| 대표값 | 언제 쓰나 |
|---|---|
평균 mean() | 극단값이 없는 숫자 데이터 |
중앙값 median() | 극단값이 있는 숫자 데이터 (극단값 영향 적음) |
최빈값 mode()[0] | 범주형(문자) 데이터 — mode()는 Series라서 [0]으로 첫 값 꺼냄 |
④ 규칙 기반 이상치 (정상 범위를 알 때)
df.query('score < 0 or score > 100') # 이상치 찾기
df['score'].between(1, 5) # 1 ≤ x ≤ 5 이면 True (양 끝 포함)
df[~df['satisfaction'].between(1, 5) & df['satisfaction'].notna()] # ~ = NOT
# 범위 밖 값 → NaN
df['score'] = np.where(df['score'].between(0, 100), df['score'], np.nan)
# 특정 오류값만 교체
df['commute_min'] = df['commute_min'].replace(999, np.nan)
⑤ IQR 통계적 이상치
Q1 = df['steps'].quantile(0.25)
Q3 = df['steps'].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
outliers = df[(df['steps'] < lower) | (df['steps'] > upper)] # 추출
df['steps_outlier'] = (df['steps'] < lower) | (df['steps'] > upper) # 유지+표시 열
⑥ 처리 판단 기준
| 상황 | 처리 | 예시 |
|---|---|---|
| 명백한 입력 오류 | 대체 (NaN) | commute_min=999, 범위 밖 만족도 |
| 실제 가능한 극단값 | 유지+표시 | steps IQR 이상치 (실제로 많이 걸었을 수 있음) |
| 확인 불가한 값 | 유지+표시 후 확인 | department='기타X' |
최종 체크리스트
- 결측치를
isna().sum()으로 확인했다 - 분석에 필요한 열만 선택하여
dropna()를 사용했다 fillna()에서 평균·중앙값·최빈값 선택 근거를 생각했다- 값의 정상 범위로 규칙 기반 이상치를 찾았다
- IQR로 통계적 이상치를 탐색했다
- 이상치를 무조건 삭제하지 않고 실제 가능성과 분석 목적을 함께 판단했다
CHAPTER 08
그래프 만들기 — seaborn
① 기본 설정 (한글 깨짐 방지)
import seaborn as sns
import matplotlib.pyplot as plt
plt.rc('font', family='Malgun Gothic') # 윈도우 한글 폰트
plt.rc('axes', unicode_minus=False) # 마이너스(-) 깨짐 방지
② 질문에 맞는 그래프 고르기 ★
| 질문 유형 | 그래프 | 코드 |
|---|---|---|
| 두 연속변수의 관계 | 산점도 | sns.scatterplot(data=df, x='displ', y='hwy') |
| 집단별 평균 비교 | 막대 (원자료 또는 요약표) | sns.barplot(data=요약표, x='drv', y='mean_hwy') |
| 집단별 빈도 | 빈도 막대 (원자료) | sns.countplot(data=df, x='category') |
| 시간에 따른 변화 | 선그래프 | sns.lineplot(data=df, x='date2', y='unemploy') |
| 집단별 분포 | 상자그림 | sns.boxplot(data=df, x='drv', y='hwy') |
barplot vs countplot: barplot은
groupby로 평균을 먼저 계산한 표를 넣고, countplot은 원자료 그대로 넣으면 알아서 개수를 셈.③ 꼭 알아야 할 옵션
sns.scatterplot(data=mpg, x='displ', y='hwy', hue='drv') # hue: 범주별 색
sns.scatterplot(data=mpg, x='displ', y='hwy').set(xlim=[1, 7], ylim=[10, 45]) # 축 범위
sns.barplot(data=df, x='drv', y='mean_hwy', order=['4', 'f', 'r']) # 막대 순서
sns.countplot(data=mpg, x='category', order=mpg['category'].value_counts().index) # 빈도순
sns.lineplot(data=economics, x='year', y='unemploy', errorbar=None) # 신뢰구간 음영 제거
④ 평균 막대 + 정렬 패턴 (자주 출제)
df_mean = mpg.groupby('drv', as_index=False).agg(mean_hwy=('hwy', 'mean'))
df_mean = df_mean.sort_values('mean_hwy', ascending=False)
sns.barplot(data=df_mean, x='drv', y='mean_hwy', order=df_mean['drv'])
⑤ 날짜 다루기
economics['date2'] = pd.to_datetime(economics['date']) # 문자 → 날짜형
economics['year'] = economics['date2'].dt.year # .dt.month / .dt.day
date_steps = df.groupby('date2', as_index=False).agg(mean_steps=('steps', 'mean'))
sns.lineplot(data=date_steps, x='date2', y='mean_steps')
⑥ 상자그림(boxplot) 읽는 법
가운데 선
중앙값 (50%)
상자 높이
Q1~Q3 = IQR, 데이터의 퍼짐
수염 밖 점
극단값 후보 (Q1−1.5·IQR, Q3+1.5·IQR 바깥) — 7장 IQR과 같은 개념
CHEAT SHEET
한눈에 보는 함수 · 메서드
| 분류 | 함수 / 메서드 | 장 |
|---|---|---|
| 불러오기 | pd.read_csv() | 5 |
| 파악 | head() tail() shape info() describe() dtypes columns | 5·7 |
| 복사·이름 | copy() rename(columns={}) | 5 |
| 파생변수 | np.where() np.select() value_counts() | 5 |
| 추출·선택 | query() df[[ ]] drop(columns=) iloc[] | 6 |
| 정렬 | sort_values(ascending=) | 6 |
| 요약 | groupby(as_index=False) agg() reset_index() round() | 6 |
| 합치기 | pd.merge(how=, on=) pd.concat() | 6 |
| 결측치 | isna() notna() dropna(subset=) fillna() | 7 |
| 대표값 | mean() median() mode()[0] quantile() | 7 |
| 이상치 | between() replace() ~ np.nan | 7 |
| 그래프 | scatterplot barplot countplot lineplot boxplot | 8 |
| 그래프 옵션 | hue= order= .set(xlim, ylim) errorbar=None | 8 |
| 날짜 | pd.to_datetime() .dt.year/.month/.day | 8 |
5~8장의 문제를 장별로 고르게 섞어 출제합니다. 난이도·유형을 전체로 선택하면 모두 함께 연습할 수 있습니다.
코드 실행 시 인터넷 연결 필요
객관식·O/X와 개념·해설은 오프라인에서도 사용 가능합니다. 코드 실행은 Pyodide를 인터넷에서 불러옵니다. 파일만 복사하면 문제와 데이터도 함께 이동합니다. 기록은 이 브라우저에 저장되며, 다른 PC에서는 내보낸 기록을 불러오세요.