如何在Pandas DataFrame的列表型列中设置自定义标签
问题描述
我有一个自定义DataFrame B,其结构通过B.dtypes查看如下:
[236 rows x 10 columns] Filename object Path object Filenumber int64 Channel object Folder object User-defined labels object Ranking int64 Lower limit int64 Upper limit int64 Enabled bool dtype: object
执行以下筛选代码:
print( B.loc[ B["User-defined labels"].explode().eq("Label 1").groupby(level=0).any() & (B["Filenumber"] == 98) ] )
得到结果:
Filename Path Filenumber Channel Folder User-defined labels Ranking Lower limit Upper limit Enabled 0 File_98.csv C:\Users\test_user\Documents\Training_data\Label 1\... 98 subfolder_0 Label 1 [Label 1] 0 0 999999 True 39 File_98.csv C:\Users\test_user\Documents\Training_data\Label 1\... 98 subfolder_1 Label 1 [Label 1] 0 0 999999 True 78 File_98.csv C:\Users\test_user\Documents\Training_data\Label 1\... 98 subfolder_2 Label 1 [Label 1] 0 0 999999 True 117 File_98.csv C:\Users\test_user\Documents\Training_data\Label 1\... 98 subfolder_3 Label 1 [Label 1] 0 0 999999 True
现在需要为符合条件的行的User-defined labels列替换/添加新标签,标签列表定义为:
new_labels = ["Label 1", "Label 3"]
用于定位目标行的代码如下:
B.loc[ B["User-defined labels"].explode().eq("Label 1").groupby(level=0).any() & (B["Filenumber"] == 98), ["User-defined labels"], ]
目标行的该列初始值为[Label 1],希望修改为[Label 1, Label 3]。
已尝试的失败操作
使用loc方法尝试的操作均失败:
B.loc[] = new_labels,报错:Must have equal len keys and value when setting with an iterableB.loc[] = [new_labels],报错:Must have equal len keys and value when setting with an ndarrayB.loc[] = pd.Series(new_labels, dtype=object),仅将第一行该列设为Label 1,其余列设为NaNB.loc[] = pd.Series([new_labels], dtype=object),仅将第一行该列设为[Label 1, Label 3],其余列设为NaN
改用at()方法尝试同样操作,结果如下:
B.at[] = new_labels,报错:Must have equal len keys and value when setting with an iterableB.at[] = [new_labels],报错:Must have equal len keys and value when setting with an ndarrayB.at[] = pd.Series(new_labels, dtype=object),报错:unhashable type: 'list'B.at[] = pd.Series([new_labels], dtype=object),报错:unhashable type: 'list'
解决方案与疑问解答
1. 修改列表列的可行方法
方法一:用apply批量赋值
先把筛选条件存为变量复用:
mask = B["User-defined labels"].explode().eq("Label 1").groupby(level=0).any() & (B["Filenumber"] == 98)
直接替换目标行的标签列表:
B.loc[mask, "User-defined labels"] = B.loc[mask, "User-defined labels"].apply(lambda x: new_labels)
如果是要合并新标签并去重(而非直接替换),可以改成:
B.loc[mask, "User-defined labels"] = B.loc[mask, "User-defined labels"].apply(lambda x: list(set(x + new_labels)))
方法二:构造匹配行数的列表
loc赋值要求右侧长度与选中行数量一致,因此可以先获取目标行数,生成对应数量的标签列表:
mask = B["User-defined labels"].explode().eq("Label 1").groupby(level=0).any() & (B["Filenumber"] == 98) row_count = B[mask].shape[0] B.loc[mask, "User-defined labels"] = [new_labels] * row_count
2. 关于多列存储标签的疑问
你担心的三个问题确实存在:
- 列结构更新问题:每次新增标签都要给整个DataFrame添加一列,即使多数行该标签为空,会额外占用内存,标签数量越多问题越明显。
- 空列清理问题:删除标签时不能直接删列(会丢失其他行的标签记录),只能将对应列设为空,但空列会一直保留,后续需要遍历所有标签列检查是否全为空才能删除,操作繁琐。
- 遍历效率问题:遍历单个列表列仅需操作一列,而多列存储需要遍历所有标签列,当标签数量较多时,前者的筛选、统计效率明显更高。
因此,继续使用列表列存储标签是更合理的选择。
内容的提问来源于stack exchange,提问作者arc_lupus
相关产品推荐
相关产品推荐

