本篇属于进阶实战专题,聚焦日常教程很少讲、但ETL/报表/复杂数据处理高频用到的高级能力,承接前面销售数据集,包含:多级索引、窗口函数、向量化条件、explode、map/apply优化、astype分类、eval、query、交换列、交叉选择、链式赋值、flags、扩展数组等。
前置导入
import pandas as pd
import numpy as np
一、示例数据
data = {
"商品": ["键盘", "鼠标", "显示器", "键盘", "鼠标", "显示器"],
"渠道": ["线上", "线上", "线下", "线上", "线下", "线下"],
"数量": [2, 1, 1, 3, 2, 2],
"单价": [89, 45, 1200, 89, 45, 1200],
"下单日期": ["2026-10-01", "2026-10-02", "2026-10-03", "2026-10-04", "2026-10-04", "2026-10-05"]
}
df = pd.DataFrame(data)
df["金额"] = df["数量"] * df["单价"]
df["下单日期"] = pd.to_datetime(df["下单日期"])
二、多级索引 MultiIndex(非常核心)
分组、透视后经常生成多级索引,支持多层维度。
2.1 创建多级索引
# 方式1:groupby 多列自动生成
df_multi = df.groupby(["渠道","商品"])["金额"].sum()
print(df_multi.index)
# 方式2:手动构造
idx = pd.MultiIndex.from_tuples(
[("线上","键盘"),("线上","鼠标"),("线下","显示器")],
names=["渠道","商品"]
)
s = pd.Series([178,45,1200], index=idx)
2.2 多级索引取值
# 取一级索引
df_multi.loc["线上"]
# 精确两层
df_multi.loc[("线上","键盘")]
# 切片
df_multi.loc[("线上",):]
2.3 多级索引转换、展平
# 转为普通列
df_multi.reset_index()
# 展平索引名称(拼接)
df_multi_flat = df_multi.reset_index()
# 重命名多级列名(透视表常用)
df_pivot = df.pivot_table(index="商品", columns="渠道", values="金额", aggfunc="sum")
df_pivot.columns = ["_".join(col) for col in df_pivot.columns]
避坑:多级索引筛选、合并容易踩坑,导出Excel前一般
reset_index()展平。
三、窗口函数 rolling / expanding(滚动统计)
时序报表、滑动均值、累计指标,替代SQL窗口函数。
# 按日期排序
df_sort = df.sort_values("下单日期")
# 3日滚动均值
df_sort["滚动3日金额均值"] = df_sort["金额"].rolling(window=3).mean()
# 滚动求和
df_sort["滚动3日总和"] = df_sort["金额"].rolling(3).sum()
# 扩展窗口(从第一行到当前行累计)
df_sort["扩展累计均值"] = df_sort["金额"].expanding().mean()
常用参数:
window:窗口大小min_periods:最少有效样本数,不足返回NaNcenter=True:窗口居中closed="left"控制区间闭合
分组滚动(组内滑动,高频)
# 每个商品单独滚动统计
df["商品滚动2笔金额"] = df.groupby("商品")["金额"].rolling(2).sum().reset_index(drop=True)
四、explode 炸开列表(一行拆多行)
处理单元格内是列表的脏数据,爬虫、多标签场景必备
df_tag = pd.DataFrame({
"订单号":["A001","A002"],
"标签":[["新品","热卖"],["特惠"]]
})
df_explode = df_tag.explode("标签")
print(df_explode)
反向:
groupby+agg(list)多行合并成列表
五、df.eval / df.query 表达式语法
适合复杂筛选、计算,代码简洁,底层更快
# query 筛选,字符串表达式
df_q = df.query("数量 > 1 and 渠道 == '线上'")
# eval 新增列,表达式计算
df["折扣金额"] = df.eval("金额 * 0.9")
优点:表达式内直接写列名,不用反复写 df["xxx"];支持 @变量 引用外部变量
threshold = 100
df.query("金额 > @threshold")
六、where / mask 条件替换(向量化if)
np.where 简写版,向量化,比apply快很多
# where:条件False的位置替换成后面的值
df["等级"] = df["金额"].where(df["金额"]>500, "小额")
# mask:条件True的位置替换
df["等级2"] = df["金额"].mask(df["金额"]<=500, "小额")
对比:np.where 三目;where/mask 适合单条件覆盖原值。
七、分类 dtype:astype(‘category’) 节省内存、加速分组
类别重复多的字段(渠道、商品、状态),转category
df["商品"] = df["商品"].astype("category")
# 查看类别
df["商品"].cat.categories
# 重新设置顺序(排序、图表顺序)
df["商品"] = df["商品"].cat.set_categories(["鼠标","键盘","显示器"], ordered=True)
优势:
- 内存大幅降低(百万行效果明显)
- groupby、筛选速度更快
- 可以自定义有序分类,排序、图表不受字典序限制
八、df.swapaxes / swaplevel 交换索引、行列
# 多级索引交换两层
df_multi.swaplevel("渠道","商品")
# 行列转置
df.T
九、df.assign 链式新增列(推荐链式写法)
assign 返回新DataFrame,不修改原df,适合pipe链式流水线
df_new = df.assign(
折扣金额 = lambda x: x["金额"] * 0.9,
毛利率 = lambda x: x["折扣金额"] * 0.2
)
配合pipe:
df_clean = (df
.assign(折扣金额=lambda x:x["金额"]*0.9)
.query("折扣金额>100")
)
十、cumsum / cumprod / cummax / cummin 累计函数
df_sort = df.sort_values("下单日期")
df_sort["累计销售额"] = df_sort["金额"].cumsum()
df_sort["累计最大值"] = df_sort["金额"].cummax()
配合分组:
df["商品累计金额"] = df.groupby("商品")["金额"].cumsum()
十一、pd.cut / pd.qcut 分箱(前面单独成文,这里归入高级功能)
df["金额档位"] = pd.cut(df["金额"], bins=[0,100,1000,10000], labels=["低","中","高"])
十二、合并、对齐:reindex、align
reindex 重置索引,补缺失行、对齐时间序列
idx_all = pd.date_range("2026-10-01","2026-10-10")
df_date = df.groupby("下单日期")["金额"].sum()
df_date = df_date.reindex(idx_all, fill_value=0)
align 两个df按索引对齐,适合多表时序对齐
s1 = pd.Series([1,2], index=[1,2])
s2 = pd.Series([10,20], index=[2,3])
s1_align, s2_align = s1.align(s2, fill_value=0)
十三、replace 高级替换、正则替换
# 字典映射
df["渠道"].replace({"线上":"online","线下":"offline"})
# 正则批量替换
df["商品"].replace(r"盘$","键盘", regex=True)
十四、flags 标记、copy 控制(链式赋值警告根源)
# 关闭链式赋值警告(谨慎使用)
pd.set_option("mode.chained_assignment", None)
# 查看是否为视图/副本
df.flags
核心知识点:Pandas 视图(view)和副本(copy),很多bug来源于视图修改。
十五、扩展数组类型(Pandas 1.0+)
原生支持更好的空值、专用数组:
pd.BooleanDtype()布尔类型,支持NApd.Int64Dtype()整数,支持NA(区别np.int,np.int空值会变成float)pd.StringDtype()字符串类型,推荐替代object
df["数量"] = df["数量"].astype(pd.Int64Dtype())
十六、apply 性能优化方案(高级避坑)
大数据禁止直接apply,优先顺序:
- 向量化表达式(eval / where)
- str / dt 访问器
- groupby + transform / agg
- list comprehension
- 最后才是 apply
# 不推荐:df.apply(lambda ..., axis=1)
# 优先拆成向量化列运算
十七、综合实战模板(窗口+多级索引+assign链式)
def advanced_sales_report(df_raw):
df = df_raw.copy()
df["下单日期"] = pd.to_datetime(df["下单日期"])
df = df.sort_values(["商品","下单日期"])
df = df.assign(
折扣金额 = lambda x: x["金额"] * 0.9,
组内累计 = lambda x: x.groupby("商品")["金额"].cumsum(),
两笔滚动和 = lambda x: x.groupby("商品")["金额"].rolling(2).sum().reset_index(drop=True)
)
return df
res = advanced_sales_report(df)
十八、高级功能速查表
| 功能 | 用途 |
|---|---|
| MultiIndex | 多层维度分组、透视 |
| rolling / expanding | 滑动窗口、时序统计 |
| explode | 列表字段拆多行 |
| query / eval | 表达式筛选、计算 |
| assign | 链式新增列 |
| category | 分类优化,自定义顺序 |
| where / mask | 向量化条件赋值 |
| reindex / align | 索引对齐、补全时间缺口 |
| Int64 / Boolean / StringDtype | 支持NA的新数据类型 |
十九、避坑清单
- rolling 分组滚动,一定要
reset_index(drop=True)否则索引对不上 - MultiIndex 导出Excel前尽量 reset_index,否则表头多层
- category 类型不能直接用str方法,需要先转string
- eval/query 里面字符串引号和外部引号不要冲突
- explode 只支持 list-like,空值会直接变成NaN
- 窗口函数min_periods默认1,数据少时容易出现异常值
二十、拓展方向
pd.merge_asof近似匹配(时序模糊关联)Styler美化输出HTML表格pandas-gbq、pandas-bigquery大数据对接- Dask 兼容Pandas API,超大数据集
0 条笔记