Cheat sheet pandas
Cập nhật lần cuối
Tạo DataFrame
DataFrame nhỏ tạo bằng tay, dùng cho ví dụ, kiểm thử và bảng tra cứu.
| Việc cần làm | Code |
|---|---|
| Import pandas và NumPy | import pandas as pd
import numpy as np |
| Từ dict các cột | df = pd.DataFrame({"name": ["Ada", "Ben"], "age": [36, 28]}) |
| Từ danh sách các hàng | pd.DataFrame([{"name": "Ada", "age": 36}, {"name": "Ben", "age": 28}]) |
| Từ mảng NumPy | pd.DataFrame(np.arange(6).reshape(3, 2), columns=["x", "y"]) |
| Series có nhãn | s = pd.Series([3, 5, 2], index=["a", "b", "c"]) |
| Một dãy ngày | pd.date_range("2026-01-01", periods=7, freq="D") |
Đọc và ghi tệp
Một lệnh nạp tệp vào DataFrame, một lệnh ghi lại ra tệp.
| Việc cần làm | Code |
|---|---|
| Đọc tệp CSV | df = pd.read_csv("sales.csv") |
| Chỉ lấy vài cột, đọc đúng kiểu ngày | pd.read_csv("sales.csv", usecols=["date", "city", "sales"], parse_dates=["date"]) |
| Dùng một cột làm index | pd.read_csv("sales.csv", index_col="id") |
| Phân tách bằng tab hoặc ký tự khác | pd.read_csv("sales.tsv", sep="\t") |
| Coi thêm một số ký hiệu là giá trị thiếu | pd.read_csv("sales.csv", na_values=["n/a", "-"]) |
| Chỉ đọc những hàng đầu | pd.read_csv("sales.csv", nrows=1000) |
| Đọc một sheet Excel (cần openpyxl) | pd.read_excel("report.xlsx", sheet_name="2026") |
| Đọc bản ghi JSON | pd.read_json("records.json") |
| Đọc từ cơ sở dữ liệu SQL | pd.read_sql("SELECT * FROM orders", conn) |
| Ghi CSV không kèm index | df.to_csv("clean.csv", index=False) |
| Ghi Excel hoặc Parquet | df.to_excel("clean.xlsx", index=False), df.to_parquet("clean.parquet") |
Xem dữ liệu
Những lệnh đầu tiên nên chạy với mọi tập dữ liệu mới.
| Việc cần làm | Code |
|---|---|
| Những hàng đầu và cuối | df.head(), df.tail(3) |
| Số hàng và số cột | df.shape # (6, 7) |
| Kiểu của cột và số giá trị khác null | df.info() |
| Thống kê tóm tắt các cột số | df.describe() |
| Tóm tắt mọi cột | df.describe(include="all") |
| Tên và kiểu của các cột | df.columns, df.dtypes |
| Đếm từng giá trị | df["city"].value_counts() |
| Tỷ lệ của từng giá trị | df["city"].value_counts(normalize=True) |
| Các giá trị khác nhau và số lượng | df["city"].unique(), df["city"].nunique() |
| Số giá trị thiếu mỗi cột | df.isna().sum() |
| Lấy mẫu ngẫu nhiên các hàng | df.sample(3, random_state=0) |
| Tương quan giữa các cột số | df.corr(numeric_only=True) |
Chọn cột và hàng
loc chọn theo nhãn và lấy cả điểm cuối của slice. iloc chọn theo vị trí và bỏ điểm cuối.
| Việc cần làm | Code |
|---|---|
| Một cột, dạng Series | df["age"] |
| Nhiều cột, dạng DataFrame | df[["name", "age"]] |
| Hàng và cột theo nhãn | df.loc[0:2, ["name", "age"]] |
| Hàng và cột theo vị trí | df.iloc[0:2, 0:3] |
| Một giá trị | df.at[0, "age"], df.iat[0, 3] |
| Hàng cuối | df.iloc[-1] |
| Các cột thuộc một kiểu | df.select_dtypes(include="number") |
| Các cột văn bản (pandas 3) | df.select_dtypes(include="str") |
| Các cột có tên khớp mẫu | df.filter(like="sal"), df.filter(regex="^(age|salary)$") |
| Tra cứu hàng theo một cột | df.set_index("name").loc["Ada", "salary"] |
| Đưa về index 0, 1, 2 | df.reset_index(drop=True) |
| Xóa cột hoặc hàng | df.drop(columns=["email"]), df.drop(index=[0, 1]) |
Lọc hàng
Đặt mỗi điều kiện trong ngoặc đơn và kết hợp bằng & (và), | (hoặc) và ~ (phủ định).
| Việc cần làm | Code |
|---|---|
| Các hàng thỏa điều kiện | df[df["age"] > 30] |
| Cả hai điều kiện | df[(df["age"] > 30) & (df["city"] == "Oslo")] |
| Một trong hai điều kiện | df[(df["city"] == "Oslo") | (df["salary"] > 6000)] |
| Giá trị nằm trong danh sách | df[df["city"].isin(["Oslo", "Rome"])] |
| Giá trị không nằm trong danh sách | df[~df["city"].isin(["Oslo", "Rome"])] |
| Nằm giữa hai giá trị, tính cả hai đầu | df[df["age"].between(30, 40)] |
| Lọc bằng chuỗi query | df.query("age > 30 and city == 'Oslo'") |
| Dùng biến trong query | min_age = 30
df.query("age > @min_age") |
| Văn bản chứa | df[df["name"].str.contains("e", case=False)] |
| Giá trị thiếu hoặc có giá trị | df[df["age"].isna()], df[df["age"].notna()] |
| Lọc hàng và chọn cột | df.loc[df["age"] > 30, ["name", "salary"]] |
| n hàng lớn nhất hoặc nhỏ nhất | df.nlargest(3, "salary"), df.nsmallest(2, "age") |
| Các hàng có giá trị lặp lại | df[df.duplicated(subset=["city"], keep=False)] |
Sắp xếp và xếp hạng
| Việc cần làm | Code |
|---|---|
| Sắp xếp theo một cột | df.sort_values("salary") |
| Lớn nhất trước | df.sort_values("salary", ascending=False) |
| Sắp xếp theo nhiều cột | df.sort_values(["city", "salary"], ascending=[True, False]) |
| Giá trị thiếu lên đầu | df.sort_values("age", na_position="first") |
| Sắp xếp theo index | df.sort_index() |
| Xếp hạng giá trị, 1 là cao nhất | df["salary"].rank(ascending=False) |
Thêm và thay đổi cột
Phép toán trên cả cột chạy rất nhanh. Chỉ dùng apply khi không có phương thức sẵn nào làm được việc đó.
| Việc cần làm | Code |
|---|---|
| Cột mới tính từ các cột khác | df["bonus"] = df["salary"] * 0.1 |
| Thêm cột ngay trong chuỗi lệnh | df.assign(bonus=lambda d: d["salary"] * 0.1) |
| Giá trị theo một điều kiện | df["level"] = np.where(df["age"] >= 40, "senior", "junior") |
| Giá trị theo nhiều điều kiện | df["band"] = np.select([df["age"] < 30, df["age"] < 45], ["young", "mid"], default="senior") |
| Đổi giá trị ở chỗ thỏa điều kiện | df.loc[df["city"] == "Lima", "salary"] = 4000 |
| Ánh xạ giá trị bằng dict | df["country"] = df["city"].map({"Oslo": "NO", "Rome": "IT", "Lima": "PE"}) |
| Áp dụng hàm cho một cột | df["salary"].apply(lambda x: round(x, -3)) |
| Áp dụng hàm cho từng ô | df[["age", "salary"]].map(lambda x: x / 1000) |
| Đổi tên cột | df.rename(columns={"salary": "pay"}) |
| Đổi kiểu của cột | df["salary"].astype("float64") |
| Văn bản thành số, giá trị lỗi thành NaN | pd.to_numeric(s, errors="coerce") |
| Chia số vào các khoảng có nhãn | pd.cut(df["age"], bins=[0, 30, 45, 120], labels=["young", "mid", "senior"]) |
| Chia số thành các nhóm bằng nhau | pd.qcut(df["salary"], q=4, labels=False) |
| Lặp qua các hàng (chậm, chỉ dùng khi hết cách) | for row in df.itertuples():
print(row.name, row.salary) |
Giá trị thiếu và làm sạch
Số bị thiếu hiển thị là NaN, ngày bị thiếu là NaT. Mỗi lệnh dưới đây trả về một đối tượng mới, nên hãy gán lại kết quả.
| Việc cần làm | Code |
|---|---|
| Tỷ lệ giá trị thiếu mỗi cột | df.isna().mean() |
| Xóa hàng có bất kỳ giá trị thiếu nào | df.dropna() |
| Xóa hàng thiếu một cột quan trọng | df.dropna(subset=["age"]) |
| Điền giá trị riêng cho từng cột | df.fillna({"age": df["age"].median()}) |
| Điền từ hàng trước hoặc hàng sau | df["age"].ffill(), df["age"].bfill() |
| Điền chỗ trống bằng nội suy | df["age"].interpolate() |
| Biến các ký hiệu thành giá trị thiếu | df.replace(["n/a", "-", ""], np.nan) |
| Xóa hàng trùng lặp | df.drop_duplicates() |
| Giữ hàng cuối cùng của mỗi khóa | df.drop_duplicates(subset=["city"], keep="last") |
| Chuẩn hóa tên cột | df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_") |
| Giới hạn giá trị cực đoan | df["salary"].clip(lower=4000, upper=6000) |
| Kiểu category cho văn bản lặp lại | df["city"] = df["city"].astype("category") |
Cột văn bản (.str)
Trong pandas 3, cột văn bản mặc định có dtype str. Các phương thức .str chạy trên cả cột, và giá trị thiếu vẫn giữ nguyên là thiếu.
| Việc cần làm | Code |
|---|---|
| Chữ hoa, chữ thường hoặc viết hoa đầu từ | df["name"].str.upper(), df["name"].str.lower(), df["name"].str.title() |
| Cắt khoảng trắng | df["name"].str.strip() |
| Thay thế văn bản | df["email"].str.replace("mail.com", "example.com") |
| Thay thế bằng regex | df["email"].str.replace(r"@.*", "", regex=True) |
| Chứa, bắt đầu bằng | df["name"].str.contains("a"), df["name"].str.startswith("E") |
| Độ dài mỗi giá trị | df["name"].str.len() |
| Những ký tự đầu | df["name"].str[:2] |
| Tách thành nhiều cột | df["email"].str.split("@", expand=True) |
| Trích xuất bằng nhóm regex | df["email"].str.extract(r"@(\w+)") |
| Nối các cột văn bản | df["name"] + " (" + df["city"] + ")" |
| Thêm số 0 vào trước số | df["salary"].astype("str").str.zfill(6) |
| Dtype văn bản mới | df["name"].dtype # str |
Ngày và giờ
Accessor .dt dùng cho cột datetime. Một index ngày đã sắp xếp (ts bên dưới) cho phép slice bằng văn bản và resample.
| Việc cần làm | Code |
|---|---|
| Chuyển một cột thành datetime | df["date"] = pd.to_datetime(df["date"]) |
| Chuyển với định dạng chính xác | pd.to_datetime(["03/01/2026", "15/01/2026"], format="%d/%m/%Y") |
| Năm, tháng, tên thứ trong tuần | df["date"].dt.year, df["date"].dt.month, df["date"].dt.day_name() |
| Nhóm ngày theo tháng | df["date"].dt.to_period("M") |
| Số ngày giữa hai ngày | (pd.Timestamp("2026-12-31") - df["date"]).dt.days |
| Cộng thêm ngày hoặc tháng | df["date"] + pd.Timedelta(days=30), df["date"] + pd.DateOffset(months=1) |
| Các hàng trong một khoảng ngày | df[df["date"].between("2021-01-01", "2022-12-31")] |
| Index ngày, slice bằng văn bản | ts = df.set_index("date").sort_index()
ts.loc["2021":"2022"] |
| Tổng theo tháng | ts["salary"].resample("ME").sum() |
Trung bình theo tuần; "QE" là theo quý, "YE" theo năm | ts["salary"].resample("W").mean() |
| Trung bình trượt | ts["salary"].rolling(3).mean() |
| Thay đổi so với hàng trước | ts["salary"].diff(), ts["salary"].pct_change() |
| Dời giá trị xuống một hàng | ts["salary"].shift(1) |
| Tổng lũy kế | ts["salary"].cumsum() |
| Đặt và chuyển múi giờ | ts.index.tz_localize("UTC").tz_convert("Europe/Oslo") |
Nhóm và tổng hợp
groupby chia các hàng theo khóa, áp dụng một hàm cho từng nhóm rồi gộp kết quả lại.
| Việc cần làm | Code |
|---|---|
| Trung bình mỗi nhóm | df.groupby("city")["salary"].mean() |
| Nhiều thống kê cùng lúc | df.groupby("city")["salary"].agg(["mean", "max", "count"]) |
| Đặt tên cho cột kết quả | df.groupby("city").agg(avg_pay=("salary", "mean"), people=("name", "count")) |
| Nhóm theo hai cột | df.groupby(["city", "team"])["salary"].sum() |
| Giữ khóa ở dạng cột | df.groupby("city", as_index=False)["salary"].mean() |
| Số hàng mỗi nhóm | df.groupby("city").size() |
| Kết quả của nhóm trên từng hàng | df["city_avg"] = df.groupby("city")["salary"].transform("mean") |
| Chỉ giữ một số nhóm | df.groupby("city").filter(lambda g: len(g) >= 2) |
| Hàng có giá trị lớn nhất mỗi nhóm | df.loc[df.groupby("city")["salary"].idxmax()] |
| n hàng đầu mỗi nhóm | df.sort_values("salary", ascending=False).groupby("city").head(2) |
| Đánh số các hàng trong mỗi nhóm | df.groupby("city").cumcount() |
| Lặp qua các nhóm | for city, group in df.groupby("city"):
print(city, len(group)) |
| Lọc, nhóm và sắp xếp trong một chuỗi lệnh | (df.query("age > 30")
.groupby("city", as_index=False)
.agg(avg_pay=("salary", "mean"))
.sort_values("avg_pay", ascending=False)) |
Pivot và reshape
Dữ liệu dạng dài có một hàng cho mỗi quan sát. Dữ liệu dạng rộng trải một biến ra nhiều cột.
| Việc cần làm | Code |
|---|---|
| Pivot table | df.pivot_table(index="city", columns="team", values="salary", aggfunc="mean", fill_value=0) |
| Bảng pivot có dòng tổng | df.pivot_table(index="city", values="salary", aggfunc="sum", margins=True) |
| Đếm các tổ hợp | pd.crosstab(df["city"], df["team"]) |
| Tỷ lệ theo từng hàng | pd.crosstab(df["city"], df["team"], normalize="index") |
| Từ rộng sang dài | long = df.melt(id_vars="name", value_vars=["age", "salary"], var_name="field", value_name="value") |
| Từ dài sang rộng | long.pivot(index="name", columns="field", values="value") |
| Đưa cấp index trong cùng thành cột | df.groupby(["city", "team"])["salary"].sum().unstack(fill_value=0) |
| Mỗi phần tử của list một hàng | pd.DataFrame({"id": [1, 2], "tags": [["a", "b"], ["c"]]}).explode("tags") |
| Mã hóa one-hot một cột | pd.get_dummies(df["team"], prefix="team") |
| Hoán đổi hàng và cột | df.T |
Kết hợp DataFrame
concat xếp chồng các DataFrame, merge nối chúng theo cột khóa giống cách SQL làm.
| Việc cần làm | Code |
|---|---|
| Xếp chồng hàng | pd.concat([df_2025, df_2026], ignore_index=True) |
| Đặt cạnh nhau | pd.concat([left, right], axis=1) |
| Nối theo một cột khóa | orders.merge(customers, on="customer_id", how="left") |
| Các kiểu nối | how="inner", "left", "right", "outer" |
| Khóa có tên khác nhau | orders.merge(users, left_on="customer_id", right_on="id") |
| Kiểm tra phép nối và đánh dấu nguồn của từng hàng | orders.merge(customers, on="customer_id", how="left", validate="many_to_one", indicator=True) |
| Nối theo index | left.join(right, how="inner") |
| Các hàng không khớp với bảng khác | orders[~orders["customer_id"].isin(customers["customer_id"])] |
Vẽ biểu đồ với pandas
pandas vẽ bằng Matplotlib. Mỗi lệnh vẽ trả về một ax của Matplotlib, nên bạn có thể tiếp tục tùy chỉnh.
| Việc cần làm | Code |
|---|---|
| Biểu đồ đường của một series | ts["salary"].plot() |
| Biểu đồ đường từ hai cột | df.plot(x="date", y="salary") |
| Biểu đồ cột của bảng tóm tắt | df.groupby("city")["salary"].mean().plot.bar() |
| Thanh ngang đã sắp xếp | df["city"].value_counts().sort_values().plot.barh() |
| Histogram | df["salary"].plot.hist(bins=20) |
| Scatter plot | df.plot.scatter(x="age", y="salary") |
| Biểu đồ hộp theo nhóm | df.boxplot(column="salary", by="city") |
| Tiêu đề và nhãn trục | ax = df.plot(x="date", y="salary", title="Salary")
ax.set_ylabel("EUR") |
| Hiển thị hoặc lưu biểu đồ | plt.show(), ax.figure.savefig("salary.png", dpi=150) |
pandas 3: cách viết cũ và cách viết bây giờ
Code chép từ các hướng dẫn cũ thường lỗi hoặc báo cảnh báo trong pandas 3. Hãy viết theo cột pandas 3.
| Việc cần làm | pandas 3 | Cách viết cũ |
|---|---|---|
| Hàm trên từng ô | df.map(f) | df.applymap(f) |
| Điền từ hàng trước | df.ffill() | df.fillna(method="ffill") |
| Thêm hàng | pd.concat([df, new_rows]) | df.append(new_rows) |
| Resample theo cuối tháng | ts["salary"].resample("ME").sum() | resample("M") |
| Ngày theo từng giờ | pd.date_range("2026-01-01", periods=24, freq="h") | freq="H" |
| Gán một giá trị | df.loc[0, "age"] = 37 | df["age"][0] = 37 |
| Thay đổi một phần đã lọc | high = df[df["salary"] > 5000]
high["salary"] = 0 | high = df[df["salary"] > 5000].copy() |
| Đây có phải cột văn bản không? | df["name"].dtype == "str" | df["name"].dtype == object |
Những đoạn code pandas mà một người phân tích dữ liệu dùng hằng ngày, xếp theo việc cần làm: tạo và nạp dữ liệu, xem dữ liệu, chọn và lọc hàng, thêm cột, xử lý giá trị thiếu, làm việc với văn bản và ngày tháng, nhóm và tổng hợp, reshape, merge và vẽ biểu đồ. Mỗi hàng là một đoạn code ngắn để bạn dán vào, kèm một dòng giải thích nó làm gì.
Mọi thứ ở đây viết cho pandas 3, phiên bản Coddy đang chạy: cột văn bản dùng dtype str mới, Copy-on-Write luôn bật, và các API đã bị gỡ như applymap và fillna(method=...) chỉ xuất hiện trong bảng cách viết cũ. Các ví dụ dùng một DataFrame df với các cột name, city, team, age, salary, date và email. Mới làm quen với Python? Hãy bắt đầu với cheat sheet Python và Python playground, rồi giữ các cheat sheet NumPy, Matplotlib và scikit-learn bên cạnh trang này.
Câu hỏi thường gặp về cheat sheet pandas
Cheat sheet pandas này có miễn phí không?
Có. Cheat sheet pandas này hoàn toàn miễn phí, không cần đăng ký. Bạn có thể lưu dấu trang hoặc in ra PDF từ trình duyệt, rồi quay lại bất cứ lúc nào cần đúng lệnh cho groupby, merge hay bảng pivot.
Cheat sheet này dùng pandas phiên bản nào?
str mới, Copy-on-Write luôn bật, và các lệnh đã bị gỡ trong pandas 3 (applymap, fillna(method=...), append, các bí danh tần suất M và H) chỉ xuất hiện trong bảng cuối, cạnh cách viết thay thế. Hầu hết các đoạn code cũng chạy được trên pandas 2.2, trừ những hàng dựa vào dtype str hoặc Copy-on-Write.loc và iloc khác nhau thế nào?
loc chọn theo nhãn: giá trị index và tên cột, và một slice như df.loc[0:2] lấy cả điểm cuối. iloc chọn theo vị trí số nguyên, giống list trong Python, nên df.iloc[0:2] trả về hai hàng đầu và bỏ điểm cuối. Dùng loc với một điều kiện để vừa lọc hàng vừa chọn cột trong một bước: df.loc[df["age"] > 30, ["name"]].Làm sao lọc DataFrame theo nhiều điều kiện?
& cho và, | cho hoặc, ~ cho phủ định: df[(df["age"] > 30) & (df["city"] == "Oslo")]. Từ khóa and và or của Python không dùng được trên cả cột. Với bộ lọc dài, df.query("age > 30 and city == 'Oslo'") dễ đọc hơn.merge, join và concat khác nhau thế nào?
merge kết hợp hai DataFrame theo cột khóa, giống phép join trong SQL, với how="inner", "left", "right" hoặc "outer". join là cách viết tắt để merge theo index. concat hoàn toàn không so khớp khóa: nó xếp chồng các DataFrame lên nhau, hoặc đặt cạnh nhau với axis=1.Vì sao phép gán không làm thay đổi DataFrame?
df["age"][0] = 37 chỉ thay đổi một đối tượng tạm và không đụng tới df. Hãy viết thành một lệnh loc: df.loc[0, "age"] = 37. Các phương thức như dropna và fillna cũng trả về đối tượng mới, nên hãy gán lại kết quả.