pandas의 모든 것은 Series(1차원) 와 DataFrame(2차원), 그리고 두 구조의 행·열에 붙는 Index(라벨) 위에 세워집니다.
DataFrame은 "같은 길이의 Series 여러 개를 열로 묶고, 공통 행 라벨(Index)을 공유"하는 구조입니다.
NumPy ndarray가 "위치(0,1,2)로만 접근하는 균일 dtype 블록"이라면, pandas는 그 위에 의미 있는 라벨과 열별 dtype, 결측 표현을 얹은 계층입니다.
import pandas as pd
import numpy as np논의의 계층을 먼저 획정합니다.
pandas는 내부적으로 열 데이터를 NumPy 배열(또는 pandas 확장 배열)로 보관합니다. 그래서 벡터화·브로드캐스팅은 그대로 쓰되, 추가로 Index 기반 자동 정렬(alignment)이 붙습니다.
a = pd.Series([1, 2, 3], index=["x", "y", "z"])
b = pd.Series([10, 20, 30], index=["y", "z", "w"])
a + b
# w NaN
# x NaN
# y 12.0
# z 23.0 ← 라벨이 일치하는 것끼리만 더하고, 없는 라벨은 NaN💡 이럴 때: 서로 다른 소스에서 온 두 시계열/표를 합칠 때. 위치가 아니라 라벨로 맞춰지므로 순서가 달라도 안전하지만, 라벨이 어긋나면 조용히
NaN이 생깁니다.
s = pd.Series([10, 20, 30], index=["a", "b", "c"], name="score")
s.values # array([10, 20, 30]) - 내부 NumPy 배열
s.index # Index(['a', 'b', 'c'], dtype='object')
s.dtype # dtype('int64')
s["b"] # 20 - 라벨 접근
s.iloc[1] # 20 - 위치 접근Series는 "정렬된 dict"로 봐도 됩니다: 키(Index) → 값.name 속성은 DataFrame의 열로 들어갈 때 열 이름이 됩니다.df = pd.DataFrame({
"name": ["Kim", "Lee", "Park"],
"age": [25, 30, 35],
"city": ["Seoul", "Busan", "Seoul"],
})
df.shape # (3, 3)
df.columns # Index(['name', 'age', 'city'], dtype='object')
df.index # RangeIndex(start=0, stop=3, step=1)
df.dtypes # name: object, age: int64, city: object
df["age"] # 열 하나 → Series
df[["name", "age"]] # 열 여러 개 → DataFrameobject로 승격되는 경우가 많습니다.Index는 값이 아니라 행·열의 이름표입니다. 다음 기능의 핵심입니다.
.loc)를 O(1)로 만듦.set_index/reset_index로 열↔인덱스 전환, MultiIndex로 계층 구성.df2 = df.set_index("name") # name 열을 행 인덱스로
df2.loc["Lee"] # 라벨로 행 조회
df2.reset_index() # 다시 기본 RangeIndex로mi = pd.MultiIndex.from_tuples([("Seoul", 2025), ("Seoul", 2026), ("Busan", 2026)],
names=["city", "year"])"city별 year별 값"처럼 2개 이상의 키로 행을 식별할 때 씁니다. → 자세히는 Python - pandas 09. 재구조화 (pivot·melt·stack).
s = pd.Series([1, 2, None])
s.dtype # float64 ← None 때문에 정수가 float로 승격됨
s2 = pd.Series([1, 2, None], dtype="Int64")
s2.dtype # Int64 ← 결측을 담고도 정수 유지
s2 + 1 # [2, 3, <NA>]💡 이럴 때: ID·개수처럼 정수여야 하는 열에 결측이 섞여 있을 때
Int64(nullable)로 지정하면1.0,2.0같은 float 표기를 피할 수 있습니다.
💡 이럴 때: 도시·등급·상태값처럼 값의 종류가 적은 문자열 열은 astype("category")로 바꾸면 메모리와 groupby 속도가 크게 개선됩니다.
NumPy처럼 pandas도 인덱싱 결과가 view일 수도, copy일 수도 있습니다. 게다가 어느 쪽인지 버전·연산에 따라 달라져 예측이 어렵습니다. 그래서 pandas는 연쇄 인덱싱(chained indexing) 대입에 경고를 띄웁니다.
# 위험: 연쇄 인덱싱 대입 → SettingWithCopyWarning
df[df["age"] > 28]["city"] = "Unknown"
# df[df["age"] > 28]가 copy일 수 있어, 원본에 반영이 안 될 수 있음
# 안전: .loc 한 번으로 "행 선택 + 열 지정 + 대입"을 한 연산에서
df.loc[df["age"] > 28, "city"] = "Unknown"규칙:
.loc/.iloc 로 끝냅니다 ([] 를 두 번 겹치지 않음)..copy() 합니다.sub = df[df["city"] == "Seoul"].copy() # 명시적 복사본
sub["age"] += 1 # 원본 df는 그대로참고: pandas 3.0부터는 Copy-on-Write(CoW)가 기본이 되어, 모든 인덱싱이 "복사처럼" 동작하고 실제 복사는 수정 시점에만 일어납니다. 이때
SettingWithCopyWarning은 사라지고, 대신 "원본을 바꾸려면 반드시.loc대입"이라는 규칙이 더 명확해집니다.
댓글
GitHub 계정으로 의견이나 질문을 남길 수 있습니다.