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

时间:2019-04-05 03:00:30

标签: python pandas dataframe pivot reshape

我具有以下格式的数据框:

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

在每个日期中都有一个类别,并且每个类别中都有多个子类别。此外,每个子类别都有多个样本。

我想要做的是将“子类别”列从长格式转换为宽格式,而无需像这样对样本进行任何汇总:

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 

我想我已经找到了一种方法来实现这一目标,但是它感觉很丑陋而且很棘手:它涉及到首先为样本编号创建一个新列,然后创建一个将所有这些信息组合在一起的新索引,然后将其再次分离:

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

我的问题:有没有更好,更有效的方法来解决此问题?

1 个答案:

答案 0 :(得分:2)

首先我们使用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