【问题标题】:Convert the data frame from long to wide format and dynamically name columns将数据框从长格式转换为宽格式并动态命名列
【发布时间】:2026-02-21 03:10:01
【问题描述】:

我正在将数据框从长格式转换为宽格式,但是我面临的问题是生成正确数量的已翻译列并动态重命名新的数据框列。

假设我有一个示例数据框,如下所示:

data = {'name':['Tom', 'nick', 'Tom', 'nick','Tom'], 'id':[20, 21, 20, 21,22], 'plan' : [100,101,102,101,100], 'drug' : ['a','b','b','c','a']}

df = pd.DataFrame(data)
drug id  name   plan
a    20  Tom    100
b    21  nick   101
b    20  Tom    102
c    21  nick   101
a    22  Tom    100

因此,对于每个给定的名称和 ID,我想为计划和药物创建多个列。例如,有 3 种不同的计划和 3 种不同的药物,所以理想情况下,我应该得到 6 个新列,指示是否已采取特定计划/药物。

我尝试从长转换为宽,但没有得到想要的结果。 长转宽:

df1 = df.groupby(['name','id'])['plan', 'drug'].apply(lambda x: pd.DataFrame(x.values)).unstack().reset_index()

实际输出:

name    id  0   1    0   1
Tom     20  100 102  a  b
nick    21  101 101  b  c
Tom     22  100 None a  None

预期输出:

   name    age  100 101 102   a   b  c 
   Tom     20   1   0    1    1   1  0
   Tom     22   1   0    0    1   0  0
   nick    21   0   1    0    0   1  1

【问题讨论】:

    标签: python pandas dataframe


    【解决方案1】:

    get_dummiesmax 一起使用:

    df1 = pd.get_dummies(df.set_index(['name','id']).astype(str)).max(level=[0,1]).reset_index()
    print(df1)
       name  id  plan_100  plan_101  plan_102  drug_a  drug_b  drug_c
    0   Tom  20         1         0         1       1       1       0
    1  nick  21         0         1         0       0       1       1
    2   Tom  22         1         0         0       1       0       0
    
    df2 = (pd.get_dummies(df.set_index(['name','id'])
            .astype(str), prefix='', prefix_sep='')
            .max(level=[0,1])
            .reset_index())
    print(df2)
       name  id  100  101  102  a  b  c
    0   Tom  20    1    0    1  1  1  0
    1  nick  21    0    1    0  0  1  1
    2   Tom  22    1    0    0  1  0  0
    

    编辑:DataFrame.pivot_tableconcatDataFrame.clip 的解决方案:

    df1 = df.pivot_table(index=['name','id'], 
                         columns=['plan'], 
                          aggfunc='size',
                          fill_value=0)
    
    df2 = df.pivot_table(index=['name','id'], 
                         columns=['drug'], 
                          aggfunc='size',
                          fill_value=0)
    
    df = pd.concat([df1, df2], axis=1).clip(upper=1).reset_index()
    print(df)
       name  id  100  101  102  a  b  c
    0   Tom  20    1    0    1  1  1  0
    1   Tom  22    1    0    0  1  0  0
    2  nick  21    0    1    0  0  1  1
    

    【讨论】:

    • 解决方案看起来不错,但有一个问题,对于 nick plan_101,值是 2 ,虽然我只希望它是 1。我试过 df1[df1>1] = 0 ,但是它似乎不适用于此数据框。有什么想法吗?
    • @bakas - 哎呀,我想念它。给我一点时间。
    • 很抱歉再次打扰您,但是 pd.get_dummies 会导致性能问题并且我的笔记本崩溃,因为我有接近一百万行,有没有更好的方法来做到这一点?
    • 非常感谢您的多种解决方案,不幸的是,我的变量之一有 700 个级别,笔记本仍然崩溃。我会接受这个答案,因为最初的问题没有考虑性能。
    【解决方案2】:
    import pandas as pd
    
    data = {
        'name':['Tom', 'nick', 'Tom', 'nick','Tom'],
        'id':[20, 21, 20, 21,22],
        'plan': [100,101,102,101,100],
        'drug': ['a','b','b','c','a']
    }
    
    df = pd.DataFrame(data)
    
    plans = df.groupby(['name', 'id', 'plan']).size().unstack()
    drugs = df.groupby(['name', 'id', 'drug']).size().unstack()
    
    merged_df = pd.merge(plans, drugs, left_index=True, right_index=True)
    
    merged_df = merged_df.fillna(0)
    

    获取每个nameidplandrug 计数。 (这就是 size()unstack() 的用途)

    然后将它们合并到它们的索引上(设置为nameid)。

    使用fillnaNaN 替换为0

    【讨论】: