본문으로 건너뛰기

pandas 100제: 판다스 연습문제와 풀이 · 12장

보너스 V01–V10 pandas 시각화: 선·막대·히스토그램·산점도

pandas plot으로 선 그래프, 막대그래프, 히스토그램, 상자그림, 산점도, 누적 막대, 이동 평균, subplots·savefig 그리기 연습문제

13분 읽기

이 장의 목차 13개

표로 구한 결과를 그래프로 그려 보는 보너스 장입니다. 앞 장에서 쓴 학생 성적표(df), 카페 매출(sales), 일별 방문자(daily)를 그대로 다시 씁니다. pandas의 plot()은 내부에서 matplotlib을 부르므로 import matplotlib.pyplot as plt 한 줄만 더하면 됩니다.

어떤 그래프를 고를까

보여 주고 싶은 것그래프pandas 코드문제
시간에 따른 변화선 그래프plot() / kind="line"V01, V09
항목끼리 크기 비교막대그래프kind="bar", kind="barh"V02, V03
값이 어디에 몰려 있나(분포)히스토그램, 상자그림kind="hist", kind="box"V04, V05
두 값의 관계산점도kind="scatter"V06
두 기준의 비교묶은 막대피벗 표 + kind="bar"V07
전체 중 구성누적 막대kind="bar", stacked=TrueV08
여러 그래프를 한 장에subplotsplt.subplots() + ax=V10

준비 코드

노트북 첫 셀에 아래 코드를 실행합니다. 첫 줄의 !pip는 코랩·주피터에서 패키지를 설치하는 명령으로, 같은 런타임에서는 한 번만 실행하면 됩니다.

Python!pip install -q koreanize-matplotlib setuptools   # 처음 한 번 설치 (터미널에서는 ! 없이)
import pandas as pd
import matplotlib.pyplot as plt
import koreanize_matplotlib            # 그래프 한글 깨짐 방지

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']

sales = pd.DataFrame({
    'date': ['2026-03-01', '2026-03-01', '2026-03-02', '2026-03-02',
             '2026-03-03', '2026-03-03', '2026-03-04', '2026-03-04'],
    'store': ['강남', '홍대', '강남', '홍대', '강남', '홍대', '강남', '홍대'],
    'menu': ['아메리카노', '라떼', '라떼', '아메리카노', '아메리카노', '케이크', '케이크', '라떼'],
    'qty': [30, 12, 18, 25, 28, 7, 9, 15],
    'price': [4500, 5000, 5000, 4500, 4500, 6500, 6500, 5000],
})
sales['revenue'] = sales['qty'] * sales['price']

daily = pd.DataFrame({
    'date': pd.date_range('2026-03-01', periods=7, freq='D'),
    'visitors': [120, 135, 128, 160, 155, 210, 198],
})

보너스 실습 노트북 내려받기 (.ipynb) — 코랩에서는 파일 → 노트 업로드로 엽니다.

책의 그래프는 아래 코드를 실제로 실행해 만든 그림입니다. 화면 크기나 글꼴에 따라 모양이 조금 달라도 내용이 같으면 정답입니다.

V01. 선 그래프

daily의 날짜별 방문자 수를 점이 찍힌 선 그래프로 그리세요. 제목은 일별 방문자 수로 합니다.

힌트 보기

DataFrame.plot(x="열", y="열")에 marker와 title을 더합니다.

정답 보기
Pythondaily.plot(x='date', y='visitors', marker='o', title='일별 방문자 수')
plt.show()
V01 실행 결과: 선 그래프
V01 실행 결과: 선 그래프

풀이: x에 가로축 열, y에 세로축 열을 지정합니다. marker="o"는 값마다 점을 찍어 실제 관측치가 어디인지 보이게 합니다. plt.show()는 그래프를 화면에 내보내는 명령으로, 노트북에서는 생략해도 보이지만 스크립트에서는 꼭 필요합니다.

V02. 막대그래프

매장별 매출 합계를 막대그래프로 그리세요. 가로축 글자는 눕히지 말고 똑바로 세우세요.

힌트 보기

groupby로 합계를 낸 Series에 .plot(kind="bar"), 글자 각도는 rot=0.

정답 보기
Pythonstore_revenue = sales.groupby('store')['revenue'].sum()
store_revenue.plot(kind='bar', title='매장별 매출 합계', rot=0)
plt.ylabel('매출(원)')
plt.show()
V02 실행 결과: 막대그래프
V02 실행 결과: 막대그래프

풀이: 071번의 집계 결과(Series)에 plot(kind="bar")를 붙이면 인덱스가 막대 이름, 값이 막대 높이가 됩니다. rot=0은 가로축 글자를 0도로 세웁니다. 세로축 단위를 plt.ylabel로 적어 두면 읽는 사람이 헷갈리지 않습니다.

V03. 정렬한 가로 막대

메뉴별 총매출을 가로 막대그래프로 그리되, 매출이 가장 큰 메뉴가 맨 위에 오게 하세요.

힌트 보기

가로 막대는 kind="barh"입니다. 막대가 아래에서부터 그려진다는 점을 생각해 정렬 방향을 정하세요.

정답 보기
Pythonmenu_revenue = sales.groupby('menu')['revenue'].sum().sort_values()
menu_revenue.plot(kind='barh', title='메뉴별 총매출')
plt.xlabel('매출(원)')
plt.show()
V03 실행 결과: 정렬한 가로 막대
V03 실행 결과: 정렬한 가로 막대

풀이: 가로 막대(barh)는 아래에서 위로 그려집니다. 그래서 오름차순으로 정렬해야 가장 큰 막대가 맨 위에 옵니다. 항목 이름이 길거나 항목이 많을 때는 세로 막대보다 가로 막대가 읽기 좋습니다.

V04. 히스토그램

수학 점수의 분포를 히스토그램으로 그리세요. 40점부터 100점까지 10점 간격(6개 구간)으로 나눕니다.

힌트 보기

kind="hist"에 bins=range(40, 101, 10).

정답 보기
Pythondf['math'].plot(kind='hist', bins=range(40, 101, 10), edgecolor='white', title='수학 점수 분포')
plt.xlabel('점수')
plt.ylabel('학생 수')
plt.show()
V04 실행 결과: 히스토그램
V04 실행 결과: 히스토그램

풀이: bins에 경계 목록(40, 50, …, 100)을 주면 구간 폭이 일정해 해석하기 쉽습니다. 막대 높이는 그 구간에 든 학생 수입니다. edgecolor="white"는 막대 사이에 경계선을 넣어 구간을 구분해 줍니다. 학생이 8명뿐이라 모양보다 "어느 구간에 몇 명"을 읽는 연습으로 보세요.

V05. 상자그림

국어·영어·수학 점수 분포를 과목별 상자그림 하나에 나란히 그리세요.

힌트 보기

세 과목 열을 고른 DataFrame에 .plot(kind="box").

정답 보기
Pythondf[['kor', 'eng', 'math']].plot(kind='box', title='과목별 점수 분포')
plt.ylabel('점수')
plt.show()
V05 실행 결과: 상자그림
V05 실행 결과: 상자그림

풀이: 상자그림은 015번 describe()의 그림판입니다. 상자의 아래·위 끝이 25%·75%, 가운데 선이 중앙값(50%)이고, 수염은 상자 높이의 1.5배 안에 드는 가장 먼 값까지 그립니다. 그 밖의 값은 점(이상치)으로 따로 표시됩니다.

V06. 산점도

국어 점수(가로)와 수학 점수(세로)의 관계를 산점도로 그리고, 제목에 두 과목의 상관계수를 소수 둘째 자리까지 넣으세요.

힌트 보기

상관계수는 df["kor"].corr(df["math"]), 그래프는 kind="scatter", x=, y=. 제목에는 f-문자열을 씁니다.

정답 보기
Pythonr = df['kor'].corr(df['math'])
df.plot(kind='scatter', x='kor', y='math', title=f'국어와 수학 (상관계수 {r:.2f})')
plt.show()
V06 실행 결과: 산점도
V06 실행 결과: 산점도

풀이: 점 하나가 학생 한 명입니다. 점들이 오른쪽 위로 몰리면 양의 상관, 오른쪽 아래로 흩어지면 음의 상관입니다. f"...{r:.2f}"는 계산한 값을 소수 둘째 자리 문자열로 제목에 끼워 넣는 f-문자열입니다. 상관은 인과가 아니라는 점(099번)도 함께 기억하세요.

V07. 묶은 막대

메뉴(가로축)별로 매장의 판매 수량을 나란히 비교하는 묶은 막대그래프를 그리세요.

힌트 보기

먼저 pivot_table(index="menu", columns="store", ...)로 표를 만든 뒤 kind="bar".

정답 보기
Pythonqty_table = sales.pivot_table(index='menu', columns='store', values='qty', aggfunc='sum')
qty_table.plot(kind='bar', rot=0, title='메뉴별 매장 판매 수량')
plt.ylabel('수량(잔·개)')
plt.show()
V07 실행 결과: 묶은 막대
V07 실행 결과: 묶은 막대

풀이: 먼저 076번처럼 피벗 표를 만들면, 인덱스가 가로축 묶음, 열이 막대 색(범례)이 됩니다. 그래프 모양이 마음에 들지 않으면 그래프 코드를 고치기보다 표의 행과 열을 바꾸는 편이 빠릅니다(qty_table.T.plot(...)).

V08. 누적 막대

날짜별 매출을 매장별로 쌓은 누적 막대그래프를 그리세요.

힌트 보기

날짜 × 매장 피벗 표에 kind="bar", stacked=True.

정답 보기
Pythondaily_store = sales.pivot_table(index='date', columns='store', values='revenue', aggfunc='sum')
daily_store.plot(kind='bar', stacked=True, rot=0, title='날짜별 매장 매출')
plt.ylabel('매출(원)')
plt.show()
V08 실행 결과: 누적 막대
V08 실행 결과: 누적 막대

풀이: stacked=True를 주면 같은 날짜의 매장 매출이 위로 쌓여 막대 전체 높이가 그날의 총매출이 됩니다. 전체 크기와 구성을 함께 보여 줄 때 쓰지만, 위쪽에 쌓인 항목끼리는 기준선이 달라 비교하기 어렵다는 단점도 있습니다.

V09. 실제 값과 이동 평균

방문자 수와 3일 이동 평균(096번)을 한 그래프에 함께 그리세요. 이동 평균은 점선으로 구분합니다.

힌트 보기

rolling(3).mean()으로 새 열을 만들고, plot(x=, y=[두 열], style=[...]).

정답 보기
Pythondaily['ma3'] = daily['visitors'].rolling(3).mean()
daily.plot(x='date', y=['visitors', 'ma3'], style=['o-', '--'], title='방문자 수와 3일 이동 평균')
plt.show()
V09 실행 결과: 실제 값과 이동 평균
V09 실행 결과: 실제 값과 이동 평균

풀이: y에 열 이름 리스트를 주면 여러 선을 한 번에 그립니다. style은 선마다 모양을 정하는데, "o-"는 점과 실선, "--"는 점선입니다. 이동 평균은 하루하루의 출렁임을 눌러 흐름을 보여 줍니다. 처음 두 날은 값이 없어 선이 3일째부터 시작합니다.

V10. 여러 그래프를 한 장에 저장

왼쪽에는 매장별 매출 막대(V02), 오른쪽에는 방문자 선 그래프(V01)를 나란히 놓은 그림을 만들고 report.png 파일로 저장하세요.

힌트 보기

fig, axes = plt.subplots(1, 2)로 칸을 만들고, 각 plot에 ax=axes[0]처럼 자리를 지정합니다. 저장은 fig.savefig(...).

정답 보기
Pythonfig, axes = plt.subplots(1, 2, figsize=(10, 4))
sales.groupby('store')['revenue'].sum().plot(kind='bar', rot=0, ax=axes[0], title='매장별 매출')
daily.plot(x='date', y='visitors', marker='o', ax=axes[1], title='일별 방문자', legend=False)
fig.tight_layout()
fig.savefig('report.png', dpi=150)
plt.show()
V10 실행 결과: 여러 그래프를 한 장에 저장
V10 실행 결과: 여러 그래프를 한 장에 저장

풀이: plt.subplots(1, 2)는 1행 2열의 그래프 칸(axes)을 만듭니다. pandas plot에 ax=axes[0]처럼 칸을 지정하면 그 자리에 그립니다. tight_layout()은 제목·축 글자가 겹치지 않게 간격을 맞추고, savefig는 파일로 저장합니다. 코랩에서는 왼쪽 파일 탭에 report.png가 생기며, 런타임이 끝나면 사라지니 내려받아 두세요.

이 장의 점검

그래프마다 "이 그래프로 무엇을 비교하려는가"를 한 문장으로 말해 보세요. 그 문장이 비교·추세·분포·관계·구성 중 무엇인지 정하면 그래프 종류가 정해집니다. 제목과 축 단위가 없는 그래프는 남이 읽을 수 없다는 것도 확인하세요.

참고: pandas 시각화 공식 안내 · matplotlib 공식 문서

전체 메뉴