Pandas. Сводная таблица со значениями одновременно по текстовым и числовым полям
Всем привет! Прошу помощи. Имею такой датафрейм:
df = pd.DataFrame({
'sku_sellout': ['a', 'a', 'b', 'c', 'a'],
'labels': ['менее 100%', '100-200%', 'более 200%', 'более 200%', 'менее 100%'],
'price': [100, 200, 300, 1000, 100],
'ranges': ['0-100', '100-200', '200-300', '900-1000', '0-100'],
'rur_extr': [1, 2, 3, 4, 1],
'qnt_extr': [10, 20, 30, 40, 15]
})
sku_sellout | labels | price | ranges | rur_extr | qnt_extr |
---|---|---|---|---|---|
a | менее 100% | 100 | 0-100 | 1 | 10 |
a | 100-200% | 200 | 100-200 | 2 | 20 |
b | более 200% | 300 | 200-300 | 3 | 30 |
c | более 200% | 1000 | 900-1000 | 4 | 40 |
a | менее 100% | 100 | 0-100 | 1 | 15 |
Из него мне необходимо получить вот такой:
df2 = pd.DataFrame({
'sku_sellout': ['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c'],
'metrics':['ranges', 'rur_extr','qnt_extr','ranges', 'rur_extr','qnt_extr','ranges', 'rur_extr','qnt_extr'],
'менее 100%': ['0-100',2,25,None,None,None,None,None,None],
'100-200%': ['100-200%',2,20,None,None,None,None,None,None],
'более 200%': [None,None,None,'200-300',3,30,'900-1000',4,40]
})
sku_sellout | metrics | менее 100% | 100-200% | более 200% |
---|---|---|---|---|
a | ranges | 0-100 | 100-200% | None |
a | rur_extr | 2 | 2 | None |
a | qnt_extr | 25 | 20 | None |
b | ranges | None | None | 200-300 |
b | rur_extr | None | None | 3 |
b | qnt_extr | None | None | 30 |
c | ranges | None | None | 900-1000 |
c | rur_extr | None | None | 4 |
c | qnt_extr | None | None | 40 |
Т.е. суть следующая - в строках иметь измерение sku_sellout и metrics (ranges, rur_extr, qnt_extr), а в столбцах значения labels. На пересечении иметь:
- если это rur_extr или qnt_extr, то сумму соответствующего поля.
- если это ranges, то первое попавшееся значение (aggfunc='first') по этому sku_sellout и label - оно всегда будет одинаковым.
Делал через melt с последующим pivot_table, но не получилось применить условную логику к столбцу: применять то 'first', то 'sum'.
Ответы (1 шт):
Автор решения: Kirill Kondratenko
→ Ссылка
Кажется, что проще будет разделить на две сводные и далее их объединить
# Разворачиваем
temp = df.melt(
id_vars=['sku_sellout', 'labels'],
value_vars=['ranges', 'rur_extr', 'qnt_extr'],
var_name='metrics')
# Сводная для суммируемых столбцов
extr = temp[
temp['metrics'].isin(['rur_extr', 'qnt_extr'])].pivot_table(
index=['sku_sellout', 'metrics'],
columns='labels',
values='value',
aggfunc='sum')
# Сводная для столбцов с первым значением
ranges = temp[
temp['metrics'] == 'ranges'].pivot_table(
index=['sku_sellout', 'metrics'],
columns='labels',
values='value',
aggfunc='first')
# Соединяем и преобразовываем
final_df = (pd.concat([extr, ranges])
.sort_values(by=['sku_sellout', 'metrics'])
.reset_index()
.rename_axis(None, axis=1)
)[['sku_sellout', 'metrics', 'менее 100%', '100-200%', 'более 200%']]
Вывод:
print(final_df)
sku_sellout metrics менее 100% 100-200% более 200%
0 a qnt_extr 25.0 20.0 NaN
1 a ranges 0-100 100-200 NaN
2 a rur_extr 2.0 2.0 NaN
3 b qnt_extr NaN NaN 30.0
4 b ranges NaN NaN 200-300
5 b rur_extr NaN NaN 3.0
6 c qnt_extr NaN NaN 40.0
7 c ranges NaN NaN 900-1000
8 c rur_extr NaN NaN 4.0