Pandas является краеугольным камнем современной аналитики данных в Python, предоставляя мощные и гибкие инструменты для манипулирования и анализа табличных данных. Среди этих инструментов функции группировки и агрегации, реализуемые через методы groupby() и agg(), занимают центральное место. Они позволяют преобразовывать сырые данные в осмысленные сводки, выявлять закономерности и получать ценные инсайты.
Однако часто возникает потребность не просто в числовых агрегациях, таких как сумма или среднее, но и в сборе всех или уникальных значений из группы в виде списков. Это открывает новые возможности для более глубокого анализа, позволяя сохранять контекст исходных данных после агрегации.
В этом руководстве мы подробно рассмотрим механизмы groupby() и agg(), уделив особое внимание техникам получения и эффективной работы со списками значений. Мы пройдем путь от базовых концепций до продвинутых методов, таких как именованная агрегация и пользовательские функции, а также рассмотрим практические сценарии и советы по оптимизации производительности. Приготовьтесь углубить свои знания и расширить арсенал инструментов Pandas.
Основы группировки данных в Pandas
После обзора важности groupby() и agg() в Pandas, углубимся в фундаментальные принципы группировки. Метод groupby() является краеугольным камнем для выполнения операций "split-apply-combine" (разделение-применение-объединение). Этот мощный шаблон включает в себя:
-
Разделение (Split): Разделение данных на группы на основе значений одного или нескольких ключей.
-
Применение (Apply): Применение функции (например, агрегации, трансформации или фильтрации) к каждой отдельной группе.
-
Объединение (Combine): Объединение результатов всех групп в единую структуру данных Pandas.
Синтаксис groupby() интуитивно понятен. Для группировки по одному столбцу достаточно указать его имя: df.groupby('Столбец1'). Если требуется группировка по нескольким критериям, передается список имен столбцов: df.groupby(['Столбец1', 'Столбец2']). Результатом является объект DataFrameGroupBy, который сам по себе не является DataFrame, но готов к применению агрегирующих или трансформирующих функций.
Что такое GroupBy и концепция ‘split-apply-combine’?
Метод groupby() в Pandas является краеугольным камнем для выполнения агрегации и анализа данных по подгруппам. Он реализует мощную парадигму "split-apply-combine" (разделение-применение-объединение), которая лежит в основе многих операций с данными.
-
Разделение (Split): На первом этапе DataFrame или Series разбивается на несколько групп на основе значений одного или нескольких ключей (столбцов). Каждая уникальная комбинация значений ключей формирует отдельную группу.
-
Применение (Apply): Затем к каждой из этих независимых групп применяется функция. Это может быть функция агрегации (например,
sum(),mean(),count()), трансформация (например, нормализация данных внутри группы) или фильтрация. -
Объединение (Combine): Наконец, результаты применения функции к каждой группе объединяются в единый объект Pandas (DataFrame или Series), сохраняя при этом информацию о группировке или агрегированные значения.
Объект GroupBy, возвращаемый методом groupby(), сам по себе не является конечным результатом, а скорее промежуточным объектом, который ожидает дальнейших операций, таких как агрегация, трансформация или фильтрация. Это позволяет эффективно обрабатывать большие объемы данных, применяя операции к подмножествам.
Синтаксис groupby(): группировка по одному или нескольким столбцам
Переходя от теории к практике, метод groupby() в Pandas является краеугольным камнем для организации данных. Его базовый синтаксис прост и интуитивно понятен.
Группировка по одному столбцу:
Для группировки DataFrame по значениям одного столбца достаточно передать имя этого столбца в метод groupby():
df.groupby('НазваниеСтолбца')
Это создаст объект DataFrameGroupBy, который сам по себе еще не выполняет никаких вычислений, а лишь подготавливает данные к последующей агрегации или трансформации.
Группировка по нескольким столбцам:
Если требуется более детализированная группировка, можно указать список имен столбцов. Pandas сгруппирует строки, имеющие одинаковые комбинации значений во всех указанных столбцах:
df.groupby(['Столбец1', 'Столбец2'])
В этом случае каждая уникальная комбинация значений из Столбец1 и Столбец2 будет формировать отдельную группу. Это позволяет проводить многомерный анализ, разбивая данные на более мелкие, осмысленные сегменты.
Введение в агрегацию данных с помощью .agg()
После того как данные сгруппированы с помощью groupby(), следующим логическим шагом является применение агрегирующих функций к каждой группе. Метод .agg() в Pandas идеально подходит для этой цели, реализуя фазу ‘apply’ в парадигме ‘split-apply-combine’. Он позволяет вычислять сводные статистики, такие как сумма, среднее, количество элементов и другие, для каждой группы.
К наиболее часто используемым базовым функциям агрегации относятся:
-
sum: вычисляет сумму значений. -
mean: вычисляет среднее арифметическое. -
count: подсчитывает количество не-NaN значений. -
min: находит минимальное значение. -
max: находит максимальное значение. -
size: возвращает общее количество элементов в группе, включая NaN.
Pandas также предоставляет гибкость для применения нескольких функций агрегации одновременно к одному или разным столбцам. Это можно сделать, передав список функций или словарь, где ключи — это названия столбцов, а значения — функции или списки функций. Например, можно одновременно получить средний возраст и максимальную зарплату для каждой группы сотрудников.
Базовые функции агрегации (sum, mean, count, min, max, size)
После группировки данных с помощью groupby(), метод .agg() позволяет применить одну или несколько агрегирующих функций к каждой группе. Pandas предоставляет набор встроенных функций, которые значительно упрощают этот процесс. Рассмотрим основные из них:
-
sum(): Вычисляет сумму значений для каждой группы. -
mean(): Определяет среднее арифметическое значений в каждой группе. -
count(): Подсчитывает количество непустых (non-NaN) значений в каждой группе. Важно отметить, чтоcount()возвращает количество значений, а не количество строк. -
size(): Возвращает количество строк в каждой группе, включаяNaNзначения. Это отличается отcount(). -
min(): Находит минимальное значение в каждой группе. -
max(): Находит максимальное значение в каждой группе.
Эти функции можно применять, передавая их имена в виде строк в метод .agg():
import pandas as pd
data = {
'Категория': ['A', 'B', 'A', 'C', 'B', 'A', 'C'],
'Значение': [10, 20, 15, 5, 25, 12, 8]
}
df = pd.DataFrame(data)
# Группировка по 'Категория' и агрегация с помощью sum()
result_sum = df.groupby('Категория')['Значение'].agg('sum')
print("\nСумма по категориям:\n", result_sum)
# Группировка и агрегация с помощью mean()
result_mean = df.groupby('Категория')['Значение'].agg('mean')
print("\nСреднее по категориям:\n", result_mean)
# Группировка и агрегация с помощью count() и size()
result_count_size = df.groupby('Категория')['Значение'].agg(['count', 'size'])
print("\nКоличество непустых значений (count) и размер группы (size):\n", result_count_size)
Использование этих базовых функций является первым шагом к глубокому анализу данных, позволяя быстро получать сводные статистики по группам.
Применение нескольких функций агрегации одновременно
После того как мы освоили применение одиночных функций агрегации, естественным шагом является возможность вычисления нескольких статистик одновременно для каждой группы. Pandas предоставляет элегантный способ сделать это, передав список имен функций в метод .agg().
Например, если мы хотим получить сумму, среднее значение и количество элементов для столбца value в каждой группе category, мы можем сделать это так:
import pandas as pd
data = {'category': ['A', 'B', 'A', 'B', 'A', 'B'],
'value': [10, 20, 15, 25, 12, 22]}
df = pd.DataFrame(data)
result = df.groupby('category')['value'].agg(['sum', 'mean', 'count'])
print(result)
В результате вы получите DataFrame, где для каждой группы будут представлены все запрошенные агрегированные значения. Столбцы результирующего DataFrame будут иметь мультииндекс, где верхний уровень — это имя агрегируемого столбца (value), а нижний — имена примененных функций (sum, mean, count). Это значительно упрощает получение комплексного обзора данных по группам.
Получение списков и уникальных значений после агрегации
После того как мы освоили базовые агрегации, часто возникает потребность собрать все значения или только уникальные элементы каждой группы в виде списка. Pandas предоставляет элегантные способы для этого.
Для получения всех значений группы в виде списка можно просто передать list в метод .agg():
df.groupby('Категория')['Значение'].agg(list)
Это создаст Series, где каждый элемент будет списком значений из соответствующей группы.
Если же требуется получить уникальные значения для каждой группы, можно использовать lambda-функцию внутри .agg():
df.groupby('Категория')['Значение'].agg(lambda x: x.unique().tolist())
Такой подход позволяет гибко применять любые операции к Series внутри каждой группы, возвращая результат в желаемом формате списка.
Агрегация данных в списки: методы и примеры (.agg(list), .apply(list))
Переходя от базовых скалярных агрегаций, таких как сумма или среднее, часто возникает необходимость собрать все элементы группы в единый список для дальнейшей обработки или анализа. Pandas предоставляет несколько эффективных способов для этого.
Наиболее прямой и производительный метод — использование agg(list). Он позволяет легко преобразовать все значения столбца в каждой группе в список:
import pandas as pd
data = {'Категория': ['A', 'B', 'A', 'C', 'B'], 'Значение': [10, 20, 15, 30, 25]}
df = pd.DataFrame(data)
результат = df.groupby('Категория')['Значение'].agg(list)
print(результат)
# Категория
# A [10, 15]
# B [20, 25]
# C [30]
# Name: Значение, dtype: object
Альтернативный подход — применение apply(list). Хотя для простой агрегации в список agg(list) обычно быстрее, apply() предлагает большую гибкость для более сложных пользовательских операций, которые могут включать несколько столбцов или сложную логику. Для получения списка значений он работает аналогично:
результат_apply = df.groupby('Категория')['Значение'].apply(list)
print(результат_apply)
# Категория
# A [10, 15]
# B [20, 25]
# C [30]
# Name: Значение, dtype: object
Оба метода возвращают Series, где индексом являются группы, а значениями — списки элементов, что удобно для последующей обработки.
Работа с уникальными значениями: получение списков уникальных элементов группы
После того как мы научились собирать все значения группы в списки, часто возникает задача получить только уникальные элементы из этих списков. Pandas предоставляет гибкие инструменты для этого. Вместо того чтобы просто агрегировать в list, мы можем применить пользовательскую функцию, которая сначала извлекает уникальные значения, а затем преобразует их в список.
Для этого можно использовать метод .agg() или .apply() с lambda-функцией, которая вызывает .unique() на Series внутри каждой группы, а затем преобразует результат в список Python.
df = pd.DataFrame({
'Категория': ['A', 'B', 'A', 'B', 'A', 'C'],
'Значение': [1, 2, 1, 3, 4, 2],
'Элемент': ['яблоко', 'груша', 'яблоко', 'апельсин', 'банан', 'груша']
})
# Получение списков уникальных элементов по группам
уникальные_элементы = df.groupby('Категория')['Элемент'].agg(lambda x: x.unique().tolist())
print(уникальные_элементы)
Этот подход позволяет легко получить списки уникальных значений для каждой группы, что крайне полезно для дальнейшего анализа или визуализации.
Продвинутые техники агрегации и кастомизация
Переходя от базовых методов, Pandas предлагает мощные инструменты для более гибкой и читаемой агрегации. Именованная агрегация (NamedAgg) позволяет присваивать осмысленные имена новым столбцам прямо в процессе агрегации, улучшая ясность кода и выходных данных.
import pandas as pd
data = {'category': ['A', 'B', 'A', 'B', 'A'],
'value': [10, 20, 15, 25, 12],
'item': ['x', 'y', 'z', 'w', 'v']}
df = pd.DataFrame(data)
# Именованная агрегация
result_named_agg = df.groupby('category').agg(
total_value=('value', 'sum'),
avg_value=('value', 'mean'),
items_list=('item', list)
)
print(result_named_agg)
Для случаев, когда стандартные функции агрегации недостаточны, можно использовать пользовательские функции. Это могут быть lambda-функции для простых операций или полноценные Python-функции для более сложной логики. Они применяются к каждой группе Series.
# Пользовательская lambda-функция для агрегации
result_custom_lambda = df.groupby('category').agg(
value_range=('value', lambda x: x.max() - x.min())
)
print(result_custom_lambda)
# Пользовательская функция Python
def get_first_two(series):
return series.head(2).tolist()
result_custom_func = df.groupby('category').agg(
first_two_items=('item', get_first_two)
)
print(result_custom_func)
Эти методы значительно расширяют возможности агрегации, позволяя точно настраивать процесс обработки данных.
Именованная агрегация (NamedAgg): улучшение читаемости кода и вывода
Продолжая углубляться в продвинутые возможности агрегации, рассмотрим NamedAgg – мощный инструмент, появившийся в Pandas версии 0.25.0. Он значительно улучшает читаемость кода и ясность выходных данных при выполнении множественной агрегации, особенно когда требуется применить несколько функций к одному столбцу или использовать пользовательские функции.
Вместо использования кортежей ('столбец', 'функция'), которые могут быть менее интуитивными, NamedAgg позволяет явно задавать новое имя для агрегированного столбца. Синтаксис прост: имя_нового_столбца = pd.NamedAgg(column='исходный_столбец', aggfunc='функция_агрегации').
Пример:
df.groupby('Категория').agg(
СреднееЗначение=('Значение', 'mean'),
СуммаЗначений=pd.NamedAgg(column='Значение', aggfunc='sum'),
КоличествоУникальных=pd.NamedAgg(column='ID', aggfunc='nunique')
)
Здесь СреднееЗначение использует более старый, но все еще допустимый синтаксис, а СуммаЗначений и КоличествоУникальных демонстрируют NamedAgg. Это делает код самодокументируемым и предотвращает путаницу с именами столбцов в результате.
Пользовательские функции агрегации: применение lambda и custom-функций
Хотя NamedAgg значительно улучшает читаемость, стандартные функции агрегации не всегда покрывают все аналитические потребности. Pandas позволяет применять пользовательские функции агрегации, что дает максимальную гибкость.
Для простых, однострочных операций идеально подходят lambda-функции. Они позволяют определить агрегацию прямо внутри вызова .agg():
df.groupby('Категория').agg(
размах_цены=('Цена', lambda x: x.max() - x.min())
)
Когда логика агрегации становится сложнее, лучше определить полноценную custom-функцию Python. Это улучшает читаемость и позволяет повторно использовать код:
def calculate_iqr(series):
return series.quantile(0.75) - series.quantile(0.25)
df.groupby('Категория').agg(
межквартильный_размах=('Значение', calculate_iqr)
)
Такие пользовательские функции легко интегрируются с NamedAgg, обеспечивая как гибкость вычислений, так и ясность выходных данных.
Практические сценарии и оптимизация производительности
После изучения гибкости пользовательских функций, рассмотрим, как эти техники применяются в реальных задачах. Типичный сценарий — сбор всех связанных элементов в список для каждой группы. Например, для анализа транзакций можно сгруппировать покупки по customer_id и агрегировать product_name в список, чтобы получить полную историю покупок каждого клиента. Это позволяет легко анализировать потребительские корзины, выявлять паттерны или рекомендовать товары. Другой пример — агрегация всех тегов, связанных с определенной статьей или продуктом.
Для оптимизации производительности при работе с большими наборами данных, предпочтительно использовать встроенные методы агрегации, такие как df.groupby('col').agg(list), которые часто оптимизированы на уровне C и работают быстрее, чем df.groupby('col').apply(list). Также, преобразование столбцов, используемых для группировки, в тип category может значительно ускорить операции groupby, особенно при большом количестве уникальных значений.
Примеры реального применения группировки и агрегации со списками
Переходя от теоретических основ к реальным задачам, рассмотрим, как группировка и агрегация со списками могут быть применены для извлечения ценной информации. Эти методы особенно полезны, когда необходимо сохранить детализированный контекст внутри каждой группы.
Пример 1: Список всех транзакций клиента
Представьте, что у нас есть данные о покупках, и мы хотим получить список всех товаров, приобретенных каждым клиентом. Это легко достигается с помощью groupby() и agg(list):
df_sales.groupby('CustomerID')['Product'].agg(list)
Этот код вернет Series, где индексом будет CustomerID, а значениями — списки всех продуктов, купленных этим клиентом.
Пример 2: Уникальные категории товаров, посещенных пользователем
Если же нам нужен список уникальных категорий, которые просматривал каждый пользователь, мы можем использовать lambda-функцию внутри agg():
df_browsing.groupby('UserID')['Category'].agg(lambda x: x.unique().tolist())
Такие подходы позволяют не только агрегировать числовые данные, но и сохранять или трансформировать нечисловые атрибуты в удобные для дальнейшего анализа структуры данных, такие как списки.
Советы по оптимизации производительности для больших наборов данных
Для эффективной работы с большими наборами данных при использовании groupby и agg, особенно когда результатом являются списки, следует учитывать несколько рекомендаций:
-
Используйте
agg(list)вместоapply(list): Методaggс функциейlist(илиnp.array) обычно значительно быстрее, чемapply(list), поскольку он реализован на более низком уровне и оптимизирован для таких операций. -
Оптимизируйте типы данных: Преобразование столбцов с низкой кардинальностью в тип
categoryможет существенно сократить потребление памяти и ускорить операции группировки. -
Избегайте пользовательских функций Python в
applyдля простых задач: Если возможно, используйте встроенные функции агрегации Pandas или NumPy. Пользовательские функции, особенно сapply, могут быть медленными из-за накладных расходов Python. -
Предварительная фильтрация: Если вам нужны только определенные группы или строки, отфильтруйте DataFrame до операции
groupbyдля уменьшения объема обрабатываемых данных.
Заключение
В этом руководстве мы подробно изучили мощные возможности Pandas для группировки и агрегации данных, с особым акцентом на работу со списками значений. Мы рассмотрели основы groupby(), различные функции агрегации, методы получения списков (.agg(list), .apply(list)), а также продвинутые техники, такие как NamedAgg и пользовательские функции. Применение этих инструментов позволяет эффективно преобразовывать и анализировать сложные наборы данных, извлекая ценные инсайты. Освоение этих методов значительно расширит ваш арсенал для решения задач по анализу данных в Pandas.