У меня есть несколько очень больших наборов данных, подобных этому (упрощенно). Однако обратите внимание, что не все SB (в данном случае 1-4) представлены в каждом LFrame...
LFrame, Pwr, SB, Channels_Active, Channels_Assigned
1, 10, 1, 2, 2
1, 2, 2, 2, 1
1, 4, 3, 3, 2
1, 6, 3, 2, 2
10, 8, 1, 2, 2
10, 2, 2, 3, 2
10, 4, 3, 2, 1
10, 2, 3, 2, 1
10, 5, 4, 2, 2
Мне нужно объединить это несколькими способами. Я считаю, что есть версия groupby, которую я могу использовать... но я не могу понять ее. Может быть, что-то вроде:
call_data_map = {
'LFrame' :('LFrame','first'),
'Total_Pwr' :("Pwr", 'sum'),
'Channels_Act' :("Channels_Active", 'sum'),
'Channels_Assigned' :("Channels_Assigned", 'sum'),
??? some kind of generator? (sb :('SB', 'sum') for sb in range(1,5)) ??
}
df.groupby('LFrame').agg(**call_data_map) ???
Я хочу, чтобы результат выглядел так (где я суммирую по SB, чтобы получить общую мощность в каждой SB и общую мощность в целом). Колонки (1-4) исходят из столбца SB, и я знаю, что диапазон всегда будет 1-4:
LFrame, Total_Pwr, Channels_Act, Channels_Assigned, 1, 2, 3, 4
1, 22, 9, 7, 10, 2, 10, 0
10, 21, 11, 8, 8, 2, 6, 5
Есть ли эффективный способ сделать это? Использование понимания списка и построение строк (в каждом файле) происходит медленно.
Я бы сделал сводную_таблицу:
out = (df.pivot_table(index = "LFrame", columns = "SB", values = "Pwr",
aggfunc = "sum", fill_value=0, margins=True,
margins_name = "Total_Pwr")
.drop("Total_Pwr").reset_index().rename_axis(None, axis=1)
)
Выход:
print(out)
LFrame 1 2 3 4 Total_Pwr
0 1 10 2 10 0 22
1 10 8 2 6 5 21
На самом деле, те другие столбцы, которые я упоминаю в комментарии выше, необходимо суммировать. :/
Думаю, с этим можно справиться. Можете ли вы соответствующим образом обновить свой ввод/ожидаемый вывод?
Пронумерованные столбцы представляют собой сводную таблицу, но другие столбцы представляют собой просто базовую группировку по сумме. Таким образом, мы могли бы просто создать их отдельно, а затем объединить.
sums = (
df.groupby('LFrame')
[['Pwr', 'Channels_Active', 'Channels_Assigned']]
.sum()
.rename(columns = {'Pwr': 'Total_Pwr', 'Channels_Active': 'Channels_Act'})
)
sb = df.pivot_table(
index='LFrame', columns='SB', values='Pwr',
aggfunc=sum, fill_value=0)
pd.concat([sums, sb], axis=1)
Результат:
Total_Pwr Channels_Act Channels_Assigned 1 2 3 4
LFrame
1 22 9 7 10 2 10 0
10 21 11 8 8 2 6 5
Это хорошо... Я думаю, что могу использовать эту версию, но мне приходится справляться, когда не все SB (7-30) есть в каждой записи. Но опять же, я думаю, что могу понять это.
@earnric Разве fill_value
не объясняет это? Может быть, я не понимаю.
Я думаю, что это часть, которую я не объяснил: у меня есть несколько (дюжины) файлов, которые я объединяю в конце, и может быть так, что не ВСЕ SB представлены в любом ОДНОМ файле ... поэтому я не у них нет столбцов для всех (их 30). Однако я смог настроить и заставить ваш подход работать! Спасибо... это самое читаемое.
Вот способ использования groupby()
и .join()
:
(df.groupby('LFrame').agg(Total_Pwr = ('Pwr','sum'))
.join(df.groupby('LFrame')['SB'].value_counts().unstack().fillna(0))
.reset_index())
Выход:
LFrame Total_Pwr 1 2 3 4
0 1 22 1.0 1.0 2.0 0.0
1 10 21 1.0 1.0 2.0 1.0
Вы можете передать fill_value=0
на .unstack()
Чтобы получить последовательность столбцов, показанную в вопросе OP, вы можете немного изменить ответ @Timeless, исключив вызов drop() и вместо этого используя pipe и iloc:
out = ( df
.pivot_table(index='LFrame', columns='SB', values='Pwr',
aggfunc='sum', fill_value=0, margins=True, margins_name='Total_Pwr')
.pipe(lambda d: d.iloc[0:-1, [-1] + list(range(d.shape[1]-1))])
.rename_axis(None, axis='columns')
.reset_index() )
Выход:
LFrame Total_Pwr 1 2 3 4
0 1 22 10 2 10 0
1 10 21 8 2 6 5
Это сработает, за исключением того, что у меня есть 4-5 других столбцов, которые я просто хочу скопировать... например
Num_Channels, Active_Channels
. Можете ли вы включить их в ответ?