基于重复ID的DataFrame Labels列填充:Pandas高效实现方案
更优雅高效的Pandas实现方案
问题背景
现有两个Pandas DataFrame:df1和df2,示例代码如下:
import pandas as pd import numpy as np # 补充原代码缺失的导入 col_1= ["A", ["B","C"], ["A","C","D"], "D"] col_id = [1,2,3,4] col_2 = [1,2,2,3,3,4,4] d1 = {'ID': [1,2,3,4], 'Labels': col_1} d2 = {'ID': col_2, } d_2_get = {'ID': col_2, "Labels": ["A", "B", "C", "A", "C", "D", np.nan] } df1 = pd.DataFrame(data=d1) df2 = pd.DataFrame(data=d2) df_2_get = pd.DataFrame(data=d_2_get)
df1的Labels列包含单个值或列表,df2的ID列存在重复条目。需要给df2添加Labels列,规则如下:
- 按
df2的行顺序,对应ID从df1的Labels中取第一个值 - 若
df2中ID重复,则取下一个可用值,无可用值时填充NaN
预期输出为df_2_get:
ID Labels 0 1 A 1 2 B 2 2 C 3 3 A 4 3 C 5 4 D 6 4 NaN
现有实现
当前已有初步实现代码:
from collections import Counter import numpy as np # 补充缺失导入 def list_flattener(list_of_lists): return [item for row in list_of_lists for item in row] def my_dataframe_filler(df1, df2): list_2_fill = [] repeats = dict(Counter(df2["ID"])) for k in repeats.keys(): available_labels_list = df1[df1["ID"]==k]["Labels"].tolist() available_labels_list+=[[np.nan]*10] available_labels_list = list_flattener(available_labels_list) list_2_fill+=available_labels_list[:repeats[k]] return list_2_fill
使用方式:
df2["Labels"] = my_dataframe_filler(df1, df2)
优化实现方案
下面是更贴合Pandas风格、高效简洁的实现方法,避免手动循环计数,利用Pandas内置的分组、映射特性完成需求:
简洁版实现代码
import pandas as pd import numpy as np # 先统一处理df1的Labels列,确保都是列表格式后展开 df1_processed = df1.assign( Labels=df1['Labels'].apply(lambda x: x if isinstance(x, list) else [x]) ).explode('Labels') # 统计df2中每个ID的出现次数,生成对应长度的标签列表(不足补NaN) label_map = {} for id_val, cnt in df2['ID'].value_counts().sort_index().items(): id_labels = list(df1_processed[df1_processed['ID'] == id_val]['Labels']) # 补充NaN到所需长度 label_map[id_val] = id_labels + [np.nan] * (cnt - len(id_labels)) # 按df2的行顺序依次取出对应标签 df2['Labels'] = [label_map[id_val].pop(0) for id_val in df2['ID']] # 查看结果 print(df2)
更贴合Pandas原生API的版本
import pandas as pd import numpy as np # 处理df1:展开Labels并添加组内序号 df1_expanded = df1.assign( Labels=df1['Labels'].apply(lambda x: x if isinstance(x, list) else [x]) ).explode('Labels').assign( seq=lambda x: x.groupby('ID').cumcount() ) # 处理df2:添加组内序号 df2_with_seq = df2.assign( seq=lambda x: x.groupby('ID').cumcount() ) # 匹配并填充Labels,最后恢复原顺序 result = df2_with_seq.merge( df1_expanded, on=['ID', 'seq'], how='left' ).drop(columns='seq').sort_index() print(result)
这两个版本均避免了手动遍历计数,利用Pandas的向量化操作提升效率,代码结构更清晰易读,符合Pandas的原生使用风格。
内容的提问来源于stack exchange,提问作者user37292
相关产品推荐
相关产品推荐

