tqdm 套 pandas.DataFrame.apply 没反应是因为 apply 不暴露迭代过程,正确做法是用 tqdm.pandas() 后调用 progress_apply,或改用 tqdm(df.itertuples()) 显式循环。

为什么 tqdm 套 pandas.DataFrame.apply 没反应?
因为 apply 默认不暴露迭代过程,tqdm 无法感知内部循环进度。直接写 tqdm(df.apply(...)) 会卡住或只显示单步——apply 返回的是结果 Series/DataFrame,不是可迭代对象。
正确做法是把操作拆成显式循环,再用 tqdm 包裹行或索引:
- 对逐行处理:用
df.iterrows()或df.itertuples()(后者更快) - 避免用
df.apply(..., axis=1)+tqdm装饰器,它不会生效 - 若必须用
apply(比如想利用向量化),改用tqdm.pandas()——但仅限简单函数,且底层仍转为itertuples
tqdm.pandas() 怎么用?有哪些限制?
tqdm.pandas() 是 pandas 扩展方法,给 apply 注入进度条支持。但它不是万能的:
- 必须在调用前执行一次
tqdm.pandas(),否则报AttributeError: 'DataFrame' object has no attribute 'progress_apply' - 它实际提供的是
progress_apply(非原生apply),且仅支持axis=1的逐行场景 - 不支持
axis=0(逐列)或groupby().apply() - 内部仍逐行迭代,性能和
itertuples()相近,别指望加速
示例:
立即学习“Python免费学习笔记(深入)”;
from tqdm import tqdm tqdm.pandas() # 必须先调用 result = df.progress_apply(lambda x: expensive_func(x['a'], x['b']), axis=1)
逐行处理时该选 iterrows 还是 itertuples?
itertuples 几乎总是更好:快 2–5 倍,且返回命名元组,字段访问更稳定(row.a 不受列名含空格/特殊字符影响);iterrows 返回 (index, Series),Series 构造开销大,且列名含空格时 row['col name'] 易错。
配合 tqdm 的典型写法:
from tqdm import tqdm
for row in tqdm(df.itertuples(), total=len(df)):
result.append(process(row.a, row.b))
# 注意:一定要传 total=len(df),否则长度未知,进度条无法计算百分比- 漏掉
total=参数会导致进度条只显示「?%」和「?? it/s」 - 如果 df 索引被重置过或含重复值,
itertuples仍安全;iterrows在某些链式操作后可能触发SettingWithCopyWarning
多进程下还能用 tqdm 吗?
不能直接用。标准 tqdm 不支持跨进程共享进度状态,子进程里调 tqdm 会各自打一行,混乱且不准。
可行方案只有两个:
- 用
tqdm.contrib.concurrent提供的封装函数,如process_map,它自动管理主进程进度条和子进程通信 - 放弃实时进度,改用日志计数(如每处理 1000 行 print 一次)
示例(需安装最新 tqdm ≥4.66):
from tqdm.contrib.concurrent import process_map results = process_map(expensive_func, df['col'].tolist(), max_workers=4)
注意:process_map 只接受一维可迭代对象,不能直接传 DataFrame 或 Series ——得先转成 list/tuple。
复杂点在于:所有参数必须能被 pickle,闭包变量、lambda、类方法都不能直接传;容易被忽略的是,子进程看不到主进程里的全局变量或导入模块,得显式传参或重构函数。


















