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. На пересечении иметь:

  1. если это rur_extr или qnt_extr, то сумму соответствующего поля.
  2. если это 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
→ Ссылка