Pandas数据转换:向量化加速循环的方案咨询
问题描述
现有如下格式的DataFrame,每行对应一位唯一的家长,每位家长可拥有多个孩子,孩子相关列遵循统一命名规则:
import pandas as pd df = pd.DataFrame({'Parent_username': ['Bob1', 'Ron23', 'Lisa00', 'Joe_'], 'Parent_age': [38, None, 40, 26], 'Child1_name': ['Mike', 'John', 'Curt', 'Kelly'], 'Child1_age': [2, None, 1, 2], 'Child2_name': ['Pat', 'Dennis', None, None], 'Child2_age': [4, None, None, None]})
原始DataFrame样式:
Parent_username Parent_age Child1_name Child1_age Child2_name Child2_age 0 Bob1 38.0 Mike 2.0 Pat 4.0 1 Ron23 NaN John NaN Dennis NaN 2 Lisa00 40.0 Curt 1.0 None NaN 3 Joe_ 26.0 Kelly 2.0 None NaN
需要将其转换为每行对应一个孩子的格式,其中Child_number表示该孩子是第几个孩子:
df2 = pd.DataFrame({'Child_name': ['Mike', 'Pat', 'John', 'Dennis', 'Curt', 'Kelly'], 'Child_number': [1, 2, 1, 2, 1, 1], 'Child_age': [2, 4, None, None, 1, 2], 'Parent_username': ['Bob1', 'Bob1', 'Ron23', 'Ron23', 'Lisa00', 'Joe_'], 'Parent_age': [38, 38, None, None, 40, 26]})
目标样式:
Child_name Child_number Child_age Parent_username Parent_age 0 Mike 1 2.0 Bob1 38.0 1 Pat 2 4.0 Bob1 38.0 2 John 1 NaN Ron23 NaN 3 Dennis 2 NaN Ron23 NaN 4 Curt 1 1.0 Lisa00 40.0 5 Kelly 1 2.0 Joe_ 26.0
原本使用嵌套循环拷贝数据的方式耗时极长:
for index in df.index: for col in df.columns: # 用dataframe.loc将df.loc[index, col]拷贝到df2对应位置
希望找到更快的实现方式,不确定向量化操作是否适用于字符串列,寻求建议。
高效实现方案
使用pandas内置的向量化函数可以完全替代循环,效率提升显著,且完美支持字符串列处理,以下提供两种可靠方案:
方案1:使用wide_to_long(推荐,专门适配宽表转长表场景)
wide_to_long是pandas专为这种"前缀+编号+属性"的宽表设计的转换工具,代码简洁高效:
# 执行宽表转长表 df_long = pd.wide_to_long(df, stubnames=['Child'], # 孩子列的统一前缀 i=['Parent_username', 'Parent_age'], # 保留的家长标识列 j='Child_number', # 生成的孩子编号列名 sep='', # 列名中前缀与编号的分隔符(此处无分隔符) suffix=r'\d+') # 匹配编号的正则规则(数字) # 重置索引为普通列,并拆分Child列的姓名与年龄 df_long = df_long.reset_index() df_long[['Child_name', 'Child_age']] = pd.DataFrame(df_long['Child'].tolist(), index=df_long.index) # 整理最终格式:删除冗余列、调整列顺序、过滤空孩子姓名行 df_final = df_long.drop('Child', axis=1)[['Child_name', 'Child_number', 'Child_age', 'Parent_username', 'Parent_age']] df_final = df_final.dropna(subset=['Child_name'])
方案2:使用melt分步处理
如果对wide_to_long的参数逻辑不熟悉,可通过melt分两次处理孩子姓名和年龄列,再合并结果:
# 拆分孩子姓名列 name_melt = df.melt(id_vars=['Parent_username', 'Parent_age'], value_vars=['Child1_name', 'Child2_name'], var_name='Child_number', value_name='Child_name') # 拆分孩子年龄列 age_melt = df.melt(id_vars=['Parent_username', 'Parent_age'], value_vars=['Child1_age', 'Child2_age'], var_name='Child_number', value_name='Child_age') # 提取孩子编号并转换为整数类型 name_melt['Child_number'] = name_melt['Child_number'].str.extract(r'(\d+)').astype(int) age_melt['Child_number'] = age_melt['Child_number'].str.extract(r'(\d+)').astype(int) # 合并姓名与年龄数据,整理最终格式 df_final = pd.merge(name_melt, age_melt, on=['Parent_username', 'Parent_age', 'Child_number']) df_final = df_final.dropna(subset=['Child_name'])[['Child_name', 'Child_number', 'Child_age', 'Parent_username', 'Parent_age']]
两种方案均为向量化操作,完全避免循环,处理大数据集时效率比嵌套循环高几个数量级,运行后均可得到目标格式的DataFrame。
内容的提问来源于stack exchange,提问作者user173729
相关产品推荐
相关产品推荐

