如何在Pandas中利用向量化实现动态组合列名取值?
问题描述
我正在学习Pandas,需要找到高效的计算方法:如何根据每行中'a'和'b'列的值,组合成如aX_bY_name、aX_bY_foo_bar格式的唯一列名,并取出对应列的值?
初始数据示例
| index | a | b | a1_b1_name | a1_b1_foo_bar | a2_b1_name | a2_b1_foo_bar | a1_b2_name | a1_b2_foo_bar | a2_b2_name | a2_b2_foo_bar | a1_b3_name | a1_b3_foo_bar | a2_b3_name | a2_b3_foo_bar |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 2 | value1 | value2 | value3 | value4 | value5 | value6 | value7 | value8 | value9 | value10 | value11 | value12 |
| 1 | 2 | 1 | value13 | value14 | value15 | value16 | value17 | value18 | value19 | value20 | value21 | value22 | value23 | value24 |
| 2 | 2 | 2 | value25 | value26 | value27 | value28 | value29 | value30 | value31 | value32 | value33 | value34 | value35 | value36 |
| 3 | 1 | 1 | value37 | value38 | value39 | value40 | value41 | value42 | value43 | value44 | value45 | value46 | value47 | value48 |
| 4 | 2 | 3 | value49 | value50 | value51 | value52 | value53 | value54 | value55 | value56 | value57 | value58 | value59 | value60 |
期望结果
| index | name | foo_bar |
|---|---|---|
| 0 | value5 | value6 |
| 1 | value15 | value16 |
| 2 | value31 | value32 |
| 3 | value37 | value38 |
| 4 | value59 | value60 |
当前使用循环实现,但效率极低,代码如下:
for col in df.columns: df['name'] = np.where(col == 'a' + (df['a'].astype('Int16').astype(str)) + '_b' + (df['b'].astype('Int16').astype(str)) + '_name', df[col].values, df['name'])
高效解决方案
以下是几种基于Pandas向量化操作的实现,完全避免循环,适配数万行数据的场景:
方法1:使用df.lookup(简洁高效)
lookup可以直接根据行、列索引对批量取值,是这类场景的最优解之一:
import pandas as pd # 构造每行对应的目标列名 name_cols = 'a' + df['a'].astype(str) + '_b' + df['b'].astype(str) + '_name' foo_cols = 'a' + df['a'].astype(str) + '_b' + df['b'].astype(str) + '_foo_bar' # 批量取值并生成结果表 result = pd.DataFrame({ 'name': df.lookup(df.index, name_cols), 'foo_bar': df.lookup(df.index, foo_cols) })
方法2:向量化索引(适配Pandas 2.0+)
若你的Pandas版本≥2.0,lookup已被标记为过时,可改用numpy数组索引实现:
import numpy as np # 建立列名到索引的映射,加快查找速度 col_idx_map = {col: idx for idx, col in enumerate(df.columns)} # 获取每行目标列的索引位置 name_col_indices = name_cols.map(col_idx_map) foo_col_indices = foo_cols.map(col_idx_map) # 从numpy数组中批量取值 df_np = df.to_numpy() result = pd.DataFrame({ 'name': df_np[np.arange(len(df)), name_col_indices], 'foo_bar': df_np[np.arange(len(df)), foo_col_indices] })
方法3:重塑数据结构(适配多后缀扩展)
如果后续需要处理更多类似name/foo_bar的后缀,可先将宽表转为长表再匹配筛选:
# 宽表转长表,拆分列名为三部分 melted = df.melt(id_vars=['a', 'b'], var_name='col') melted[['a_col', 'b_col', 'suffix']] = melted['col'].str.split('_', expand=True) # 统一格式后匹配a/b列与拆分出的标识 melted['a_col'] = melted['a_col'].str.replace('a', '').astype(int) melted['b_col'] = melted['b_col'].str.replace('b', '').astype(int) matched = melted[(melted['a_col'] == melted['a']) & (melted['b_col'] == melted['b'])] # 重塑为目标格式 result = matched.pivot(index=matched.index, columns='suffix', values='value').rename_axis(None, axis=1)
性能说明
- 循环方法:时间复杂度为O(n*m)(n为行数,m为列数),数万行数据会明显卡顿。
- 向量化方法:时间复杂度为O(n),性能提升至少一个数量级,甚至更高。
内容的提问来源于stack exchange,提问作者sergeyvyazov
相关产品推荐
相关产品推荐

