Pandas 是 Python 处理表格数据的工具包。日常工作中遇到的"打开 csv → 筛选几行 → 算每组平均"这类任务, Excel 能做,但样本量到十万行就卡。 Pandas 处理千万行表格仍是秒级。
本篇覆盖 ML 数据预处理 90% 的 Pandas 操作。读者读完应能独立完成"读 csv → 清洗 → 分组聚合 → 输出图表数据"全流程。
参考来源:Pandas 官方文档[1]、Wes McKinney 《 Python for Data Analysis 》第 3 版[2]、Pandas Cookbook[3]。
一、 Series 与 DataFrame
用 Excel 类比理解: DataFrame 就是一张 Excel 表,行是记录、列是字段; Series 是单独抽出来的一列。和 Excel 的区别是: Pandas 用 Python 对象表示,可以批量操作数十万行,且支持复杂的条件筛选和分组。
pd.Series([1,2,3]) | pd.DataFrame({'a':[1,2], 'b':[3,4]}) | |
import pandas as pdimport numpy as nps = pd.Series([1.2, 3.4, 5.6, 7.8], name="temperature")print(s.index.tolist()) # [0, 1, 2, 3] ← 左侧的行号print(s.values) # array([1.2, 3.4, 5.6, 7.8]) ← 数据本身print(s.name) # temperature ← 列名df = pd.DataFrame({"date": ["2026-06-01", "2026-06-02", "2026-06-03"],"temp": [28.5, 30.1, 26.7],"sales": [152, 287, 134],})print(df.shape) # (3, 3) ← 3 行 3 列print(df.columns.tolist()) # ['date', 'temp', 'sales'] ← 列名清单print(df.dtypes.tolist())
注解: Pandas 的 object dtype 表示 Python 对象(通常是字符串)。机器学习模型只吃数字,所以后续要把 object 列转为 category 或数值。
二、读写文件
用日常操作类比:读 csv 就是用 Excel 打开文件,写 csv 就是另存为。差别在于 Pandas 不需要 GUI ,可以脚本化批量处理。
df = pd.read_csv("data.csv", encoding="utf-8")df = pd.read_csv("data.csv", sep="\t", na_values=["", "NA", "N/A"]) # TSV,自定义空值df = pd.read_csv("data.csv", parse_dates=["date"], index_col="date") # 把 date 列解析为时间,并设为索引df = pd.read_csv("data.csv", usecols=["temp", "sales"], nrows=1000) # 只读两列、前 1000 行for chunk in pd.read_csv("huge.csv", chunksize=10000):process(chunk) # 每次处理 1 万行df.to_csv("out.csv", index=False, encoding="utf-8-sig") # 不写行索引(避免多一列)df.to_parquet("out.parquet") # Parquet 格式,比 csv 小 5-10 倍、读取快df.to_excel("out.xlsx", sheet_name="data") # Excel,需要 openpyxldf = pd.read_excel("data.xlsx", sheet_name="Sheet1")df = pd.read_json("data.json")df = pd.read_html("https://example.com/table.html")[0] # 网页表格
注解: ML 项目中间数据优先用 parquet 。它保留 dtype ( csv 会把所有数据当文本),体积小,读取快。
三、索引: loc vs iloc
最常踩坑的两个 API。一句话区分: loc 用"名字"取, iloc 用"位置"取。
loc | |||
iloc |
df = pd.DataFrame(np.random.randint(0, 100, (5, 4)),columns=list("ABCD"),index=["r1", "r2", "r3", "r4", "r5"],)df.loc["r2", "B"] # 单个标量df.loc["r1":"r3", ["A", "C"]] # 标签切片(闭区间,含 r3)df.loc[df["A"] > 50, "B"] # 布尔筛选 + 列名df.iloc[1, 2] # 第 2 行第 3 列(从 0 数)df.iloc[0:3, [0, 2]] # 位置切片(左闭右开,不含第 4 行)df.iloc[-1] # 最后一行
注解:处理原始数据时用 loc (按列名明确),切分 batch 时用 iloc (按位置)。
df[df["A"]>50]["B"]=999# 不生效!会警告df.loc[df["A"]>50,"B"]=999# 正确写法
四、筛选与排序
用 SQL/Excel 类比:筛选就是 SQL 的 WHERE ,排序就是 Excel 的"按列排序"。
df[df["A"] > 50] # 单条件:A 列大于 50df[(df["A"] > 50) & (df["B"] < 30)] # 且(注意括号必须)df[(df["A"] > 50) | (df["B"] < 30)] # 或df[df["C"].isin([10, 20, 30])] # 取值在集合中(SQL 的 IN)df[df["C"].between(10, 50)] # 区间筛选(10 ≤ C ≤ 50)df[~df["A"].isna()] # A 列非空df.sort_values("A", ascending=False) # 按 A 降序df.sort_values(["A", "B"], ascending=[True, False]) # 多列排序:A 升 B 降df.sort_index() # 按行索引排序df.drop_duplicates() # 整行去重df.drop_duplicates(subset=["A"], keep="last") # 按列 A 去重,保留最后一条df["C"].unique() # 该列唯一值df["C"].value_counts() # 该列值计数(按出现次数排序)
注解:& 和 | 在 Pandas 中必须用括号包裹每个条件,否则优先级报错。and/or 关键字不能用于 Series 。
五、 groupby 分组聚合
用 Excel 数据透视表类比: groupby 就是数据透视表的代码版。先按某列拆分(如按"班级"),每组求和/求平均(如班级总分、班级平均分),最后合并成结果表。
该策略由 Hadley Wickham 在 2011 年 Journal of Statistical Software 第 40 卷第 1 期论文 The Split-Apply-Combine Strategy for Data Analysis[4] 中形式化。三步对应 Pandas 实现:
df.groupby(key) | |||
.agg(func).apply(func) | |||
df = pd.DataFrame({"category": ["A", "B", "A", "B", "A"],"value": [10, 20, 30, 40, 50],})df.groupby("category")["value"].sum()df.groupby("category")["value"].agg(["sum", "mean", "count"])df.groupby("category").agg({"value": ["sum", "mean"],"count": "size",})df.groupby("category")["value"].apply(lambda x: x.max() - x.min())df.groupby(["year", "month"])["sales"].sum()
注解: groupby 后默认分组键成为 Index 。加 as_index=False 保留为普通列:
df.groupby("category",as_index=False)["value"].sum()六、 merge 与 concat
用 SQL/HR 类比: merge 就是 SQL 的 JOIN ,按某个 key 把两张表拼起来。例如员工表 + 部门表按 dept_id 合并,得到带部门名的完整员工表。
left = pd.DataFrame({"id": [1, 2, 3], "name": ["a", "b", "c"]}) # 员工表right = pd.DataFrame({"id": [1, 2, 4], "score": [80, 90, 70]}) # 评分表pd.merge(left, right, on="id", how="inner") # 内连接:只保留两边都有的 id(1,2)pd.merge(left, right, on="id", how="left") # 左连接:保留左表全部(1,2,3),缺的填 NaNpd.merge(left, right, on="id", how="outer") # 外连接:并集(1,2,3,4),缺的填 NaNpd.merge(left, right, left_on="id", right_on="user_id")pd.concat([df1, df2], axis=0) # 纵向(行拼接,类似 UNION ALL)pd.concat([df1, df2], axis=1) # 横向(列拼接)df1.set_index("id").join(df2.set_index("id"), how="inner")
四种 how 参数对比:
how="inner" | |||
how="left" | |||
how="right" | |||
how="outer" |
注解: merge 后行数可能变化, concat 后行数或列数严格等于 sum 。
七、 pivot 与 melt
用宽表/长表类比:宽表是一行一个样本、各列是不同指标(适合人看);长表是一条记录一行(适合机器存)。 pivot 把长转宽(生成报告用), melt 把宽转长(数据清洗用)。两者互逆。
long_df = pd.DataFrame({"date": ["2026-01-01"]*3 + ["2026-01-02"]*3,"city": ["BJ", "SH", "GZ"]*2,"temp": [5, 8, 15, 3, 6, 13],})wide = long_df.pivot(index="date", columns="city", values="temp")long_again = wide.reset_index().melt(id_vars="date", var_name="city", value_name="temp")pd.pivot_table(long_df,index="date",columns="city",values="temp",aggfunc="mean", # 默认 mean)
注解:pivot 要求 index+columns 唯一,重复时报错;pivot_table 自动聚合重复值。日常工作中数据有重复就用 pivot_table 。
八、缺失值处理
用 Excel 类比:缺失值就是 Excel 的空单元格。 Pandas 用 NaN ( Not a Number )表示。处理前先看缺失比例,再决定 drop (删除)还是 fill (填充)。
df.isna().sum() # 每列缺失数df.isna().mean() # 每列缺失比例(0-1)df.dropna() # 删除含缺失的行df.dropna(subset=["A", "B"]) # 仅当 A 或 B 列空时才删df.dropna(thresh=3, axis=1) # 删除非空值少于 3 的列df.fillna(0) # 全部填 0df["A"].fillna(df["A"].mean()) # 用均值填 A 列df["A"].fillna(df["A"].median()) # 中位数(对异常值更稳健)df["A"].fillna(method="ffill") # 前向填充(时间序列常用:用昨天的值填今天)df["A"].interpolate() # 线性插值(用相邻值的平均填)
注解: ML 中缺失值处理顺序——先看缺失比例(< 5% 可丢,> 50% 该列考虑丢,中间用 fill ),再分析缺失是否随机( MCAR/MAR/MNAR ),最后选 drop/fill/标记。直接 fillna(0) 在数值特征上会引入偏差。
九、时间序列
典型场景:股票日线数据、电商日销量、传感器每秒采样。这些数据有时间索引,常用滑动统计、重采样。
dates = pd.date_range("2026-01-01", periods=10, freq="D") # 10 天的日期序列ts = pd.Series(np.random.randn(10), index=dates)ts.resample("W").mean() # 周均值ts.resample("M").sum() # 月求和ts.rolling(window=3).mean() # 3 日滑动均值ts.rolling(window=7).std() # 7 日滑动标准差(波动率)ts.diff() # 一阶差分(今天的值 - 昨天的值)ts.pct_change() # 百分比变化(涨跌幅)ts_utc = ts.tz_localize("UTC")ts_bj = ts_utc.tz_convert("Asia/Shanghai")
注解:时间序列预测任务(销量、股价)的标准特征工程——滞后值、滑动统计、差分。
十、性能优化
何时关心性能:数据量到百万行级, Pandas 默认写法会慢。下面是几个常用加速技巧。
for idx, row in df.iterrows() | df.apply(func, axis=1) | ||
.loc | df['c'] = df['a'] + df['b'] | ||
df['s'].apply(lambda x: x.lower()) | df['s'].str.lower() | ||
downcastastype('category') | |||
df[df[...]] | df.query("A > 50 and B < 30") | ||
df['cat'].map({...}) | df['cat'].astype('category').cat.codes |
df = pd.read_csv("data.csv")print(f"默认内存: {df.memory_usage(deep=True).sum() / 1e6:.1f} MB")for col in df.select_dtypes(include="int64").columns:df[col] = pd.to_numeric(df[col], downcast="integer")for col in df.select_dtypes(include="float64").columns:df[col] = pd.to_numeric(df[col], downcast="float")for col in df.select_dtypes(include="object").columns:if df[col].nunique() / len(df) < 0.5: # 唯一值比例 <50%df[col] = df[col].astype("category")print(f"优化后内存: {df.memory_usage(deep=True).sum() / 1e6:.1f} MB")
注解:百万行 csv 经类型优化通常节省 50-80% 内存。
十一、电影评分数据案例
完整场景:模拟 100 个用户对 50 部电影评分( 1-5 星), 2000 条评分记录。计算:评分最高的用户、热门高分电影、用户偏好类型。
import pandas as pdimport numpy as nprng = np.random.default_rng(42)n_users, n_movies = 100, 50users = [f"u{i:03d}" for i in range(n_users)]movies = [f"m{i:03d}" for i in range(n_movies)]genres = ["动作", "喜剧", "剧情", "科幻", "爱情", "动画"]records = []for _ in range(2000):u = rng.choice(users)m = rng.choice(movies)s = int(rng.integers(1, 6))records.append({"user": u, "movie": m, "score": s})df = pd.DataFrame(records)df["genre"] = df["movie"].map({m: rng.choice(genres) for m in movies})top_users = df.groupby("user")["score"].mean().sort_values(ascending=False).head(5)print("评分最高的 5 个用户:")print(top_users)movie_stats = df.groupby("movie")["score"].agg(["count", "mean", "std"])print(f"\n电影评分统计前 3:\n{movie_stats.head(3)}")pivot = df.pivot_table(index="user", columns="genre", values="score", aggfunc="mean")print(f"\n透视表 shape: {pivot.shape}") # (100, 6)popular = movie_stats[movie_stats["count"] > 30].sort_values("mean", ascending=False)print(f"\n热门高分电影前 5:\n{popular.head()}")
注解:此例覆盖创建 → groupby 聚合 → pivot_table 透视 → 多条件筛选,是 ML 推荐系统数据预处理的标准范式。
参考链接
[1] Pandas 官方文档: https://pandas.pydata.org/docs/
[2] Wes McKinney 《 Python for Data Analysis 》第 3 版: https://wesmckinney.com/book/
[3] Pandas Cookbook: https://pandas.pydata.org/docs/user_guide/cookbook.html
[4] The Split-Apply-Combine Strategy for Data Analysis: https://www.jstatsoft.org/v40/i01/

夜雨聆风