You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何更简洁地将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 04:11:00