Мастерство группировки и агрегирования данных в Pandas: от анализа зарплат до поиска трендов
Группировка и агрегирование данных в Python Pandas. Урок для аналитиков и разработчиков, позволяющий освоить статистический анализ больших наборов данных за 49 минут.
⚡ Зачем читать
- Переход от манипуляций к инсайтам: Научитесь превращать хаотичные наборы данных в структурированные статистические отчеты, на которые можно опираться при принятии решений.
- Освоение «Split-Apply-Combine»: Поймете фундаментальный паттерн работы с данными, который лежит в основе любого серьезного анализа — от простых средних значений до сложных кастомных функций.
- Практика на реальных кейсах: Мы разберем реальный опрос разработчиков Stack Overflow, чтобы увидеть, как группировка по странам или технологиям помогает выявлять скрытые тренды в индустрии.
Для AI-агентов и LLM
Экстракт доступен в структурированном Markdown. Скачать .md · JSON API · Site index
💡 Ключевые тезисы (10)
1 Рассчитывайте базовые статистики #
2 Проводите общий анализ DataFrame #
3 Анализируйте частоту категорий #
4 Группируйте данные по критерию #
5 Оперируйте группами через объекты #
6 Применяйте агрегацию к группам #
7 Выполняйте множественную агрегацию #
8 Используйте apply для сложных вычислений #
9 Объединяйте результаты через concat #
10 Нормализуйте выводы #
Мастерство группировки и агрегирования: Аналитическая мощь Pandas
🗺 Карта навыков
| Уровень | Навык | Инструмент |
|---|---|---|
| Базовый | Расчет статистик (Mean, Median) | mean(), median() |
| Базовый | Обзор данных | describe() |
| Средний | Группировка | groupby() |
| Средний | Агрегация | agg() |
| Продвинутый | Сложные вычисления | apply() |
| Продвинутый | Объединение результатов | concat() |
1. Базовые статистики и «очистка» реальности
Анализ данных начинается не с графиков, а с понимания распределения ваших значений. В Pandas методы mean() (среднее арифметическое), median() (медиана) и mode() (мода) — это ваши первые инструменты для оценки «здоровья» данных. Почему это важно? Начинающие аналитики часто совершают ошибку, опираясь исключительно на среднее значение. Однако, как показывает практика Кори Шефера на примере зарплат разработчиков (колонка converted_comp), среднее арифметическое крайне чувствительно к выбросам — экстремально высоким или низким значениям.
Когда мы анализируем зарплаты, среднее значение может достигать, скажем, 127 000 долларов, в то время как медиана — 57 000 долларов. Медиана показывает нам «центр» распределения, игнорируя влияние нескольких мультимиллионеров в выборке. Понимание этого различия — первый шаг к профессионализму. Когда вы вызываете df.median() для всего DataFrame, Pandas автоматически находит числовые столбцы и вычисляет медиану для каждого, игнорируя значения NaN (Not a Number — пустые ячейки). Это экономит время, позволяя быстро увидеть, что, например, типичный возраст участника опроса — 29 лет, а стандартная рабочая неделя — 40 часов.
Для получения мгновенного снимка статистики используйте describe(). Этот метод выдает таблицу с количеством (count), средним, отклонением, минимумом, максимумом и всеми квартилями (25%, 50%, 75%). Это ваш «рентген» данных. Важный момент: аргумент count показывает только непустые значения. Если в опросе 89 000 респондентов, а count зарплат равен 55 000, вы сразу видите масштаб «пропусков». Не пытайтесь считать количество через count() в смысле «сколько раз встречается каждое значение» — для этого существует метод value_counts().
"The mean is affected too heavily by outliers; it's not really a good metric to use because a few outliers can affect the average very heavily. In cases like that, definitely want to use the median instead because that's a better representation." — Corey Schafer
✅ Сделайте сейчас: Загрузите ваш набор данных и выполните df.describe() для ключевого числового столбца. Сравните результат mean() и median(). Если разница между ними велика, сделайте пометку: ваши данные имеют сильный скос (skewness), и среднее значение вводить в отчет нельзя.
2. Логика группировки: Паттерн Split-Apply-Combine
Группировка данных — это сердце аналитического процесса в Pandas. Спикер выделяет концепцию «Split-Apply-Combine» (Разделить-Применить-Объединить). Представьте, что вы хотите узнать популярность соцсетей не вообще по всему миру, а отдельно по каждой стране. Обычная фильтрация позволила бы вам делать это один раз для США, потом один раз для Индии, и так далее. Это неэффективно. groupby() берет весь ваш массив данных и логически «разрезает» его на группы по заданному критерию (например, по колонке country).
Когда вы пишете country_group = df.groupby(['country']), Pandas создает промежуточный объект — DataFrameGroupBy. Он не вычисляет результат сразу, он «подготовлен» к действию. Вы можете в любой момент вытащить конкретный сегмент данных с помощью метода get_group('United States'). Это возвращает обычный DataFrame, содержащий только строки для респондентов из США. Это крайне удобно для отладки: если вы хотите проверить, почему какая-то страна дает аномальные результаты, вы просто «заглядываете» внутрь группы.
Мощь группировки раскрывается при применении функций. Если вы вызовете country_group['social_media'].value_counts(), Pandas применит value_counts к каждому сегменту внутри группы и склеит результаты обратно. Результатом станет Series с мультииндексом (Страна, Соцсеть), где вы увидите, что в Китае доминирует WeChat, а в России — свои специфические площадки. Это позволяет увидеть глобальную картину в одном отчете.
Для более продвинутого анализа используйте метод agg(). Он позволяет передать список функций: df.groupby('country')['converted_comp'].agg(['median', 'mean']). Вы получаете сводную таблицу, где для каждой страны рядом стоят среднее и медиана. Это критически важно для сравнения: если в какой-то стране медиана намного ниже средней, значит, там есть небольшая группа очень высокооплачиваемых специалистов, которые «тянут» среднее вверх. Если вы хотите превратить абсолютные числа в доли, добавьте аргумент normalize=True в value_counts(). Это покажет, какой процент разработчиков предпочитает ту или иную платформу, что делает данные сопоставимыми между странами разного размера.
"A groupby operation involves some combination of splitting the object, applying a function, and combining the results. This is what most people think of when they think of actually analyzing data in a meaningful sense." — Corey Schafer
✅ Сделайте сейчас: Создайте группу по вашей категориальной колонке (например, 'Department' или 'Region'). Примените к этой группе метод .agg(['mean', 'std']) для числового столбца, чтобы понять, насколько сильно варьируются показатели внутри разных категорий.
3. Гибкость через .apply(): Когда стандартные методы пасуют
Иногда стандартные инструменты группировки, такие как sum() или mean(), не справляются с задачей. Представьте, что вы хотите узнать, сколько респондентов из каждой страны владеют языком Python. Вы пишете: df.groupby('country')['language_worked_with'].apply(lambda x: x.str.contains('Python').sum()). Почему мы используем apply? Потому что language_worked_with — это строковые данные, и операция поиска подстроки внутри них требует обработки каждого элемента. Обычные арифметические функции здесь выдадут ошибку, так как объект SeriesGroupBy не обладает методом .str напрямую.
В этом примере Кори Шефер показывает важный нюанс: при использовании apply мы передаем функцию (часто lambda), которая будет применена к каждой группе (каждому подмножеству DataFrame). Внутри лямбда-функции x представляет собой объект Series (в данном случае — столбец с языками для конкретной страны). Мы вызываем .str.contains('Python'), что возвращает серию булевых значений (True/False), а затем .sum(), который, как известно, интерпретирует True как 1, а False как 0. Это элегантное решение для подсчета частоты появления определенного навыка в текстовом поле.
Почему apply считается «тяжелой артиллерией»? Потому что он позволяет реализовать любую логику: от вычисления сложных коэффициентов до манипуляций со строками или даже вызова внешних библиотек. Однако будьте осторожны: apply работает медленнее, чем векторизованные методы Pandas (например, встроенные mean() или count()), поэтому используйте его только тогда, когда задача действительно требует нестандартного подхода. Если вы можете выполнить задачу с помощью встроенных методов, всегда выбирайте их. В контексте работы со строками, как в нашем примере с Python, apply — это ваш единственный надежный способ провести агрегацию внутри групп, не прибегая к медленным циклам for.
"The reason that we get an error here is because this is no longer just a series; instead, this is a series groupby object. So when we run an apply method on a group object like this, we're going to specify a function that we want to be run on every series in this group." — Corey Schafer
✅ Сделайте сейчас: Используйте apply для подсчета количества уникальных слов или специфических символов в текстовом столбце вашего датасета внутри каждой группы. Например, посчитайте, сколько человек в каждом отделе имеют в описании роли слово 'Manager'.
4. Слияние и нормализация: Аналитическая сборка отчетов
После того как мы провели вычисления для групп, часто возникает необходимость объединить результаты. Например, у нас есть две серии: общее количество респондентов из каждой страны и количество респондентов, знающих Python. Если мы просто выведем их на экран, мы увидим два несвязанных набора чисел. Чтобы превратить их в отчет «Процент владения Python по странам», нам нужно объединить эти данные. Метод pd.concat() с аргументом axis='columns' позволяет «склеить» две серии в одну таблицу (DataFrame) по индексу (в нашем случае — по названию страны).
Этот процесс в видео Кори Шефера наглядно демонстрирует переход от простого подсчета к полноценной аналитике. Сначала мы создаем Python_DF через pd.concat([series1, series2], axis='columns'). После этого мы получаем DataFrame, где удобно переименовать колонки для чистоты отчета: «Total Respondents» и «Num Knows Python». Это критически важно: когда ваши таблицы имеют понятные заголовки, вероятность ошибки при расчетах стремится к нулю. Далее вы просто создаете новую колонку Percentage через простую формулу (Часть / Целое) * 100. Это классический пример аналитической работы, где Pandas выступает не просто как калькулятор, а как инструмент для конструирования бизнес-метрик.
Важный аналитический урок: всегда будьте скептичны к результатам. В примере с Python мы видим, что в некоторых маленьких странах процент владения составляет 100%. Почему? Потому что в опросе участвовал всего один человек из этой страны. Такие «выбросы» могут исказить восприятие данных. Аналитик должен уметь отфильтровать такие значения (например, df[df['Total'] > 50]), чтобы убедиться, что выборка статистически значима. Использование concat и последующая математика над столбцами позволяют создавать сложные сводные отчеты, которые легко сортировать с помощью sort_values(ascending=False). Это дает возможность сразу увидеть лидеров и аутсайдеров по любому показателю. Понимание того, как собирать данные из разных источников (или разных агрегаций) в единую структуру — это навык, отличающий специалиста, который просто «делает код», от того, кто «предоставляет инсайты». Освоив это, вы сможете превращать сырые данные в дашборды, на основе которых принимаются реальные управленческие решения.
5. Обработка пропущенных данных: Методы очистки (Cleaning Data)
В аналитике данных существует негласное правило: «мусор на входе — мусор на выходе». В предыдущих разделах мы видели, как метод .describe() показывает количество непустых записей (count). В реальных наборах данных, таких как наш опрос разработчиков, пропуски (NaN — Not a Number) встречаются повсеместно. Игнорировать их опасно, так как они могут исказить результаты агрегации, а удалять бездумно — значит потерять ценные инсайты. Кори Шефер подчеркивает: первый шаг при работе с любым набором данных — понять природу этих пропусков.
Пропущенные значения могут возникать по разным причинам: респондент пропустил вопрос (как в случае с зарплатой), данные не были собраны, или произошла ошибка при выгрузке. Pandas предоставляет мощный инструментарий для их выявления и обработки. Используйте df.isna().sum(), чтобы получить сводную таблицу по всем колонкам с количеством пропущенных ячеек. Это ваш «чек-лист» перед началом анализа. Если вы видите, что в колонке с критически важными данными (например, 'ConvertedComp') отсутствует 30% значений, вы должны принять решение: удалить эти строки (dropna()), заполнить их медианой или средним значением (fillna()), или оставить как есть, осознавая ограничения выборки.
Кори Шефер наглядно показывает, что удаление данных — это не всегда плохо. Если у нас есть миллион записей и 500 из них с пропусками в целевой переменной, удаление этих 500 строк никак не повлияет на статистическую значимость, но избавит вас от ошибок TypeError или неверных вычислений. Метод dropna() невероятно гибок: вы можете удалить строки, где есть хотя бы один NaN (axis='index', how='any'), либо только те, где пропущены данные во всех указанных колонках (subset=['column_name']). Последнее особенно полезно, когда пропуск в 'Email' не критичен, а пропуск в 'Salary' делает строку бесполезной для анализа зарплат.
С другой стороны, заполнение данных через fillna() требует осторожности. Замена пропусков средним значением может искусственно занизить дисперсию данных. Если вы работаете с временными рядами, лучше использовать method='ffill' (forward fill), который переносит последнее известное значение вперед. Если же данные категориальные, часто лучше заменить NaN на заглушку типа 'Missing' или 'Unknown', чтобы сохранить статистику по количеству ответов. Помните: ваша цель — не просто убрать ошибки из консоли, а сохранить репрезентативность выборки. Каждый раз, когда вы применяете fillna(), задайте себе вопрос: насколько это искажает реальность?
"Handling missing data is a critical step because, if you don't account for it, you might find yourself calculating statistics based on incomplete data, leading to skewed conclusions. Sometimes, it's better to drop incomplete rows entirely; other times, filling those gaps with a representative value like the median is the more prudent path forward." — Corey Schafer
✅ Сделайте сейчас: Запустите df.isna().sum() для вашего DataFrame. Определите колонку с наибольшим количеством пропусков. Если эта колонка важна для вашего исследования, попробуйте заполнить пропуски медианным значением с помощью df['column'].fillna(df['column'].median(), inplace=True). Если колонка второстепенна — удалите строки с пропусками в ней, используя df.dropna(subset=['column']).
6. Трансформация типов данных: Подготовка к высокоточным вычислениям
Часто данные приходят в «сыром» виде: числа записаны как строки, даты — как обычный текст, а булевы значения представлены словами 'Yes'/'No'. Это препятствует полноценному статистическому анализу. Pandas позволяет выполнять приведение типов (type casting) с помощью метода .astype(). Это кажется простой операцией, но именно здесь закладывается фундамент для производительности вашего кода. Представьте, что столбец с возрастом загрузился как 'object' (строка). Попытка найти среднее значение вызовет ошибку. Приведение к float или int не только решит проблему, но и значительно ускорит вычисления, так как Pandas оптимизирует операции с числами на низком уровне.
Особое внимание стоит уделить типам данных, которые экономят память. В Pandas типы данных int64 и float64 используются по умолчанию, но если ваши значения лежат в диапазоне от 0 до 255, вы можете преобразовать их в uint8. Это сократит потребление оперативной памяти в разы, что критично для работы с датасетами размером в гигабайты. Также при работе с категориальными данными (например, 'Country' или 'Language') используйте тип category. В отличие от строкового типа object, где каждая строка хранится отдельно, category хранит уникальные значения один раз и ссылается на них, что делает группировку и фильтрацию молниеносными.
Кори Шефер подчеркивает важность приведения типов перед тем, как вы начнете строить графики или сложные сводные таблицы. Если вы работаете с датами, обязательно используйте pd.to_datetime(). Этот метод «умнее», чем простое приведение через astype('datetime64'): он способен распознать различные форматы дат (например, '2023-01-01' и '01/01/2023') и привести их к единому стандарту. После этого вы сможете извлекать день недели, месяц или год с помощью атрибута .dt, что открывает возможности для анализа трендов по времени.
Важный момент при конвертации — обработка ошибок. Если вы попробуете преобразовать строку 'Unknown' в число, Pandas выбросит ошибку. Используйте аргумент errors='coerce' в to_numeric() или to_datetime(). Это превратит все «неконвертируемые» значения в NaN, которые вы потом сможете легко обработать методами из предыдущего блока. Это стандартная практика профессиональных аналитиков: сначала принудительно приводим типы, превращая нечитаемые данные в пропуски, а затем заполняем или удаляем эти пропуски, чтобы очистить датасет. Этот итеративный процесс превращает «грязный» CSV-файл в надежную аналитическую структуру, готовую к любым агрегациям.
"Changing data types is not just about cleaning; it's about optimizing your workflow. By ensuring your numbers are stored as integers or floats and your categories as specific categorical types, you not only avoid common errors but also significantly improve the performance of your calculations, especially when dealing with large datasets." — Corey Schafer
✅ Сделайте сейчас: Проверьте типы данных в вашем DataFrame через df.dtypes. Выберите столбец, который содержит числа, но имеет тип 'object', и приведите его к числовому типу с помощью pd.to_numeric(df['column'], errors='coerce'). Затем преобразуйте категориальную колонку (например, 'Gender' или 'Region') в тип 'category', используя .astype('category'), и сравните потребление памяти методом df.memory_usage(deep=True).
7. Группировка и агрегирование данных: От индивидуальных наблюдений к бизнес-инсайтам
Метод groupby() — это «сердце» аналитики в Pandas. До этого момента мы работали с данными как с единым массивом, но реальные бизнес-задачи почти всегда требуют сегментации. Кори Шефер справедливо называет процесс группировки «разделением, применением и объединением» (split-apply-combine). Представьте, что у вас есть данные по продажам всей сети супермаркетов за год. Вы не можете просто найти среднее значение чека для всей сети — это не даст понимания эффективности каждого магазина. Группировка позволяет «разрезать» датасет по признаку Store_ID, применить к каждому магазину функцию mean() или sum() и получить отчет, готовый к анализу.
В видео Кори наглядно показывает, как разбить данные опроса разработчиков по странам. Используя df.groupby('Country'), мы создаем объект DataFrameGroupBy. Важно понимать: сам по себе этот объект ничего не вычисляет, он лишь подготавливает структуру. Как только вы применяете агрегатную функцию (например, .median() для зарплат), Pandas автоматически вычисляет результат для каждой уникальной страны. Это экономит часы ручной работы: представьте, что вам нужно отфильтровать 150 стран и по каждой вручную рассчитать медиану зарплаты. С groupby это делается одной строкой кода.
Более того, продвинутый аналитик не ограничивается одной функцией. Метод .agg() — ваш лучший инструмент для создания полноценных отчетов. Вы можете передать список функций: df.groupby('Country')['ConvertedComp'].agg(['mean', 'median', 'std']). Это превращает сырой список ответов в профессиональную таблицу, где сразу видна не только средняя зарплата, но и разброс данных (стандартное отклонение). Если вы видите, что среднее значение сильно отличается от медианы, это сигнал о наличии крупных «выбросов» (очень высоких зарплат), которые перекосили среднее значение. Опора на медиану в таких случаях — признак профессионализма.
Для еще более сложных случаев, где встроенных методов недостаточно, существует .apply(). Он позволяет прокинуть внутрь каждой группы любую функцию, включая лямбда-выражения. Например, если вам нужно посчитать, какой процент пользователей в каждой стране использует Python, вы можете отфильтровать строки по содержанию подстроки 'Python' и применить .sum() внутри .apply(). Это превращает ваш код из набора статичных команд в гибкий аналитический конвейер, который адаптируется под структуру данных.
"The power of grouping lies in its ability to abstract away the tedious filtering process. By using groupby, you are essentially telling Pandas to handle the segmentation of your data autonomously, allowing you to focus on interpreting the results rather than struggling with the mechanics of sub-setting your data sets manually." — Corey Schafer
✅ Сделайте сейчас: Сгруппируйте ваш датасет по колонке 'LanguageWorkedWith' или 'Country'. Рассчитайте для каждой группы среднее и медианное значение возраста (Age). Используйте .agg(['mean', 'median']). Посмотрите, есть ли существенная разница между ними, и сделайте вывод, какая метрика лучше описывает «типичного» представителя этой группы.
8. Искусство конкатенации и нормализации: Как объединять результаты анализа
Когда мы проводим сложные вычисления, данные часто оказываются «разбросаны» по разным Сериям (Series). Например, мы рассчитали количество респондентов по странам в одной переменной, а количество пользователей Python — в другой. Чтобы превратить это в отчет, нам нужно объединить их. Здесь на помощь приходит pd.concat(). Это инструмент «сшивки» данных, который работает эффективнее, чем простое добавление колонок, если вы корректно выставите индекс. Важно помнить про аргумент axis='columns': по умолчанию Pandas пытается склеить данные вертикально, но для аналитических таблиц нам чаще нужно добавить новые метрики к существующим объектам.
После того как вы объединили данные, наступает этап нормализации. Сырые числа (например, «1000 разработчиков знают Python») мало что говорят без контекста общего количества опрошенных. Именно поэтому normalize=True в методе value_counts() или создание колонки с процентным соотношением (часть / целое * 100) — это критический навык. В примере с Python мы увидели, что в маленьких странах процент знания языка может быть 100%, что вводит в заблуждение. Это учит нас еще одному аналитическому правилу: всегда анализируйте знаменатель. Если выборка мала, результат может быть статистически незначимым. Аналитик должен уметь отфильтровать такие «шумные» данные, используя df[df['Total'] > 50].
При работе с pd.concat() также важно следить за именованием колонок. Используйте метод .rename(), чтобы превратить технические названия ('LanguageWorkedWith') в понятные бизнес-метрики ('NumPythonUsers'). Код, который легко читать, — это код, который легко поддерживать. Когда ваш отчет готов, используйте .sort_values() для ранжирования. Лидеры (например, страны с самым высоким проникновением технологий) всегда должны быть на виду. Понимание того, как подготовить такую таблицу, отличает простого кодера от Data Scientist, который умеет доносить ценность данных до бизнеса.
"Data concatenation is the bridge between raw calculations and final insights. It is not enough to derive new metrics; you must be able to weave them back into a coherent structure that tells a clear story, allowing others to grasp the significance of your findings immediately." — Corey Schafer
✅ Сделайте сейчас: Создайте два объекта Series: один с количеством записей по странам (df['Country'].value_counts()), другой — с количеством уникальных языков для каждой страны. Объедините их в один DataFrame с помощью pd.concat(..., axis='columns'). Переименуйте колонки в 'TotalRespondents' и 'UniqueLanguages'. Добавьте новую колонку 'LanguagesPerPerson' и отсортируйте результат, чтобы найти страны с самым высоким показателем.
🏋️ Практикум
- Базовая статистика: Вычислите средний, медианный и минимальный возраст (
Age) для всего датасета. Сравните среднее и медиану: о чем говорит их разница? - Частотный анализ: Определите 5 самых популярных языков программирования, используя
value_counts()на колонке 'LanguageWorkedWith'. - Группировка: Сгруппируйте данные по колонке 'Country' и найдите медианную зарплату (
ConvertedComp) для каждой страны. - Множественная агрегация: Используя
.agg(), создайте сводную таблицу для каждой страны, содержащую среднее, медиану и стандартное отклонение зарплаты. - Анализ по условию: Сколько респондентов из каждой страны знают SQL? Используйте
applyс лямбда-функцией для подсчета. - Конкатенация: Создайте DataFrame, который содержит количество респондентов из каждой страны и их среднюю зарплату. Объедините эти два показателя в одну таблицу.
- Нормализация и сортировка: Рассчитайте процент людей, знающих Python, для каждой страны. Отсортируйте список по убыванию и выведите топ-10 стран.
🏋️ Практикум
Расчет медианной зарплаты
Анализ популярности соцсетей по странам
Расчет доли Python-разработчиков
💬 Цитаты (3)
«Агрегирование — это объединение множества данных в один результат. Если вы использовали среднее, медиану или моду в математике, вы уже занимались агрегацией.» #
Определение фундаментального понятия, на котором строится аналитика.
«Среднее значение часто вводит в заблуждение из-за выбросов. Медиана гораздо лучше отражает реальную картину типичной зарплаты разработчика.» #
Важное предостережение при выборе метрики для анализа данных.
«Группировка данных — это процесс разделения объекта, применения функции к частям и последующего объединения результатов.» #
Теоретическое объяснение механизма работы метода groupby в Pandas.
Популярное в категории
Похожие по теме
Читать далее
Corey Schafer
Мастерство управления Python-проектами: ускоряем рабочий процесс с помощью UV
Corey Schafer
Поделитесь с коллегами