Подробное руководство по группировке и агрегированию с помощью pandas

Open in Colab


telegram

Введение

Одна из базовых функций анализа данных - группировка и агрегирование. В некоторых случаях этого уровня анализа может быть достаточно, чтобы ответить на вопросы бизнеса. В других случаях - это может стать первым шагом в более сложном анализе.

В pandas функцию groupby можно комбинировать с одной или несколькими функциями агрегирования, чтобы быстро и легко обобщать данные. Эта концепция обманчиво проста и большинство новых пользователей pandas поймут ее. Однако они удивятся тому, насколько полезными могут стать функции агрегирования для проведения сложного анализа данных.

В этом Блокноте кратко изложены основные функции агрегирования pandas и показаны примеры более сложных настраиваемых агрегаций. Независимо от того, являетесь ли вы начинающим или опытным пользователем pandas, я думаю, вы узнаете что-то новое для себя.

Оригниал статьи Криса тут.

Агрегирование

В контексте даннной статьи функция агрегирования - это функция, которая принимает несколько отдельных значений и возвращает сводные данные. В большинстве случаев возвращаемые данные представляют собой одно значение.

Наиболее распространенные функции агрегирования - это простое среднее (simple average) или суммирование (summation) значений.

Далее представлен пример расчета суммарной и средней стоимости билетов для набора данных "Титаник", загруженного из пакета seaborn.

15 апреля 1912 года самый большой пассажирский лайнер в истории во время своего первого рейса столкнулся с айсбергом. Когда Титаник затонул, погибли 1502 из 2224 пассажиров и членов экипажа. Эта сенсационная трагедия потрясла международное сообщество и привела к улучшению правил безопасности для судов. Одна из причин, по которой кораблекрушение привело к гибели людей, заключалась в том, что не хватало спасательных шлюпок для пассажиров и экипажа. Несмотря на то, что в выживании после затопления была определенная доля удачи, некоторые группы людей имели больше шансов выжить, чем другие.

Каждая строка набора данных представляет одного человека. Столбцы описывают различные атрибуты, включая то, выжили ли они (survived), их возраст (age), класс пассажира (pclass), пол (sex) и стоимость проезда (fare).

Эта простая концепция - необходимый строительный блок для более сложного анализа.

Одна из областей, которую необходимо обсудить, - это то, что существует несколько способов вызова функции агрегирования. Как показано выше, вы можете передать список функций для применения к одному или нескольким столбцам данных.

Что, если вы хотите выполнить анализ только подмножества столбцов?

Есть два других варианта агрегирования: использование словаря и именованное агрегирование (named aggregation).

Использование словаря:

Использование кортежей (именованное агрегирование):

Важно знать об этих параметрах и понимать, какой из них и когда использовать.

Я предпочитаю использовать словари для агрегирования.

Подход с кортежами ограничен возможностью применять только одно агрегирование за раз к определенному столбцу. Если мне нужно переименовать столбцы, я буду использовать функцию rename после завершения агрегации. В некоторых случаях подход со списком является более рациональным. Тем не менее, я повторю, что, на мой взгляд, словарный подход обеспечивает наиболее надежный способ для большинства ситуаций.

Groupby

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

Основы математики

Наиболее распространенными встроенными функциями агрегирования являются базовые математические функции, включая сумму (sum), среднее значение (mean), медианное значение (median), минимум (minimum), максимум (maximum), стандартное отклонение (standard deviation), дисперсию (variance), среднее абсолютное отклонение (mean absolute deviation) и произведение (product).

Мы можем применить все эти функции к fare (стоимости проезда) при группировке по embark_town (городу посадки на корабль):

Это все относительно простая математика.

Кстати, я не нашел подходящего варианта использования функции prod, которая вычисляет произведение всех значений в группе, и включил ее для полноты картины.

Еще один полезный трюк - использовать describe для одновременного выполнения нескольких встроенных агрегаторов:

Подсчет

После базовой математики подсчет (counting) является следующим наиболее распространенным агрегированием, которое я выполняю для сгруппированных данных.

Он несколько сложнее, чем простая математика. Вот три примера подсчета:

Главное отличие, о котором следует помнить, заключается в том, что count не включает значения NaN, тогда как size их включает. В зависимости от набора данных это различие может оказаться полезным.

Кроме того, функция nunique исключит значения NaN из уникальных счетчиков.

Продолжайте читать, чтобы увидеть пример того, как включить NaN в подсчет уникальных значений.

Первый и последний

В следующем примере мы можем выбрать самую высокую и самую низкую стоимость билета в зависимости от города, в котором совершили посадку пассажиры Титаника.

Следует помнить один важный момент: вы должны сначала отсортировать данные, если хотите, чтобы в качестве first (первого) и last (последнего) были выбраны максимальное и минимальное значения.

В приведенном выше примере я бы рекомендовал использовать max и min, но для полноты картины включил first и last. В других приложениях (например, при анализе временных рядов) вы можете выбрать значения first и last для дальнейшего анализа.

Другой подход к выбору - использовать idxmax и idxmin для выбора значения индекса, соответствующего максимальному или минимальному значениям.

Можем проверить результаты:

Вот еще один трюк, который можно использовать для просмотра строк с максимальной стоимостью проезда (fare):

Приведенный выше пример - одно из тех мест, где агрегирование на основе списка является полезным.

Другие библиотеки

Вы не ограничены функциями агрегирования в pandas. К примеру, можно использовать функции статистики из scipy или numpy.

Вот пример расчета моды (mode) и асимметрии (skew) данных для стоимости проезда.

Интересны результаты вычисления моды (mode). Функция mode из scipy.stats возвращает наиболее часто встречающееся значение, а также количество вхождений. Если вам просто нужно наиболее частое значение, то используйте pd.Series.mode.

Ключевым моментом является то, что вы можете использовать любую функцию, которую хотите, если она знает, как интерпретировать массив значений pandas и возвращает одно значение.

Работа с текстом

При работе с текстом функции подсчета будут работать должным образом. Вы также можете использовать функцию mode из scipy для текстовых данных.

Одно интересное приложение состоит в том, что если у вас небольшое количество различных значений, то можете использовать питоновскую функцию set для отображения списка уникальных значений.

Следующая краткая сводка для class (класса каюты) и deck (палубы) показывает, как данный подход можно использовать:

Пользовательские функции

Стандартные функции агрегирования pandas и функции из экосистемы Python удовлетворят многие ваши потребности в анализе данных. Однако вы, вероятно, захотите создать свои собственные пользовательские функции агрегирования. Есть четыре способа для создания собственных функций.

Чтобы проиллюстрировать различия, давайте вычислим 25-й процентиль данных (также называемый квантилью .25 или нижней квартилью), используя четыре подхода.

Во-первых, мы можем использовать функцию partial:

Затем мы определяем нашу собственную функцию (которая представляет собой небольшую обертку для quantile):

Далее определяем лямбда-функцию и даем ей имя:

Затем задаем встроенную (inline) лямбду и формируем словарь:

Как видите, результаты одинаковые, но названия столбцов немного отличаются. Это область предпочтений программистов, но я рекомендую ознакомиться с вариантами, поскольку вы встретите большинство из них в онлайн-решениях.

Я предпочитаю использовать собственные функции или встроенные (inline) лямбды.

Как и во многих других областях программирования - это элемент стиля и предпочтений, но я рекомендую вам выбрать один или два подхода и придерживаться их для единообразия.

Примеры пользовательских функций

Как показано выше, существует несколько подходов к разработке пользовательских функций агрегирования.

В большинстве случаев функции представляют собой легкие обертки (wrappers) для встроенных функций pandas. Они нужны, т.к. нет возможности передать аргументы в агрегаты (aggregations).

Следующие примеры должны пояснить этот момент.

Если вы хотите подсчитать количество нулевых значений, вы можете использовать эту функцию:

Если вы хотите включить значения NaN в свои уникальные счетчики, вам необходимо указать параметр dropna=False у функции nunique.

Вот результат применения всех функций:

Если вы хотите рассчитать 90-й процентиль, используйте quantile:

Если вы хотите вычислить усеченное среднее (trimmed mean) значение, из которого исключен самый низкий 10-й процент, используйте функцию trim_mean из scipy:

Если вы хотите получить наибольшее значение, независимо от порядка сортировки (см. ранее в этом Блокноте о first и last):

Это эквивалентно max, но я приведу еще один пример с nlargest ниже, чтобы подчеркнуть разницу.

Ранее я уже писал о sparkline. Обратитесь к этой статье за инструкциями по установке.

Вот как включить их в агрегатную функцию для уникального представления данных:

Вот они все вместе:

Функции nlargest и nsmallest могут быть полезны для резюмирования данных в различных сценариях.

Следующий код показывает суммарную стоимость для 10 первых и 10 последних пассажиров:

Использование этого подхода может быть полезно для применения закона Парето к вашим собственным данным.

Пользовательские функции с несколькими столбцами

Если у вас есть сценарий, в котором небходимо запустить несколько агрегаций по столбцам, то вы можете использовать groupby в сочетании с apply, как описано в этом ответе на stack overflow.

Используя этот метод, вы получите доступ ко всем столбцам данных и сможете выбрать подходящий способ агрегирования для создания итогового DataFrame (включая наименование столбцов):

Использование apply с groupby дает максимальную гибкость. Однако есть и обратная сторона. Функция apply работает медленно, поэтому этот подход следует использовать с осторожностью.

Работа с групповыми объектами

После группировки и агрегирования данных вы можете выполнять дополнительные вычисления для сгруппированных объектов.

В следующем примере определим, какой процент от общего количества проданных билетов можно отнести к каждой комбинации embark_town и class.

Мы используем метод assign() и лямбда-функцию для добавления столбца pct_total:

Следует отметить, что можно сделать проще с использованием кросс-таблицы pd.crosstab, как описано в статье:

Пока мы говорим о crosstab (кросс-таблицах), полезно иметь в виду, что функции агрегации также можно комбинировать со сводными таблицами (pivot tables).

Вот небольшой пример:

Иногда необходимо выполнить множество группировок (multiple groupby), чтобы ответить на вопрос. Например, если мы хотим увидеть кумулятивную сумму стоимости билетов, мы можем сгруппировать и агрегировать по городу (town) и классу (class), затем сгруппировать полученный объект и вычислить кумулятивную сумму (cumulative sum):

Это может быть сложным для понимания. Вот краткое пояснение того, что мы делаем:

Пример с данными о продажах

В следующем примере резюмируем ежедневные данные о продажах и преобразуем их в совокупное ежедневное и ежеквартальное представление.

Обратитесь к статье о Grouper, если вы не знакомы с использованием метода pd.Grouper().

В этом примере мы хотим включить сумму ежедневных продаж, а также совокупную (cumulative) сумму за квартал:

Чтобы получить хорошее представление о том, что происходит, вам нужно взглянуть на границу квартала (с конца марта по начало апреля):

Если вы хотите просто получить совокупный (cumulative) квартальный итог, вы можете связать несколько функций groupby.

Сначала сгруппируйте ежедневные результаты, затем сгруппируйте эти результаты по кварталам и используйте кумулятивную сумму:

В этом примере я включил именованный подход агрегации (named aggregation approach), чтобы переименовать переменную и уточнить, что теперь это ежедневные продажи. Затем я снова группирую и использую совокупную (cumulative) сумму, чтобы получить текущую сумму за квартал. Наконец, я переименовал столбец в квартальные продажи (quarterly sales).

По отзывам, на первый взгляд, это сложно понять. Однако, если выполните по шагам, т.е. построите функцию и будете проверять результаты на каждом шаге, то начнете понимать ее.

Не расстраивайтесь!

Сглаживание иерархических индексов столбцов

По умолчанию pandas в сводном DataFrame создает иерархический индекс у столбца:

В какой-то момент в процессе анализа вы, вероятно, захотите «сгладить» (flatten) столбцы, чтобы получилась одна строка с именами.

Я обнаружил, что мне лучше всего подходит следующий подход.

Я использую параметр as_index=False при группировке, а затем создаю новое имя свернутого (collapsed) столбца.

Вот код:

Вот изображение, показывающее, как выглядит сплющенный кадр данных:

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

Промежуточные итоги

Если вы хотите добавить промежуточные итоги (subtotal), я рекомендую пакет sidetable.

Инструкция по работе с sidetable на русском языке тут.

Вот как вы можете суммировать fares по class, embark_town и sex с промежуточным итогом на каждом уровне, а также общим итогом внизу:

sidetable также позволяет настраивать уровни промежуточных итогов и итоговые метки. Обратитесь к документации пакета для получения дополнительных примеров того, как sidetable может резюмировать данные.

Резюме

Спасибо, что прочитали эту статью. Здесь много деталей, но это связано с тем, что существует множество различных применений для группировки и агрегирования данных с помощью pandas. Я надеюсь, что этот пост станет полезным ресурсом, который вы сможете добавить в закладки и вернуться к нему, когда столкнетесь с собственной сложной проблемой.

Если у вас есть другие распространенные техники, которые вы часто используете, дайте мне знать в комментариях к статье. Если я получу что-нибудь полезное, я включу его в этот пост или как обновленную статью.