Пошаговые примеры и различные варианты использования Pandas GroupBy
Введение
В сообществе специалистов по данным Pandas стал очень популярным фреймворком для обработки и манипулирования данными. Он основан на Python, очень простом и универсальном языке, с которым мы все знакомы. Он предлагает множество полезных функций, помогающих преобразовывать данные в нужный формат. Одна из них — groupby
, функция, которая может разбивать строки DataFrame на группы на основе значений определенных столбцов.
Когда я впервые изучал groupby
, я был немного сбит с толку, поскольку, похоже, groupby
можно было использовать по-разному. Цель этого поста — объяснить, как работает groupby
, на конкретных примерах и в различных вариантах использования.
Обратите внимание, что я предполагаю, что у вас есть базовые знания о Pandas. Без лишних слов, давайте сразу приступим!
Данные
Чтобы проиллюстрировать примеры, связанные с groupby
, полезно сначала иметь какие-то данные. Здесь я предоставляю изготовленный на заказ DataFrame, состоящий из некоторой информации о нескольких странах. Здесь у нас есть Соединенные Штаты и Канада в Северной Америке; Соединенное Королевство, Франция и Германия в Европе; и Китай, Япония и Корея в Азии.
import pandas as pd df = pd.DataFrame({ "country" : ["United States", "Canada", "United Kingdom", "France", "Germany", "China", "Japan", "South Korea"], "continent" : ["North America", "North America", "Europe", "Europe", "Europe", "Asia", "Asia", "Asia"], "population" : [332722557, 38711108, 67081234, 67853000, 83222442, 1412600000, 125502000, 51745000], "area" : [3796742, 3855100, 93628, 247368, 137882, 3705407, 145937, 38690], "population percentage of world": [4.18, 0.487, 0.843, 0.853, 1.05, 17.8, 1.58, 0.650] }) df
Вот как выглядит полученный DataFrame:
В этом DataFrame единица площади находится в квадратных милях, а процент населения мира в процентах.
Группировать по объектам
Теперь, когда у нас есть данные, давайте начнем изучать возможности groupby
.
Допустим, мы хотим разделить наши данные по континентам. Мы можем начать с выполнения:
df.groupby("continent")
Обратите внимание, что выполнение этого в ячейке jupyter печатает объект groupby
, хранящийся по определенному адресу памяти. Что происходит за капотом, так это то, что объект groupby
сгруппировал DataFrame на основе указанного нами столбца, continent
.
GroupBy с функциями агрегирования
Чтобы получить что-то более полезное, нам нужно указать groupby
, что делать после того, как мы сгруппируем наши данные.
Приведем пример. Предположим, мы хотим напечатать количество записей (строк) на каждом континенте и сохранить все столбцы. Как мы могли этого добиться? Вот самый простой способ сделать это:
df.groupby("continent").count()
Здесь count()
принимает все записи, принадлежащие каждой группе (континенту), и подсчитывает количество записей, эффективно объединяя записи каждой группы в число, представляющее количество. count()
является примером функции агрегирования. Другие популярные функции агрегирования включают sum()
, min()
и max()
, которые выполняют соответствующие операции над каждой группой. Вот список всех функций агрегации, поддерживаемых объектом groupby
.
GroupBy с функциями агрегирования — мультииндекс
Использование одной функции агрегации для объекта groupby
— это прекрасно, но что, если вам нужны разные функции агрегации для каждого столбца? Например, скажем, для каждого континента мы хотим подсчитать страну, а также минимальное, максимальное и общее население. Как мы можем этого добиться?
Что ж, функция agg
— именно то, что нужно для этого. Мы можем передать словарь в agg
, который содержит имена столбцов в качестве ключей и имена функций агрегации в качестве значений, например:
df_agg = df.groupby("continent").agg({"country": "count", "population": ["sum", "min", "max"]}) df_agg
Полученный DataFrame выглядит следующим образом:
Это то, чего мы хотим. Как видите, теперь у нас есть подсчет по стране и три статистики по населению.
Интересно отметить, что здесь существует двухуровневая структура столбцов для DataFrame. В Pandas это называется мультииндекс, когда для доступа к значению определенного столбца требуется несколько значений. Чтобы убедиться в этом, мы можем распечатать столбцы DataFrame:
print(df_agg.columns)
Например, чтобы получить доступ к значениям максимального населения, нам нужно передать кортеж населения и максимальное значение в качестве индекса для DataFrame:
print(df_agg[("population", "max")])
GroupBy с функциями агрегирования — единый индекс
Если вы не хотите иметь дело с несколькими индексами, а хотите вернуться к одному индексу, вы можете добиться этого, указав новые имена столбцов в качестве аргументов в функции agg
:
df_agg_single = df.groupby("continent").agg(country_count = ("country", "count"), population_sum = ("population", "sum"), population_min = ("population", "min"), population_max = ("population", "max")) df_agg_single
Это даст результат с одним индексом с новыми именами столбцов, указанными вами:
Мы можем проверить это, вернувшись назад и распечатав столбцы и получив доступ через одиночные индексы.
print(df_agg_single.columns)
print(df_agg_single["population_max"])
GroupBy с функцией применения
К настоящему моменту мы научились применять разные функции агрегирования к разным столбцам. Но что, если вы хотите вычислить значения, зависящие от двух или более столбцов?
Например, допустим, вы хотите рассчитать плотность населения континента на основе стран внутри континента в данных. Мы также хотим рассчитать это для континента, только если процент его населения в мире превышает 3%. С тем, что у нас есть сейчас, мы не можем добиться этого с помощью функций агрегации.
Нам нужно что-то еще. И это что-то еще — apply
, функция Pandas, которая позволяет нам указать нашу собственную функцию обработки после группировки данных, в нашем случае, по континентам.
Если нам нужна общая численность населения и плотность населения, решение будет выглядеть примерно так:
def process_continent(continent): result = {} if continent["population percentage of world"].sum() > 3: result["population"] = continent["population"].sum() result["population density"] = result["population"] / continent["area"].sum() return pd.Series(result, index = ["population", "population density"], dtype = "float64") df_density = df.groupby("continent").apply(process_continent) df_density
Как видите, мы передаем нашу определенную функцию process_continent
в apply
. В process_continent
у нас есть доступ к каждой группе, соответствующей каждому континенту. Мы проверяем, превышает ли его процент населения мира 3; если это так, то мы вычисляем его общую численность и плотность и возвращаем результат в виде Серии.
Вы могли заметить, что есть NaN
записей для Европы. Это связано с тем, что в наших данных население Европы не превышает 3%, и, следовательно, ничего не возвращается. Чтобы исключить его из результирующего DataFrame, мы можем использовать .dropna()
:
df_density.dropna()
И, наконец, у нас есть DataFrame, показывающий именно то, что мы хотим :)
Заключение
Если вы дочитали до этого места, поздравляю и надеюсь, что вы узнали что-то полезное из этого поста. Я надеюсь, что этот пост дал более четкое объяснение того, как работает groupby
, и как его можно использовать на практике. Короче говоря, groupby
хорошо работает с функциями агрегирования, и если требуются более конкретные операции с несколькими столбцами, то apply
можно использовать с пользовательской функцией.
Если вам это нужно, вот ссылка на все коды, обсуждаемые в этом посте.
Это все для этого! Следите за будущими статьями и не забудьте подписаться на меня, если вы найдете мои статьи полезными.
Вот некоторые из моих других постов, не стесняйтесь проверить их:
Рекомендации
Pandas GroupBy: ваше руководство по группировке данных в Python, настоящий Python
Питон | Pandas dataframe.groupby(), GeeksForGeeks
Панды Python — GroupBy, TutorialsPoint