当前位置: 首页 > news >正文

Pandas apply() 实战避坑指南:性能、类型与索引三大陷阱

1. 这不是“调个函数”那么简单:为什么你写的 apply() 总是慢、报错、结果不对?

Pandas 的apply()是我带新人时第一个重点讲、也第一个被反复踩坑的函数。它表面看就是“对每行或每列执行一个函数”,但实际用起来,90%的人在前三天都会遇到三类典型问题:性能断崖式下跌、返回值类型混乱、索引对不齐导致数据错位。这不是你代码写得差,而是apply()本身就是一个“高自由度+低容错”的设计——它把选择权全交给你,但没告诉你每个选择背后藏着多少暗流。比如你写df.apply(lambda x: x.sum()),看起来没问题,但如果你的 DataFrame 里混了字符串列和数值列,这个 lambda 就会在某列上直接抛TypeError;再比如你用axis=1处理百万级行数据,实测下来比原生 for 循环还慢 3 倍,因为apply()在逐行模式下会强制触发 pandas 内部的 Series 构造与销毁开销。更隐蔽的是索引问题:当你对一列做apply()返回新值,pandas 默认保留原始索引,但如果原始索引是乱序或有重复,你得到的结果看似“对得上”,实则第 100 行的输出可能对应第 98 行的输入——这种错位在后续 join 或 groupby 时才会暴露,排查成本极高。所以这篇文章不叫“apply() 用法大全”,而叫“apply() 实战避坑手册”。它面向两类人:一类是刚学完pd.read_csv()就急着用apply()做清洗的新手,另一类是写了三年 pandas 却还在用for idx, row in df.iterrows():硬扛性能问题的老手。我会从底层机制讲起,告诉你什么时候该用apply(),什么时候必须换方案;会拆解每一个参数的真实作用域,而不是照搬文档里的“axis=0 表示列”这种废话;会给出可直接粘贴运行的对比测试代码,让你亲眼看到apply()vectorize()map()numpy.where()在不同场景下的真实耗时差异。这不是 API 文档复读,而是我在金融风控、电商用户行为分析、IoT 设备日志处理等十几个真实项目中,用掉 27 个调试小时、重写 14 次核心逻辑后总结出的血泪经验。

2. 核心设计逻辑与适用边界:别再无脑套用 apply()

2.1 apply() 的本质不是“遍历”,而是“委托执行器”

很多人误以为apply()是 pandas 提供的“高级 for 循环”,这是根本性误解。它的底层逻辑是:将 DataFrame/Series 的结构信息(dtype、index、name)封装成上下文,再把数据块以最小粒度(单个 Series 或单个值)传递给用户函数,最后将函数返回值按规则重组为新的 pandas 对象。这个“封装-传递-重组”过程决定了它的三大特性:

第一,强类型约束apply()不会帮你做类型转换。如果你传入一个返回str的函数去处理int64列,结果列的 dtype 会变成object,后续所有.sum().mean()都会静默失败(返回NaN而非报错)。这不像astype(str)那样明确声明意图,而是隐式污染整个数据流。

第二,索引绑定不可解耦。无论你用axis=0还是axis=1apply()返回的新对象默认继承原始对象的索引。这意味着:如果你的函数内部做了sorted()random.shuffle()或任何打乱顺序的操作,返回值的索引顺序和原始顺序必然不一致,但 pandas 不会警告,只会默默按索引对齐——结果就是“数据内容和索引名对不上”。我在处理用户点击流数据时就栽过这个坑:原始数据按时间戳排序,apply()里用了np.random.choice()抽样,结果生成的“用户活跃度”指标全错位,花了两天才定位到是索引对齐机制在作祟。

第三,执行粒度不可控apply()的最小执行单元是 Series(axis=0)或 Series(axis=1),它无法像 numpy 向量化那样直接操作底层内存块。即使你的函数逻辑极其简单(比如lambda x: x * 2),pandas 仍要为每一列/行构造临时 Series 对象,这个构造成本在小数据集上不明显,但在 10 万行以上就会成为瓶颈。我做过一组基准测试:对 50 万行 × 10 列的数值 DataFrame,用apply(lambda x: x * 2)耗时 1.8 秒;用df * 2(纯向量化)仅需 0.012 秒,相差 150 倍。这不是函数写得不好,而是apply()的设计定位本就不是高性能计算。

所以apply()的真实适用边界非常清晰:只用于无法向量化、且逻辑复杂到必须用 Python 控制流(if/else/try/except)的场景。比如“根据用户等级和最近 3 笔订单金额动态计算信用分”,其中涉及多条件嵌套、外部 API 调用、异常兜底逻辑——这种场景apply()是唯一选择。但如果你只是“把价格列四舍五入到整数”,请立刻换成round();如果是“判断是否为周末订单”,请用dt.dayofweek.isin([5,6]);如果是“替换指定字符串”,请用str.replace()。记住一个铁律:能用 pandas 原生方法解决的,绝不写apply();能用 numpy 向量化的,绝不让apply()接管;只有当 Python 的灵活性成为刚需时,apply()才是你最后的武器

2.2 为什么 axis 参数常被误解?关键在“输入单元”的定义

文档里说axis=0表示按列操作,axis=1表示按行操作,但这只是表层描述。真正决定apply()行为的是“输入单元”的数据结构。我们用一个具体例子说明:

import pandas as pd import numpy as np df = pd.DataFrame({ 'A': [1, 2, 3], 'B': [4, 5, 6], 'C': ['x', 'y', 'z'] })

当你执行df.apply(lambda x: type(x), axis=0),输出是:

A <class 'pandas.core.series.Series'> B <class 'pandas.core.series.Series'> C <class 'pandas.core.series.Series'> dtype: object

注意:xSeries,其索引是['A','B','C'](即列名),值是该列所有行的数据。也就是说,axis=0时,apply()每一列当作一个 Series 输入,函数接收的是“列数据 + 列名索引”。

df.apply(lambda x: type(x), axis=1)输出是:

0 <class 'pandas.core.series.Series'> 1 <class 'pandas.core.series.Series'> 2 <class 'pandas.core.series.Series'> dtype: object

此时x仍是Series,但其索引是['A','B','C'](列名),值是该行所有列的数据。也就是说,axis=1时,apply()每一行当作一个 Series 输入,函数接收的是“行数据 + 列名索引”。

这个细节至关重要。很多初学者写df.apply(lambda x: x['A'] + x['B'], axis=1)觉得理所当然,但一旦 DataFrame 列名包含空格或特殊字符(如'user id'),x['user id']就会报错,而x.iloc[0]才是安全的。更隐蔽的问题是性能:axis=1模式下,pandas 必须为每一行构造一个新 Series,这个构造成本远高于axis=0(列数量通常远少于行数量)。在我的电商订单分析项目中,一个 200 万行 × 15 列的 DataFrame,用axis=1计算订单状态(需访问 3 个字段),耗时 42 秒;改用axis=0分别提取三列再用np.where()组合,耗时仅 0.3 秒。差距来自哪里?就是 Series 构造的次数:axis=1要构造 200 万次 Series,axis=0只需构造 3 次。

因此,选择axis的决策树应该是:

  1. 先问:我的逻辑是否必须基于整行数据?(比如“如果 A 列>10 且 B 列为空,则 C 列设为 X”)
  2. 如果是,再问:能否把行逻辑拆解为列操作?(比如用mask()+loc替代)
  3. 如果不能,再确认:列名是否规范?是否需用iloc替代[]访问?
  4. 最后,务必对axis=1场景做性能压测,不要凭直觉判断。

2.3 raw 参数:不是“加速开关”,而是“数据裸奔模式”

raw=Trueapply()最被滥用的参数。文档说它“传递 numpy 数组而非 Series”,很多人理解为“开了就变快”,于是无脑加上。但真相是:raw=True关闭了 pandas 的所有元数据保护,把原始内存块直接扔给你的函数,你获得速度的同时,也失去了索引、列名、dtype 等一切上下文

看这个例子:

df = pd.DataFrame({'A': [1,2,3], 'B': [4,5,6]}, index=['x','y','z']) # raw=False(默认) df.apply(lambda x: x.index, axis=0) # 输出:A Index(['x', 'y', 'z'], dtype='object'), B 同理 # raw=True df.apply(lambda x: x.index, axis=0, raw=True) # 报错:'numpy.ndarray' object has no attribute 'index'

raw=True时,x变成了numpy.ndarray,你连x.shape都要自己推导(axis=0时是(n_rows,)axis=1时是(n_cols,)),更别说x.namex.dtype。这意味着:如果你的函数里写了x.name.upper(),加了raw=True就直接崩;如果你依赖x.dtype == 'int64'做分支判断,raw=Truex.dtypenumpy.dtype('int64'),比较结果为False

那什么场景该用raw=True?只有两种:

  • 你的函数完全不依赖 pandas 元数据,纯数学计算(如np.log(x)scipy.stats.zscore(x));
  • 你已通过其他方式(如df.dtypes预检查)确保输入数据类型安全,且函数内部用np.array()显式处理。

我在处理传感器时序数据时用过一次raw=True:需要对每列 100 万点数据做 FFT 变换,函数里只调用np.fft.fft(x),不碰任何索引或名称。开启raw=True后耗时从 8.2 秒降到 1.9 秒。但前提是:我提前用assert df.dtypes.eq('float64').all()确保所有列都是 float64,否则np.fft.fft()对 int 类型会静默转为 float,精度丢失。

所以raw=True的正确用法是:先做类型和结构预检,再用raw=True卸载元数据开销,最后在函数内用 numpy 原生方法处理。把它当“加速开关”用,等于在高速公路上蒙眼开车。

3. 四大核心使用模式与实操细节:从入门到避坑

3.1 模式一:单列处理(axis=0)——最安全,但最容易写出“伪向量化”

这是apply()最常用也最易被滥用的场景。典型错误是把本可用 pandas 原生方法实现的逻辑硬套apply()。比如:

# ❌ 错误示范:用 apply 做基础数学运算 df['price_rounded'] = df['price'].apply(lambda x: round(x, 0)) # ✅ 正确做法:用原生方法 df['price_rounded'] = df['price'].round(0) # ❌ 错误示范:用 apply 做字符串操作 df['name_upper'] = df['name'].apply(lambda x: x.upper()) # ✅ 正确做法:用 str 访问器 df['name_upper'] = df['name'].str.upper()

这些错误不会报错,但会带来三重损失:性能下降(apply构造 Series 开销)、内存占用上升(临时对象)、可读性降低(lambda隐藏业务意图)。那么什么情况下单列apply()是必要的?答案是:当逻辑涉及跨行状态或外部依赖时

例如,计算“用户连续登录天数”:

def calc_consecutive_days(series): """输入:按时间排序的 login_date Series,输出:连续登录天数""" if len(series) == 0: return pd.Series([], dtype='int64') # 转为 datetime 并排序(确保输入有序) dates = pd.to_datetime(series).sort_values() # 计算相邻日期差 diffs = dates.diff().dt.days # 连续登录:diff==1,否则重置为1 result = [] count = 1 for i, diff in enumerate(diffs): if i == 0: result.append(1) else: if diff == 1: count += 1 else: count = 1 result.append(count) return pd.Series(result, index=dates.index) # 应用 df['consecutive_days'] = df.groupby('user_id')['login_date'].apply(calc_consecutive_days)

这里的关键是:calc_consecutive_days必须知道“前一行的日期”,而 pandas 原生方法无法直接提供这种行间状态。apply()的优势在于它把整个 Series 当作一个整体输入,函数内部可以自由遍历、记录状态。

实操要点:

  • 永远用groupby().apply()而非df[col].apply()处理分组逻辑:前者保证输入 Series 是同一组内的有序数据,后者会打乱分组。
  • 在函数开头做数据校验if series.empty: return series.copy()避免空组报错。
  • 显式指定返回值 dtype:用pd.Series(..., dtype='int64')而非依赖自动推断,防止object类型污染。

提示:如果逻辑简单到只需shift()diff(),优先用transform()。比如“计算每日销售额环比”,df.groupby('date')['sales'].transform(lambda x: x/x.shift(1))apply()更高效,因为transform()专为标量到标量映射优化。

3.2 模式二:整行处理(axis=1)——性能杀手,但不可替代

axis=1apply()的“高压线”,用得好事半功倍,用不好系统卡死。它的核心价值在于:当业务逻辑必须同时访问多个列的值,且无法用向量化表达式(如np.where)描述时

经典案例:订单状态判定。

def get_order_status(row): """根据多列判断订单状态""" # 注意:row 是 Series,索引是列名 if pd.isna(row['payment_time']): return 'unpaid' elif row['payment_time'] > row['order_time'] + pd.Timedelta('2h'): return 'delayed_payment' elif row['delivery_time'] is not None: return 'delivered' else: return 'shipped' # ⚠️ 危险写法(列名含空格时崩溃) # df['status'] = df.apply(lambda x: x['order time'] + x['payment time'], axis=1) # ✅ 安全写法(用 iloc 避免列名问题) df['status'] = df.apply( lambda x: 'unpaid' if pd.isna(x.iloc[2]) else 'delayed_payment' if x.iloc[2] > x.iloc[1] + pd.Timedelta('2h') else 'delivered' if pd.notna(x.iloc[3]) else 'shipped', axis=1 )

这里用iloc替代[]是关键。iloc基于位置,不受列名影响;而[]基于标签,列名一变就崩。我在维护一个跨国电商数据管道时,法语版 CSV 导入后列名变成'date de commande',所有用x['order_date']apply()全部报错,改成iloc后零修改通过。

性能优化技巧:

  • 预过滤再 apply:如果 80% 的行满足某个简单条件(如status != 'cancelled'),先用df.query("status != 'cancelled'")过滤,再对子集apply(),比全量apply()快 3-5 倍。
  • numba.jit加速计算密集型函数:对纯数值计算的axis=1函数,加@numba.jit(nopython=True)装饰器,实测提速 10-50 倍。但注意:numba不支持 pandas 对象,函数内只能用numpy和原生 Python。

注意:axis=1result_type参数极少用,但关键时刻救命。默认result_type=None表示“尽量保持输入类型”,但有时你需要强制返回Series(如函数返回字典),这时设result_type='expand'会把字典键转为新列,result_type='reduce'强制返回单个标量。

3.3 模式三:自定义聚合(agg)——apply() 的隐藏形态

很多人不知道,df.groupby().apply()df.groupby().agg()在底层共享同一套引擎,但agg()对聚合函数有更严格的契约。apply()的聚合模式适用于:需要返回多个值、或返回值结构不固定(如字典、列表)的场景

例如,计算每组的统计摘要:

def group_summary(group): """返回字典,key 为统计项名,value 为值""" return { 'count': len(group), 'mean_price': group['price'].mean(), 'max_quantity': group['quantity'].max(), 'top_category': group['category'].mode().iloc[0] if not group['category'].mode().empty else 'unknown' } # ✅ 正确:用 apply 返回字典,pandas 自动展开为多列 result = df.groupby('region').apply(group_summary) # result 是 DataFrame,列名为 'count', 'mean_price', 'max_quantity', 'top_category' # ❌ 错误:用 agg 试图返回字典(会报错) # df.groupby('region').agg(group_summary) # TypeError

这里apply()的优势是“无契约约束”:你可以返回任意 Python 对象,pandas 会尽力将其展平。但代价是:agg()支持的并行优化(如engine='numba')在apply()中不可用。

实操要点:

  • 返回字典时,确保所有组返回相同的 key 集合:如果某组group['category'].mode()为空,top_category缺失,会导致apply()返回NaN列,后续fillna()成本高。应在函数内补全默认值。
  • 大数据量时,用as_index=False避免索引重建开销df.groupby('col', as_index=False).apply(func)比默认as_index=True快 15-20%,因为省去了索引对齐步骤。

3.4 模式四:广播式应用(broadcast)——apply() 的进阶玩法

apply()还能配合broadcast参数(虽已弃用,但原理重要)实现跨维度计算。现代写法是用apply()+axis+result_type组合模拟。

例如,计算每行与全局均值的偏差:

global_mean = df['price'].mean() # ✅ 标准写法:用向量化 df['deviation'] = df['price'] - global_mean # ✅ apply() 写法(仅当需动态计算时) df['deviation'] = df.apply( lambda row: row['price'] - df['price'].mean(), # 注意:这里每次调用都重算 mean! axis=1 )

但上面apply()版本效率极低,因为df['price'].mean()在每行都被重复计算。正确做法是:

global_mean = df['price'].mean() # 提前计算 df['deviation'] = df.apply(lambda row: row['price'] - global_mean, axis=1)

更高级的广播是“列间广播”:比如用 A 列的值作为权重,计算 B 列的加权平均。

def weighted_avg_by_a(row): # row 是 Series,包含所有列 return row['B'] * row['A'] / row['A'].sum() # ❌ 错!row['A'].sum() 是单个值,不是列和 # ✅ 正确:权重应来自全局列,而非当前行 weights = df['A'] / df['A'].sum() df['weighted_B'] = df['B'] * weights

这再次印证:apply()不是万能广播器,真正的广播靠 pandas 原生的索引对齐机制apply()只负责“把函数应用到每个单元”,广播能力由 pandas 的+-*等运算符提供。

4. 性能对比与实测数据:什么情况下该放弃 apply()

4.1 五种常见场景的耗时实测(10 万行 × 5 列)

我用真实硬件(Intel i7-10875H, 32GB RAM)对以下场景做了 10 次取平均的基准测试,DataFrame 为数值型(float64),避免字符串处理干扰:

场景方法平均耗时说明
四舍五入df['col'].apply(lambda x: round(x, 2))124 msapply()最基础用法
df['col'].round(2)0.8 ms原生方法快 155 倍
条件赋值df.apply(lambda x: 'high' if x['A']>10 else 'low', axis=1)386 msaxis=1开销巨大
np.where(df['A']>10, 'high', 'low')1.2 msnp.where快 320 倍
字符串分割df['text'].apply(lambda x: x.split()[0] if x else '')215 ms字符串操作成本高
df['text'].str.split().str[0].fillna('')4.7 msstr访问器快 45 倍
分组聚合df.groupby('group')['val'].apply(lambda x: x.max() - x.min())89 msapply()做聚合
df.groupby('group')['val'].agg(lambda x: x.max() - x.min())63 msagg()快 1.4 倍(优化更多)
复杂逻辑df.apply(custom_logic_with_if_else, axis=1)1520 ms真实业务函数(含 3 层 if)

关键结论:

  • 纯数学运算:原生方法 >np.vectorize()>apply()np.vectorize()apply()的 numpy 化身,但仍有封装开销。
  • 字符串操作str访问器 >apply()。pandas 的str方法底层用 Cython 优化,apply()是纯 Python 解释执行。
  • 分组聚合agg()>apply()agg()专为聚合设计,支持numba加速和并行。
  • 复杂逻辑apply()是唯一选择,但可通过numba.jit优化。我测试过一个含 5 层嵌套 if 的信用评分函数,加@numba.jit(nopython=True)后从 1520ms 降到 210ms。

4.2 apply() 的性能拐点:何时必须重构?

根据 20+ 个项目经验,apply()的性能拐点有三个硬指标:

  1. 行数超过 5 万且axis=1:此时apply()耗时呈线性增长,10 万行约 400ms,50 万行超 2 秒。重构方案:用pd.concat()拆分为多列独立处理,再用pd.DataFrame重组。例如:

    # 原始(慢) df['status'] = df.apply(get_status, axis=1) # 重构(快) status_col = pd.Series(index=df.index, dtype='object') status_col[df['payment_time'].isna()] = 'unpaid' status_col[(df['payment_time'].notna()) & (df['payment_time'] > df['order_time'] + pd.Timedelta('2h'))] = 'delayed_payment' # ... 其他条件 df['status'] = status_col
  2. 函数内含 I/O 操作(API 调用、文件读写)apply()是同步阻塞的,1000 次 API 调用会串行执行。重构方案:用concurrent.futures.ThreadPoolExecutor批量并发,或改用dask分布式处理。

  3. 返回值 dtype 为object且后续需数值计算object列无法使用numpy向量化,df['col'].sum()会退化为 Python 循环。重构方案:在apply()函数内显式转换类型,如return float(result)而非return str(result)

实操心得:我在一个实时风控项目中,曾用apply()处理 20 万条交易流水,函数内调用外部反欺诈 API。单次apply()耗时 3.2 秒,无法满足 100ms 响应要求。最终重构为:用ThreadPoolExecutor(max_workers=20)并发调用 API,再用pd.concat()合并结果,耗时降至 180ms。关键教训:apply()的“便利性”在生产环境常是毒药,必须为性能妥协。

5. 常见问题与独家排查技巧:那些文档不会写的坑

5.1 问题一:apply() 返回 NaN,但函数明明没报错

现象:df['new_col'] = df['col'].apply(my_func)后,new_col全是NaNmy_func单独测试却正常。

原因:函数返回了None。Python 函数默认返回None,而 pandas 将None映射为NaN。常见于忘记return语句,或if分支缺少else

排查技巧:

  • 在函数开头加print(f"Input: {x}, Type: {type(x)}"),确认输入正常;
  • 在函数末尾加print(f"Output: {result}, Type: {type(result)}"),确认返回值非None
  • df['col'].apply(my_func).isna().sum()统计NaN数量,若等于行数,基本确定是None问题。

解决方案:函数末尾必须有return,且所有分支路径都要覆盖。用pylint检查no-else-returninconsistent-return-statements

5.2 问题二:apply() 结果索引错乱,数据和标签对不上

现象:df.apply(lambda x: x.sum(), axis=0)返回的 Series,索引是['A','B','C'],但值顺序和原始列顺序不一致。

原因:pandas 对返回值做自动排序。当apply()返回字典或pd.Series时,pandas 会按 key 名字母序重排索引,而非保持原始顺序。

验证方法:

df = pd.DataFrame({'Z': [1], 'A': [2], 'M': [3]}) print(df.columns) # Index(['Z', 'A', 'M'], dtype='object') result = df.apply(lambda x: x.sum(), axis=0) print(result.index) # Index(['A', 'M', 'Z'], dtype='object') ← 已排序!

解决方案:

  • pd.Series(..., index=df.columns)显式指定索引顺序;
  • 或用result.reindex(df.columns)重排;
  • 最佳实践:避免在apply()中返回字典,改用zip(df.columns, values)构造元组列表再转Series

5.3 问题三:apply() 在 Jupyter 中正常,脚本中报错

现象:Jupyter notebook 里df.apply(func)完美运行,但保存为.py脚本后执行报NameError: name 'pd' is not defined

原因:Jupyter 的全局命名空间污染。你在 notebook 里import pandas as pd后,apply()函数内可以直接用pd,但脚本中函数是独立作用域,无法访问模块。

排查技巧:在函数内加import pandas as pd,或确保所有依赖都在函数外导入。

解决方案:函数内不依赖外部变量。把pdnp等模块作为参数传入,或用functools.partial预绑定:

from functools import partial def safe_func(x, pd_module, np_module): return pd_module.Series([1,2,3]) df['col'] = df['col'].apply(partial(safe_func, pd_module=pd, np_module=np))

5.4 问题四:apply() 处理空 DataFrame 时崩溃

现象:df_empty = pd.DataFrame(columns=['A','B']),执行df_empty.apply(lambda x: x.sum(), axis=0)ValueError: No objects to concatenate

原因:空 DataFrame 的apply()会尝试对空 Series 调用函数,而某些函数(如sum())对空序列无定义。

解决方案:在函数开头加空值检查:

def robust_sum(x): if len(x) == 0: return 0 # 或 np.nan,根据业务定 return x.sum()

或者用df.apply(..., result_type='reduce')强制返回标量,pandas 会自动处理空情况。

5.5 问题五:apply() 与链式操作(chaining)冲突

现象:df.assign(new_col=lambda x: x['A'].apply(func)).query("new_col > 10")报错,提示new_col不存在。

原因:assign()的 lambda 是在query()之前执行,但query()的字符串解析器无法识别assign()新增的列名。

解决方案:拆分为两步,或用eval()(不推荐):

# ✅ 正确 df = df.assign(new_col=df['A'].apply(func)) df = df.query("new_col > 10")

独家技巧:用pipe()方法实现安全链式调用:

def add_status(df): df = df.copy() df['status'] = df.apply(get_status, axis=1) return df df.pipe(add_status).query("status == 'delivered'")

pipe()显式传递 DataFrame,避免作用域混淆。

6. 替代方案全景图:什么情况下该彻底抛弃 apply()

6.1 向量化方法优先级清单(按推荐顺序)

当你的需求落入以下类别时,请立即关闭本文,去查对应文档:

  1. 数学运算+ - * / ** %np.log()np.sin()df.round()df.clip()
    → 用原生运算符或numpy函数,100% 向量化。

  2. 条件逻辑np.where(condition, x, y)np.select(conditions, choices, default)df.mask()df.where()
    np.whereapply()条件赋值的黄金替代,支持多维数组。

  3. 字符串处理df['col'].str.contains().str.replace().str.extract().str.split()
    → pandasstr访问器专为字符串优化,比apply()快数十倍。

  4. 时间处理df['col'].dt.year.dt.dayofweek.dt.floor('D')
    dt访问器底层用cftimeapply()调用pd.to_datetime()是自杀行为。

  5. 分组聚合df.groupby().agg({'col1': 'sum', 'col2': 'mean'}).transform().filter()
    agg()支持字典配置,transform()保持形状,filter()

http://www.jsqmd.com/news/1053178/

相关文章:

  • [Django] DisallowedHost突然爆发?ALLOWED_HOSTS=‘*‘为什么没用+中间件根治方案(附代码)
  • 5分钟掌握英雄联盟内存换肤:R3nzSkin终极使用指南
  • Ubuntu 18.04 Node.js 安装避坑指南:nvm、NodeSource 与 apt 选型逻辑
  • Qwen 3.6-27B本地部署实战:vLLM优化、长上下文对齐与PLC智能体落地
  • Selenium架构深度解析:从WebDriver协议到自动化测试框架设计
  • 如何永久保存微信聊天记录?WeChatMsg完整指南帮你掌控个人数据
  • 终极AMD处理器性能调优指南:掌握SMU调试工具的专业技巧
  • [特殊字符] 从零到一:Python 爬取微博热搜与热门话题实时帖子的终极实战指南(2026最新版)
  • 5步高效部署HunterPie:Monster Hunter: World游戏覆盖层终极指南
  • Java Playwright自动化测试:高级元素定位策略与实战技巧
  • LPC21xx/22xx Flash编程与代码保护:ISP/IAP实战与CRP避坑指南
  • LS1028A/i.MX 8M嵌入式图形与多外设开发实战:从GPU加速到NFC/BLE集成
  • [Android] FixPlus-AI一键擦除衣服变性感美女
  • LinkSwift:九大网盘直链下载助手,告别限速的本地解析方案
  • NoFences:终极免费桌面整理神器,3步打造整洁高效工作空间
  • 嵌入式GUI开发:emWin SWIPELIST控件配置、API与界面设计实战
  • Qwen3.7-Max 实操指南:百炼平台调用、结构化输出与Token Plan配置
  • OpenClaw与Grok Build 0.1集成:本地智能体工作流引擎+模型服务化实战
  • 3分钟掌握Translumo:让外语游戏和视频瞬间变中文的智能翻译神器
  • ARKit 6.0空间锚点动态持久化实战
  • 【基于机器学习的租房信息分析系统】Python+mysql+Django,2(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码
  • 终极指南:如何在Mac上完美使用Xbox手柄驱动解决方案
  • NXP MCUXpresso FOC参数调优实战:从电机辨识到速度环整定
  • 嵌入式GUI开发利器:emWin仿真器从入门到实战应用
  • 接口自动化测试实战:从零搭建Python+pytest框架与CI/CD集成
  • NXP Real-time Edge Yocto项目实战:构建确定性实时边缘计算系统
  • 第5章:HTTP API入门——用curl调用本地模型
  • Java 插入排序:抓牌怎么排,它就怎么排
  • HandheldCompanion:为掌上游戏电脑打造的全能控制中心
  • 流媒体下载失败频发?N_m3u8DL-RE 5分钟解决90%常见问题