pandas 100제: 판다스 연습문제와 풀이 · 11장
091–100 실전 종합
cut, map, idxmax(axis=1), qcut, cumsum, rolling, pct_change, shift, corr, agg 종합 연습문제
이 장의 목차 13개
마지막 장은 구간 나누기, 누적·이동 평균 같은 실전 기법입니다. 91~94번은 학생 표, 95~98번은 방문자 표를 씁니다.
pd.cut의 구간은 기본적으로 왼쪽 끝은 빼고 오른쪽 끝은 포함합니다. 그래서 199점은 C, 200점은 B, 239점은 B, 240점은 A입니다. 첫 구간의 왼쪽 끝 0은 include_lowest=True가 있어야 포함됩니다. 경계값을 직접 넣어 보며 확인하는 습관을 들이세요.
rolling(3)은 길이 3짜리 창을 한 칸씩 밀면서 그 안의 값으로 계산합니다. 처음 두 날은 값이 3개가 되지 않아 NaN입니다. 창은 "3일"이 아니라 "3행"이므로, 날짜가 빠진 데이터라면 먼저 날짜를 채우거나 rolling("3D")처럼 기간으로 지정해야 합니다.
준비 코드와 데이터
노트북 첫 셀에 아래 준비 코드를 붙여 넣어 실행한 뒤 문제를 푸세요.
Pythonimport pandas as pd
import numpy as np
df = pd.DataFrame({
'name': ['김민준', '이서연', '박지호', '최수아', '정민서', '강도윤', '윤하은', '임시우'],
'class': ['A', 'B', 'A', 'C', 'B', 'C', 'A', 'B'],
'kor': [85, 92, 58, 77, 66, 90, 73, 81],
'eng': [78, 88, 64, 95, 59, 70, 82, 91],
'math': [92, 75, 49, 88, 71, 66, 95, 60],
})
df['total'] = df['kor'] + df['eng'] + df['math']
daily = pd.DataFrame({
'date': pd.date_range('2026-03-01', periods=7, freq='D'),
'visitors': [120, 135, 128, 160, 155, 210, 198],
})
이 장 실습 노트북 내려받기 (.ipynb) — 준비 코드와 문제가 들어 있고 답 칸은 비어 있습니다. 코랩에서는 파일 → 노트 업로드로 엽니다.
준비 코드를 실행하면 df는 다음 표가 됩니다(8행 × 6열, 맨 왼쪽 숫자는 인덱스).
결과 name class kor eng math total
0 김민준 A 85 78 92 255
1 이서연 B 92 88 75 255
2 박지호 A 58 64 49 171
3 최수아 C 77 95 88 260
4 정민서 B 66 59 71 196
5 강도윤 C 90 70 66 226
6 윤하은 A 73 82 95 250
7 임시우 B 81 91 60 232
준비 코드를 실행하면 daily는 다음 표가 됩니다(7행 × 2열, 맨 왼쪽 숫자는 인덱스).
결과 date visitors
0 2026-03-01 120
1 2026-03-02 135
2 2026-03-03 128
3 2026-03-04 160
4 2026-03-05 155
5 2026-03-06 210
6 2026-03-07 198
장 안의 문제는 번호 순서대로 이어집니다. 앞 문제에서 만든 열이나 변수를 다음 문제가 쓸 수 있으니, 중간부터 풀 때는 준비 코드와 앞의 필요한 문제를 먼저 실행하세요.
091. 구간 나누기
0~300점의 정수인 합계를 0~199, 200~239, 240~300 세 구간(C, B, A)으로 나눈 grade 열을 만드세요. 0점도 C에 포함하세요.
힌트 보기
pd.cut(값, bins=[0, 199, 239, 300], labels=[...], include_lowest=True).
정답 보기
Pythondf['grade'] = pd.cut(df['total'], bins=[0, 199, 239, 300], labels=['C', 'B', 'A'], include_lowest=True)
df[['name', 'total', 'grade']]
결과 name total grade
0 김민준 255 A
1 이서연 255 A
2 박지호 171 C
3 최수아 260 A
4 정민서 196 C
5 강도윤 226 B
6 윤하은 250 A
7 임시우 232 B
풀이: include_lowest=True가 첫 경계 0도 포함시킵니다. 각 구간은 오른쪽 끝을 포함하며 범위 밖 값은 NaN입니다.
092. 값 바꿔 끼우기
반 이름을 담임 이름(A: 한선생, B: 오선생, C: 문선생)으로 바꾼 teacher 열을 만드세요.
힌트 보기
map({"A": "한선생", ...})으로 값을 바꿔 끼웁니다.
정답 보기
Pythondf['teacher'] = df['class'].map({'A': '한선생', 'B': '오선생', 'C': '문선생'})
df[['name', 'class', 'teacher']].head(4)
결과 name class teacher
0 김민준 A 한선생
1 이서연 B 오선생
2 박지호 A 한선생
3 최수아 C 문선생
풀이: 딕셔너리의 키와 반 이름을 대응시킵니다. 사전에 없는 반은 NaN이므로 실제 데이터에서는 미등록 값을 확인합니다.
093. 가장 잘한 과목
학생마다 국어·영어·수학 중 점수가 가장 높은 과목의 열 이름을 구하세요. 결과의 인덱스는 학생 이름으로 하세요.
힌트 보기
이름을 인덱스로 두고 과목 세 열만 남긴 뒤 idxmax(axis=1).
정답 보기
Pythondf.set_index('name')[['kor', 'eng', 'math']].idxmax(axis=1)
결과name
김민준 math
이서연 kor
박지호 eng
최수아 eng
정민서 math
강도윤 kor
윤하은 math
임시우 eng
dtype: str
풀이: 세 과목 중 최댓값이 있는 열 이름을 반환합니다. 동점이면 선택한 열 순서상 첫 과목입니다.
axis=1이면 행마다 최댓값의 열 이름을 돌려줍니다.
pandas 2.x에서는 마지막 줄이 dtype: object로 보입니다. 값은 같습니다.
094. 같은 인원으로 나누기
수학 점수를 사분위로 나눠 Q1~Q4를 붙이세요.
힌트 보기
pd.qcut(값, 4, labels=[...])은 인원이 비슷하도록 4등분합니다.
정답 보기
Pythonpd.qcut(df['math'], 4, labels=['Q1', 'Q2', 'Q3', 'Q4'])
결과0 Q4
1 Q3
2 Q1
3 Q3
4 Q2
5 Q2
6 Q4
7 Q1
Name: math, dtype: category
Categories (4, str): ['Q1' < 'Q2' < 'Q3' < 'Q4']
풀이: 이 표에서는 각 구간 두 명입니다. 일반 데이터에서는 동점과 분위수 경계 중복 때문에 같은 인원을 보장하지 않습니다.
cut은 지정한 경계나 같은 폭의 구간으로, qcut은 분위수로 나눕니다. 동점이 많으면 인원이 같지 않거나 경계 중복 오류가 날 수 있습니다.
pandas 2.x에서는 값은 같고 표시만 다음과 같습니다.
결과0 Q4
1 Q3
2 Q1
3 Q3
4 Q2
5 Q2
6 Q4
7 Q1
Name: math, dtype: category
Categories (4, object): ['Q1' < 'Q2' < 'Q3' < 'Q4']095. 누적 합계
daily의 날짜별 누적 방문자 수를 구하세요.
힌트 보기
cumsum().
정답 보기
Pythondaily['visitors'].cumsum()
결과0 120
1 255
2 383
3 543
4 698
5 908
6 1106
Name: visitors, dtype: int64
풀이: 현재 행 순서대로 누적합니다. 날짜가 뒤섞였다면 먼저 날짜순으로 정렬해야 합니다.
096. 이동 평균
daily의 3일 이동 평균 방문자 수를 소수 첫째 자리까지 구하세요.
힌트 보기
rolling(3).mean() 뒤에 .round(1).
정답 보기
Pythondaily['visitors'].rolling(3).mean().round(1)
결과0 NaN
1 NaN
2 127.7
3 141.0
4 147.7
5 175.0
6 187.7
Name: visitors, dtype: float64
풀이: 3은 여기서 세 행을 뜻합니다. 하루 한 행이고 빠진 날짜가 없어 3일 평균으로 해석할 수 있습니다.
앞 2일은 값이 모자라 NaN입니다.
097. 전날 대비 증감률
전날 대비 방문자 증감률(%)을 소수 첫째 자리까지 구하세요.
힌트 보기
pct_change()는 비율(0.125)을 줍니다. 100을 곱해 %로 바꾸세요.
정답 보기
Python(daily['visitors'].pct_change() * 100).round(1)
결과0 NaN
1 12.5
2 -5.2
3 25.0
4 -3.1
5 35.5
6 -5.7
Name: visitors, dtype: float64
풀이: pct_change는 비율을 반환하므로 100을 곱해 퍼센트로 바꿉니다. 이전 값이 없으면 NaN, 이전 값이 0이면 무한대가 될 수 있습니다.
098. 전날 값 옆에 놓기
전날 방문자 수 열 prev와 차이 diff를 만드세요.
힌트 보기
shift(1)은 한 칸 아래로 밉니다. 그 열과의 차이를 diff로 만듭니다.
정답 보기
Pythondaily['prev'] = daily['visitors'].shift(1)
daily['diff'] = daily['visitors'] - daily['prev']
daily
결과 date visitors prev diff
0 2026-03-01 120 NaN NaN
1 2026-03-02 135 120.0 15.0
2 2026-03-03 128 135.0 -7.0
3 2026-03-04 160 128.0 32.0
4 2026-03-05 155 160.0 -5.0
5 2026-03-06 210 155.0 55.0
6 2026-03-07 198 210.0 -12.0
풀이: shift(1)은 행 하나 전 값을 같은 행에 맞춰 놓습니다. 날짜를 하루 이동시키는 것이 아니므로 날짜 정렬과 누락 여부를 확인합니다.
099. 과목 간 상관관계
세 과목 점수의 상관계수 표를 구하세요.
힌트 보기
세 과목 열을 고른 뒤 corr().
정답 보기
Pythondf[['kor', 'eng', 'math']].corr().round(2)
결과 kor eng math
kor 1.00 0.52 0.31
eng 0.52 1.00 0.42
math 0.31 0.42 1.00
풀이: 숫자 열 사이의 선형 상관을 계산합니다. 표본이 작고 상관만으로 인과관계를 설명할 수는 없습니다.
100. 종합: 반별 리포트
반별 학생 수, 평균 합계(소수 첫째 자리), 최고 득점자 이름을 한 표로 만들고 평균 합계가 높은 반부터 보이세요.
힌트 보기
groupby("class").agg(학생수=(...), 평균합계=(...), 최고득점자=("total", lambda s: ...)) — 람다 안에서 s.idxmax()로 최고점 학생의 행 라벨을 얻습니다.
정답 보기
Pythonreport = df.groupby('class').agg(
학생수=('name', 'size'),
평균합계=('total', 'mean'),
최고득점자=('total', lambda s: df.loc[s.idxmax(), 'name']),
)
report['평균합계'] = report['평균합계'].round(1)
report.sort_values('평균합계', ascending=False)
결과 학생수 평균합계 최고득점자
class
C 2 243.0 최수아
B 3 227.7 이서연
A 3 225.3 김민준
풀이: 그룹별 집계와 최고 합계의 이름을 결합합니다. 최고점 동점은 원래 표의 첫 학생이며 반올림한 평균을 기준으로 정렬합니다.
집계 함수 자리에 람다를 넣으면 원하는 계산을 그룹마다 할 수 있습니다.
이 장 요약
| 문법 | 하는 일 | 문제 |
|---|---|---|
pd.cut(s, bins=, labels=, include_lowest=True) | 경계값으로 구간 | 091 |
s.map({옛값: 새값}) | 값 바꿔 끼우기 | 092 |
df.idxmax(axis=1) | 행마다 최댓값의 열 이름 | 093 |
pd.qcut(s, 4, labels=) | 분위수로 구간 | 094 |
s.cumsum() | 누적 합계 | 095 |
s.rolling(3).mean() | 이동 평균 | 096 |
s.pct_change() | 직전 대비 변화율 | 097 |
s.shift(1) | 한 칸 밀기(전날 값) | 098 |
df.corr() | 상관계수 표 | 099 |
groupby().agg(이름=(열, 함수)) | 종합 리포트 | 100 |
이 장의 점검
점수 경계 0·199·200·239·240·300과 동점, 날짜 누락을 따로 확인하세요. 예제에서 실행됐다는 사실만으로 다른 데이터의 조건까지 보장되지는 않습니다.
참고: pandas 공식 문서 · 문법이 기억나지 않으면 부록 치트시트를 찾아보세요.