pandas · matplotlib · seaborn · 논문 Figure · robots.txt · 판례
박수현 · aSSIST University · 2026
🎨 이미지 프롬프트(배경): "A balanced scale with data visualization charts on one side and a legal book with gavel on the other, soft teal and red lighting, editorial illustration, no text, 16:9"
robots.txt = 사이트 루트의 텍스트 파일, 봇에게 어디까지 접근 허용할지 알려주는 약속 — 법적 강제력 ❌, 그러나 무시 = 차단·소송 빌미
📖 도서 매핑: Ch.6 §1~3 + Ch.9 §1~2 + Ch.4 §6
엑셀의 100배 빠른 일을 한 줄로
키워드: DataFrame · groupby · merge · pivot · 결측치
| 작업 | 엑셀 | pandas |
|---|---|---|
| 1만 행 정렬 | 5초 | < 1ms |
| 그룹별 평균 | 피벗 4클릭 | groupby 1줄 |
| 두 표 합치기 | VLOOKUP 수동 | merge 1줄 |
| 자동화 | 매크로 | 함수 |
| 재현성 | 수동 단계 | 코드로 기록 |

import pandas as pd
df = pd.read_csv("my_books.csv")
# 1) FILTER
df[df["rating"] >= 4]
df.query("price > 30 and rating >= 3")
# 2) SELECT
df[["title", "price"]]
# 3) GROUPBY
df.groupby("rating").agg(
n=("title", "count"),
avg_price=("price", "mean"),
std_price=("price", "std"),
)
# 4) SORT
df.sort_values("price",
ascending=False).head(10)
# 5) MERGE
df.merge(other_df,
on="title", how="left")

🎯 이 5개로 연구 데이터 80% 처리.
💬 Antigravity / ChatGPT 입력 analyze_books.py
📋 prompt prompts/ch06_prompts.md 📄 example examples/ch06/analyze_books.py
Python 스크립트 analyze_books.py 를 만들어 줘.
목적: 현재 디렉토리의 books.csv (컬럼: title, price,
rating, link; price 는 "£12.34" 같은 문자열)를
pandas 로 로드하고 구조/분포를 간단히 요약한다.
요구사항:
1. 라이브러리: pandas>=2.2
2. 파일 읽기: pd.read_csv("books.csv",
encoding="utf-8-sig")
3. 단계별 동작:
1) print(df.shape, df.dtypes) 로 구조 확인
2) 가격 문자열에서 숫자만 추출:
df["price_num"] = df["price"].str.replace(
r"[^\d.]", "", regex=True).astype(float)
3) df["rating"].value_counts().sort_index() 로
평점 분포 출력
4) df["price_num"].describe() 로 가격 분포 요약
4. 출력은 모두 print 로 — 터미널에서 바로 확인 가능
5. 에러 처리:
- books.csv 가 없으면 안내 후 종료
- price 변환 시 errors="coerce" 금지,
실패 시 예외 출력
6. 0행이면 "데이터가 비어 있습니다." 후 종료
환경: Python 3.11, venv. 코드만, 한국어 주석 최소.
analyze_books.py) → 단일 산출물price 는 "£12.34" 같은 문자열 ← 이 한 줄이 AI 의 가격 파싱 코드를 정확하게 만듦💡 나만의 데이터에 적용 하려면:
"books.csv" → 본인 CSV 경로,
"rating" / "price" → 본인 컬럼명,
분포 종류는 그대로 두면 자동 적응.
📖 출처:
ch06_prompts.md§13 — 실습 미니 프로젝트.
IQR (Inter-Quartile Range) = Q3(75% 분위수) − Q1(25% 분위수). Tukey 의 1.5×IQR 규칙: Q1 − 1.5·IQR 미만 또는 Q3 + 1.5·IQR 초과 = 이상치 후보.
# 1) 결측치 (NaN)
df.isna().sum()
df = df.dropna(subset=["price"])
df["stock"] = df["stock"].fillna("Unknown")
# 2) 중복
df.duplicated().sum()
df = df.drop_duplicates(subset=["title"])
# 3) 이상치 (IQR)
q1, q3 = df["price"].quantile([0.25, 0.75])
iqr = q3 - q1
mask = (df["price"] >= q1 - 1.5*iqr) & \
(df["price"] <= q3 + 1.5*iqr)
df_clean = df[mask]
| 방법 | 사용 시점 |
|---|---|
dropna() |
데이터 충분, 결측 적음 |
fillna(0) |
0이 의미 있을 때 |
fillna(mean) |
분포 보존 |
interpolate() |
시계열 |
# 별점별 가격 통계
agg = df.groupby("rating").agg(
count=("title", "size"),
avg=("price", "mean"),
median=("price", "median"),
std=("price", "std"),
).round(2)
print(agg)
# count avg median std
# rating
# 1 50 35.21 34.80 9.43
# 2 ...
# pivot_table — 2차원 교차
df.pivot_table(
index="rating", columns="in_stock",
values="price", aggfunc="mean",
)
📌 groupby = 한 키로 묶어 집계 / pivot_table =
groupby+ 행/열 재배치 (멀티 인덱스도 가능).
한 그림에 한 메시지
키워드: Figure · Axes · 컬러팔레트 · 300 DPI
| 별점 | 평균가(£) | 표본(n) |
|---|---|---|
| 1 | 35.21 | 50 |
| 2 | 38.45 | 100 |
| 3 | 41.30 | 250 |
| 4 | 43.10 | 300 |
| 5 | 47.88 | 200 |
£ = 영국 파운드 (books.toscrape 데이터)
표는 정확한 값을 본다. 학회 발표에서는 차트가 먼저.


import matplotlib.pyplot as plt
import seaborn as sns
sns.set_theme(style="whitegrid", palette="colorblind") # 톤
fig, ax = plt.subplots(figsize=(7, 4))
sns.barplot(data=df, x="rating", y="price", ax=ax) # seaborn
ax.set_title("Average Price by Rating") # matplotlib
fig.savefig("fig1.png", dpi=300, bbox_inches="tight") # matplotlib
🎯 seaborn 으로 그리고, matplotlib 으로 다듬는다.
KDE (Kernel Density Estimation, 커널 밀도 추정) = 히스토그램에 곡선을 덧씌워 분포를 부드럽게 추정.
cmap="vlag"= 양/음 발산 컬러맵 (상관계수 ±부호 시각화에 적합),center=0= 0 을 중간 색으로.
# 1) 막대
sns.barplot(data=df, x="rating",
y="price", errorbar="sd")
# 2) 박스
sns.boxplot(data=df, x="rating", y="price")
# 3) 히스토그램
sns.histplot(df["price"], bins=30, kde=True)
# 4) 산점도
sns.scatterplot(data=df, x="price",
y="reviews", hue="rating")
# 5) 히트맵 — 상관관계
sns.heatmap(
df[["price","rating","reviews"]].corr(),
annot=True, cmap="vlag", center=0)

📌 5종으로 학위논문의 90% 커버.
📊 막대

📊 히스토그램

📈 박스플롯

📈 산점도

🎯 시각적 선택의 기준 = 무엇을 보여주고 싶은가.
💬 Antigravity / ChatGPT 입력 books_summary.py
📋 prompt prompts/ch09_prompts.md 📄 example examples/ch09/books_summary.py
Python 스크립트 books_summary.py 를 만들어 줘.
목적: books.csv 에서 파생 변수 price_num 을 만들고
2x2 서브플롯으로 한 장짜리 데이터 요약 차트를
PNG 로 저장한다.
요구사항:
1. 라이브러리: pandas, matplotlib, seaborn.
한글 폰트는 fontconf.setup_korean_font() 호출.
2. sns.set_theme(style="whitegrid") 적용.
3. 데이터 로드: pd.read_csv("books.csv",
encoding="utf-8-sig"). price_num 파생.
4. fig, axes = plt.subplots(2, 2, figsize=(13, 9))
5. 서브플롯 4장:
- axes[0,0] 평점 분포 막대 (#00b894)
- axes[0,1] 가격 히스토그램, bins=30 (#6c5ce7)
- axes[1,0] 평점별 가격 박스플롯 (palette="Set2")
- axes[1,1] 가격 vs 평점 regplot (#e17055)
6. plt.suptitle("books.toscrape.com · 1000권 요약")
+ plt.tight_layout()
7. plt.savefig("books_summary.png", dpi=180,
bbox_inches="tight")
에러 처리:
- books.csv 가 없거나 0행이면 안내 후 종료
- 한글 폰트 설정 실패 시 경고만 출력하고 진행
환경: Python 3.11, venv. 코드만, 한국어 주석 최소.
2x2 서브플롯, figsize=(13, 9)dpi=180, bbox_inches="tight") 까지 명시💡 연구용 응용 — 본인 분석 변수에 맞춰
"평점→온도", "가격→측정값" 으로
치환하면 즉시 본인 논문 Figure 1 로 사용 가능.
📖 출처:
ch09_prompts.md§10 — 실습 books.csv 한 장 요약.

sns.heatmap(df.corr(numeric_only=True),
annot=True, cmap="vlag", center=0)

# 가격 vs 리뷰 수 — 두 연속형 변수의 회귀선 + 95% 신뢰구간
sns.regplot(data=df,
x="price", y="reviews",
scatter_kws={"alpha":0.4})
📌 별점(rating, 1~5 정수)처럼 범주형 변수는
regplot보다lmplot+x_jitter권장 — 회귀선의 통계적 의미가 살아남.
📖 도서 참조: Ch.9 §6 — "seaborn 통계 분석 한 줄로"
| # | 원칙 | 코드 단서 |
|---|---|---|
| 1 | 300 DPI 이상 | dpi=300 |
| 2 | 벡터 PDF/SVG (확대해도 안 깨짐) | savefig("fig.pdf") |
| 3 | 8.5~17 cm 너비 (대다수 학술지: 단일 컬럼 ≈ 8.5cm, 더블 컬럼 ≈ 17cm) | figsize=(3.5, 2.5) |
| 4 | 글꼴 8~10pt | font.size=10 |
| 5 | colorblind 팔레트 | palette="colorblind" |
| 6 | 축 라벨·단위 | set_xlabel("Price (£)") |
| 7 | 여백 자동 트림 | bbox_inches="tight" |
plt.rcParams.update({
"font.size": 10,
"axes.titlesize": 11,
"axes.labelsize": 10,
"savefig.dpi": 300,
"savefig.bbox": "tight",
"font.family": "sans-serif",
})
import platform, matplotlib.pyplot as plt
from matplotlib import font_manager
if platform.system() == "Darwin": # macOS
plt.rcParams["font.family"] = "AppleGothic"
elif platform.system() == "Windows":
plt.rcParams["font.family"] = "Malgun Gothic"
else: # Linux/Colab
# NanumGothic 설치: !apt-get install fonts-nanum
plt.rcParams["font.family"] = "NanumGothic"
plt.rcParams["axes.unicode_minus"] = False # 음수 깨짐 방지
□ □ □ (영문권에선 tofu — 폰트에 해당 글리프가 없을 때 표시되는 사각형) → 폰트 미설정− 음수 부호 깨짐 → unicode_minus=False🎯 한 번 설정 → 모든 차트 적용.
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# 0) 폰트·테마
sns.set_theme(style="whitegrid", palette="colorblind",
font_scale=1.0,
rc={"axes.spines.top": False,
"axes.spines.right": False})
# 1) 로드
df = pd.read_csv("my_books.csv")
# 2) 차트
fig, ax = plt.subplots(figsize=(7, 4))
sns.barplot(data=df, x="rating", y="price", ax=ax,
errorbar=("ci", 95))
ax.set_title("Average book price by star rating", fontsize=11)
ax.set_xlabel("Star rating")
ax.set_ylabel("Price (£)")
# 3) 저장
fig.savefig("fig_price_by_rating.pdf", dpi=300, bbox_inches="tight")
fig.savefig("fig_price_by_rating.png", dpi=300, bbox_inches="tight")
robots.txt한 줄을 무시한 대가는 수억 원의 손해배상 — 야놀자·여기어때 사건에서 실제로 일어난 일입니다.
키워드: 법적 효력 · 이용약관 · 손해배상 · 판례

A
🟢 안전
robots.txt 준수
공개 · 합리적 빈도
B
🟡 회색
robots.txt 무시
but 공개 데이터
C
🟠 약관 위반
로그인·약관 명시
(민사 손해배상)
D
🔴 업무방해
과도한 요청
(형사 + 민사)
E
⚫ PII 위반
개인정보 수집
(형사·과징금)
| 단계 | 위험 |
|---|---|
| 🟢 A | 안전 — 사이트 의사 존중 + 합리적 빈도 |
| 🟡 B | 회색지대 — 법적 책임 약함, 도덕적 비난·차단 가능 |
| 🟠 C | 약관 위반 (민사 손해배상 — 한국 판례에서 약관 위반 단독은 형사 무죄가 일반적) |
| 🔴 D | 업무방해 — 형법 §314 (컴퓨터등업무방해) + 민사 |
| ⚫ E | 개인정보보호법 위반 (형사·과징금) |
⚠️ 연구라도 C/D/E 영역은 IRB·법자문 필수.
| 판례 | 결과 | 주요 메시지 |
|---|---|---|
| 🇺🇸 HiQ vs LinkedIn | 합의 종결, 사업 종료 | CFAA (Computer Fraud and Abuse Act, 미국 컴퓨터사기남용법) 면해도 약관·계약은 살아있다 |
| 🇰🇷 잡코리아 vs 사람인 (2018) | 민사 합의 — 약 120억 원 | 체계적·반복 크롤링 = 명백한 위법 |
| 🇰🇷 야놀자 vs 여기어때 (2022) | 형사 무죄 / 민사 10억 | 형사 면해도 민사는 못 피함 |
| 🇰🇷 네이버 vs 다윈중개 (2024) | 7~8천만 원 배상 | DB 제작자 권리(§93) 적극 보호 |
🔑 시각화·분석 단계에서도 같은 원칙
수집 단계의 윤리 가 분석·시각화·재배포 모든 단계에 그대로 작동.
데이터셋을 공개 배포 할 때 잡코리아·다윈중개 판례가 그대로 적용 가능.📚 상세 사실관계·출처 링크 → 회차 0(OT) PART C 참고
| 법·조항 | 핵심 (웹스크래핑 맥락) |
|---|---|
| 저작권법 제93조 (데이터베이스권) | 사이트가 모아둔 DB 자체를 무단 복제·배포하면 위반 — 크롤링에서 가장 자주 걸리는 조항 |
| 저작권법 제35조의5 (공정이용) | 비영리·교육·연구는 일부 허용 — 단 4요소(목적·성격·양·시장 영향) 종합 판단 |
| 부정경쟁방지법 제2조 (성과 도용) | 남이 노력해 만든 결과물 무임승차 금지 — 최근 판례 다수 |
| 정보통신망법 제48조 (정보통신망 침해행위 등의 금지) | 허락 없는 정보통신망 침입·장애 — 과도 크롤링이 여기 해당 가능 (형사 처벌 영역) |
| 개인정보보호법 제15·17조 | §15 수집 시 본인 동의 / §17 제3자 제공 시 별도 동의 |
저작권법 §35의5 (공정이용) — 비영리·교육·연구 목적이면 일부 허용.
단, 4가지 요소 종합 판단 (목적, 성격, 양, 시장 영향).⚠️ "연구라서 괜찮다" 만으로는 방어 불충분. IRB + 법자문 권장.
GDPR = General Data Protection Regulation (EU 일반 데이터 보호 규정, 2018)
📌 회차 7 에서 한 시간 분량으로 자세히 다룸.
🎯 8개 중 1개라도 빨강 이면 → 멈추고 자문.
| # | 시나리오 | 의견? |
|---|---|---|
| 1 | 본인 학위논문용으로 네이버 영화 별점 1만 건 수집 | 🟢 🟡 🔴 ? |
| 2 | 같은 데이터를 GitHub 에 공개 데이터셋 으로 배포 | 🟢 🟡 🔴 ? |
| 3 | 인스타그램 공개 게시물의 댓글 수집 (로그인 X) | 🟢 🟡 🔴 ? |
| 4 | 동일 데이터를 LLM 훈련 데이터 로 사용 | 🟢 🟡 🔴 ? |
| 5 | 정부 공공데이터포털 API 로 5만 건 다운 | 🟢 🟡 🔴 ? |
30줄로 다듬은 PDF 1장
my_books.csv 또는 본인 수집 데이터df.describe() 확인figures/ 폴더에 PNG + PDF 6개 + viz.pyunicode_minus=False"예쁜 그림 + 단단한 논거 = 게재 통과의 절반."