当每组有多个值时,将 pandas 数据框列从长列转换为宽列

pan*_*ump 1 python pivot reshape dataframe pandas

我有以下格式的数据框:

df = pd.DataFrame({'Date': np.repeat(['20190101', '20190102'], 8),
                     'Category': list(np.repeat(['A', 'B'], 4))*2,
                     'Sub-Category': list(np.repeat(['X', 'Y'], 2))*4,
                     'Samples': range(16)})

# print(df)
        Date Category Sub-Category  Samples
0   20190101        A            X        0
1   20190101        A            X        1
2   20190101        A            Y        2
3   20190101        A            Y        3
4   20190101        B            X        4
5   20190101        B            X        5
6   20190101        B            Y        6
7   20190101        B            Y        7
8   20190102        A            X        8
9   20190102        A            X        9
10  20190102        A            Y       10
11  20190102        A            Y       11
12  20190102        B            X       12
13  20190102        B            X       13
14  20190102        B            Y       14
15  20190102        B            Y       15
Run Code Online (Sandbox Code Playgroud)

每个日期内都有一个类别,每个类别内又有多个子类别。此外,每个子类别都有多个样本。

我想要做的是将“子类别”列从长格式转换为宽格式,而不对样本进行任何聚合,如下所示:

desired_df = pd.DataFrame({'Date': np.repeat(['20190101', '20190102'], 4),
                           'Category': list(np.repeat(['A', 'B'], 2))*2,
                           'X': [0, 1, 4, 5, 8, 9, 12, 13],
                           'Y': [2, 3, 6, 7, 10, 11, 14, 15]
                           })

# print(desired_df)
       Date Category   X   Y
0  20190101        A   0   2
1  20190101        A   1   3
2  20190101        B   4   6
3  20190101        B   5   7
4  20190102        A   8  10
5  20190102        A   9  11
6  20190102        B  12  14
7  20190102        B  13  15 
Run Code Online (Sandbox Code Playgroud)

我想我已经找到了实现这一目标的方法,但感觉相当丑陋和老套:它涉及首先为样本编号创建一个新列,然后创建一个新索引,将所有这些信息组合在一起,旋转,然后分离一切再次发生:

def add_sample_index(df):
    df['sample_index'] = range(len(df))
    return df


new_df = df.groupby(['Date', 'Category', 'Sub-Category']).apply(add_sample_index)
new_df['new_index'] = new_df['Date'] + '_' + new_df['Category'] + '_' + new_df['sample_index'].astype(str)

wide_df = new_df.pivot(index='new_index', columns='Sub-Category', values='Samples').reset_index()
wide_df['Date'], wide_df['Category'], wide_df['sample_index'] = wide_df['new_index'].str.split('_').str
wide_df.drop(['new_index', 'sample_index'], 
             inplace=True, axis='columns')
final_df = wide_df[['Date', 'Category', 'X', 'Y']]

# print((final_df == desired_df).all().all())
True
Run Code Online (Sandbox Code Playgroud)

我的问题:有没有更好、更有效的方法来解决这个问题?

WeN*_*Ben 5

首先我们使用groupby cumcount创建附加密钥,那么它更像是pivot问题

df['New']=df.groupby(['Date','Category','Sub-Category']).cumcount()
yourdf=df.pivot_table(values='Samples',index=['Date','Category','New'],columns='Sub-Category').reset_index()
yourdf
Out[703]: 
Sub-Category      Date Category  New   X   Y
0             20190101        A    0   0   2
1             20190101        A    1   1   3
2             20190101        B    0   4   6
3             20190101        B    1   5   7
4             20190102        A    0   8  10
5             20190102        A    1   9  11
6             20190102        B    0  12  14
7             20190102        B    1  13  15
Run Code Online (Sandbox Code Playgroud)