Menu
CoddyTech

pandas 速查表

最后更新

创建 DataFrame

手动构建的小型 DataFrame,适合用来写示例、测试和查找表。

操作代码
导入 pandas 和 NumPyimport 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 或 Parquetdf.to_excel("clean.xlsx", index=False), df.to_parquet("clean.parquet")

查看数据

拿到新数据集后最先要用的几个调用。

操作代码
开头和末尾几行df.head(), df.tail(3)
行数和列数df.shape # (6, 7)
列类型和非空值数量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 按位置选择,切片不含终点。

操作代码
选一列,返回 Seriesdf["age"]
选多列,返回 DataFramedf[["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])

筛选行

每个条件都用括号括起来,再用 &(与)、|(或)和 ~(非)组合。

操作代码
满足条件的行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")
文本转数字,无效值变为 NaNpd.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 数据类型。.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"] + ")"
数字前面补零df["salary"].astype("str").str.zfill(6)
新的文本数据类型df["name"].dtype # str

日期与时间

.dt 访问器用于日期时间列。有序的日期索引(下面的 ts)支持按文本切片和重采样。

操作代码
把一列解析为日期时间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 把多个 DataFrame 堆叠起来,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 绘图。每个绘图调用都会返回 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 数据类型,写时复制(Copy-on-Write)始终开启,applymap 和 fillna(method=...) 等已删除的 API 只出现在旧写法对照表里。示例使用一个名为 df 的 DataFrame,包含 name、city、team、age、salary、date 和 email 这几列。还不熟悉 Python 本身?先看 Python 速查表,试试 Python 在线编辑器,再把 NumPy、Matplotlib 和 scikit-learn 速查表放在手边一起用。

pandas 速查表常见问题

这份 pandas 速查表免费吗?

免费。这份 pandas 速查表完全免费,无需注册。把它加入书签,或者在浏览器里打印成 PDF,需要查 groupby、merge 或数据透视表的写法时随时回来看。

这份速查表对应哪个 pandas 版本?
pandas 3。文本列使用新的 str 数据类型,写时复制始终开启,pandas 3 中已删除的调用(applymap、fillna(method=...)、append,以及 M 和 H 这两个频率别名)只出现在最后一张表里,旁边写明了现在的写法。除了依赖 str 数据类型或写时复制的那几行,几乎所有代码片段在 pandas 2.2 上也能运行。
loc 和 iloc 有什么区别?
loc 按标签选择,也就是索引值和列名,像 df.loc[0:2] 这样的切片包含终点。iloc 像 Python 列表一样按整数位置选择,所以 df.iloc[0:2] 返回前两行,不含终点。把 loc 和条件一起用,可以一步完成筛选行和选择列:df.loc[df["age"] > 30, ["name"]]。
怎么按多个条件筛选 DataFrame?
把每个条件放在括号里,用 & 表示与、| 表示或、~ 表示非: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 是按索引合并的快捷方式。concat 完全不匹配键:它把多个 DataFrame 上下堆叠,或者用 axis=1 左右并排。
为什么赋值后 DataFrame 没有变化?
从 pandas 3 开始,写时复制让每次选择都表现得像一个副本。像 df["age"][0] = 37 这样的链式赋值修改的是一个临时对象,df 本身不会变。改成一次 loc 调用:df.loc[0, "age"] = 37。dropna 和 fillna 等方法也会返回新对象,记得把结果赋值回去。
可以在线运行 pandas 代码吗?
可以。免费的 Matplotlib 在线编辑器在浏览器里运行带有 pandas、NumPy、Matplotlib 和 scikit-learn 的 Python,你可以粘贴这份速查表里的任意代码片段直接运行,无需安装任何东西。Coddy 的机器学习课程会一步步教你使用 pandas,从你的第一个 DataFrame 一直讲到喂给模型的数据。
Coddy 编程语言插图

在 Coddy 学习 Pandas

开始