如何更简洁地将DataFrame多列的唯一值展开为多列?
问题
现有如下结构的pandas DataFrame:
import pandas as pd import numpy as np # input DataFrame df = pd.DataFrame({ 'foo': ['one', 'one', 'one', 'two', 'two', 'two', 'three', 'three', 'three'], 'tak': ['dgad', 'dgad', 'dgad', 'ogfagas', 'ogfagas', 'ogfagas', 'adgadg', 'adgadg', 'adgadg'], 'bar': ['B', 'B', 'A', 'C', 'A', 'C', 'C', 'C', 'C'], 'nix': ['Z', 'Z', 'Z', 'G', 'G', 'G', 'Z', 'G', 'G'] })
需求:将其转换为以foo和tak为索引的DataFrame(每个foo对应唯一的tak值),并将bar、nix等多列(实际有10列)的唯一值展开为多列,如bar_one为每组foo中bar的第一个唯一值,bar_two为第二个,不足则填充np.nan,期望输出如下:
# desired output DataFrame pd.DataFrame({ 'foo': ['one', 'two', 'three'], 'tak': ['dgad', 'ogfagas', 'adgadg'], 'bar_one': ['B', 'C', 'C'], 'bar_two': ['A', 'A', np.nan], 'nix_one': ['Z' , 'G', 'Z'], 'nix_two': [np.nan, np.nan, 'G'] })
当前使用的实现方式:
pivot_df = df.pivot_table( index=['foo', 'tak'], values=['bar', 'nix'], aggfunc = lambda x: list(set(x)) ) pd.concat( [ pivot_df[column].apply(pd.Series) for column in ['bar', 'nix'] ], axis=1 )
请问是否有更简洁、更Pythonic的实现方式?
更简洁的实现方式
方法1:链式调用 melt + 去重 + 透视
这是最紧凑且通用的写法,自动处理所有目标列,还能保留唯一值的原始出现顺序(避免原方法中set打乱顺序的问题):
result = ( df.melt(id_vars=['foo', 'tak']) .drop_duplicates(subset=['foo', 'tak', 'variable', 'value']) .assign(seq=lambda x: x.groupby(['foo', 'tak', 'variable']).cumcount() + 1) .pivot(index=['foo', 'tak'], columns=['variable', 'seq'], values='value') .rename(columns=lambda col: f'{col[0]}_{"one" if col[1]==1 else "two"}') .reset_index() )
如果有超过2个唯一值,只需修改列名映射逻辑(比如把序号转成英文序数词或直接用数字后缀)即可适配。
方法2:分组去重 + 展开透视
适合需要显式控制分组逻辑的场景:
# 分组后对每列保留唯一值的列表(按出现顺序) grouped = df.groupby(['foo', 'tak']).agg(lambda x: x.drop_duplicates().tolist()) # 展开列表并生成序号,再透视成目标格式 expanded = grouped.apply(pd.Series.explode).reset_index(level=2, name='value') expanded['seq'] = expanded.groupby(level=[0,1,2]).cumcount() + 1 result = ( expanded.pivot_table(index=['foo', 'tak'], columns=['level_2', 'seq'], values='value') .rename(columns=lambda col: f'{col[0]}_{"one" if col[1]==1 else "two"}') .reset_index() )
核心优势
- 通用性强:无需手动指定列名,自动处理所有非索引列(适配你提到的10列场景)
- 保留顺序:用
drop_duplicates替代set,保证唯一值的原始出现顺序 - Pythonic风格:链式调用减少中间变量,代码更简洁易读
内容的提问来源于stack exchange,提问作者semblable
相关产品推荐
相关产品推荐

