Tricky Long Pivot путем преобразования обратного агрегирования (Pandas)

У меня есть набор данных, в котором я хотел бы разделить значения на их собственные уникальные строки, а также выполнить сводку, группируя по категориям.

Данные обновлены

Period      Date        Area    BB stat AA stat CC stat DD stat BB test AA test CC test DD test BB re   AA re   CC re BB test2  AA test2 CC test2  DD test2                                                
8/1/2016    9/1/2016    NY      5       5       5               1       1       1               0       0       0     0          0       0         0
9/1/2016    10/1/2016   NY      6       6       6               4       4       4               0       0       0     0          0       0         0
8/1/2016    9/1/2016    CA      2       2       2               4       4       4               0       0       0     0          0       0         0
9/1/2016    10/1/2016   CA      1       1       1              -2      -2      -2               0       0       0     0          0       0         0

Желанный

Period      Date            Area    stat    test    type    re  test2
8/1/2016    9/1/2016        NY      5       1       BB      0   0
9/1/2016    10/1/2016       NY      6       4       BB      0   0
8/1/2016    9/1/2016        NY      5       1       AA      0   0   
9/1/2016    10/1/2016       NY      6       4       AA      0   0   
8/1/2016    9/1/2016        NY      5       1       CC      0   0
9/1/2016    10/1/2016       NY      6       4       CC      0   0   
8/1/2016    9/1/2016        NY      0       0       DD      0   0
9/1/2016    10/1/2016       NY      0       0       DD      0   0
8/1/2016    9/1/2016        CA      2       4       BB      0   0
9/1/2016    10/1/2016       CA      1       -2      BB      0   0
8/1/2016    9/1/2016        CA      2       4       AA      0   0
9/1/2016    10/1/2016       CA      1       -2      AA      0   0
8/1/2016    9/1/2016        CA      2       4       CC      0   0
9/1/2016    10/1/2016       CA      1       -2      CC      0   0
8/1/2016    9/1/2016        CA      0       0       DD      0   0
9/1/2016    10/1/2016       CA      0       0       DD      0   0

Делает

value_vars = ["BB stat",    "AA stat",  "CC stat",  "DD stat",  "BB test",
"AA test",  "CC test",  "DD test",  "BB re",    "AA re",    "CC re"]
df = df.melt(id_vars=["Period", "Date", "Area"], value_vars=value_vars)


temp_df = df.variable.str.split("_", 1, expand=True)
df["type"] = temp_df[0]
df["name"] = temp_df[1]
df = df.drop(columns=["variable"])
first_half = df.iloc[:len(df)//2]
second_half = df.iloc[len(df)//2:]
df = pd.merge(first_half, second_half, on=["Period", "Date", "Area", "type"], suffixes=("_1", "_2"))


df.rename(columns = {'value_3':'stat''value_2':'test', 'value_1':'re'}, inplace = True)
df.drop(columns=["name_1", "name_2"], inplace=True)
df = df[[ "Period",     "Date",         "Area", "stat", "test", "type", "re"    ]]



df.sort_values(["Area", "type"], ascending=False, inplace=True)
df.to_markdown()

Следующий код не может захватить все выходные столбцы. Любое предложение приветствуется.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
6
0
116
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Хорошо поставленный вопрос.

Я думаю, что нам нужно использовать сводную таблицу

import pandas as pd

# OP data
data = {'Period': ['8/1/2016', '9/1/2016', '8/1/2016', '9/1/2016'],
        'Date': ['9/1/2016', '10/1/2016', '9/1/2016', '10/1/2016'],
        'Area': ['NY', 'NY', 'CA', 'CA'],
        'BB stat': [5, 6, 2, 1],
        'AA stat': [5, 6, 2, 1],
        'CC stat': [5, 6, 2, 1],
        'DD stat': [0, 0, 0, 0],
        'BB test': [1, 4, 4, -2],
        'AA test': [1, 4, 4, -2],
        'CC test': [1, 4, 4, -2],
        'DD test': [0, 0, 0, 0],
        'BB re': [0, 0, 0, 0],
        'AA re': [0, 0, 0, 0],
        'CC re': [0, 0, 0, 0]}

# Convert the dictionary to a DataFrame and process it
df = pd.DataFrame(data) \
    .melt(id_vars=["Period", "Date", "Area"]) \
    .assign(**pd.DataFrame(df.melt(id_vars=["Period", "Date", "Area"])['variable'].str.extract(r'([A-Z]{2})\s([a-z]{2,4})', expand=True).values, columns=['type', 'category'])) \
    .drop(columns=['variable']) \
    .pivot_table(index=["Period", "Date", "Area", "type"], columns = "category", values = "value").reset_index() \
    .reindex(columns=['Period', 'Date', 'Area', 'stat', 'test', 'type', 're']) \
    .sort_values(['Area', 'type'], ascending=False)

Спасибо много. Красиво сжато. Я постараюсь увидеть, получу ли я желаемый результат. @артемида

Lynn 06.04.2023 06:50

Привет @artemis - консоль говорит: NameError: имя 'melted_df' не определено - я изучу это

Lynn 06.04.2023 07:04

Извините, это была опечатка с моей стороны. Код исправлен @Lynn

artemis 06.04.2023 07:13
Ответ принят как подходящий

Попробуйте pd.wide_to_long:

pd.wide_to_long(df, 
                stubnames=['AA', 'BB','CC','DD'],
                i=['Period','Date','Area'],
                j='',
                sep=' ',
                suffix='(test|re|stat)'
).unstack(level=-1, fill_value=0).stack(level=0).reset_index()

Выход:

      Period       Date Area type   re  stat  test
0   8/1/2016   9/1/2016   CA   AA  0.0   2.0   4.0
1   8/1/2016   9/1/2016   CA   BB  0.0   2.0   4.0
2   8/1/2016   9/1/2016   CA   CC  0.0   2.0   4.0
3   8/1/2016   9/1/2016   CA   DD  NaN   0.0   0.0
4   8/1/2016   9/1/2016   NY   AA  0.0   5.0   1.0
5   8/1/2016   9/1/2016   NY   BB  0.0   5.0   1.0
6   8/1/2016   9/1/2016   NY   CC  0.0   5.0   1.0
7   8/1/2016   9/1/2016   NY   DD  NaN   0.0   0.0
8   9/1/2016  10/1/2016   CA   AA  0.0   1.0  -2.0
9   9/1/2016  10/1/2016   CA   BB  0.0   1.0  -2.0
10  9/1/2016  10/1/2016   CA   CC  0.0   1.0  -2.0
11  9/1/2016  10/1/2016   CA   DD  NaN   0.0   0.0
12  9/1/2016  10/1/2016   NY   AA  0.0   6.0   4.0
13  9/1/2016  10/1/2016   NY   BB  0.0   6.0   4.0
14  9/1/2016  10/1/2016   NY   CC  0.0   6.0   4.0
15  9/1/2016  10/1/2016   NY   DD  NaN   0.0   0.0

Привет, спасибо - по какой-то причине вывод «DD» не отображается. Любое предложение?

Lynn 06.04.2023 07:02

потому что в ваших данных нет столбца DD re. Вы можете связать это с fillna(0).

Quang Hoang 06.04.2023 07:05

спасибо - я думаю, что это работает во всех случаях - постараюсь обновить решение @Quang Hoang

Lynn 06.04.2023 08:29

Один из вариантов — с pivot_longer от pyjanitor — в этом случае мы используем специальный заполнитель .value, чтобы определить части столбца, которые мы хотим оставить в качестве заголовков, а остальные сопоставляем в новый столбец:

# pip install pyjanitor
import pandas as pd
import janitor

(df
.pivot_longer(
    index = ['Period', 'Date', 'Area'], 
    names_to = ('type', '.value'), 
    names_sep = " ",
    sort_by_appearance=True)
.fillna({"re":0}, downcast='infer')
) 
      Period       Date Area type  stat  test  re  test2
0   8/1/2016   9/1/2016   NY   BB     5     1   0      0
1   8/1/2016   9/1/2016   NY   AA     5     1   0      0
2   8/1/2016   9/1/2016   NY   CC     5     1   0      0
3   8/1/2016   9/1/2016   NY   DD     0     0   0      0
4   9/1/2016  10/1/2016   NY   BB     6     4   0      0
5   9/1/2016  10/1/2016   NY   AA     6     4   0      0
6   9/1/2016  10/1/2016   NY   CC     6     4   0      0
7   9/1/2016  10/1/2016   NY   DD     0     0   0      0
8   8/1/2016   9/1/2016   CA   BB     2     4   0      0
9   8/1/2016   9/1/2016   CA   AA     2     4   0      0
10  8/1/2016   9/1/2016   CA   CC     2     4   0      0
11  8/1/2016   9/1/2016   CA   DD     0     0   0      0
12  9/1/2016  10/1/2016   CA   BB     1    -2   0      0
13  9/1/2016  10/1/2016   CA   AA     1    -2   0      0
14  9/1/2016  10/1/2016   CA   CC     1    -2   0      0
15  9/1/2016  10/1/2016   CA   DD     0     0   0      0

Другой вариант, с pd.stack:

index = ['Period', 'Date', 'Area']
temp = df.set_index(index)
# default separator is white space
temp.columns = temp.columns.str.split(expand = True)
temp.columns.names = ['type', None]
(temp
.stack("type")
.fillna({"re":0}, downcast='infer')
.reset_index()
)

      Period       Date Area type  re  stat  test  test2
0   8/1/2016   9/1/2016   NY   AA   0     5     1      0
1   8/1/2016   9/1/2016   NY   BB   0     5     1      0
2   8/1/2016   9/1/2016   NY   CC   0     5     1      0
3   8/1/2016   9/1/2016   NY   DD   0     0     0      0
4   9/1/2016  10/1/2016   NY   AA   0     6     4      0
5   9/1/2016  10/1/2016   NY   BB   0     6     4      0
6   9/1/2016  10/1/2016   NY   CC   0     6     4      0
7   9/1/2016  10/1/2016   NY   DD   0     0     0      0
8   8/1/2016   9/1/2016   CA   AA   0     2     4      0
9   8/1/2016   9/1/2016   CA   BB   0     2     4      0
10  8/1/2016   9/1/2016   CA   CC   0     2     4      0
11  8/1/2016   9/1/2016   CA   DD   0     0     0      0
12  9/1/2016  10/1/2016   CA   AA   0     1    -2      0
13  9/1/2016  10/1/2016   CA   BB   0     1    -2      0
14  9/1/2016  10/1/2016   CA   CC   0     1    -2      0
15  9/1/2016  10/1/2016   CA   DD   0     0     0      0

Привет, спасибо @sammywemmy - выдает эту ошибку: .fillna({"re":0}, downcast='infer') ^ SyntaxError: неожиданный EOF при синтаксическом анализе Есть ли необходимость в закрытом () или его отсутствие?

Lynn 06.04.2023 07:00

ты скобку закрыл? посмотри на скобки, может чего не хватает

sammywemmy 06.04.2023 07:02

Привет @sammywemmy У меня есть еще один набор из 4 столбцов («AA test2», «BB test2», «CC test2», «DD test2») предоставили?) Ошибка, которую я получаю: ValueError: Длина named_to не соответствует количеству извлеченных уровней. Длина name_to равна 2, а количество извлеченных уровней равно 4. Пытаюсь это настроить - есть предложения?

Lynn 06.04.2023 07:54

В ваших данных нет AA test2

sammywemmy 06.04.2023 08:09

Мои извинения, я только что обновил - интересно, как включить, если у меня есть 4 дополнительных столбца или более @sammywemmy

Lynn 06.04.2023 08:12

он должен работать до тех пор, пока это одно и то же пустое пространство для каждого столбца.

sammywemmy 06.04.2023 08:57

Я обновил результаты на основе ваших обновленных данных. Обратите внимание, что я не менял код. Пока шаблон непротиворечив, работает либо решение pivot_longer, либо решение pd.stack.

sammywemmy 06.04.2023 08:59

Другое возможное решение:

cols = ['Period', 'Date', 'Area']
out = df.melt(cols)
out = (pd.concat([out, 
                  out['variable'].str.split(' ', expand=True)
                  .set_axis(['type', 'aux'], axis=1)], axis=1)
       .drop(['variable'], axis=1))
(out.pivot(index = cols + ['type'], columns='aux').droplevel(0, axis=1)
 .reset_index().rename_axis(None, axis=1).fillna(0))

Выход:

      Period       Date Area type   re  stat  test
0   8/1/2016   9/1/2016   CA   AA  0.0   2.0   4.0
1   8/1/2016   9/1/2016   CA   BB  0.0   2.0   4.0
2   8/1/2016   9/1/2016   CA   CC  0.0   2.0   4.0
3   8/1/2016   9/1/2016   CA   DD  0.0   0.0   0.0
4   8/1/2016   9/1/2016   NY   AA  0.0   5.0   1.0
5   8/1/2016   9/1/2016   NY   BB  0.0   5.0   1.0
6   8/1/2016   9/1/2016   NY   CC  0.0   5.0   1.0
7   8/1/2016   9/1/2016   NY   DD  0.0   0.0   0.0
8   9/1/2016  10/1/2016   CA   AA  0.0   1.0  -2.0
9   9/1/2016  10/1/2016   CA   BB  0.0   1.0  -2.0
10  9/1/2016  10/1/2016   CA   CC  0.0   1.0  -2.0
11  9/1/2016  10/1/2016   CA   DD  0.0   0.0   0.0
12  9/1/2016  10/1/2016   NY   AA  0.0   6.0   4.0
13  9/1/2016  10/1/2016   NY   BB  0.0   6.0   4.0
14  9/1/2016  10/1/2016   NY   CC  0.0   6.0   4.0
15  9/1/2016  10/1/2016   NY   DD  0.0   0.0   0.0

Другие вопросы по теме