Pandas groupby и agg с несколькими уровнями

У меня есть несколько очень больших наборов данных, подобных этому (упрощенно). Однако обратите внимание, что не все 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

Есть ли эффективный способ сделать это? Использование понимания списка и построение строк (в каждом файле) происходит медленно.

Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
65
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Я бы сделал сводную_таблицу:

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

Это сработает, за исключением того, что у меня есть 4-5 других столбцов, которые я просто хочу скопировать... например Num_Channels, Active_Channels. Можете ли вы включить их в ответ?

earnric 12.04.2023 01:28

На самом деле, те другие столбцы, которые я упоминаю в комментарии выше, необходимо суммировать. :/

earnric 12.04.2023 01:51

Думаю, с этим можно справиться. Можете ли вы соответствующим образом обновить свой ввод/ожидаемый вывод?

Timeless 12.04.2023 08:33
Ответ принят как подходящий

Пронумерованные столбцы представляют собой сводную таблицу, но другие столбцы представляют собой просто базовую группировку по сумме. Таким образом, мы могли бы просто создать их отдельно, а затем объединить.

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 12.04.2023 20:11

@earnric Разве fill_value не объясняет это? Может быть, я не понимаю.

wjandrea 12.04.2023 21:32

Я думаю, что это часть, которую я не объяснил: у меня есть несколько (дюжины) файлов, которые я объединяю в конце, и может быть так, что не ВСЕ SB представлены в любом ОДНОМ файле ... поэтому я не у них нет столбцов для всех (их 30). Однако я смог настроить и заставить ваш подход работать! Спасибо... это самое читаемое.

earnric 16.04.2023 18:28

Вот способ использования 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()

wjandrea 12.04.2023 03:21

Чтобы получить последовательность столбцов, показанную в вопросе 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

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