Menu
CoddyTech

pandas 치트시트

마지막 업데이트

DataFrame 만들기

예제, 테스트, 조회용 테이블에 쓰는 작은 DataFrame을 직접 만들어요.

작업코드
pandas와 NumPy 임포트import pandas as pd import numpy as np
열 딕셔너리로 만들기df = pd.DataFrame({"name": ["Ada", "Ben"], "age": [36, 28]})
행 리스트로 만들기pd.DataFrame([{"name": "Ada", "age": 36}, {"name": "Ben", "age": 28}])
NumPy 배열로 만들기pd.DataFrame(np.arange(6).reshape(3, 2), columns=["x", "y"])
레이블이 있는 Seriess = pd.Series([3, 5, 2], index=["a", "b", "c"])
날짜 범위pd.date_range("2026-01-01", periods=7, freq="D")

파일 읽기와 쓰기

한 번의 호출로 파일을 DataFrame으로 불러오고, 한 번의 호출로 다시 저장해요.

작업코드
CSV 파일 읽기df = pd.read_csv("sales.csv")
일부 열만, 날짜는 날짜 타입으로pd.read_csv("sales.csv", usecols=["date", "city", "sales"], parse_dates=["date"])
열을 인덱스로 사용pd.read_csv("sales.csv", index_col="id")
탭 등 다른 구분자pd.read_csv("sales.tsv", sep="\t")
추가 표기를 결측값으로 처리pd.read_csv("sales.csv", na_values=["n/a", "-"])
앞부분 행만 읽기pd.read_csv("sales.csv", nrows=1000)
Excel 시트 읽기 (openpyxl 필요)pd.read_excel("report.xlsx", sheet_name="2026")
JSON 레코드 읽기pd.read_json("records.json")
SQL 데이터베이스에서 읽기pd.read_sql("SELECT * FROM orders", conn)
인덱스 없이 CSV 저장df.to_csv("clean.csv", index=False)
Excel 또는 Parquet 저장df.to_excel("clean.xlsx", index=False), df.to_parquet("clean.parquet")

데이터 살펴보기

새 데이터셋을 받으면 가장 먼저 실행할 호출들이에요.

작업코드
처음과 마지막 행df.head(), df.tail(3)
행과 열의 개수df.shape # (6, 7)
열 타입과 non-null 개수df.info()
숫자 열의 요약 통계df.describe()
모든 열의 요약df.describe(include="all")
열 이름과 타입df.columns, df.dtypes
값별 개수 세기df["city"].value_counts()
값별 비율df["city"].value_counts(normalize=True)
고유값과 그 개수df["city"].unique(), df["city"].nunique()
열별 결측값 개수df.isna().sum()
무작위 행 샘플df.sample(3, random_state=0)
숫자 열의 상관관계df.corr(numeric_only=True)

열과 행 선택

loc은 레이블로 선택하고 슬라이스 끝을 포함해요. iloc은 위치로 선택하고 끝을 제외해요.

작업코드
열 하나를 Series로df["age"]
여러 열을 DataFrame으로df[["name", "age"]]
레이블로 행과 열 선택df.loc[0:2, ["name", "age"]]
위치로 행과 열 선택df.iloc[0:2, 0:3]
값 하나df.at[0, "age"], df.iat[0, 3]
마지막 행df.iloc[-1]
특정 타입의 열df.select_dtypes(include="number")
텍스트 열 (pandas 3)df.select_dtypes(include="str")
이름이 일치하는 열df.filter(like="sal"), df.filter(regex="^(age|salary)$")
열 값으로 행 조회df.set_index("name").loc["Ada", "salary"]
0, 1, 2 인덱스로 되돌리기df.reset_index(drop=True)
열 또는 행 삭제df.drop(columns=["email"]), df.drop(index=[0, 1])

행 필터링

조건마다 괄호로 감싸고 &(and), |(or), ~(not)로 조합하세요.

작업코드
조건에 맞는 행df[df["age"] > 30]
두 조건 모두df[(df["age"] > 30) & (df["city"] == "Oslo")]
둘 중 하나의 조건df[(df["city"] == "Oslo") | (df["salary"] > 6000)]
리스트에 있는 값df[df["city"].isin(["Oslo", "Rome"])]
리스트에 없는 값df[~df["city"].isin(["Oslo", "Rome"])]
두 값 사이 (양 끝 포함)df[df["age"].between(30, 40)]
쿼리 문자열로 필터링df.query("age > 30 and city == 'Oslo'")
쿼리에서 변수 사용min_age = 30 df.query("age > @min_age")
텍스트 포함 여부df[df["name"].str.contains("e", case=False)]
결측값 또는 값이 있는 행df[df["age"].isna()], df[df["age"].notna()]
행 필터링과 열 선택 동시에df.loc[df["age"] > 30, ["name", "salary"]]
상위 또는 하위 n개 행df.nlargest(3, "salary"), df.nsmallest(2, "age")
중복된 값이 있는 행df[df.duplicated(subset=["city"], keep=False)]

정렬과 순위

작업코드
열 기준 정렬df.sort_values("salary")
큰 값부터df.sort_values("salary", ascending=False)
여러 열 기준 정렬df.sort_values(["city", "salary"], ascending=[True, False])
결측값을 앞으로df.sort_values("age", na_position="first")
인덱스 기준 정렬df.sort_index()
순위 매기기 (가장 큰 값이 1)df["salary"].rank(ascending=False)

열 추가와 변경

열 전체 연산은 빨라요. 기본 제공 메서드로 안 될 때만 apply를 쓰세요.

작업코드
다른 열로 새 열 만들기df["bonus"] = df["salary"] * 0.1
체인 안에서 열 추가df.assign(bonus=lambda d: d["salary"] * 0.1)
조건에 따른 값df["level"] = np.where(df["age"] >= 40, "senior", "junior")
여러 조건에 따른 값df["band"] = np.select([df["age"] < 30, df["age"] < 45], ["young", "mid"], default="senior")
조건을 만족하는 곳의 값 변경df.loc[df["city"] == "Lima", "salary"] = 4000
딕셔너리로 값 매핑df["country"] = df["city"].map({"Oslo": "NO", "Rome": "IT", "Lima": "PE"})
열에 함수 적용df["salary"].apply(lambda x: round(x, -3))
모든 셀에 함수 적용df[["age", "salary"]].map(lambda x: x / 1000)
열 이름 변경df.rename(columns={"salary": "pay"})
열 타입 변경df["salary"].astype("float64")
텍스트를 숫자로, 잘못된 값은 NaN으로pd.to_numeric(s, errors="coerce")
숫자를 레이블 구간으로pd.cut(df["age"], bins=[0, 30, 45, 120], labels=["young", "mid", "senior"])
숫자를 같은 크기 그룹으로pd.qcut(df["salary"], q=4, labels=False)
행 반복 (느림, 최후의 수단)for row in df.itertuples(): print(row.name, row.salary)

결측값과 데이터 정제

숫자 결측값은 NaN, 날짜 결측값은 NaT로 표시돼요. 아래 호출은 모두 새 객체를 반환하니 결과를 변수에 할당하세요.

작업코드
열별 결측값 비율df.isna().mean()
결측값이 하나라도 있는 행 삭제df.dropna()
핵심 열이 비어 있는 행 삭제df.dropna(subset=["age"])
열마다 다른 값으로 채우기df.fillna({"age": df["age"].median()})
이전 또는 다음 행 값으로 채우기df["age"].ffill(), df["age"].bfill()
보간으로 빈칸 채우기df["age"].interpolate()
특정 표기를 결측값으로 바꾸기df.replace(["n/a", "-", ""], np.nan)
중복 행 삭제df.drop_duplicates()
키별 마지막 행만 남기기df.drop_duplicates(subset=["city"], keep="last")
열 이름 정리df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
극단값 제한df["salary"].clip(lower=4000, upper=6000)
반복되는 텍스트는 category 타입으로df["city"] = df["city"].astype("category")

텍스트 열 (.str)

pandas 3에서는 텍스트 열이 기본적으로 str dtype이 돼요. .str 메서드는 열 전체에 동작하고, 결측값은 그대로 결측값으로 남아요.

작업코드
대문자, 소문자, 첫 글자 대문자df["name"].str.upper(), df["name"].str.lower(), df["name"].str.title()
공백 제거df["name"].str.strip()
텍스트 바꾸기df["email"].str.replace("mail.com", "example.com")
정규식으로 바꾸기df["email"].str.replace(r"@.*", "", regex=True)
포함 여부, 시작 문자열df["name"].str.contains("a"), df["name"].str.startswith("E")
각 값의 길이df["name"].str.len()
앞쪽 문자df["name"].str[:2]
여러 열로 분리df["email"].str.split("@", expand=True)
정규식 그룹으로 추출df["email"].str.extract(r"@(\w+)")
텍스트 열 합치기df["name"] + " (" + df["city"] + ")"
숫자 앞을 0으로 채우기df["salary"].astype("str").str.zfill(6)
새 텍스트 dtypedf["name"].dtype # str

날짜와 시간

.dt 접근자는 datetime 열에서 동작해요. 정렬된 날짜 인덱스(아래의 ts)가 있으면 텍스트로 슬라이싱하고 리샘플링할 수 있어요.

작업코드
열을 datetime으로 변환df["date"] = pd.to_datetime(df["date"])
정확한 형식으로 변환pd.to_datetime(["03/01/2026", "15/01/2026"], format="%d/%m/%Y")
연도, 월, 요일 이름df["date"].dt.year, df["date"].dt.month, df["date"].dt.day_name()
날짜를 월별로 묶기df["date"].dt.to_period("M")
두 날짜 사이의 일수(pd.Timestamp("2026-12-31") - df["date"]).dt.days
일 또는 월 더하기df["date"] + pd.Timedelta(days=30), df["date"] + pd.DateOffset(months=1)
날짜 범위 안의 행df[df["date"].between("2021-01-01", "2022-12-31")]
날짜 인덱스, 텍스트로 슬라이싱ts = df.set_index("date").sort_index() ts.loc["2021":"2022"]
월별 합계ts["salary"].resample("ME").sum()
주별 평균, "QE"는 분기, "YE"는 연도ts["salary"].resample("W").mean()
이동 평균ts["salary"].rolling(3).mean()
이전 행 대비 변화ts["salary"].diff(), ts["salary"].pct_change()
값을 한 행 아래로 밀기ts["salary"].shift(1)
누적 합계ts["salary"].cumsum()
시간대 지정과 변환ts.index.tz_localize("UTC").tz_convert("Europe/Oslo")

그룹화와 집계

groupby는 키로 행을 나누고, 그룹마다 함수를 적용한 뒤 결과를 합쳐요.

작업코드
그룹별 평균df.groupby("city")["salary"].mean()
여러 통계를 한 번에df.groupby("city")["salary"].agg(["mean", "max", "count"])
결과 열 이름 지정df.groupby("city").agg(avg_pay=("salary", "mean"), people=("name", "count"))
두 열로 그룹화df.groupby(["city", "team"])["salary"].sum()
키를 열로 유지df.groupby("city", as_index=False)["salary"].mean()
그룹별 행 개수df.groupby("city").size()
그룹 결과를 모든 행에df["city_avg"] = df.groupby("city")["salary"].transform("mean")
일부 그룹만 남기기df.groupby("city").filter(lambda g: len(g) >= 2)
그룹별 최댓값 행df.loc[df.groupby("city")["salary"].idxmax()]
그룹별 상위 n개 행df.sort_values("salary", ascending=False).groupby("city").head(2)
그룹 안에서 행 번호 매기기df.groupby("city").cumcount()
그룹별로 반복for city, group in df.groupby("city"): print(city, len(group))
필터링, 그룹화, 정렬을 한 체인으로(df.query("age > 30") .groupby("city", as_index=False) .agg(avg_pay=("salary", "mean")) .sort_values("avg_pay", ascending=False))

피벗과 재구조화

롱 데이터는 관측값 하나가 한 행이에요. 와이드 데이터는 변수 하나를 여러 열에 펼쳐 놓아요.

작업코드
피벗 테이블df.pivot_table(index="city", columns="team", values="salary", aggfunc="mean", fill_value=0)
합계가 있는 피벗 테이블df.pivot_table(index="city", values="salary", aggfunc="sum", margins=True)
조합별 개수pd.crosstab(df["city"], df["team"])
행별 비율pd.crosstab(df["city"], df["team"], normalize="index")
와이드에서 롱으로long = df.melt(id_vars="name", value_vars=["age", "salary"], var_name="field", value_name="value")
롱에서 와이드로long.pivot(index="name", columns="field", values="value")
안쪽 인덱스 레벨을 열로df.groupby(["city", "team"])["salary"].sum().unstack(fill_value=0)
리스트 항목마다 한 행pd.DataFrame({"id": [1, 2], "tags": [["a", "b"], ["c"]]}).explode("tags")
열 원-핫 인코딩pd.get_dummies(df["team"], prefix="team")
행과 열 바꾸기df.T

DataFrame 합치기

concat은 프레임을 이어 붙이고, merge는 SQL처럼 키 열을 기준으로 조인해요.

작업코드
행 이어 붙이기pd.concat([df_2025, df_2026], ignore_index=True)
옆으로 나란히 붙이기pd.concat([left, right], axis=1)
키 열로 조인orders.merge(customers, on="customer_id", how="left")
조인 종류how="inner", "left", "right", "outer"
이름이 다른 키orders.merge(users, left_on="customer_id", right_on="id")
조인 검증과 행 출처 표시orders.merge(customers, on="customer_id", how="left", validate="many_to_one", indicator=True)
인덱스로 조인left.join(right, how="inner")
다른 테이블에 짝이 없는 행orders[~orders["customer_id"].isin(customers["customer_id"])]

pandas로 시각화

pandas는 Matplotlib으로 그려요. 모든 plot 호출은 Matplotlib ax를 반환하므로 이어서 스타일을 꾸밀 수 있어요.

작업코드
Series 선 그래프ts["salary"].plot()
두 열로 선 그래프df.plot(x="date", y="salary")
요약값 막대 그래프df.groupby("city")["salary"].mean().plot.bar()
정렬된 가로 막대df["city"].value_counts().sort_values().plot.barh()
히스토그램df["salary"].plot.hist(bins=20)
산점도df.plot.scatter(x="age", y="salary")
그룹별 박스 플롯df.boxplot(column="salary", by="city")
제목과 축 레이블ax = df.plot(x="date", y="salary", title="Salary") ax.set_ylabel("EUR")
차트 보기 또는 저장plt.show(), ax.figure.savefig("salary.png", dpi=150)

pandas 3: 예전 방식과 지금 쓰는 방식

예전 튜토리얼에서 복사한 코드는 pandas 3에서 오류나 경고가 나는 경우가 많아요. 대신 pandas 3 열의 방식으로 작성하세요.

작업pandas 3예전 방식
모든 셀에 함수 적용df.map(f)df.applymap(f)
이전 행 값으로 채우기df.ffill()df.fillna(method="ffill")
행 추가pd.concat([df, new_rows])df.append(new_rows)
월말 기준 리샘플링ts["salary"].resample("ME").sum()resample("M")
시간 단위 날짜pd.date_range("2026-01-01", periods=24, freq="h")freq="H"
값 하나 설정df.loc[0, "age"] = 37df["age"][0] = 37
필터링한 부분 변경high = df[df["salary"] > 5000] high["salary"] = 0high = df[df["salary"] > 5000].copy()
텍스트 열인지 확인df["name"].dtype == "str"df["name"].dtype == object

현업 데이터 분석가가 매일 쓰는 pandas 코드를 작업별로 모았어요. 데이터 만들기와 불러오기, 살펴보기, 행 선택과 필터링, 열 추가, 결측값 정리, 텍스트와 날짜 다루기, 그룹화와 집계, 재구조화, 병합, 시각화까지 담았어요. 각 행에는 바로 붙여 넣을 수 있는 짧은 코드와 무엇을 하는지 설명하는 한 줄이 있어요.

모든 내용은 Coddy에서 실행하는 버전인 pandas 3 기준이에요. 텍스트 열은 새로운 str dtype을 쓰고, Copy-on-Write는 항상 켜져 있으며, applymap이나 fillna(method=...)처럼 제거된 API는 예전 방식 표에만 나와요. 예제는 name, city, team, age, salary, date, email 열이 있는 DataFrame df를 사용해요. Python 자체가 처음이라면 Python 치트시트와 Python 플레이그라운드부터 시작하고, NumPy, Matplotlib, scikit-learn 치트시트도 함께 띄워 두세요.

pandas 치트시트 자주 묻는 질문

이 pandas 치트시트는 무료인가요?
네. 이 pandas 치트시트는 완전히 무료이고 가입도 필요 없어요. 북마크해 두거나 브라우저에서 PDF로 인쇄해 두고, groupby, merge, 피벗 테이블의 정확한 호출이 필요할 때마다 다시 찾아오세요.
이 치트시트는 어떤 pandas 버전을 다루나요?
pandas 3이에요. 텍스트 열은 새로운 str dtype을 쓰고, Copy-on-Write는 항상 켜져 있으며, pandas 3에서 제거된 호출(applymap, fillna(method=...), append, M과 H 빈도 별칭)은 마지막 표에만 대신 쓸 코드와 함께 나와요. str dtype이나 Copy-on-Write에 의존하는 행을 빼면 거의 모든 코드가 pandas 2.2에서도 실행돼요.
loc과 iloc의 차이는 무엇인가요?
loc은 레이블, 즉 인덱스 값과 열 이름으로 선택하며, df.loc[0:2] 같은 슬라이스는 끝을 포함해요. iloc은 Python 리스트처럼 정수 위치로 선택하므로 df.iloc[0:2]는 처음 두 행을 반환하고 끝은 제외해요. loc에 조건을 넣으면 행 필터링과 열 선택을 한 번에 할 수 있어요: df.loc[df["age"] > 30, ["name"]].
DataFrame을 여러 조건으로 필터링하려면 어떻게 하나요?
조건마다 괄호로 감싸고 and는 &, or는 |, not은 ~로 연결하세요: df[(df["age"] > 30) & (df["city"] == "Oslo")]. Python의 and와 or는 열 전체에는 동작하지 않아요. 필터가 길다면 df.query("age > 30 and city == 'Oslo'")가 더 읽기 쉬워요.
merge, join, concat의 차이는 무엇인가요?
merge는 SQL 조인처럼 키 열을 기준으로 두 DataFrame을 합치며, how="inner", "left", "right", "outer"를 지정해요. join은 인덱스 기준으로 merge하는 단축 방법이에요. concat은 키를 전혀 맞추지 않고 프레임을 위아래로 쌓거나, axis=1이면 옆으로 나란히 붙여요.
값을 할당했는데 왜 DataFrame이 바뀌지 않나요?
pandas 3부터는 Copy-on-Write 때문에 모든 선택 결과가 복사본처럼 동작해요. df["age"][0] = 37 같은 연쇄 할당은 임시 객체만 바꾸고 df는 그대로 둬요. 대신 loc 호출 하나로 쓰세요: df.loc[0, "age"] = 37. dropna, fillna 같은 메서드도 새 객체를 반환하니 결과를 할당하세요.
pandas 코드를 온라인에서 실행할 수 있나요?
네. 무료 Matplotlib 플레이그라운드는 pandas, NumPy, Matplotlib, scikit-learn이 포함된 Python을 브라우저에서 실행해요. 이 치트시트의 코드를 아무거나 붙여 넣고 설치 없이 바로 실행해 보세요. Coddy의 머신러닝 코스는 첫 DataFrame부터 모델에 들어갈 데이터까지 pandas를 단계별로 가르쳐요.
Coddy 프로그래밍 언어 일러스트

Coddy로 Pandas 배우기

시작하기