У меня есть следующий фрейм данных:
и т. д...
Я хотел бы развернуть этот фрейм данных, чтобы он выглядел следующим образом:
и т. д..
Я пробовал использовать функцию панд melt, но безуспешно. Поскольку каждый столбец должен быть отключен, в этом нет никакой ценности id_vars, любая помощь будет оценена по достоинству!






вы можете использовать stack и pivot_table,
import pandas as pd
data = {
'volume_brand1': [1000],
'volume_productX_brand1': [100],
'amount_brand1': [10],
'amount_productX_brand1': [50],
'volume_productX_brand2': [2000],
'amount_productX_brand2': [200]
}
df = pd.DataFrame(data)
df_stacked = df.stack()
df_stacked = df_stacked.reset_index()
df_stacked.columns = ['row', 'original_column', 'value']
df_stacked[['metric', 'product', 'brand']] = df_stacked['original_column'].str.extract(r'(volume|amount)_([^_]+)(?:_(.*))?')
df_stacked = df_stacked.drop(columns=['original_column', 'row'])
df_pivoted = df_stacked.pivot_table(index=['brand', 'product'], columns='metric', values='value').reset_index()
df_pivoted['brand'] = df_pivoted['brand'].ffill()
df_pivoted['product'] = df_pivoted['product'].fillna('not product X')
df_pivoted['volume'] = df_pivoted['volume'].astype(int)
df_pivoted['amount'] = df_pivoted['amount'].astype(int)
df_pivoted.columns.name = None
df_final = df_pivoted[['brand', 'product', 'volume', 'amount']]
print(df_final)
Выход,
brand product volume amount
0 brand1 productX 100 50
1 brand2 productX 2000 200
Вы можете использовать melt и pivot_table для преобразования:
# Assuming `df` is your original DataFrame
df_melted = df.melt()
df_melted['brand'] = df_melted['variable'].apply(lambda x: x.split('_')[-1])
df_melted['product'] = df_melted['variable'].apply(
x: 'productX' if 'productX' in x else 'not product X'
)
df_melted['metric'] = df_melted['variable'].apply(
lambda x: 'volume' if 'volume' in x else 'amount'
)
df_unpivoted = df_melted.pivot_table(
index=['brand', 'product'], columns='metric', values='value', aggfunc='first'
).reset_index()
df_unpivoted = df_unpivoted.sort_values("volume").reset_index(drop=True)
print(df_unpivoted)
Результат:
metric brand product amount volume
0 brand1 productX 50 100
1 brand1 not product X 10 1000
2 brand2 productX 200 2000
Другое возможное решение:
Он переименовывает столбцы для создания согласованного шаблона с помощью str.replace , затем складывает фрейм данных, чтобы преобразовать его из широкого формата в длинный.
После суммирования столбец level_1 разбивается на metric, product и brand, и эти столбцы объединяются обратно в исходные данные.
Наконец, фрейм данных поворачивается, чтобы вернуть его в широкий формат, где volume и amount становятся отдельными столбцами, организованными brand и product.
df.columns = df.columns.str.replace(
r'(volume|amount)_(brand)', r'\1_NotProductX_\2', regex=True)
d = df.stack().reset_index()
(pd.concat([
d, d['level_1'].str.split('_', expand=True)], axis=1).iloc[:, [5, 4, 3, 2]]
.set_axis(['brand', 'product', 'metric', 'value'], axis=1)
.pivot(index=['brand', 'product'], columns='metric', values='value')
.reset_index())
Выход:
metric brand product amount volume
0 brand1 NotProductX 10 1000
1 brand1 productX 50 100
2 brand2 productX 200 2000
Начнем с Pivot_longer_spec — это дает вам более детальный контроль над тем, как вы хотите изменить форму данных — вам нужно передать фрейм данных, который имеет столбец .name, который содержит имена столбцов, которые нужно изменить, и столбец .value, который содержит новые имена столбцов. Вы можете добавить дополнительные столбцы, если они не существуют в исходном фрейме данных:
# pip install pyjanitor
import pandas as pd
import janitor as jn
from janitor import pivot_longer_spec
# build your spec
product_x = np.where(df.columns.str.contains('productX'),
'product_X',
'not_product_X')
spec = {'.name':df.columns,
'.value':df.columns.str.split('_').str[0],
'brand': df.columns.str.split('_').str[-1],
'product':product_x}
spec = pd.DataFrame(spec)
# for this particular data,
# we need to make it unique
grp = spec.groupby('.value', sort=False)
spec['uniq'] = grp.cumcount()
pivot_longer_spec(df=df,spec=spec).drop(columns='uniq')
brand product volume amount
0 brand1 not_product_X 1000 10
1 brand1 product_X 100 50
2 brand2 product_X 2000 200
dff = df.copy()
new_cols = [col
if 'productX' in col
else [col[0], 'notproductX', col[1]]
for col in dff.columns.str.split('_')]
new_cols = ["_".join(col) for col in new_cols]
dff.pivot_longer(index=None,
names_to=('.value', 'product', 'brand'),
names_sep='_')
product brand volume amount
0 notproductX brand1 1000 10
1 productX brand1 100 50
2 productX brand2 2000 200