[Zero to ML Master #41] 판다스(Pandas) 입문 — Series·DataFrame 만들기, CSV·엑셀 읽고 쓰기, loc·iloc·조건 필터까지 (02권 8부 1)

오톨로리즘·1일 전
post-thumbnail

["165", "158"]을 더했더니 323이 아니라 "165158"이 나오고, 이름 리스트와 점수 리스트가 한 칸 어긋나 다른 사람의 성적표가 되어 버립니다. 😱 무서운 건 이 모든 사고가 에러 하나 없이 조용히 일어난다는 점이에요.

머신러닝은 데이터로 배우는 기계이고, 세상의 데이터는 거의 전부 표로 들어옵니다. 그래서 02권 8부에서는 표를 정확하게 다루는 표준 도구, 판다스(Pandas)를 배웁니다. 이번 1부에서는 엑셀 시트에 빗대어 Series·DataFrame을 만들고, CSV·엑셀 파일을 읽고 쓰며 한글 인코딩 문제를 풀고, shape·info()·describe()로 데이터를 건강검진한 뒤, loc·iloc·조건 필터로 원하는 칸을 골라 행·열을 고치는 데까지 갑니다.

📘 이 글은 위키독스 도서 Zero to 머신러닝 딥러닝 Master 의 02-08 🐼 8부 — Pandas: 파이썬으로 쓰는 엑셀 (1부) 을 블로그용으로 다듬은 글입니다.
💻 실습 코드: GitHub · 🏠 mldict.net · ✍️ 기술 블로그

💻 이 글의 코드: 02-08-pandas · Open in Colab — 핵심 코드와 실제 실행 결과는 글 아래 💻 GitHub 실습 섹션에 있습니다.

⬅️ 이전 글: #40 NumPy 배열 입문 — shape·axis·브로드캐스트와 * vs @ 행렬 곱, for문보다 빠른 벡터화까지 (02권 7부) \
➡️ 다음 글: #42 판다스(Pandas) 2부 — 결측치·정렬·중복, 통계·문자열 처리, groupby·피벗·merge부터 공공데이터 전기차 분석까지 (02권 8부 2)

TL;DR — 3줄 요약

  • 판다스의 두 자료구조는 Series(엑셀의 열 하나 = 값 + 이름표)와 DataFrame(시트 한 장 = 같은 index를 공유하는 Series 묶음)입니다. 판다스의 모든 계산은 위치가 아니라 이름표를 맞춰서 일어나므로, s1 + s2에서 짝이 없는 이름표는 NaN이 됩니다.
  • to_csv는 기본으로 index까지 저장하니 index=False를 쓰고, 윈도우 엑셀용 한글 파일은 cp949(또는 utf-8-sig)로 맞춥니다. 새 데이터는 shape·info()·describe()·head()로 크기·자료형·빈칸·요약 통계부터 확인합니다.
  • loc은 이름표로 고르고 슬라이싱 끝을 포함하며, iloc은 위치로 고르고 끝을 뺍니다. 조건 필터는 df[(조건1) & (조건2)]처럼 조건마다 괄호가 필수이고, 행·열은 concat으로 붙이고 drop으로 빼며 df.loc[조건, 열] = 값으로 고칩니다.

🔗 지난 글 복습: #40 NumPy 배열 입문 (02권 7부)에서 배열의 shape·axis, 불리언 인덱싱, 브로드캐스트와 벡터화를 익혔습니다. 판다스는 그 NumPy 배열 위에 이름표(index·columns)를 붙인 표라서, 지난 글의 불리언 마스크와 axis가 이번 글에도 그대로 나옵니다.


🔥 왜 배우나요?

⭐⭐⭐⭐⭐

머신러닝 책에서 왜 갑자기 엑셀 같은 표 도구가 나올까요? 이유는 단순합니다. 머신러닝 = 데이터로 배우는 기계이고, 세상의 데이터는 거의 전부 표 모양으로 들어오며, 그 표를 정확하게 정리하는 표준 도구가 판다스이기 때문입니다.

이 장을 끝내면 이런 것들을 혼자 힘으로 할 수 있습니다.\
✔ CSV·엑셀 파일을 열어 몇 행 몇 열인지, 빈칸은 어디인지 1분 안에 파악한다\
✔ 엑셀의 자동 필터·피벗 테이블·VLOOKUP을 코드 한 줄로 대신한다\
✔ loc·iloc·fillna·groupby 같은 낯선 이름이 더 이상 겁나지 않는다\
✔ 지저분한 공공데이터를 머신러닝 모델이 먹을 수 있는 깨끗한 표(X, y)로 바꾼다\
✔ 왜 숫자 열이 갑자기 글자가 되는지, 왜 평균이 이상한지 원인을 스스로 찾는다

🧒 시작하기 전에 — 머신러닝을 공부하는데 왜 판다스부터 배울까?

🙋 이 부분은 중학생도 읽을 수 있게 썼습니다. 코드를 몰라도 괜찮습니다. "아, 그래서 판다스가 필요하구나"라는 느낌만 가져가면 충분합니다. 코드는 나중에 다시 와서 실행해 봐도 됩니다.

① 머신러닝은 "데이터로 공부하는 학생"입니다

여러분은 시험을 잘 보기 위해 문제집을 풉니다. 문제를 풀고, 뒤쪽 정답지와 맞춰 보고, 틀린 걸 고치면서 실력이 늡니다. 머신러닝도 똑같습니다. 다만 문제집 대신 데이터로 공부합니다.

사람 학생머신러닝(기계 학생)판다스에서는
📘 문제지 (키, 몸무게, 공부 시간…)입력 데이터 X (특성, feature)DataFrame의 여러 열
📗 정답지 (수학 점수)정답 데이터 y (라벨, label)DataFrame의 한 열 = Series
✏️ 문제 풀고 채점하기학습 (fit)판다스가 정리한 표를 모델에 넘김
📝 새 시험 보기예측 (predict)새 데이터도 같은 방식으로 정리

🔍 더 쉽게 이해하기 — 📚 정답지가 틀린 문제집

만약 문제집의 정답지가 군데군데 틀려 있거나, 페이지가 찢겨 빠져 있거나, 같은 문제가 두 번 인쇄되어 있다면 어떨까요? 아무리 성실한 학생도 엉뚱하게 공부하게 됩니다.\
\
기계도 똑같습니다. 데이터가 틀리면 기계는 틀린 것을 열심히 배웁니다. 컴퓨터 과학자들은 이것을 "쓰레기가 들어가면 쓰레기가 나온다(Garbage In, Garbage Out)"라고 부릅니다.\
그래서 머신러닝의 첫 번째 일은 모델을 만드는 게 아니라, 문제집(데이터)을 깨끗하게 정리하는 것입니다.

② 왜 하필 "기초적인 표 데이터"부터 시작할까?

인공지능 하면 사진을 알아보고, 글을 쓰고, 말을 알아듣는 모습이 떠오릅니다. 그런데 왜 이 책은 "이름·키·점수" 같은 평범한 표부터 다룰까요? 세 가지 이유가 있습니다.

🔢 이유 1 — 사진도 글도 결국 숫자 표

사진은 점(픽셀) 하나하나의 밝기를 적은 숫자 표이고, 문장은 단어마다 번호를 붙인 숫자 줄입니다. 컴퓨터 눈에는 전부 표입니다. 표를 다룰 줄 알면 모든 데이터의 기본기를 갖춘 셈입니다.

🚲 이유 2 — 보조바퀴로 먼저 연습

자전거를 배울 때 보조바퀴부터 답니다. 작은 표는 눈으로 한 줄 한 줄 확인할 수 있어서, 계산이 맞는지 직접 검산할 수 있습니다. 원리를 익힌 뒤에 큰 데이터로 넘어가야 길을 잃지 않습니다.

🏪 이유 3 — 실제 일의 대부분이 표

회사·병원·학교·관공서의 데이터는 대부분 엑셀이나 CSV 표입니다. 매출 예측, 고객 이탈, 성적 분석, 이 장 15단계의 전기차 공공데이터까지 — 머신러닝 실무의 절반 이상이 표 데이터입니다.

🧠 이유 4 — 03권·04권으로 가는 다리

다음 권의 선형회귀·분류·부스팅은 모두 표 X와 정답 y를 입력으로 받습니다. 04권 딥러닝의 데이터셋도 내부에서 표를 읽어 텐서로 바꿉니다. 표를 정확히 다루는 힘이 모든 모델의 출발선입니다.

③ 자료 구조를 "정확하게" 써야 하는 이유 — 세 가지 사고 실험

"자료 구조"란 데이터를 담는 그릇의 모양입니다. 🔗 02-02(#35 자료형)에서 배운 리스트·딕셔너리도 자료 구조입니다. 그런데 표 데이터를 파이썬 기본 그릇으로만 다루면 아래와 같은 사고가 에러 메시지도 없이 조용히 일어납니다.

Python — 0-1: 사고 ① 글자가 된 숫자

# 사고 ① 숫자인데 '글자'로 저장된 키
heights = ["165", "158"]                  # 파일에서 읽으면 이렇게 글자로 들어오는 일이 흔합니다
print("키의 합:", heights[0] + heights[1])  # 더하기를 했는데...?

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L12–L14

▶ 실행 결과

키의 합: 165158

🤯 165 + 158 = 323이 아니라 "165158"이 나왔습니다. 글자끼리 +를 하면 이어 붙이기가 되기 때문입니다. 숫자는 숫자 그릇에, 글자는 글자 그릇에 담아야 합니다. 이것이 자료형(dtype)을 정확히 써야 하는 이유입니다.

Python — 0-2: 사고 ② 한 칸 밀린 줄

# 사고 ② 이름과 점수를 따로따로 리스트에 담았더니...
names  = ["민준", "서연", "도윤", "하은"]
scores = [88, 92, 75]                     # 도윤의 점수를 깜빡하고 안 적음 (한 칸이 빠짐)

for name, score in zip(names, scores):    # 짝지어 출력해 보면
    print(name, score)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L18–L23

▶ 실행 결과

민준 88
서연 92
도윤 75

😱 실제로는 하은이 75점인데, 도윤이 75점인 것처럼 짝지어졌고 하은은 아예 사라졌습니다. 두 리스트가 "몇 번째 칸"이라는 위치로만 연결되어 있어서, 한 칸만 빠져도 다른 사람의 성적표가 됩니다. 엑셀이라면 한 사람의 정보가 같은 줄(행)에 묶여 있어 이런 일이 없죠.

Python — 0-3: 사고 ③ 빈칸과 중복

# 사고 ③ 빈칸과 중복 제출이 섞인 설문
scores2 = [88, 92, None, 92, 75]          # None = 빈칸, 92가 두 번 = 서연이 두 번 제출
valid = [m for m in scores2 if m is not None]
print("평균(중복 포함):", sum(valid) / len(valid))
print("올바른 평균    :", (88 + 92 + 75) / 3)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L27–L31

▶ 실행 결과

평균(중복 포함): 86.75
올바른 평균    : 85.0

📉 서연의 점수가 두 번 세어져서 평균이 부풀려졌습니다. 머신러닝 모델이 이런 데이터로 배우면 "우리 반은 수학을 잘한다"고 잘못 믿게 됩니다.

🚨 가장 무서운 점 — 조용히 틀린다

세 사고의 공통점은 에러가 나지 않는다는 것입니다. 프로그램은 멀쩡히 돌아가고, 숫자도 그럴듯하게 나옵니다. 그래서 더 위험합니다. 데이터를 정확한 자료 구조에 담고 눈으로 확인하는 습관이 없으면, 틀린 줄도 모르고 틀린 모델을 만들게 됩니다.

④ 그래서 판다스 — 세 사고를 한꺼번에 막는 "똑똑한 정리 선반"

🔍 더 쉽게 이해하기 — 📒 낱장 메모지 vs 🗄️ 정리 선반

📒 파이썬 리스트는 낱장 메모지입니다. 이름은 이 메모지에, 키는 저 메모지에, 점수는 또 다른 메모지에 적습니다. 한 장만 순서가 섞여도 누가 누군지 모릅니다.\
\
🗄️ 판다스는 칸막이가 있는 정리 선반(엑셀 시트)입니다.\
· 한 사람의 정보는 한 줄(행)에 묶여 함께 움직입니다 → 사고 ② 방지\
· 칸(열)마다 숫자 칸인지 글자 칸인지 정해져 있습니다 → 사고 ① 방지\
· 빈칸 찾기·중복 지우기 도구가 기본으로 들어 있습니다 → 사고 ③ 방지\
· 그리고 줄마다 이름표(index)가 붙어 있어, 순서를 섞어도 이름표로 다시 찾을 수 있습니다.

Python — 0-4: 지저분한 원본을 판다스로 열기

import pandas as pd

# 설문 원본: 사람이 대충 적어서 지저분한 상태
raw = pd.DataFrame({
    "이름":   ["민준", "서연", "도윤", "서연", "하은"],
    "반":     ["1반", "2반", "1반", "2반", "2반"],
    "키":     ["165", "158", "172", "158", " 161 "],   # 글자 + 공백 섞임
    "몸무게": [55, None, 63, None, 50],                # 빈칸
    "수학":   [88, 92, None, 92, 75],                  # 빈칸 + 중복
})
print(raw)
print("-" * 40)
print(raw.dtypes)            # 키가 '숫자'가 아니라 '글자(str)' 칸이라는 걸 바로 알려 줌

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L35–L47

▶ 실행 결과

   이름   반      키   몸무게    수학
0  민준  1반    165  55.0  88.0
1  서연  2반    158   NaN  92.0
2  도윤  1반    172  63.0   NaN
3  서연  2반    158   NaN  92.0
4  하은  2반   161   50.0  75.0
----------------------------------------
이름         str
반          str
키          str
몸무게    float64
수학     float64
dtype: object

Python — 0-5: 네 줄로 정리하고 반별 평균까지

clean = raw.drop_duplicates()                                  # 사고 ③: 중복 제출 지우기
clean["키"] = clean["키"].str.strip().astype(int)              # 사고 ①: 공백 지우고 숫자로
clean["몸무게"] = clean["몸무게"].fillna(clean["몸무게"].mean()) # 빈 몸무게는 평균으로 채우기
clean = clean.dropna(subset=["수학"])                          # 정답(수학)이 없는 줄은 공부에 못 씀

print(clean)
print("-" * 40)
print("반별 수학 평균:")
print(clean.groupby("반")["수학"].mean())                      # 엑셀의 피벗 한 번

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L51–L59

▶ 실행 결과

   이름   반    키   몸무게    수학
0  민준  1반  165  55.0  88.0
1  서연  2반  158  56.0  92.0
4  하은  2반  161  50.0  75.0
----------------------------------------
반별 수학 평균:
반
1반    88.0
2반    83.5
Name: 수학, dtype: float64

Python — 0-6: 머신러닝 모델이 먹을 수 있는 X, y 만들기

X = clean[["키", "몸무게"]]      # 📘 문제지: 모델이 보고 판단할 정보
y = clean["수학"]                # 📗 정답지: 맞혀야 할 값

print("X (문제지) 모양:", X.shape)
print(X)
print("y (정답지):", y.tolist())

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L63–L68

▶ 실행 결과

X (문제지) 모양: (3, 2)
     키   몸무게
0  165  55.0
1  158  56.0
4  161  50.0
y (정답지): [88.0, 92.0, 75.0]

✅ 실행 결과 요약 — 판다스가 한 일

지저분한 원본 5줄이 중복 1줄 제거 → 글자 키를 숫자로 → 빈 몸무게 채우기 → 정답 없는 줄 제외를 거쳐 깨끗한 3줄이 되었습니다. 그리고 X(문제지)와 y(정답지)로 나누는 순간, 03권의 모든 머신러닝 모델에 그대로 넣을 수 있는 상태가 됩니다(model.fit(X, y)).\
이 과정 전체가 엑셀로는 수십 번의 클릭, 파이썬 리스트로는 수십 줄의 반복문이지만, 판다스로는 몇 줄입니다. 그리고 다음 달 새 설문이 와도 같은 코드를 다시 실행하기만 하면 됩니다.

⑤ 판다스는 머신러닝 과정의 어디에 있나

(시각화) 머신러닝은 요리 — 원본 데이터 → 판다스(읽기·정리·가공, 전체 시간의 약 80%) → X, y → 모델 fit() → 예측

📌 이야기 정리

① 머신러닝은 데이터(문제지 X + 정답지 y)로 공부하는 기계다. 데이터가 틀리면 기계도 틀리게 배운다.\
② 사진·글·소리도 결국 숫자 표다. 그래서 기초적인 표 데이터로 원리를 먼저 익힌다.\
③ 파이썬 기본 그릇만 쓰면 글자가 된 숫자 · 한 칸 밀린 줄 · 빈칸과 중복 사고가 에러 없이 일어난다.\
④ 판다스는 한 줄 = 한 사람, 한 칸 = 한 자료형, 줄마다 이름표로 이 사고들을 막아 주는 정리 선반이다.\
⑤ 판다스로 만든 X, y가 03권 머신러닝 모델의 입구다.

⭐ 오늘의 한 줄 — 좋은 모델은 좋은 데이터에서 나오고, 좋은 데이터는 판다스로 만듭니다.

📚 선행지식

파이썬 기초만 있으면 됩니다. 특히 아래 세 가지가 이 장에서 계속 쓰입니다.\
🔗 02-02 D단원 "변수는 이름표"(#35) — 판다스 3.0의 Copy-on-Write와 fillna 함정(AM단원)을 이해하는 열쇠\
🔗 02-02 리스트·딕셔너리(#35) — DataFrame을 만드는 재료\
🔗 02-07 NumPy(#40) — 불리언 인덱싱·브로드캐스트·axis가 판다스에서 그대로 쓰입니다\
🖥️ 준비물 — 구글 Colab이면 설치 없이 바로 됩니다(판다스 기본 포함).

🧪 실행 환경 안내

이 장의 모든 실행 결과는 코드를 실제로 실행해서 얻은 값입니다(pandas 3.0 기준). pandas 2.x(구글 Colab 등)에서는 문자열 열의 자료형이 str 대신 object로 표시되고 일부 경고 문구가 다를 수 있지만, 표의 내용과 숫자는 같습니다.

✏️ 꼭 알아야 할 TIP

판다스를 관통하는 단 하나의 문장\
👉 "판다스의 모든 계산은 위치가 아니라 이름표(index · columns)를 맞춰서 일어난다."\
\
이 한 문장을 이해하면 판다스의 절반이 풀립니다.\
· 왜 s1 + s2에서 NaN이 생기는지 (짝이 없는 이름표)\
· 왜 loc은 끝을 포함하고 iloc은 안 하는지 (이름표 vs 위치)\
· 왜 merge·groupby가 엑셀보다 실수가 적은지 (이름표로 줄을 맞춤)\
이 세 가지가 초보자가 가장 많이 헤매는 지점이고, AF · AJ · AQ단원에서 그림으로 완전히 해결합니다.

🎯 학습 목표

☐머신러닝에서 표 데이터와 자료 구조가 왜 중요한지 사고 실험으로 설명할 수 있다.

☐엑셀의 시트·열·행 번호·머리글이 DataFrame·Series·index·columns에 어떻게 대응되는지 설명할 수 있다.

☐CSV·엑셀 파일을 읽고 쓰며 한글 인코딩(utf-8 / cp949) 문제를 스스로 해결할 수 있다.

☐shape·info()·describe()로 새 데이터의 건강검진을 할 수 있다.

☐loc(이름표)과 iloc(위치)을 구분하고 슬라이싱 끝 포함 여부를 설명할 수 있다.

☐불리언 마스크·& | ~·isin으로 엑셀 자동 필터를 대신할 수 있다.

☐행·열을 추가·삭제하고 concat의 axis와 join을 구분해 쓸 수 있다.

☐결측치를 찾고·채우고·지우는 세 전략을 상황에 맞게 고를 수 있다.

☐정렬·인덱스 교체·중복 제거로 표를 정돈할 수 있다.

☐산술·통계 연산을 하고 판다스 std(n−1)와 NumPy std(n)의 차이를 설명할 수 있다.

☐.str 접근자로 찾기·바꾸기·나누기를 한 번에 할 수 있다.

☐groupby·pivot_table·merge로 SUMIF·피벗·VLOOKUP을 코드로 할 수 있다.

☐판다스 표를 Plotly 그래프로 바꿀 수 있다.

☐공공데이터를 읽어 정리 → 분석 → 시각화하는 전체 파이프라인을 구현할 수 있다.

⚙️ 핵심 학습 내용

🗂️ 이 장 전체를 관통하는 엑셀 비유

· DataFrame = 엑셀 시트 한 장 📄 (행과 열로 된 표)\
· Series = 시트의 열 하나 📏 (값 + 이름표)\
· index = 왼쪽 행 번호 🔢 (판다스는 0부터, 이름으로도 가능)\
· columns = 맨 위 열 머리글 🏷️\
· 불리언 마스크 = 자동 필터 ▼ 🔽\
· groupby / pivot_table / merge = SUMIF / 피벗 테이블 / VLOOKUP 📊\
판다스는 엑셀에서 마우스로 하던 일을 "다시 실행할 수 있는 레시피(코드)"로 바꾸는 도구입니다.

편단원주제머신러닝에서 어디에 쓰이나
1편 🌱 판다스와 자료구조AE판다스 시작하기데이터 준비의 표준 관문
〃AFSeries — 이름표 붙은 한 줄정답 y는 Series
〃AGDataFrame 만들기특성 X는 DataFrame
2편 📂 불러오기AHCSV·엑셀 읽고 쓰기캐글·공공데이터 불러오기
〃AIshape·info·describeEDA(탐색적 분석) 첫 단계
3편 🎯 고르기AJ[] · loc · iloc특성 열 고르기, 샘플 자르기
〃AK조건 필터클래스별 분리, 이상치 제거
4편 ✂️ 다듬기AL행·열 추가·삭제·수정특성 추가(feature engineering)
〃AM결측치 처리 (2부)NaN이 있으면 모델이 멈춤
〃AN정렬·인덱스·중복 (2부)시계열 정렬, 누수 방지
5편 🔢 계산·묶기 (2부)AO연산과 통계표준화·정규화의 재료
〃AP문자열 .strNLP 텍스트 전처리
〃AQ날짜·그룹·피벗·병합집계 특성 만들기
6편 📊 실전 (2부)ARPlotly 시각화분포·추세 눈으로 확인
〃AS공공데이터 전기차 분석이 장의 결승점 🏁

🗂️ 엑셀 기능 ↔ 판다스 대응 지도 — 책갈피처럼 두고 보세요

엑셀에서 하던 일판다스에서는단원
엑셀 파일 하나 / 시트 한 장DataFrameAG
한 열(세로줄)SeriesAF
왼쪽 행 번호 · 맨 위 열 머리글index · columnsAF · AG
파일 열기 · 다른 이름으로 저장read_csv / read_excel / to_csv / to_excelAH
Ctrl+End로 표 크기 보기, 셀 서식 확인shape · info() · describe()AI
셀 주소 B3, 범위 A1:C5loc · ilocAJ
자동 필터 (▼ 단추)df[조건] · isin · queryAK
열 삽입 · 행 추가 · 삭제insert · concat · dropAL
빈 셀 찾기 · 채우기 핸들로 아래로 채우기isnull · fillna · ffillAM
정렬 · 중복된 항목 제거sort_values · drop_duplicatesAN
=A1*2, =SUM(), =AVERAGE()산술 연산 · sum() · mean()AO
찾기 및 바꾸기 · 텍스트 나누기 · =LEFT().str.replace · .str.split · .str[0]AP
SUMIF · 피벗 테이블 · VLOOKUPgroupby · pivot_table · mergeAQ
차트 삽입plotly.express · df.plotAR
행/열 바꿈(선택하여 붙여넣기).T (전치)AQ · AS
비교 항목📗 엑셀🐼 판다스
작업 기록클릭이라 남지 않음코드로 전부 남음 → 재현 가능
데이터 크기1,048,576행 제한, 수십만 행부터 느려짐메모리가 허락하는 만큼 (수백만~수천만 행)
반복 작업매번 다시 클릭코드 한 번 실행으로 자동화
머신러닝 연결내보내기 → 다시 불러오기model.fit(X, y)에 바로 투입
실수 추적어느 셀을 고쳤는지 모름코드 한 줄씩 되짚어 볼 수 있음

(시각화) 02-08 전체 로드맵 — 1편 자료구조 · 2편 불러오기 · 3편 고르기 · 4편 다듬기 · 5편 계산·묶기 · 6편 그리기·실전

🧭 이번 글의 범위: 8부가 길어서 두 편으로 나눴습니다. 이번 1부는 🌱 1편(AE·AF·AG) · 📂 2편(AH·AI) · 🎯 3편(AJ·AK) · ✂️ AL단원까지이고, 결측치(AM)부터 실전 전기차 분석(AS)과 실습·연습문제는 #42 2부에서 이어집니다.

📖 처음부터 끝까지 상세 풀이

🌱 1편 — 판다스와 두 자료구조 (AE · AF · AG)

02-08-AE 판다스 시작하기 — 파이썬 안의 엑셀 켜기 🟢

판다스(pandas)라는 이름은 귀여운 판다가 아니라 Panel Data(여러 시점에 걸쳐 관찰한 표 형식 데이터)라는 경제학 용어에서 왔습니다. 2008년 금융회사에서 일하던 웨스 매키니(Wes McKinney)가 "엑셀과 R로 하던 표 작업을 파이썬에서 빠르게 하고 싶다"는 이유로 만들었습니다. 그래서 판다스는 처음부터 엑셀 사용자를 위한 도구로 태어났다고 해도 과언이 아닙니다.

판다스에는 딱 두 가지 핵심 자료구조가 있습니다. 이 둘만 확실히 잡으면 나머지는 모두 이 둘을 다루는 방법입니다.

자료구조차원엑셀로 치면한 줄 설명
Series (시리즈)1차원열 하나(세로줄)값들 + 각 값의 이름표(index)
DataFrame (데이터프레임)2차원시트 한 장(표)같은 index를 공유하는 Series 여러 개를 옆으로 붙인 것

🔍 더 쉽게 이해하기

판다스를 도서관에 비유해 봅시다.\
📕 Series는 책장 한 칸입니다. 책(값)마다 청구기호(index)가 붙어 있어서 "A-3번 책 주세요"라고 하면 바로 찾을 수 있습니다.\
📚 DataFrame은 책장 전체입니다. 같은 층(행 번호)에 있는 여러 칸(열)을 함께 봅니다. "3층의 소설 칸, 역사 칸 책 주세요"처럼 행과 열 두 개의 이름표로 찾습니다.\
🏷️ 중요한 건 위치가 아니라 이름표로 찾는다는 점입니다. 책장 순서를 섞어도 청구기호로는 여전히 같은 책을 찾을 수 있지요.

📋 진행 과정

  1. 설치 확인 — 아나콘다·코랩에는 기본 설치. 없으면 pip install pandas
  2. 불러오기 — import pandas as pd — 별명 pd는 전 세계 공통 약속
  3. 버전 확인 — pd.__version__로 문서와 버전 맞추기
  4. 표 만들기 — Series·DataFrame으로 데이터 담기

🖼️ 시각화 — 판다스의 두 자료구조

(시각화) 판다스의 두 자료구조 — Series(열 하나 = index + values)와 DataFrame(시트 한 장, 열 하나가 Series)

💻 핵심 코드

Python — AE-1: 판다스 불러오기

import pandas as pd            # 판다스를 pd라는 짧은 별명으로 불러옵니다
import numpy as np             # 판다스는 NumPy 위에서 동작하므로 함께 불러 두면 편합니다

print("판다스 버전:", pd.__version__)   <# 설치된 판다스 버전 확인
print("NumPy 버전:", np.__version__)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L72–L76

▶ 실행 결과

판다스 버전: 3.0.5
NumPy 버전: 2.5.3

🧠 코드 이해 — 한 줄씩 뜯어보기

코드무슨 일이 일어나나
import pandas as pd판다스 라이브러리 전체를 가져오고, 앞으로 pd라는 별명으로 부릅니다. pandas.DataFrame 대신 pd.DataFrame으로 짧게 쓰기 위한 약속입니다.
import numpy as np판다스의 내부 저장소는 NumPy 배열입니다. 🔗 02-07에서 배운 NumPy 함수를 판다스 데이터에 그대로 쓸 수 있습니다.
pd.__version__앞뒤 밑줄 두 개(__)가 붙은 이름은 파이썬이 정해 둔 특별한 속성입니다. 라이브러리 버전 문자열이 들어 있습니다.

🐍 파이썬 문법 짚고 가기

import A as B — 모듈 A를 불러와 B라는 이름표를 붙입니다. 🔗 02권에서 배운 "변수는 이름표" 원리 그대로입니다.\
__version__ — 던더(double underscore) 이름. 파이썬 생태계에서 버전 정보를 담는 관례적인 속성입니다.

🧭 네 가지 관점으로 보기 — 머신러닝까지 연결

📐 수학적 의미 — 판다스의 표는 행 이름 집합 I와 열 이름 집합 C를 정의역으로 하는 함수 f:I×C→값f: I \times C \to \text{값} 으로 볼 수 있습니다. 위치가 아니라 이름 쌍으로 값을 찾습니다.\
💡 직관적 의미 — 엑셀 시트에 행 이름표와 열 이름표를 단단히 붙여 둔 것. 이름표 덕분에 순서가 섞여도 길을 잃지 않습니다.\
🔧 구현 관점 — 각 열은 하나의 NumPy 배열(또는 문자열 배열)로 따로 저장됩니다. 그래서 열 단위 계산이 빠르고, 열마다 자료형이 다를 수 있습니다.\
🏗️ 프레임워크 관점 — NumPy = 숫자 격자판 · 판다스 = 이름표 붙은 표 · scikit-learn = 표를 받아 학습 · PyTorch = torch.tensor(df.values)로 텐서 변환. 판다스는 데이터 준비의 표준 관문입니다.

📌 개념 정리

· 판다스 = Panel Data, 표 데이터를 다루는 파이썬 라이브러리\
· 자료구조는 Series(1차원, 열)와 DataFrame(2차원, 표) 두 가지\
· import pandas as pd는 전 세계 공통 관례

⭐ 오늘의 한 줄 — 판다스는 모든 행과 열에 이름표를 붙인 엑셀 시트다

02-08-AF Series — 이름표 붙은 한 줄 🟢

Series는 값의 목록과 각 값의 이름표(index)를 한 쌍으로 묶은 1차원 자료구조입니다. 파이썬 리스트는 "0번째, 1번째"처럼 위치로만 찾을 수 있지만, Series는 "a", "서울", "2024-03"처럼 의미 있는 이름으로 값을 찾을 수 있습니다.

만드는 방법: pd.Series(data, index=index_data) — data에는 리스트·딕셔너리·NumPy 배열 무엇이든 들어갈 수 있고, index를 주지 않으면 0, 1, 2 … 정수가 자동으로 붙습니다.

🔍 더 쉽게 이해하기

Series는 택배 보관함입니다.\
보관함 칸마다 번호표(index)가 붙어 있고, 칸 안에 물건(value)이 들어 있습니다. "c번 칸 열어 주세요"라고 하면 위치를 몰라도 바로 꺼낼 수 있지요.\
그리고 판다스의 진짜 마법은 두 보관함을 합칠 때 나옵니다. 같은 번호표끼리 짝지어 더하기 때문에, 한쪽에만 있는 번호는 "짝이 없음(NaN)"이 됩니다. 엑셀처럼 "같은 줄끼리"가 아니라 "같은 이름표끼리" 계산합니다.

📋 진행 과정

  1. 값 준비 — 리스트 [1, 2, 3, 4]
  2. 이름표 붙이기 — index=['a','b','c','d']
  3. 속성 확인 — index · values · dtype
  4. 꺼내기 — s['c'], s[['b','c']]
  5. 거르기 — s[s > 2]

🖼️ 시각화 — 같은 이름표끼리 짝짓는 덧셈(인덱스 정렬)

(시각화) 같은 이름표끼리 짝짓는 덧셈 — s1 + s2에서 짝이 없는 a와 d는 NaN

💻 핵심 코드

Python — AF-1: Series 만들기와 속성 확인

import pandas as pd

# 값 4개에 이름표 a~d를 붙인 Series 만들기
s = pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])
print(s)
print("-" * 30)
print("index :", s.index)      # 이름표 목록
print("values:", s.values)     # 값만 꺼낸 NumPy 배열
print("dtype :", s.dtype)      # 값의 자료형

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L80–L88

▶ 실행 결과

a    1
b    2
c    3
d    4
dtype: int64
------------------------------
index : Index(['a', 'b', 'c', 'd'], dtype='str')
values: [1 2 3 4]
dtype : int64

Python — AF-2: 이름표로 꺼내기와 조건 필터

print(s['c'])          # 이름표 하나로 꺼내면 → 값 하나(스칼라)
print("-" * 30)
print(s[['b', 'c']])   # 이름표 목록(리스트)으로 꺼내면 → Series
print("-" * 30)
print(s[s > 2])        # 조건으로 거르기 → 조건이 True인 칸만 남은 Series

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L92–L96

▶ 실행 결과

3
------------------------------
b    2
c    3
dtype: int64
------------------------------
c    3
d    4
dtype: int64

Python — AF-3: RangeIndex와 딕셔너리로 만들기

s2 = pd.Series([1, 2, 3, 4])   # index를 주지 않으면?
print(s2.index)                # 0부터 시작하는 RangeIndex가 자동으로 붙음

# 딕셔너리로 만들면 key가 곧 이름표가 됩니다
population = pd.Series({'서울': 940, '부산': 330, '대구': 237})
print(population)
print("서울 인구(만 명):", population['서울'])

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L100–L106

▶ 실행 결과

RangeIndex(start=0, stop=4, step=1)
서울    940
부산    330
대구    237
dtype: int64
서울 인구(만 명): 940

Python — AF-4: 판다스의 핵심 — 인덱스 정렬 연산

s1 = pd.Series([1, 2, 3], index=['a', 'b', 'c'])
s2 = pd.Series([10, 20, 30], index=['b', 'c', 'd'])

print(s1 + s2)          # 같은 이름표끼리 더하기 → a, d는 짝이 없어 NaN
print("-" * 30)
print(s1.add(s2, fill_value=0))   # 짝이 없을 때 0으로 간주하고 더하기
print("-" * 30)
print(s1 * 10)          # 스칼라 연산은 모든 칸에 한꺼번에 (브로드캐스트)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L110–L117

▶ 실행 결과

a     NaN
b    12.0
c    23.0
d     NaN
dtype: float64
------------------------------
a     1.0
b    12.0
c    23.0
d    30.0
dtype: float64
------------------------------
a    10
b    20
c    30
dtype: int64

🧠 코드 이해 — 한 줄씩 뜯어보기

코드무슨 일이 일어나나
pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])값 리스트와 이름표 리스트를 같은 길이로 넘깁니다. 길이가 다르면 에러가 납니다.
s.values이름표를 떼고 값만 NumPy 배열로 꺼냅니다. 머신러닝 모델에 넣을 때 자주 씁니다.
s['c']대괄호 안에 이름표 하나 → 값 하나. 엑셀에서 셀 하나를 클릭한 것과 같습니다.
s[['b', 'c']]대괄호가 두 겹입니다. 바깥은 "꺼내기", 안쪽은 "이름표 목록(리스트)". 결과는 Series입니다.
s[s > 2]s > 2가 먼저 계산되어 True/False Series(마스크)가 되고, True인 칸만 남깁니다. 🔗 02-07 불리언 인덱싱과 똑같습니다.
s1 + s2위치가 아니라 이름표를 맞춰서 더합니다. 한쪽에만 있는 이름표는 결과가 NaN(Not a Number, 값 없음)입니다.
s1.add(s2, fill_value=0)연산 메서드를 쓰면 fill_value로 "짝이 없을 때 쓸 기본값"을 정할 수 있습니다.

🐍 파이썬 문법 짚고 가기

s[[...]] 이중 대괄호 — 바깥 []는 "꺼내 줘"라는 명령, 안쪽 []는 파이썬 리스트입니다. 리스트를 넘기면 여러 개를 한꺼번에 꺼냅니다.\
비교 연산자 — >, <, ==(같다), !=(다르다), >=, <=. Series에 쓰면 칸마다 True/False가 나옵니다.\
dtype int64 — 64비트 정수. 정수 사이에 NaN이 끼면 float64(실수)로 바뀝니다. NaN은 실수 세계의 값이기 때문입니다.

🧭 네 가지 관점으로 보기 — 머신러닝까지 연결

📐 수학적 의미 — Series는 함수 s:I→Rs: I \to \mathbb{R}. 덧셈은 (s1+s2)(i)=s1(i)+s2(i)(s_1+s_2)(i) = s_1(i)+s_2(i) 이고, 한쪽 정의역에 ii가 없으면 결과는 정의되지 않음(NaN)입니다.\
💡 직관적 의미 — 번호표가 붙은 보관함. 두 보관함을 합칠 때 같은 번호표끼리 짝짓습니다.\
🔧 구현 관점 — 연산 전에 두 index의 합집합을 만들고(정렬), 각 위치에 값을 다시 배치(reindex)한 뒤 NumPy 연산을 합니다.\
🏗️ 프레임워크 관점 — NumPy는 위치로 짝짓기 때문에 길이가 다르면 에러, 판다스는 이름으로 짝짓기 때문에 NaN. 이 차이가 날짜·지역별 데이터 합치기에서 실수를 막아 줍니다.

📌 개념 정리

· Series = 값 + 이름표(index)\
· s['c']는 값, s[['b','c']]는 Series\
· 연산은 이름표 기준 정렬 후 실행 — 짝이 없으면 NaN

⭐ 오늘의 한 줄 — Series는 번호표 붙은 보관함, 계산은 같은 번호표끼리

02-08-AG DataFrame 만들기 — 엑셀 시트 한 장 🟢

DataFrame은 판다스의 주인공입니다. 만드는 방법은 pd.DataFrame(data, index=index_data, columns=columns_data)이고, 재료로는 딕셔너리(열 단위로 담기)와 2중 리스트(행 단위로 담기)가 가장 많이 쓰입니다.

🔍 더 쉽게 이해하기

엑셀 표를 채우는 방법은 두 가지입니다.\
🅰️ 세로로 채우기 — "이름 열에 소라, 창훈, 영미를 쭉 적고, 나이 열에 25, 30, 35를 쭉 적는다." → 딕셔너리 방식 (key = 열 이름, value = 그 열의 값 목록)\
🅱️ 가로로 채우기 — "첫 줄에 소라·25·인천, 둘째 줄에 창훈·30·순천…" → 2중 리스트 방식 (안쪽 리스트 하나 = 한 행). 이때 열 제목은 따로 columns=로 알려 줘야 합니다.\
어느 쪽으로 채워도 완성된 표는 똑같습니다.

📋 진행 과정

  1. 재료 선택 — 딕셔너리(열 단위) 또는 2중 리스트(행 단위)
  2. 표 생성 — pd.DataFrame(...)
  3. 뼈대 확인 — index · columns
  4. 내용 확인 — values · dtypes
  5. 열 꺼내기 — df['나이'] → Series

🖼️ 시각화 — 딕셔너리(세로 채우기) vs 2중 리스트(가로 채우기)

(시각화) DataFrame 만들기 — 딕셔너리는 열 단위(세로로), 2중 리스트는 행 단위(가로로)

💻 핵심 코드

Python — AG-1: 딕셔너리로 DataFrame 만들기

import pandas as pd

# 🅰️ 딕셔너리로 만들기 — key는 열 이름, value는 그 열의 값 목록
data_dict = {
    '이름': ['소라', '창훈', '영미'],
    '나이': [25, 30, 35],
    '거주지': ['인천', '순천', '동해']
}
df_dict = pd.DataFrame(data_dict)
print(df_dict)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L121–L130

▶ 실행 결과

   이름  나이 거주지
0  소라  25  인천
1  창훈  30  순천
2  영미  35  동해

Python — AG-2: 2중 리스트로 DataFrame 만들기

# 🅱️ 2중 리스트로 만들기 — 안쪽 리스트 하나가 한 행
data_list = [
    ['소라', 25, '인천'],
    ['창훈', 30, '순천'],
    ['영미', 35, '동해']
]
columns = ['이름', '나이', '거주지']           # 열 제목은 따로 알려 줘야 함
df_list = pd.DataFrame(data_list, columns=columns)
print(df_list)
print("두 표가 똑같은가?", df_dict.equals(df_list))

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L134–L143

▶ 실행 결과

   이름  나이 거주지
0  소라  25  인천
1  창훈  30  순천
2  영미  35  동해
두 표가 똑같은가? True

Python — AG-3: DataFrame의 네 가지 속성

print("index  :", df_list.index)      # 행 이름표 (RangeIndex)
print("columns:", df_list.columns)    # 열 이름표 (Index 객체)
print("values :")
print(df_list.values)                 # 2차원 NumPy 배열
print("dtypes :")
print(df_list.dtypes)                 # 열마다 자료형

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L147–L152

▶ 실행 결과

index  : RangeIndex(start=0, stop=3, step=1)
columns: Index(['이름', '나이', '거주지'], dtype='str')
values :
[['소라' 25 '인천']
 ['창훈' 30 '순천']
 ['영미' 35 '동해']]
dtypes :
이름       str
나이     int64
거주지      str
dtype: object

Python — AG-4: 열 하나 = Series, 행 이름표 직접 붙이기

age = df_list['나이']          # 열 하나를 꺼내면?
print(type(age))               # → Series
print(age)

# 행 이름표를 직접 지정할 수도 있습니다
df_named = pd.DataFrame(data_dict, index=['A01', 'A02', 'A03'])
print(df_named)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L156–L162

▶ 실행 결과

<class 'pandas.Series'>
0    25
1    30
2    35
Name: 나이, dtype: int64
     이름  나이 거주지
A01  소라  25  인천
A02  창훈  30  순천
A03  영미  35  동해

🧠 코드 이해 — 한 줄씩 뜯어보기

코드무슨 일이 일어나나
pd.DataFrame(data_dict)딕셔너리의 key 3개가 열 3개가 되고, 각 value 리스트가 세로로 채워집니다. 모든 리스트 길이는 같아야 합니다.
pd.DataFrame(data_list, columns=columns)안쪽 리스트 하나하나가 행이 됩니다. columns를 빼먹으면 열 이름이 0, 1, 2가 됩니다.
df_dict.equals(df_list)두 DataFrame의 모양·이름표·값이 모두 같은지 True/False로 알려 줍니다.
df_list.indexRangeIndex(start=0, stop=3, step=1) — 0, 1, 2를 실제로 저장하지 않고 "0부터 3 전까지 1씩"이라는 규칙만 저장해 메모리를 아낍니다.
df_list.values표 전체를 2차원 NumPy 배열로. 문자열과 숫자가 섞여 있어 dtype이 object(아무거나 담는 상자)가 됩니다.
df_list['나이']DataFrame에서 열 하나를 꺼내면 Series가 나옵니다. DataFrame = Series를 옆으로 붙인 것이라는 증거입니다.

🐍 파이썬 문법 짚고 가기

딕셔너리 {key: value} — 이름표(key)로 값(value)을 찾는 자료형. DataFrame에서는 key가 열 이름이 됩니다.\
2중 리스트 [[...], [...]] — 리스트 안에 리스트. 바깥 = 표 전체, 안쪽 = 한 행.\
type(x) — x가 어떤 종류의 객체인지 알려 주는 내장 함수.

🧭 네 가지 관점으로 보기 — 머신러닝까지 연결

📐 수학적 의미 — DataFrame은 nn개의 행과 mm개의 열을 가진 행렬 X∈Rn×mX \in \mathbb{R}^{n \times m} 에 행 이름표 II와 열 이름표 CC를 붙인 것입니다. 머신러닝에서 nn은 샘플 수, mm은 특성 수입니다.\
💡 직관적 의미 — 행 = 한 사람(한 건의 기록), 열 = 그 사람의 속성 하나. 엑셀 시트 그대로입니다.\
🔧 구현 관점 — 내부적으로 열 단위로 저장됩니다. 그래서 열을 꺼내거나 열끼리 계산하는 것은 빠르고, 행을 하나씩 도는 것은 느립니다.\
🏗️ 프레임워크 관점 — scikit-learn의 fit(X, y)에서 X는 DataFrame, y는 Series를 그대로 넣을 수 있습니다. PyTorch는 torch.tensor(df.values)로 변환합니다.

📌 개념 정리

· 딕셔너리 = 열 단위, 2중 리스트 = 행 단위(columns 필요)\
· index · columns · values · dtypes 네 속성으로 표의 뼈대를 확인\
· 열 하나를 꺼내면 Series

⭐ 오늘의 한 줄 — DataFrame은 같은 이름표를 공유하는 Series들의 묶음

📂 2편 — 데이터 불러오고 살펴보기 (AH · AI)

02-08-AH 파일 읽고 쓰기 — CSV와 엑셀 🟢

실무에서는 표를 직접 타이핑하지 않습니다. 회사 시스템에서 내려받은 CSV나 동료가 보낸 엑셀 파일을 읽어 오는 것이 시작입니다. CSV(Comma-Separated Values)는 값을 쉼표로 구분한 순수 텍스트 파일이라 어떤 프로그램에서도 열 수 있고, 엑셀 파일(.xlsx)은 여러 시트·서식을 담을 수 있는 압축 파일입니다.

작업CSV엑셀
읽기pd.read_csv(경로)pd.read_excel(경로)
쓰기df.to_csv(경로)df.to_excel(경로)
여러 시트불가 (파일 하나 = 표 하나)pd.ExcelWriter로 가능
한글 문제인코딩(utf-8 / cp949)에 민감거의 없음
필요한 추가 설치없음openpyxl (코랩·아나콘다는 기본 포함)

🔍 더 쉽게 이해하기

인코딩은 글자를 숫자로 바꾸는 암호표입니다.\
컴퓨터는 "가"라는 글자를 그대로 저장하지 못하고 숫자로 바꿔 저장합니다. 이때 쓰는 암호표가 utf-8(전 세계 표준)과 cp949(옛 윈도우 한글 표준) 두 종류가 있습니다.\
📮 utf-8로 잠근 편지를 cp949 열쇠로 열면 "媛" 같은 깨진 글자가 나오고, 반대도 마찬가지입니다. 그래서 공공데이터처럼 윈도우 엑셀용으로 만든 CSV는 encoding="cp949"로 열어야 하고, 우리가 만든 CSV를 윈도우 엑셀에서 열 거라면 "cp949"나 "utf-8-sig"로 저장합니다.

📋 진행 과정

  1. 표 준비 — DataFrame 만들기
  2. 저장 — to_csv / to_excel
  3. 불러오기 — read_csv / read_excel
  4. 옵션 조정 — index · encoding · usecols …
  5. 검증 — 저장 전과 같은지 확인

🖼️ 시각화 — 파일과 DataFrame 사이의 왕복

(시각화) 파일과 DataFrame 사이의 왕복 — read_csv·read_excel로 읽고 to_csv·to_excel로 저장

💻 핵심 코드 ① — CSV 저장하고 다시 읽기

Python — AH-1: to_csv 기본값의 함정

import pandas as pd

data = {
    '연도': [2010, 2011, 2012, 2013, 2014],
    '인구(만명)': [5000, 5050, 5100, 5150, 5200],
    'GDP(억달러)': [1000, 1050, 1100, 1150, 1200]
}
df_years = pd.DataFrame(data)

df_years.to_csv('years_data.csv')          # 기본값: index도 함께 저장, utf-8

loaded = pd.read_csv('years_data.csv')     # 다시 읽어 보면?
print(loaded)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L166–L178

▶ 실행 결과

   Unnamed: 0    연도  인구(만명)  GDP(억달러)
0           0  2010    5000      1000
1           1  2011    5050      1050
2           2  2012    5100      1100
3           3  2013    5150      1150
4           4  2014    5200      1200

🚨 왜 Unnamed: 0 열이 생겼을까?

저장할 때 index가 함께 기록되었다가, 읽을 때는 그냥 데이터 열로 들어와 Unnamed: 0이라는 정체불명의 열이 생겼습니다. 저장·불러오기를 반복할 때마다 이 열이 하나씩 늘어나는 것이 초보자의 단골 실수입니다. 해결책은 두 가지입니다.

Python — AH-2: 해결 방법 두 가지

# 해결 ① 저장할 때 index를 빼기
df_years.to_csv('years_data_noidx.csv', index=False)
print(pd.read_csv('years_data_noidx.csv'))
print("-" * 40)

# 해결 ② 읽을 때 첫 번째 열을 index로 쓰기
print(pd.read_csv('years_data.csv', index_col=0))

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L182–L188

▶ 실행 결과

     연도  인구(만명)  GDP(억달러)
0  2010    5000      1000
1  2011    5050      1050
2  2012    5100      1100
3  2013    5150      1150
4  2014    5200      1200
----------------------------------------
     연도  인구(만명)  GDP(억달러)
0  2010    5000      1000
1  2011    5050      1050
2  2012    5100      1100
3  2013    5150      1150
4  2014    5200      1200

Python — AH-3: 인코딩 맞추기

# 윈도우 엑셀에서 한글이 안 깨지게 cp949로 저장
df_years.to_csv('years_data_cp949.csv', encoding='cp949', index=False)

# 같은 파일을 잘못된 암호표(utf-8)로 열면?
try:
    pd.read_csv('years_data_cp949.csv')                  # 기본 encoding='utf-8'
except UnicodeDecodeError as e:
    print("❌ 에러:", type(e).__name__)

# 올바른 암호표로 열기
print(pd.read_csv('years_data_cp949.csv', encoding='cp949'))

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L192–L202

▶ 실행 결과

❌ 에러: UnicodeDecodeError
     연도  인구(만명)  GDP(억달러)
0  2010    5000      1000
1  2011    5050      1050
2  2012    5100      1100
3  2013    5150      1150
4  2014    5200      1200

Python — AH-4: read_csv 옵션 실습

# read_csv의 자주 쓰는 옵션들
print(pd.read_csv('years_data_noidx.csv', usecols=['연도', 'GDP(억달러)']))  # 필요한 열만
print("-" * 40)
print(pd.read_csv('years_data_noidx.csv', nrows=2))                          # 앞 2행만
print("-" * 40)
print(pd.read_csv('years_data_noidx.csv', skiprows=[1, 2]))                  # 제목줄(0번) 다음 1·2번 줄(2010·2011년) 건너뛰기
print("-" * 40)
print(pd.read_csv('years_data_noidx.csv', header=0,
                  names=['year', 'pop', 'gdp']))                              # 열 이름 새로 붙이기

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L206–L214

▶ 실행 결과

     연도  GDP(억달러)
0  2010      1000
1  2011      1050
2  2012      1100
3  2013      1150
4  2014      1200
----------------------------------------
     연도  인구(만명)  GDP(억달러)
0  2010    5000      1000
1  2011    5050      1050
----------------------------------------
     연도  인구(만명)  GDP(억달러)
0  2012    5100      1100
1  2013    5150      1150
2  2014    5200      1200
----------------------------------------
   year   pop   gdp
0  2010  5000  1000
1  2011  5050  1050
2  2012  5100  1100
3  2013  5150  1150
4  2014  5200  1200
read_csv 옵션뜻기본값
sep구분자 (탭 구분 파일이면 "\t")","
header열 제목으로 쓸 행 번호 (제목이 없으면 None)0
names열 이름을 직접 지정없음
index_colindex로 쓸 열없음
usecols읽을 열만 고르기 (큰 파일에서 메모리 절약)전체
skiprows건너뛸 행 (숫자면 앞에서부터 개수, 리스트면 해당 줄)없음
nrows읽을 행 수 (큰 파일 미리보기)전체
encoding문자 인코딩"utf-8"
to_csv 옵션뜻기본값
sep구분자","
na_rep결측치를 어떤 글자로 쓸지빈 문자열
columns저장할 열만 고르기전체
header열 이름 저장 여부True
indexindex 저장 여부True ← 대부분 False로 바꿈
encoding문자 인코딩"utf-8"

💻 핵심 코드 ② — 엑셀 파일 저장하고 읽기

Python — AH-5: to_excel / read_excel

books_df = pd.DataFrame({
    '도서ID': [1, 2, 3, 4],
    '도서명': ['파이썬 프로그래밍', '데이터 분석', '머신러닝 입문', '인공지능의 이해'],
    '저자': ['김파이썬', '이데이터', '박머신', '최인공'],
    '출판년도': [2019, 2020, 2018, 2021]
})
members_df = pd.DataFrame({
    '회원ID': [101, 102, 103],
    '이름': ['홍길동', '김영희', '이철수'],
    '가입년도': [2020, 2021, 2019]
})
borrow_df = pd.DataFrame({
    '회원ID': [101, 102, 101, 103],
    '도서ID': [2, 3, 1, 4],
    '대출일': ['2024-03-01', '2024-03-02', '2024-03-05', '2024-03-07']
})

books_df.to_excel('도서_데이터.xlsx', index=False)     # 시트 하나짜리 엑셀 파일
print(pd.read_excel('도서_데이터.xlsx'))

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L218–L236

▶ 실행 결과

   도서ID        도서명    저자  출판년도
0     1  파이썬 프로그래밍  김파이썬  2019
1     2     데이터 분석  이데이터  2020
2     3    머신러닝 입문   박머신  2018
3     4   인공지능의 이해   최인공  2021

Python — AH-6: ExcelWriter — 여러 시트 저장

# 여러 DataFrame을 한 파일의 여러 시트에 저장
with pd.ExcelWriter('도서_데이터_전체.xlsx') as writer:
    books_df.to_excel(writer, sheet_name='도서_목록', index=False)
    members_df.to_excel(writer, sheet_name='회원_목록', index=False)
    borrow_df.to_excel(writer, sheet_name='대출_기록', index=False)

# sheet_name=None → 모든 시트를 {시트이름: DataFrame} 딕셔너리로 읽기
sheets = pd.read_excel('도서_데이터_전체.xlsx', sheet_name=None)
print("시트 목록:", list(sheets.keys()))
print(sheets['대출_기록'])
print("-" * 40)
print(pd.read_excel('도서_데이터_전체.xlsx', sheet_name='회원_목록'))   # 시트 하나만

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L240–L251

▶ 실행 결과

시트 목록: ['도서_목록', '회원_목록', '대출_기록']
   회원ID  도서ID         대출일
0   101     2  2024-03-01
1   102     3  2024-03-02
2   101     1  2024-03-05
3   103     4  2024-03-07
----------------------------------------
   회원ID   이름  가입년도
0   101  홍길동  2020
1   102  김영희  2021
2   103  이철수  2019

Python — AH-7: startrow · startcol — 한 시트에 배치

# 한 시트 안에 표 여러 개를 위치를 정해 배치하기 (startrow, startcol은 0부터)
with pd.ExcelWriter('도서_데이터_onesheet.xlsx') as writer:
    books_df.to_excel(writer, sheet_name='도서데이터', index=False)
    members_df.to_excel(writer, sheet_name='도서데이터', startrow=0, startcol=5, index=False)
    borrow_df.to_excel(writer, sheet_name='도서데이터', startrow=6, startcol=0, index=False)

raw = pd.read_excel('도서_데이터_onesheet.xlsx', header=None)   # 시트를 있는 그대로 보기
print(raw.fillna(''))

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L255–L262

▶ 실행 결과

       0          1           2     3 4     5    6     7
0   도서ID        도서명          저자  출판년도    회원ID   이름  가입년도
1      1  파이썬 프로그래밍        김파이썬  2019     101  홍길동  2020
2      2     데이터 분석        이데이터  2020     102  김영희  2021
3      3    머신러닝 입문         박머신  2018     103  이철수  2019
4      4   인공지능의 이해         최인공  2021
5
6   회원ID       도서ID         대출일
7    101          2  2024-03-01
8    102          3  2024-03-02
9    101          1  2024-03-05
10   103          4  2024-03-07

🧠 코드 이해 — 한 줄씩 뜯어보기

코드무슨 일이 일어나나
df_years.to_csv('years_data.csv')DataFrame을 CSV 파일로 저장. 지금 작업 폴더에 파일이 생깁니다. 코랩에서는 왼쪽 📁 폴더 아이콘에서 확인할 수 있습니다.
index=False행 이름표(0, 1, 2…)는 저장하지 않습니다. 저장할 때 거의 항상 붙이는 옵션입니다.
encoding='cp949'윈도우 한글 엑셀이 기본으로 쓰는 암호표로 저장/읽기.
try: ... except UnicodeDecodeError as e:에러가 나도 프로그램을 멈추지 않고 except 블록으로 넘어가 메시지를 출력합니다.
with pd.ExcelWriter('...') as writer:엑셀 파일 하나를 열어 두고, 블록 안의 모든 to_excel을 같은 파일에 씁니다. 블록이 끝나면 자동으로 저장·닫기.
pd.read_excel(..., sheet_name=None)모든 시트를 읽어 딕셔너리로 돌려줍니다. sheet_name=0은 첫 시트, "회원_목록"은 이름으로 지정.
startrow=6, startcol=0엑셀의 7행 A열(0부터 세므로 6행, 0열)부터 표를 씁니다.

🐍 파이썬 문법 짚고 가기

with ... as 이름: — "열고, 쓰고, 반드시 닫는다"를 자동으로 해 주는 문법. 파일을 열어 둔 채 잊어버리는 실수를 막습니다. 🔗 02권 파일 입출력.\
try / except — 에러가 날 수 있는 코드를 감싸고, 에러가 나면 대신 실행할 코드를 정합니다.\
dict.keys() — 딕셔너리의 key 목록. list()로 감싸면 보통 리스트가 됩니다.

🧭 네 가지 관점으로 보기 — 머신러닝까지 연결

📐 수학적 의미 — 파일 입출력은 직렬화(serialization)입니다. 메모리 속 2차원 구조를 1차원 바이트 열로 펴서 저장하고, 읽을 때 다시 2차원으로 접습니다.\
💡 직관적 의미 — CSV는 메모지(아무나 읽을 수 있지만 서식 없음), 엑셀은 바인더(여러 장, 서식 포함).\
🔧 구현 관점 — read_csv는 한 줄씩 읽어 구분자로 자르고, 열마다 자료형을 추론합니다. 그래서 숫자 열에 글자가 하나라도 섞이면 열 전체가 문자열이 됩니다.\
🏗️ 프레임워크 관점 — 머신러닝 데이터셋(캐글 등)은 대부분 CSV. 대용량에는 to_parquet(열 단위 압축 포맷)도 많이 씁니다. PyTorch의 Dataset도 내부에서 read_csv로 시작하는 경우가 많습니다.

📌 개념 정리

· CSV: read_csv/to_csv, 엑셀: read_excel/to_excel\
· 저장할 때는 index=False, 한글 깨지면 encoding 확인\
· 여러 시트는 ExcelWriter, 모든 시트 읽기는 sheet_name=None

⭐ 오늘의 한 줄 — 파일은 입구와 출구, 저장할 땐 index=False, 깨지면 인코딩

02-08-AI 데이터 첫인상 — shape · info · describe · head 🟢

새 데이터를 받으면 분석부터 하고 싶어지지만, 숙련자일수록 먼저 건강검진을 합니다. "몇 행 몇 열인가? 열 이름은? 자료형은 맞나? 빈 칸은 얼마나? 숫자 범위는 상식적인가?" 이 다섯 질문에 답하는 도구가 바로 shape, columns, info(), describe(), head()/tail()입니다.

🔍 더 쉽게 이해하기

병원 건강검진을 떠올려 보세요.\
📏 shape = 키와 몸무게 (행 수, 열 수) — 덩치 파악\
🪪 info() = 문진표 (각 열의 이름, 자료형, 빈 칸 없는 개수) — 기본 상태 파악\
🩸 describe() = 혈액검사 수치표 (평균, 표준편차, 최솟값, 사분위수, 최댓값) — 숫자가 정상 범위인지\
👀 head()/tail() = 얼굴 직접 보기 — 실제 생김새 확인\
검진 없이 수술(분석)부터 하면, 나이가 -5살이거나 키가 문자열인 데이터로 모델을 학습시키는 사고가 납니다.

📋 진행 과정

  1. 크기 — shape → (행, 열)
  2. 이름 — columns
  3. 상태 — info() → 자료형·결측
  4. 수치 — describe() → 요약 통계
  5. 생김새 — head() / tail()

🖼️ 시각화 — 건강검진 순서

(시각화) 데이터 건강검진 순서 — shape(덩치) → info()(문진표) → describe()(혈액검사) → head()(얼굴 보기)

💻 핵심 코드

Python — AI-1: shape와 columns

import pandas as pd

data = {
    "이름": ["김철수", "이영희", "박민수"],
    "나이": [25, 32, 19],
    "도시": ["서울", "부산", "대구"]
}
df = pd.DataFrame(data)
print(df)
print("데이터프레임의 차원:", df.shape)     # (행 수, 열 수) 튜플
print("행 수:", df.shape[0], "/ 열 수:", df.shape[1])
print(df.columns)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L266–L277

▶ 실행 결과

    이름  나이  도시
0  김철수  25  서울
1  이영희  32  부산
2  박민수  19  대구
데이터프레임의 차원: (3, 3)
행 수: 3 / 열 수: 3
Index(['이름', '나이', '도시'], dtype='str')

Python — AI-2: 열 이름 바꾸기

df_copy = df.copy()                       # 원본을 지키기 위해 복사본에서 실험
df_copy.columns = ['A', 'B', 'C']         # 열 이름 전체를 한 번에 바꾸기 (개수가 같아야 함)
print(df_copy)
print("-" * 30)
print(df.rename(columns={'도시': '거주도시'}))   # 일부만 바꿀 때는 rename

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L281–L285

▶ 실행 결과

     A   B   C
0  김철수  25  서울
1  이영희  32  부산
2  박민수  19  대구
------------------------------
    이름  나이 거주도시
0  김철수  25   서울
1  이영희  32   부산
2  박민수  19   대구

Python — AI-3: info() — 문진표

df.info()       # 문진표: 행 수, 열마다 결측 아닌 개수, 자료형, 메모리

▶ 실행 결과

<class 'pandas.DataFrame'>
RangeIndex: 3 entries, 0 to 2
Data columns (total 3 columns):
 #   Column  Non-Null Count  Dtype
---  ------  --------------  -----
 0   이름      3 non-null      str
 1   나이      3 non-null      int64
 2   도시      3 non-null      str
dtypes: int64(1), str(2)
memory usage: 204.0 bytes

Python — AI-4: describe() — 혈액검사 수치표

print(df.describe())                     # 숫자 열의 요약 통계
print("-" * 30)
print(df.describe(include='all'))        # 문자열 열까지 포함 (unique, top, freq)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L293–L295

▶ 실행 결과

              나이
count   3.000000
mean   25.333333
std     6.506407
min    19.000000
25%    22.000000
50%    25.000000
75%    28.500000
max    32.000000
------------------------------
         이름         나이   도시
count     3   3.000000    3
unique    3        NaN    3
top     김철수        NaN   서울
freq      1        NaN    1
mean    NaN  25.333333  NaN
std     NaN   6.506407  NaN
min     NaN  19.000000  NaN
25%     NaN  22.000000  NaN
50%     NaN  25.000000  NaN
75%     NaN  28.500000  NaN
max     NaN  32.000000  NaN

Python — AI-5: head · tail · option_context

print(df.head(1))     # 앞에서 1행 (기본값 5행)
print("-" * 30)
print(df.tail(1))     # 뒤에서 1행 (기본값 5행)
print("-" * 30)
# 출력 설정을 잠깐만 바꾸기: 최대 2행, 2열까지만 표시
with pd.option_context('display.max_rows', 2, 'display.max_columns', 2):
    print(df)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L299–L305

▶ 실행 결과

    이름  나이  도시
0  김철수  25  서울
------------------------------
    이름  나이  도시
2  박민수  19  대구
------------------------------
     이름  ...  도시
0   김철수  ...  서울
..  ...  ...  ..
2   박민수  ...  대구

[3 rows x 3 columns]

🧠 코드 이해 — 한 줄씩 뜯어보기

코드무슨 일이 일어나나
df.shape(3, 3) 같은 튜플. 괄호가 없는 속성입니다. df.shape()로 쓰면 에러가 납니다.
df.copy()완전히 독립된 복사본을 만듭니다. 복사본을 고쳐도 원본은 그대로입니다.
df_copy.columns = ['A', 'B', 'C']열 이름 목록을 통째로 갈아 끼웁니다. 개수가 다르면 에러.
df.rename(columns={'도시': '거주도시'}){옛 이름: 새 이름} 딕셔너리로 일부만 바꿉니다. 원본은 바뀌지 않고 새 DataFrame을 돌려줍니다.
df.info()괄호가 있는 메서드. 결과를 반환하지 않고 화면에 직접 출력하므로 print()로 감쌀 필요가 없습니다.
df.describe()count(개수), mean(평균), std(표준편차), min, 25%·50%·75%(사분위수), max. 50%가 중앙값입니다.
pd.option_context('display.max_rows', 2, ...)with 블록 안에서만 출력 설정을 바꿉니다. 블록을 나가면 원래 설정으로 돌아옵니다. 중간이 ...로 생략된 모습을 보세요.

🐍 파이썬 문법 짚고 가기

속성 vs 메서드 — df.shape, df.columns처럼 괄호 없는 것은 이미 계산된 값(속성), df.info(), df.head()처럼 괄호 있는 것은 실행하는 동작(메서드)입니다.\
튜플 인덱싱 — df.shape[0]은 튜플의 0번째 = 행 수, [1]은 열 수.

📐 수식으로 보기

describe()의 std는 표본 표준편차입니다. 🔗 01권 확률과 통계(#29)의 자유도 n−1이 여기서 쓰입니다.\
xˉ=1n∑i=1nxi,s=1n−1∑i=1n(xi−xˉ)2\bar{x} = \frac{1}{n}\sum_{i=1}^{n} x_i, \qquad s = \sqrt{\frac{1}{n-1}\sum_{i=1}^{n}(x_i-\bar{x})^2}나이 [25, 32, 19]: 평균 xˉ=76/3≈25.33\bar{x} = 76/3 \approx 25.33, 편차제곱합 =0.11+44.44+40.11=84.67= 0.11 + 44.44 + 40.11 = 84.67, s=84.67/2≈6.51s = \sqrt{84.67/2} \approx 6.51 — 위 실행 결과의 std와 일치합니다.

🧭 네 가지 관점으로 보기 — 머신러닝까지 연결

📐 수학적 의미 — describe()는 분포의 위치(평균·중앙값)와 퍼짐(표준편차·사분위 범위)을 숫자 8개로 요약합니다.\
💡 직관적 의미 — 데이터의 건강검진표. 이상한 수치(음수 나이, 최댓값 99999)를 첫눈에 잡아냅니다.\
🔧 구현 관점 — info()의 Non-Null Count가 행 수보다 작으면 결측치가 있다는 뜻입니다. Dtype이 예상과 다르면(숫자인데 str) 데이터에 글자가 섞인 것입니다.\
🏗️ 프레임워크 관점 — 머신러닝 전처리 1단계는 항상 df.info()와 df.describe(). 캐글 노트북의 첫 셀 대부분이 이 두 줄입니다.

📌 개념 정리

· shape(크기) → columns(이름) → info(자료형·결측) → describe(수치) → head(실물)\
· shape·columns는 속성, info·describe·head는 메서드\
· std는 n−1로 나눈 표본 표준편차

⭐ 오늘의 한 줄 — 분석 전에 건강검진 — shape, info, describe, head

🛑 잠깐! 초보자 체크포인트 ①

1·2편이 끝났습니다. 판다스의 뼈대를 세웠습니다.\
☐ Series와 DataFrame의 관계를 엑셀 시트·열로 설명할 수 있다\
☐ s1 + s2에서 NaN이 생기는 이유를 "이름표 맞추기"로 말할 수 있다\
☐ CSV를 저장·불러올 때 Unnamed: 0이 생기는 이유와 해결법을 안다\
→ 하나라도 막히면 AF · AH단원을 5분만 다시 읽고 오세요. 뒤로 갈수록 계속 쓰입니다.

🎯 3편 — 고르고 거르기 (AJ · AK)

02-08-AJ 골라 보기 — [] · loc · iloc 🟢 ⭐

엑셀에서 B3 셀을 클릭하거나 A1:C5 범위를 드래그하는 일을 판다스에서는 세 가지 방법으로 합니다.

방법기준엑셀로 치면슬라이싱 끝
df['열']열 이름열 머리글 클릭— (행 슬라이싱 df[0:2]는 위치 기준, 끝 미포함)
df.loc[행, 열]이름표(label)"3번 행, 나이 열"포함 ✅
df.iloc[행, 열]위치(integer location)"위에서 세 번째, 왼쪽에서 두 번째"미포함 ❌ (파이썬 리스트와 같음)

🔍 더 쉽게 이해하기

극장 좌석을 떠올려 보세요.\
🎫 loc은 좌석 번호표로 찾습니다. "F열 7번부터 9번까지" 하면 7, 8, 9번 세 자리 모두 드립니다. 사람에게 말하는 방식이죠.\
📏 iloc은 왼쪽에서 몇 번째로 찾습니다. 파이썬 리스트처럼 "0번째부터 2번째 전까지" → 0, 1번째 두 자리만 드립니다.\
이름표가 0, 1, 2…로 위치와 똑같이 생긴 표에서는 둘이 헷갈리기 쉽습니다. 그래서 "loc은 끝 포함, iloc은 끝 미포함"을 반드시 외워 두세요.

📋 진행 과정

  1. 열 고르기 — df['도시'], df[['도시','이름']]
  2. 행 고르기 — head/tail, df[0:2]
  3. 이름표로 — df.loc[행, 열]
  4. 위치로 — df.iloc[행, 열]
  5. 셀 하나 — df.loc[0, '이름']

🖼️ 시각화 — loc(끝 포함) vs iloc(끝 미포함)

(시각화) loc으로 1:2를 자르면 끝 포함(1·2행), iloc으로 0:2를 자르면 끝 미포함(0·1행)

💻 핵심 코드 ① — 열 고르기와 head/tail

Python — AJ-1: 열 고르기

column_data = df['도시']               # 열 하나 → Series
print(type(column_data).__name__)
print(column_data)
print("-" * 30)
selected_columns = df[['도시', '이름']]  # 열 여러 개(순서도 내 마음대로) → DataFrame
print(selected_columns)
print("-" * 30)
result = df[['도시']]                   # 열 하나지만 표 모양으로 받고 싶으면 이중 대괄호
print(type(result).__name__)
print(result)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L309–L318

▶ 실행 결과

Series
0    서울
1    부산
2    대구
Name: 도시, dtype: str
------------------------------
   도시   이름
0  서울  김철수
1  부산  이영희
2  대구  박민수
------------------------------
DataFrame
   도시
0  서울
1  부산
2  대구

💻 핵심 코드 ② — loc: 이름표 기준

Python — AJ-2: loc으로 행 고르기

print(df.loc[0])                       # 0번 행 → Series (열 이름이 이름표가 됨)
print("-" * 30)
print(df.loc[[0, 2]])                  # 떨어진 행 여러 개 → DataFrame
print("-" * 30)
print(df.loc[1:2])                     # 이어진 행: 1부터 2까지 (끝 포함!)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L322–L326

▶ 실행 결과

이름    김철수
나이     25
도시     서울
Name: 0, dtype: object
------------------------------
    이름  나이  도시
0  김철수  25  서울
2  박민수  19  대구
------------------------------
    이름  나이  도시
1  이영희  32  부산
2  박민수  19  대구

Python — AJ-3: loc으로 열과 셀 고르기

print(df.loc[:, '도시'])                # 모든 행(:)의 도시 열 → Series
print("-" * 30)
print(df.loc[:, ['도시', '이름']])       # 모든 행, 열 두 개 → DataFrame
print("-" * 30)
print(df.loc[:, '나이':'도시'])          # 나이부터 도시까지 (끝 포함)
print("-" * 30)
print(df.loc[0, '이름'])                # 셀 하나 → 값(스칼라)
print("-" * 30)
print(df.loc[1:2, ['이름', '나이']])     # 행 범위 × 열 목록

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L330–L338

▶ 실행 결과

0    서울
1    부산
2    대구
Name: 도시, dtype: str
------------------------------
   도시   이름
0  서울  김철수
1  부산  이영희
2  대구  박민수
------------------------------
   나이  도시
0  25  서울
1  32  부산
2  19  대구
------------------------------
김철수
------------------------------
    이름  나이
1  이영희  32
2  박민수  19

💻 핵심 코드 ③ — iloc: 위치 기준

Python — AJ-4: iloc으로 하나 고르기

print(df.iloc[0])          # 첫 번째 행
print("-" * 30)
print(df.iloc[:, 1])       # 두 번째 열(나이)
print("-" * 30)
print(df.iloc[-1])         # 마지막 행 (음수 = 뒤에서부터)
print("-" * 30)
print(df.iloc[:, -2])      # 뒤에서 두 번째 열

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L342–L348

▶ 실행 결과

이름    김철수
나이     25
도시     서울
Name: 0, dtype: object
------------------------------
0    25
1    32
2    19
Name: 나이, dtype: int64
------------------------------
이름    박민수
나이     19
도시     대구
Name: 2, dtype: object
------------------------------
0    25
1    32
2    19
Name: 나이, dtype: int64

Python — AJ-5: iloc 범위·목록 + 기본 슬라이싱

print(df.iloc[0:2])        # 0, 1번째 행 (2는 미포함!)
print("-" * 30)
print(df.iloc[:, 1:2])     # 1번째 열만 (2는 미포함) → DataFrame
print("-" * 30)
print(df.iloc[[0, 2]])     # 떨어진 행
print("-" * 30)
print(df.iloc[:, [0, 2]])  # 떨어진 열
print("-" * 30)
print(df[0:2])             # 대괄호에 슬라이스를 넣으면 행을 위치로 자름 (끝 미포함)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L352–L360

▶ 실행 결과

    이름  나이  도시
0  김철수  25  서울
1  이영희  32  부산
------------------------------
   나이
0  25
1  32
2  19
------------------------------
    이름  나이  도시
0  김철수  25  서울
2  박민수  19  대구
------------------------------
    이름  도시
0  김철수  서울
1  이영희  부산
2  박민수  대구
------------------------------
    이름  나이  도시
0  김철수  25  서울
1  이영희  32  부산

Python — AJ-6: 이름표 ≠ 위치일 때

# loc과 iloc이 갈리는 순간: 이름표가 위치와 다를 때
df_idx = df.set_index('이름')            # 이름 열을 행 이름표로
print(df_idx)
print("-" * 30)
print(df_idx.loc['이영희'])               # 이름표로
print("-" * 30)
print(df_idx.iloc[1])                    # 위치로 — 같은 행
print("-" * 30)
print(df_idx.loc['김철수':'이영희', '나이'])  # 이름표 범위도 끝 포함

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L364–L372

▶ 실행 결과

     나이  도시
이름
김철수  25  서울
이영희  32  부산
박민수  19  대구
------------------------------
나이    32
도시    부산
Name: 이영희, dtype: object
------------------------------
나이    32
도시    부산
Name: 이영희, dtype: object
------------------------------
이름
김철수    25
이영희    32
Name: 나이, dtype: int64

🧠 코드 이해 — 한 줄씩 뜯어보기

코드무슨 일이 일어나나
df['도시']대괄호 한 겹 + 열 이름 하나 → Series.
df[['도시']]대괄호 두 겹 → 열이 하나여도 DataFrame. 이후 다른 표와 합칠 때 모양을 맞추기 좋습니다.
df.loc[1:2]이름표 1부터 2까지. 끝 포함. 이름표에는 "다음 것"이라는 개념이 없어서 끝까지 포함하는 것이 자연스럽기 때문입니다.
df.loc[:, '나이':'도시']쉼표 앞은 행, 뒤는 열. : 혼자 있으면 "전부".
df.iloc[-1]파이썬 리스트처럼 음수 위치를 쓸 수 있습니다. loc에서는 -1이라는 이름표가 없으면 에러.
df.iloc[:, 1:2]위치 1 이상 2 미만 → 열 하나지만 슬라이스로 골랐으므로 DataFrame.
df_idx.loc['김철수':'이영희', '나이']문자열 이름표 범위 + 열 하나 → Series.

🐍 파이썬 문법 짚고 가기

슬라이싱 start:stop — 파이썬 기본 규칙은 stop 미포함. [0:2] → 0, 1. iloc과 df[0:2]는 이 규칙을 따르고, loc만 예외(포함)입니다.\
음수 인덱스 — -1은 마지막, -2는 뒤에서 두 번째.\
type(x).__name__ — 클래스 이름만 문자열로 꺼냅니다("Series", "DataFrame").

🧭 네 가지 관점으로 보기 — 머신러닝까지 연결

📐 수학적 의미 — loc은 이름표 집합 위의 닫힌 구간 [a,b][a, b], iloc은 위치 집합 위의 반열린 구간 [a,b)[a, b) 입니다.\
💡 직관적 의미 — loc = 좌석 번호표로 찾기, iloc = 왼쪽부터 세기.\
🔧 구현 관점 — 하나를 고르면 차원이 줄고(DataFrame→Series→값), 목록이나 슬라이스로 고르면 차원이 유지됩니다.\
🏗️ 프레임워크 관점 — NumPy의 arr[0:2, 1]은 iloc과 같은 규칙입니다. PyTorch 텐서 인덱싱도 iloc 방식. 판다스만의 특별 기능이 loc입니다.

📌 개념 정리

· 열은 df['열'](Series) / df[['열']](DataFrame)\
· loc = 이름표, 끝 포함 · iloc = 위치, 끝 미포함\
· [행, 열] 순서, :는 전부

⭐ 오늘의 한 줄 — loc은 번호표(끝 포함), iloc은 위치(끝 미포함)

02-08-AK 조건 필터 — 엑셀의 자동 필터 🟢 ⭐

엑셀에서 열 머리글의 ▼ 단추를 눌러 "나이 20 초과"만 보이게 하는 자동 필터를, 판다스에서는 불리언 인덱싱으로 합니다. 원리는 두 단계입니다. ① 조건식으로 행마다 True/False가 적힌 마스크를 만들고, ② 그 마스크를 대괄호에 넣어 True인 행만 남깁니다.

🔍 더 쉽게 이해하기

마스크는 체크리스트가 적힌 투명 필름입니다.\
명단 위에 필름을 겹치는데, 필름에는 행마다 ⭕(True) 또는 ❌(False)가 적혀 있습니다. ⭕ 자리의 사람만 통과시키는 거죠.\
조건이 두 개면 필름 두 장을 겹칩니다. &(그리고)는 두 필름 모두 ⭕인 자리만, |(또는)는 한 장이라도 ⭕인 자리, ~(아니다)는 ⭕와 ❌를 뒤집습니다.

📋 진행 과정

  1. 조건식 — df['나이'] > 20
  2. 마스크 — [True, True, False]
  3. 적용 — df[마스크]
  4. 조건 조합 — (조건1) & (조건2)
  5. 목록 조건 — isin(['대구','부산'])

🖼️ 시각화 — 마스크 두 장 겹치기

(시각화) 마스크 두 장 겹치기 — 나이 > 20 그리고 도시 != 서울을 모두 통과한 건 이영희 한 명

💻 핵심 코드

Python — AK-1: 조건 하나

mask = df['나이'] > 20          # ① 마스크 만들기
print(mask)
print("-" * 30)
filtered_rows1 = df[mask]       # ② 마스크 적용 (한 줄로 df[df['나이'] > 20] 도 같음)
print(filtered_rows1)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L376–L380

▶ 실행 결과

0     True
1     True
2    False
Name: 나이, dtype: bool
------------------------------
    이름  나이  도시
0  김철수  25  서울
1  이영희  32  부산

Python — AK-2: & · | · ~ 로 조건 조합

# 조건 여러 개: 각 조건을 반드시 괄호로 감싸기
filtered_rows2 = df[(df['나이'] > 20) & (df['도시'] != '서울')]
print(filtered_rows2)
print("-" * 30)
print(df[(df['나이'] < 20) | (df['도시'] == '서울')])   # 또는
print("-" * 30)
print(df[~(df['도시'] == '서울')])                      # 아니다 (서울이 아닌 사람)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L384–L390

▶ 실행 결과

    이름  나이  도시
1  이영희  32  부산
------------------------------
    이름  나이  도시
0  김철수  25  서울
2  박민수  19  대구
------------------------------
    이름  나이  도시
1  이영희  32  부산
2  박민수  19  대구

Python — AK-3: isin · loc 조합 · query

filtered_rows_isin = df[df['도시'].isin(['대구', '부산'])]   # 목록 안에 있는 값이면 True
print(filtered_rows_isin)
print("-" * 30)
print(df.loc[df['나이'] >= 25, ['이름', '나이']])           # 조건 + 열 고르기는 loc으로 한 번에
print("-" * 30)
print(df.query("나이 > 20 and 도시 != '서울'"))             # 문자열로 조건 쓰기 (SQL 느낌)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L394–L399

▶ 실행 결과

    이름  나이  도시
1  이영희  32  부산
2  박민수  19  대구
------------------------------
    이름  나이
0  김철수  25
1  이영희  32
------------------------------
    이름  나이  도시
1  이영희  32  부산

Python — AK-4: ❌ 자주 하는 실수 — 괄호 누락

# 괄호를 빼먹으면 어떻게 될까?
try:
    df[df['나이'] > 20 & df['도시'] != '서울']
except Exception as e:
    print("❌", type(e).__name__)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L403–L407

▶ 실행 결과

❌ TypeError

🚨 왜 괄호가 필수인가?

파이썬에서 &는 >, !=보다 먼저 계산됩니다. 괄호가 없으면 20 & df['도시']를 먼저 계산하려다 에러가 납니다. 또 and/or는 Series에 쓸 수 없습니다(값 하나의 참/거짓만 다루기 때문). 반드시 & | ~와 괄호를 쓰세요. 단, query() 문자열 안에서는 and/or를 씁니다.

🧠 코드 이해 — 한 줄씩 뜯어보기

코드무슨 일이 일어나나
df['나이'] > 20열 전체에 비교가 한 번에 적용되어 True/False Series(마스크)가 됩니다. 🔗 02-07 브로드캐스트.
df[mask]대괄호에 불리언 Series를 넣으면 "열 고르기"가 아니라 "행 거르기"로 동작합니다.
(조건1) & (조건2)원소별 AND. 같은 행끼리 두 마스크를 겹칩니다.
df['도시'].isin(['대구', '부산'])`(도시==대구)
df.loc[조건, ['이름', '나이']]행은 조건으로, 열은 이름으로. 실무에서 가장 많이 쓰는 형태입니다.
df.query("...")조건을 문자열로 적습니다. 열 이름에 공백·괄호가 있으면 '열 이름'처럼 백틱으로 감쌉니다.

🐍 파이썬 문법 짚고 가기

연산자 우선순위 — 비트 연산자 & |가 비교 연산자보다 먼저. 그래서 괄호가 필요합니다.\
~ — 원래는 비트 반전, 판다스에서는 True↔False 뒤집기.\
and vs & — and는 "값 하나가 참인가"를 묻는 파이썬 문법, &는 "칸마다 둘 다 참인가"를 계산하는 연산자.

🧭 네 가지 관점으로 보기 — 머신러닝까지 연결

📐 수학적 의미 — 조건은 집합입니다. A={i:나이i>20}A = \{i : \text{나이}_i > 20\}, B={i:도시i≠서울}B = \{i : \text{도시}_i \ne \text{서울}\} 일 때 & 는 A∩BA \cap B, | 는 A∪BA \cup B, ~ 는 여집합 AcA^c.\
💡 직관적 의미 — 투명 필름(마스크)을 겹쳐 ⭕ 자리만 통과.\
🔧 구현 관점 — 마스크는 bool 배열 하나일 뿐이라 매우 빠릅니다. 100만 행도 순식간입니다. for문으로 한 행씩 검사하는 것보다 수십~수백 배 빠릅니다.\
🏗️ 프레임워크 관점 — ML에서는 df[df['label'] == 1]로 클래스별 데이터 분리, 이상치 제거(df[df['나이'].between(0, 120)])에 매일 씁니다.

📌 개념 정리

· 조건식 → 마스크 → df[마스크]\
· 조합은 & | ~ + 괄호 필수\
· 목록 조건은 isin, 문자열 조건은 query

⭐ 오늘의 한 줄 — 조건이 마스크를 만들고, 마스크가 행을 거른다

✂️ 4편 — 고치고 다듬기 (AL · AM · AN)

02-08-AL 행·열 추가·삭제·값 수정 — 표 고치기 🔵

이제 표를 직접 고쳐 봅시다. 온라인 쇼핑몰 주문 데이터를 예제로, 열 추가([], insert), 행 추가(loc, concat), 삭제(drop), 값 수정(loc/iloc 대입)을 차례로 해 봅니다. 이 단계에서 만든 표는 9·10단계(결측치·정렬)에서 계속 씁니다.

🔍 더 쉽게 이해하기

표 고치기는 레고 조립과 같습니다.\
🧱 열 추가 = 옆에 새 블록 줄 끼우기. 맨 끝에 붙이면 df['새열'] = 값, 중간에 끼우면 insert.\
🧱 행 추가 = 아래에 새 블록 줄 쌓기. 한 줄은 loc[새번호], 여러 줄 묶음은 concat.\
🔨 삭제 = drop. 단, 기본적으로는 "뺀 모습의 사진"만 보여 주고 원본 레고는 그대로 둡니다. 정말로 바꾸려면 결과를 다시 변수에 담아야 합니다.\
🎨 값 수정 = 특정 블록에 색칠하기. df.loc[행, 열] = 새값.

📋 진행 과정

  1. 열 추가 — df['배송료']=3000, insert
  2. 행 추가 — loc[7]=dict, concat
  3. 열로 이어붙이기 — concat(axis=1)
  4. 삭제 — drop(axis=0/1)
  5. 값 수정 — loc/iloc 대입, 조건부 대입

🖼️ 시각화 — concat의 두 방향

(시각화) concat의 두 방향 — axis=0은 아래로 쌓기, axis=1은 옆으로 붙이기

💻 핵심 코드 ① — 열 추가

Python — AL-1: 열 추가 — 대입과 insert

import pandas as pd

data = {
    "주문번호": [1, 2, 3, 4, 5, 6, 7],
    "고객명": ["유재석", "조세호", "유재석", "권상우", "조세호", "권상우", "유재석"],
    "주문금액": [20000, 50000, 15000, 30000, 100000, 25000, 20000],
    "주문일자": ["2024-03-01", "2024-03-01", "2024-03-02", "2024-03-02",
                 "2024-03-03", "2024-03-03", "2024-03-04"],
    "배송지역": ["서울", "부산", "서울", "대구", "부산", "대구", "서울"]
}
df = pd.DataFrame(data)

df['배송료'] = 3000                     # 맨 오른쪽에 새 열 (모든 행에 3000)
df.insert(6, '배송상태', '배송완료')     # 위치 6(7번째 자리)에 새 열 끼우기
print(df)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L411–L425

▶ 실행 결과

   주문번호  고객명    주문금액        주문일자 배송지역   배송료  배송상태
0     1  유재석   20000  2024-03-01   서울  3000  배송완료
1     2  조세호   50000  2024-03-01   부산  3000  배송완료
2     3  유재석   15000  2024-03-02   서울  3000  배송완료
3     4  권상우   30000  2024-03-02   대구  3000  배송완료
4     5  조세호  100000  2024-03-03   부산  3000  배송완료
5     6  권상우   25000  2024-03-03   대구  3000  배송완료
6     7  유재석   20000  2024-03-04   서울  3000  배송완료

💻 핵심 코드 ② — 행 추가

Python — AL-2: loc으로 한 행 추가

new_order = {"주문번호": 8, "고객명": "이무진", "주문금액": 45000, "주문일자": "2024-03-05"}
df.loc[7] = new_order          # 이름표 7인 행이 없으므로 새로 만들어짐
print(df.tail(2))              # 딕셔너리에 없던 열은 NaN(빈 칸)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L429–L431

▶ 실행 결과

   주문번호  고객명   주문금액        주문일자 배송지역     배송료  배송상태
6     7  유재석  20000  2024-03-04   서울  3000.0  배송완료
7     8  이무진  45000  2024-03-05  NaN     NaN   NaN

Python — AL-3: concat으로 행 추가

# concat: 표와 표를 이어 붙이기 — 딕셔너리를 한 줄짜리 DataFrame으로 만들어 붙임
new_dict = {'주문번호': 9, '고객명': '이상순', '주문금액': 40000, '배송료': 3000}
df = pd.concat([df, pd.DataFrame([new_dict])], ignore_index=True)

new_dict = {'주문번호': 10, '고객명': '이효리', '주문금액': 35000, '배송료': 3000}
df = pd.concat([df, pd.DataFrame([new_dict])], ignore_index=True)
print(df.tail(3))
print("-" * 40)

# 여러 행을 한꺼번에 — 결과를 새 변수에 (원본 df는 그대로 10행 유지)
new_orders_df = pd.DataFrame({'주문번호': [11, 12],
                              '고객명': ['김종국', '하하'],
                              '주문금액': [25000, 15000],
                              '배송료': [3000, 3000]})
df_updated = pd.concat([df, new_orders_df], ignore_index=True)
print("df:", df.shape, "/ df_updated:", df_updated.shape)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L435–L450

▶ 실행 결과

   주문번호  고객명   주문금액        주문일자 배송지역     배송료 배송상태
7     8  이무진  45000  2024-03-05  NaN     NaN  NaN
8     9  이상순  40000         NaN  NaN  3000.0  NaN
9    10  이효리  35000         NaN  NaN  3000.0  NaN
----------------------------------------
df: (10, 7) / df_updated: (12, 7)

Python — AL-4: ⚠️ Series.to_frame().T의 함정

# Series를 행으로 바꿔 붙이는 방법도 있지만…
new_series = pd.Series([9, '이상순', 40000, 3000], index=['주문번호', '고객명', '주문금액', '배송료'])
tmp = pd.concat([df.head(2), new_series.to_frame().T], ignore_index=True)
print(tmp.dtypes)
print("-" * 30)
print(tmp.infer_objects().dtypes)     # 자료형을 다시 추론해 복구

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L454–L459

▶ 실행 결과

주문번호    object
고객명     object
주문금액    object
주문일자       str
배송지역    object
배송료     object
배송상태    object
dtype: object
------------------------------
주문번호      int64
고객명         str
주문금액      int64
주문일자        str
배송지역        str
배송료     float64
배송상태        str
dtype: object

🚨 왜 숫자 열이 object가 됐을까?

문자열과 숫자가 섞인 Series는 자료형이 object 하나로 통일됩니다. 이걸 행으로 뒤집어(.T) 붙이면 숫자 열까지 전부 object가 되어 mean() 같은 계산이 느려지거나 에러가 납니다. 행을 추가할 때는 딕셔너리 → pd.DataFrame([dict])가 가장 안전하고, 이미 망가졌다면 infer_objects()로 복구합니다.

Python — AL-5: concat(axis=1) — 열로 이어 붙이기

concat_series = pd.Series(
    [1000, 2000, 1500, 3000, 5000, 2500, 2000, 1000, 4000],   # 9개뿐 (df는 10행)
    name='할인금액'                                           # name이 열 이름이 됨
)
df = pd.concat([df, concat_series], axis=1, join='inner')    # 공통 index(0~8)만 남김
print(df)

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L463–L468

▶ 실행 결과

   주문번호  고객명    주문금액        주문일자 배송지역     배송료  배송상태  할인금액
0     1  유재석   20000  2024-03-01   서울  3000.0  배송완료  1000
1     2  조세호   50000  2024-03-01   부산  3000.0  배송완료  2000
2     3  유재석   15000  2024-03-02   서울  3000.0  배송완료  1500
3     4  권상우   30000  2024-03-02   대구  3000.0  배송완료  3000
4     5  조세호  100000  2024-03-03   부산  3000.0  배송완료  5000
5     6  권상우   25000  2024-03-03   대구  3000.0  배송완료  2500
6     7  유재석   20000  2024-03-04   서울  3000.0  배송완료  2000
7     8  이무진   45000  2024-03-05  NaN     NaN   NaN  1000
8     9  이상순   40000         NaN  NaN  3000.0   NaN  4000

💻 핵심 코드 ③ — 삭제

Python — AL-6: drop

print(df.drop('배송료', axis=1).head(3))                 # 열 하나 삭제 (axis=1 = 열)
print("-" * 40)
print(df.drop(['주문일자', '배송지역'], axis=1).head(3))   # 열 여러 개
print("-" * 40)
print(df.drop(8, axis=0).tail(3))                       # 이름표 8인 행 삭제 (axis=0 = 행)
print("-" * 40)
print("원본은 그대로:", df.shape)                         # drop은 원본을 바꾸지 않음

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L472–L478

▶ 실행 결과

   주문번호  고객명   주문금액        주문일자 배송지역  배송상태  할인금액
0     1  유재석  20000  2024-03-01   서울  배송완료  1000
1     2  조세호  50000  2024-03-01   부산  배송완료  2000
2     3  유재석  15000  2024-03-02   서울  배송완료  1500
----------------------------------------
   주문번호  고객명   주문금액     배송료  배송상태  할인금액
0     1  유재석  20000  3000.0  배송완료  1000
1     2  조세호  50000  3000.0  배송완료  2000
2     3  유재석  15000  3000.0  배송완료  1500
----------------------------------------
   주문번호  고객명   주문금액        주문일자 배송지역     배송료  배송상태  할인금액
5     6  권상우  25000  2024-03-03   대구  3000.0  배송완료  2500
6     7  유재석  20000  2024-03-04   서울  3000.0  배송완료  2000
7     8  이무진  45000  2024-03-05  NaN     NaN   NaN  1000
----------------------------------------
원본은 그대로: (9, 8)

💻 핵심 코드 ④ — 값 수정

Python — AL-7: loc/iloc 대입과 조건부 수정

df.iloc[0, 5] = 2500                                   # 0번째 행, 5번째 열(배송료)
df.loc[1, '배송료'] = df.loc[1, '배송료'] - 500          # 기존 값에서 500 빼기
print(df.loc[0:1, ['고객명', '배송료']])
print("-" * 40)
df.loc[df['고객명'] != '유재석', '배송료'] = 2800         # 조건에 맞는 행들의 배송료를 한 번에
print(df[['고객명', '배송료']])

🔗 GitHub에서 코드 보기 — 02_08_pandas.py L482–L487

▶ 실행 결과

   고객명     배송료
0  유재석  2500.0
1  조세호  2500.0
----------------------------------------
   고객명     배송료
0  유재석  2500.0
1  조세호  2800.0
2  유재석  3000.0
3  권상우  2800.0
4  조세호  2800.0
5  권상우  2800.0
6  유재석  3000.0
7  이무진  2800.0
8  이상순  2800.0

🧠 코드 이해 — 한 줄씩 뜯어보기

코드무슨 일이 일어나나
df['배송료'] = 3000없는 열 이름에 대입하면 새 열이 생깁니다. 값 하나를 주면 모든 행에 복사(브로드캐스트).
df.insert(6, '배송상태', '배송완료')(위치, 열 이름, 값). insert는 원본을 바로 바꾸고 아무것도 반환하지 않습니다.
df.loc[7] = new_order없는 이름표에 대입하면 행 추가. 딕셔너리의 key와 열 이름이 맞는 곳에 들어가고 나머지는 NaN. 배송료가 3000에서 3000.0으로 바뀐 이유는 NaN이 실수(float) 세계의 값이라 정수 열이 실수 열로 바뀌었기 때문입니다.
pd.DataFrame([new_dict])딕셔너리를 리스트로 한 번 감싸야 "한 행짜리 표"가 됩니다.
ignore_index=True붙인 뒤 이름표를 0부터 새로 매깁니다. 빼면 0, 0, 1… 처럼 중복 이름표가 생길 수 있습니다.
axis=1, join='inner'옆으로 붙이되 양쪽 모두에 있는 index(0~8)만 남깁니다. 기본값 join='outer'면 9번 행의 할인금액이 NaN으로 남습니다.
df.drop('배송료', axis=1)삭제한 새 표를 돌려줄 뿐 원본은 그대로. 바꾸려면 df = df.drop(...).
df.loc[df['고객명'] != '유재석', '배송료'] = 2800"조건에 맞는 행의 배송료 칸"에 2800 대입. 엑셀의 "필터 → 보이는 셀에 값 붙여넣기"와 같습니다.

🐍 파이썬 문법 짚고 가기

axis — 판다스의 영원한 헷갈림 포인트. axis=0은 "행 방향(세로로 이동)", axis=1은 "열 방향(가로로 이동)". 삭제에서는 axis=0 → 행 삭제, axis=1 → 열 삭제로 외우면 됩니다. 🔗 02-07 NumPy의 axis와 같은 개념입니다.\
inplace=True — 원본을 직접 바꾸는 옵션. 최근 판다스는 df = df.drop(...)처럼 다시 대입하는 방식을 권장합니다.

🧭 네 가지 관점으로 보기 — 머신러닝까지 연결

📐 수학적 의미 — concat(axis=0)은 행 집합의 합집합 I1∪I2I_1 \cup I_2, concat(axis=1, join=inner)은 index의 교집합 I1∩I2I_1 \cap I_2 위에서 열을 합칩니다.\
💡 직관적 의미 — 레고 줄을 아래로 쌓기(axis=0) / 옆으로 끼우기(axis=1).\
🔧 구현 관점 — concat은 매번 새 표를 통째로 복사합니다. 반복문 안에서 한 행씩 concat하면 매우 느리므로, 행들을 리스트에 모았다가 마지막에 한 번 concat하세요.\
🏗️ 프레임워크 관점 — NumPy의 np.concatenate/vstack/hstack, PyTorch의 torch.cat(dim=0/1)과 같은 개념. 판다스는 여기에 이름표 맞추기가 더해집니다.

📌 개념 정리

· 열 추가: df['새열']=값, 중간 삽입은 insert\
· 행 추가: loc[새번호] 또는 concat([df, pd.DataFrame([dict])])\
· drop은 원본 불변 → 다시 대입, axis=0 행 / axis=1 열\
· 조건부 수정은 df.loc[조건, 열] = 값

⭐ 오늘의 한 줄 — 붙일 땐 concat, 뺄 땐 drop, 고칠 땐 loc


💻 GitHub 실습 — 코드로 직접 확인하기

📂 이 장의 코드 폴더: book/02-hello-python/02-08-pandas

책에 실린 코드를 장(절)별로 모아 둔 폴더입니다. 아래 파일을 열어 보거나, Colab에서 바로 실행해 보세요.

파일용도링크
02_08_pandas.py책의 코드 블록 89개를 순서대로 모은 실행 스크립트 (# %% 셀 구분)코드 보기
02_08_pandas.ipynb같은 코드를 블록당 셀 1개로 담은 주피터 노트북보기 · Colab
README.md코드 블록 목록 · 실행 방법 · 검증 결과보기

🧪 저장소 검증 상태: ✅ 실행 OK

🔑 핵심 코드 1 — 판다스의 핵심 — 이름표를 맞춰 더하는 Series 연산과 NaN, fill_value

📄 02_08_pandas.py L110–L117 · Block 11 (02-08-AF Series — 이름표 붙은 한 줄 🟢)

s1 = pd.Series([1, 2, 3], index=['a', 'b', 'c'])
s2 = pd.Series([10, 20, 30], index=['b', 'c', 'd'])

print(s1 + s2)          # 같은 이름표끼리 더하기 → a, d는 짝이 없어 NaN
print("-" * 30)
print(s1.add(s2, fill_value=0))   # 짝이 없을 때 0으로 간주하고 더하기
print("-" * 30)
print(s1 * 10)          # 스칼라 연산은 모든 칸에 한꺼번에 (브로드캐스트)

▶ 실행 결과 — 위 코드를 실행한 출력 (직접 실행: Linux · Python 3.13.5 · numpy 2.5.3, 앞 블록부터 순서대로 실행)

a     NaN
b    12.0
c    23.0
d     NaN
dtype: float64
------------------------------
a     1.0
b    12.0
c    23.0
d    30.0
dtype: float64
------------------------------
a    10
b    20
c    30
dtype: int64

🔑 핵심 코드 2 — 조건 필터 — & · | · ~ 와 조건마다 괄호

📄 02_08_pandas.py L384–L390 · Block 35 (02-08-AK 조건 필터 — 엑셀의 자동 필터 🟢 ⭐ — 조건 여러 개: 각 조건을 반드시 괄호로 감싸기)

# 조건 여러 개: 각 조건을 반드시 괄호로 감싸기
filtered_rows2 = df[(df['나이'] > 20) & (df['도시'] != '서울')]
print(filtered_rows2)
print("-" * 30)
print(df[(df['나이'] < 20) | (df['도시'] == '서울')])   # 또는
print("-" * 30)
print(df[~(df['도시'] == '서울')])                      # 아니다 (서울이 아닌 사람)

▶ 실행 결과 — 위 코드를 실행한 출력 (직접 실행: Linux · Python 3.13.5 · numpy 2.5.3, 앞 블록부터 순서대로 실행)

    이름  나이  도시
1  이영희  32  부산
------------------------------
    이름  나이  도시
0  김철수  25  서울
2  박민수  19  대구
------------------------------
    이름  나이  도시
1  이영희  32  부산
2  박민수  19  대구

☁️ Colab에서 바로 실행

👉 Open in Colab — 02_08_pandas.ipynb · 설치 없이 브라우저에서 셀을 위에서부터 순서대로 실행하세요.

🖥️ 내 컴퓨터에서 실행하기

git clone https://github.com/otoloism/Zero_to_ML_Master.git
cd Zero_to_ML_Master
python -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate
pip install -r book/requirements.txt   # 책 전체용 (PyTorch 등 포함, 용량 큼)
# 이 장만 실행한다면 이것만으로 충분합니다: pip install numpy openpyxl pandas plotly
cd book/02-hello-python/02-08-pandas
python 02_08_pandas.py

⭐ GitHub Star / Fork

⭐ 코드가 도움이 되셨다면 GitHub 저장소에 Star를 눌러 주세요. 새 장의 코드가 올라올 때 가장 먼저 알 수 있습니다.
🍴 직접 고쳐 보며 실험하고 싶다면 Fork 해서 내 저장소에서 마음껏 바꿔 보세요. 오류 제보·개선 제안은 Issues로 환영합니다!


🔮 다음 글 예고

다음 글은 #42 판다스(Pandas) 2부 — 결측치·정렬·중복, 통계·문자열 처리, groupby·피벗·merge부터 공공데이터 전기차 분석까지 입니다. 빈칸(NaN)을 찾고 채우고 지우는 세 가지 전략, 정렬과 중복 제거, .str 문자열 처리, 엑셀의 SUMIF·피벗 테이블·VLOOKUP을 대신하는 groupby·pivot_table·merge, Plotly 시각화, 그리고 공공데이터로 지역별 전기차·충전기를 분석하는 실전까지 이어 갑니다.

⬅️ 이전 글: #40 NumPy 배열 입문 — shape·axis·브로드캐스트와 * vs @ 행렬 곱, for문보다 빠른 벡터화까지 (02권 7부) \
➡️ 다음 글: #42 판다스(Pandas) 2부 — 결측치·정렬·중복, 통계·문자열 처리, groupby·피벗·merge부터 공공데이터 전기차 분석까지 (02권 8부 2)


📚 더 깊이 공부하고 싶다면

이 시리즈는 수학을 놓았던 비전공자가 AI를 직접 구현하기까지의 공부 기록을 그대로 옮긴 연재입니다. 원문 책, 코드, 개념 사전을 함께 보면 훨씬 빨리 늡니다. 🚀

바로가기이런 분께
📘 책 (위키독스, 무료)Zero to 머신러닝 딥러닝 Master5권 · 20개 장 · 163개 절 전체를 순서대로 읽고 싶은 분
📄 이 글의 원문02-08 🐼 8부 — Pandas: 파이썬으로 쓰는 엑셀 (1부)연습문제·해설까지 전부 보고 싶은 분
💻 실습 코드 (GitHub)otoloism/Zero_to_ML_Master.py·.ipynb를 Colab에서 바로 돌려보고 싶은 분
🏠 머신러닝 백과사전mldict.net본문 속 핵심 개념을 카드로 다시 확인하고 싶은 분
✍️ 기술 블로그wikidocs.net/blog/@mldict새 글 알림을 가장 먼저 받고 싶은 분

⭐ 도움이 되셨다면 velog 팔로우와 GitHub Star를 눌러 주세요. 다음 글을 쓰는 가장 큰 힘이 됩니다. 궁금한 점은 댓글로 편하게 남겨 주세요! 🙌

profile
자유주의자

0개의 댓글