У меня есть набор данных, в котором я хотел бы разделить значения на их собственные уникальные строки, а также выполнить сводку, группируя по категориям.
Данные обновлены
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()
Следующий код не может захватить все выходные столбцы. Любое предложение приветствуется.
Хорошо поставленный вопрос.
Я думаю, что нам нужно использовать сводную таблицу
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)
Привет @artemis - консоль говорит: NameError: имя 'melted_df' не определено - я изучу это
Извините, это была опечатка с моей стороны. Код исправлен @Lynn
Попробуйте 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» не отображается. Любое предложение?
потому что в ваших данных нет столбца DD re
. Вы можете связать это с fillna(0)
.
спасибо - я думаю, что это работает во всех случаях - постараюсь обновить решение @Quang Hoang
Один из вариантов — с 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 при синтаксическом анализе Есть ли необходимость в закрытом () или его отсутствие?
ты скобку закрыл? посмотри на скобки, может чего не хватает
Привет @sammywemmy У меня есть еще один набор из 4 столбцов («AA test2», «BB test2», «CC test2», «DD test2») предоставили?) Ошибка, которую я получаю: ValueError: Длина named_to не соответствует количеству извлеченных уровней. Длина name_to равна 2, а количество извлеченных уровней равно 4. Пытаюсь это настроить - есть предложения?
В ваших данных нет AA test2
Мои извинения, я только что обновил - интересно, как включить, если у меня есть 4 дополнительных столбца или более @sammywemmy
он должен работать до тех пор, пока это одно и то же пустое пространство для каждого столбца.
Я обновил результаты на основе ваших обновленных данных. Обратите внимание, что я не менял код. Пока шаблон непротиворечив, работает либо решение pivot_longer
, либо решение pd.stack
.
Другое возможное решение:
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
Спасибо много. Красиво сжато. Я постараюсь увидеть, получу ли я желаемый результат. @артемида