Преобразование столбца данных pandas из длинного в широкий, если в группе несколько значений - PullRequest
0 голосов
/ 05 апреля 2019

У меня есть следующий фрейм данных в следующем формате:

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 Ответ

2 голосов
/ 05 апреля 2019

Сначала мы с помощью 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
...