Новые столбцы в Dataframe с данными из другого DataFrame
Есть датафрейм вида
Var analog Кол-во
s1 s2 6
NaN s3 NaN
NaN s4 NaN
NaN s5 NaN
NaN NaN NaN
r1 NaN 4
l1 l2 1
NaN l3 NaN
Этот датафрейм описывает комплектующие и их аналоги. Например, s1
имеет аналоги s2
, s3
, s4
и s5
, всего их необходимо 6 шт. r1
не имеет аналогов и этого компонента нужно 4 шт. l1
имеет аналоги l2
и l3
и таких компонентов нужен 1 шт.
Необходимо преобразовать данный датафрейм в новый датафрейм с числом столбцов, равным максимальному количеству однотипных значений столбца analog
во всем датафрейме плюс два столбца, а новые столбцы должны быть заполнены значениями этих аналогов. Пустые строки должны быть удалены. Ожидаемый результат:
Var analog1 analog2 analog3 analog4 Кол-во
s1 s2 s3 s4 s5 6
r1 NaN NaN NaN NaN 4
l1 l2 l3 NaN NaN 1
Приходят идеи обрабатывать всё это в цикле и if-ами анализировать содержимое каждой новой строки, сравнивая с предыдущей, но можно ли это сделать без циклов?
Ответы (1 шт):
Будем исходить из того, что строки упорядочены как представлено в примере:
- все аналоги идут подряд;
- название и количество указаны в первой строке непрерывной группы;
- для каждой группы название и количество не
Nan
.
В таком контексте вам нужно сгруппировать аналоги в список и преобразовать результат в pandas.Series
в методе apply
. Например:
import pandas as pd
df = pd.DataFrame({'Var': ['s1', None, None, None, None, 'r1', 'l1', None],
'analog': ['s2', 's3', 's4', 's5', None, None, 'l2', 'l3'],
'Кол-во': [6.0, None, None, None, None, 4.0, 1.0, None]})
df = df.dropna(how='all') # сбрасываем пустые строки
grouper = df['Var'].ffill() # заменяем пустые `Var` ближайшим сверху непустым значением
analogs = (
df.groupby(grouper)['analog']
.agg(list)
.apply(pd.Series)
.rename(columns=lambda x: f'analog{x+1}')
)
counts = df[['Var', 'Кол-во']].dropna().set_index('Var')
answer = analogs.join(counts).reset_index()
print(answer)
Var analog1 analog2 analog3 analog4 Кол-во
0 l1 l2 l3 NaN NaN 1.0
1 r1 NaN NaN NaN NaN 4.0
2 s1 s2 s3 s4 s5 6.0
Мы могли бы передать pandas.Series
в метод SeriesGroupBy.apply сгруппированных значений. Но этот метод отличается от такого же по названию, но другого по реализации метода Series.apply. А именно, он сохранит исходные индексы аналогов и разместит их вторым уровнем (на первом уровне - значения Var
). Если идти по этому пути, то нужно передать функцию, которая в группе аналогов вытрет индексы исходной таблицы. После этого очищенный индекс, нужно развернуть в столбцы методом unstack:
(
df.groupby(grouper)['analog']
.apply(lambda series: series.reset_index(drop=True))
.unstack()
)
0 1 2 3
Var
l1 l2 l3 NaN NaN
r1 NaN NaN NaN NaN
s1 s2 s3 s4 s5