自学教程 学编程从这里开始

Pandas 高级功能

本篇属于进阶实战专题,聚焦日常教程很少讲、但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:最少有效样本数,不足返回NaN
  • center=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() 布尔类型,支持NA
  • pd.Int64Dtype() 整数,支持NA(区别np.int,np.int空值会变成float)
  • pd.StringDtype() 字符串类型,推荐替代object
df["数量"] = df["数量"].astype(pd.Int64Dtype())

十六、apply 性能优化方案(高级避坑)

大数据禁止直接apply,优先顺序:

  1. 向量化表达式(eval / where)
  2. str / dt 访问器
  3. groupby + transform / agg
  4. list comprehension
  5. 最后才是 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的新数据类型

十九、避坑清单

  1. rolling 分组滚动,一定要 reset_index(drop=True) 否则索引对不上
  2. MultiIndex 导出Excel前尽量 reset_index,否则表头多层
  3. category 类型不能直接用str方法,需要先转string
  4. eval/query 里面字符串引号和外部引号不要冲突
  5. explode 只支持 list-like,空值会直接变成NaN
  6. 窗口函数min_periods默认1,数据少时容易出现异常值

二十、拓展方向

  • pd.merge_asof 近似匹配(时序模糊关联)
  • Styler 美化输出HTML表格
  • pandas-gbq、pandas-bigquery 大数据对接
  • Dask 兼容Pandas API,超大数据集

标签:

0 条笔记