Как обратиться к столбцу сводной таблицы после GROUPBY?
У меня есть таблица, из которой я делаю сводную таблицу путём группировки операций и максимальных сумм для этого типа операций. После этого я хочу взять 5 минимальных значений путём nsmallest, но возникает ошибка. Как можно в полученной сводной таблице обратиться к столбцу MAX ?

Отслеживать
задан 27 апр 2020 в 12:12
Arkady Horse Arkady Horse
11 2 2 бронзовых знака
Приведите воспроизводимый фрагмент исходных данных и свой код в электронном виде.
27 апр 2020 в 12:19
А print(a.columns) что говорит? Возможно, столбец называется ‘amount max’, но точно не помню, проще самому проверить, просто посмотрев список столбцов. Либо там двухуровневая индексация, но с ней я уже не помню как работать.
Основы Pandas №2 // Агрегация и группировка
Во втором уроке руководства по работе с pandas речь пойдет об агрегации (min, max, sum, count и дргуих) и группировке. Это популярные методы в аналитике и проектах data science, поэтому убедитесь, что понимаете все в деталях!
Примечание: это руководство, поэтому рекомендуется самостоятельно писать код, повторяя инструкции!
Агрегация данных — теория
Агрегация — это процесс превращения значений набора данных в одно значение. Например, у вас есть следующий набор данных…
| animal | water_need |
|---|---|
| zebra | 100 |
| lion | 350 |
| elephant | 670 |
| kangaroo | 200 |
…простейший метод агрегации для него — суммирование water_needs , то есть 100 + 350 + 670 + 200 = 1320. Как вариант, можно посчитать количество животных — 4. Теория не так сложна. Но пора переходить к практике.
Агрегация данных — практика
Где мы остановились в последний раз? Открыли Jupyter Notebook, импортировали pandas и numpy и загрузили два набора данных: zoo.csv и article_reads . Продолжим с этого же места. Если вы не прошли первую часть, вернитесь и начните с нее.
Начнем с набора zoo . Он был загружен следующим образом:
pd.read_csv('zoo.csv', delimiter = ',')

Дальше сохраним набор данных в переменную zoo .
zoo = pd.read_csv('zoo.csv', delimiter = ',')
Теперь нужно проделать пять шагов:
- Посчитать количество строк (количество животных) в zoo .
- Посчитать общее значение water_need животных.
- Найти наименьшее значение water_need .
- И самое большое значение water_need .
- Наконец, среднее water_need .
Агрегация данных pandas №1: .count()
Посчитать количество животных — то же самое, что применить функцию count к набору данных zoo :
zoo.count()

А что это за строки? На самом деле, функция count() считает количество значений в каждой колонке. В случае с zoo было 3 колонки, в каждой из которых по 22 значения.
Чтобы сделать вывод понятнее, можно выбрать колонку animal с помощью оператора выбора из предыдущей статьи:
zoo[['animal']].count()
В этом случае результат будет даже лучше, если написать следующим образом:
zoo.animal.count()
Также будет выбрана одна колонка, но набор данных pandas превратится в объект series (а это значит, что формат вывода будет отличаться).

Агрегация данных pandas №2: .sum()
Следуя той же логике, можно с легкостью найти сумму значений в колонке water_need с помощью:
zoo.water_need.sum()

Просто из любопытства можно попробовать найти сумму во всех колонках:
zoo.sum()

Примечание: интересно, как .sum() превращает слова из колонки animal в строку названий животных. (Кстати, это соответствует всей логике языка Python).
Агрегация данных pandas №3 и №4: .min() и .max()
Какое наименьшее значение в колонке water_need ? Определить это несложно:
zoo.water_need.min()

То же и с максимальным значением:
zoo.water_need.max()

Агрегация данных pandas №5 и №6: .mean() и .median()
Наконец, стоит посчитать среднестатистические показатели, например среднее и медиану:
zoo.water_need.mean()

zoo.water_need.median()

Это было просто. Намного проще, чем агрегация в SQL.
Но можно усложнить все немного с помощью группировки.
Группировка в pandas
Работая аналитиком или специалистом Data Science, вы наверняка постоянно будете заниматься сегментациями. Например, хорошо знать количество необходимой воды ( water_need ) для всех животных (это 347,72 ). Но удобнее разбить это число по типу животных.
Вот упрощенная репрезентация того, как pandas осуществляет «сегментацию» (группировку и агрегацию) на основе значений колонок!
Функция .groupby в действии
Проделаем эту же группировку с DataFrame zoo .
Между переменной zoo и функцией . mean() нужно вставить ключевое слово groupby :
zoo.groupby('animal').mean()

Как и раньше, pandas автоматически проведет расчеты . mean() для оставшихся колонок (колонка animal пропала, потому что по ней проводилась группировка). Можно или игнорировать колонку uniq_id или удалить ее одним из следующих способов:
zoo.groupby(‘animal’).mean()[[‘water_need’]] — возвращает объект DataFrame.
zoo.groupby(‘animal’).mean().water_need — возвращает объект Series.
Можно поменять метод агрегации с . mean() на любой изученный до этого.
Проверить себя №1
Вернемся к набору данных article_read .
Примечание: стоит напомнить, что в этом наборе хранятся данные из блога о путешествиях. Скачать его можно отсюда. Пошаговый процесс загрузки, открытия и сохранения есть в прошлом материале руководства.
Если все готово, вот первое задание:
Какой источник используется в article_read чаще остальных?
Правильный ответ:
Reddit!
Получить его можно было с помощью кода:
article_read.groupby('source').count()
Взять набор данных article_read , создать сегменты по значениям колонки source ( groupby(‘source’) ) и в конце концов посчитать значения по источникам ( .count() ).

Также можно удалить ненужные колонки и сохранить только user_id :
article_read.groupby('source').count()[['user_id']]
Проверить себя №2
Вот еще одна, более сложная задача:
Какие самые популярные источник и страна для пользователей country_2 ? Другими словами, какая тема из какого источника принесла больше всего просмотров из country_2 ?
Правильный ответ: Reddit (источник) и Азия (тема) с 139 прочтениями.
Вот Python-код для получения результата:
article_read[article_read.country == 'country_2'].groupby(['source', 'topic']).count()

Вот краткое объяснение:
В первую очередь отфильтровали пользователей из country_2 ( article_read[article_read.country == ‘country_2’] ). Затем для этого подмножества был использован метод groupby . (Да, группировку можно осуществлять для нескольких колонок. Для этого их названия нужно собрать в список. Поэтому квадратные скобки используются между круглыми. Это что касается части groupby([‘source’, ‘topic’]) ).
А функция count() — заключительный элемент пазла.
Итого
Это была вторая часть руководства по работе с pandas. Теперь вы знаете, что агрегация и группировка в pandas— это простые операции, а использовать их придется часто.
Примечание: если вы ранее пользовались SQL, сделайте перерыв и сравните методы агрегации в SQL и pandas. Так лучше станет понятна разница между языками.
В следующем материале вы узнаете о четырех распространенных методах форматирования данных: merge , sort , reset_index и fillna .
3 функции Pandas для группировки и агрегирования данных
При работе с данными в Pyth on у программистов есть инструмент, который никогда не подведет: pandas. Это полнофункциональная и интуитивно понятная библиотека с открытым ПО, предоставляющая структуры данных для работы с высокоразмерными датасетами.
Выделяют 2 основные структуры данных:
- Series для одномерных массивов;
- DataFrame для двухмерных таблиц, содержащих строки и столбцы.
В статье мы рассмотрим наиболее эффективные функции для разделения датасетов на группы. После этого можно проводить статические вычисления, например находить стандартное отклонение, среднее, максимальное и минимальное значение, а также многое другое.
Вы научитесь применять функции apply , cut , groupby и agg . Они могут весьма пригодиться для лучшего понимания данных на основе их графического представления .
Содержание:1. Импорт данных
2. Простые агрегации
3. Множественные агрегации
1. Импорт данных
Начнем с импорта библиотек и датасетов. В работе будем использовать датасет с ценами на недвижимость Бостона, доступный в библиотеке Scikit-learn.
import numpy as np # линейная алгебра
import pandas as pd # обработка данных, CSV файл ввода-вывода (например, pd.read_csv)
from sklearn.datasets import load_boston
boston = load_boston()
df = pd.DataFrame(data=boston.data,columns=boston.feature_names)
df['price']=boston.target
df.head()
Данный DataFrame содержит только числовые признаки, а нам нужны категориальные переменные для разделения датасета на группы. Следовательно, займемся их созданием с помощью его описательной статистики:
df.describe()
Допустим, нам нужны категориальные признаки для RM (число комнат) и price (цены), которые представляют собой среднее число комнат на жилое помещение и медиану цен на жилье в размере $10,000 соответственно. В зависимости от диапазона значений указанных числовых признаков у этих переменных должны быть разные уровни (level), для создания которых задействуем значения первого и третьего квантиля. С целью генерирования этих новых переменных воспользуемся функцией apply .
df['RM_levels']=df['RM'].apply(lambda x: 'low' if xdf['price_levels']=df['price'].apply(lambda x: 'low' if xdf[['RM','RM_levels','price','price_levels']].head()
Новые столбцы появились благодаря функции apply , которая по умолчанию работает с каждой строкой датасета. Она по порядку проверяет условия. Первый уровень содержит значения числовых признаков, не превышающих первый квантиль. “Средний” уровень включает значения, располагающиеся в диапазоне между первым и третьим квантилем. Третий и последний уровень охватывают только значения, превосходящие третий квантиль.
Функция cut — альтернативный и более эффективный способ создания таких переменных, в особенности тех, что обладают несколькими уровнями. Как и в предыдущем случае, делим значения price на 3 группы: [3,5.8),[5.8,6.6),[6.6,9), где 3 и 9 соответствуют минимальной и максимальной границам переменной. Эту же логику применяем и к числу комнат. Передав только интервальные значения и аргумент right=False для игнорирования правого максимума, получаем следующие группы:
df['RM_levels'] = pd.cut(df['RM'],bins=[3,5.8,6.6,9],right=False)
df['price_levels'] = pd.cut(df['price'],bins=[5,17,25,51],right=False)
df[['RM','RM_levels','price','price_levels']].head()
При необходимости сохранить эти метки учитывайте тот факт, что несмотря на категориальный тип новых столбцов, отдельные элементы являются не строками, а объектами Interval ! Попытка осуществить поиск конкретного класса значений price окажется безрезультатной.
df[df.price_levels=='[17, 25)']
Если проверить тип столбца price_levels , взяв лишь первую строку, то увидим, что у нас есть на самом деле:
type(df['price_levels'][0])
Если вы намерены сохранить эти интервалы, то во избежание сложностей подскажу один прием — передайте метку для каждой группы, установив аргумент labels со списком строк:
l=[[3,5.8,6.6,9],[5,17,25,51]]
lbs1 = ['[<>,<>)'.format(l[0][i],l[0][i+1]) for i in range(len(l[0])-1)]
lbs2 = ['[<>,<>)'.format(l[1][i],l[1][i+1]) for i in range(len(l[1])-1)]
df['RM_levels'] = pd.cut(df['RM'],bins=[3,5.8,6.6,9],right=False,labels=lbs1)
df['price_levels'] = pd.cut(df['price'],bins=[5,17,25,51],right=False,labels=lbs2)
print(type(df['price_levels'][0]))
Теперь у вас есть строковые объекты! В любом случае в данной статье я буду сохранять метки, как показано ниже, всегда устанавливая аргумент labels :
df['RM_levels'] = pd.cut(df['RM'],bins=[3,5.8,6.6,9],labels=['low','middle','high'],right=False)
df['price_levels'] = pd.cut(df['price'],bins=[5,17,25,51],labels=['low','middle','high'],right=False)
2. Простые агрегации
Итак, первый этап пройден, и мы приступаем к группировке датасета на основании заданных признаков. Допустим, нужно узнать среднее значение каждого столбца для каждого типа price:
df_price = df.groupby(by=['price_levels']).mean()
df_price
Как видно, по каждому уровню мы получили строку, содержащую соответствующие средние значения всех переменных, представленных в датасете. Переменная price_levels считается индексом, поскольку groupby по умолчанию в качестве такого индекса возвращает метку группы. Обратите внимание на произвольный порядок уровней. Выясним причину:
df.info()
На данный момент новые переменные сохранены в памяти как объекты. Очевидно, что их необходимо преобразовать в категории. Как только мы это сделали, определяем порядок уровней переменных.
df['price_levels']=df['price_levels'].astype('category')
df['price_levels'].cat.reorder_categories(['low','middle','high'], inplace=True)
df['RM_levels']=df['RM_levels'].astype('category')
df['RM_levels'].cat.reorder_categories(['low','middle','high'], inplace=True)
Попробуем заново сгруппировать датасет для каждого уровня price. Если установить аргумент as_index в значение False , то метка группы более не будет рассматриваться в качестве индекса:
df_price = df.groupby(by=['price_levels'],as_index=False).mean()
df_price
В результате получаем упорядоченные уровни и переменную price_levels , которая более не является индексом. Как видим, датасет содержит много столбцов. Их число можно сократить, если выбрать лишь несколько признаков (например, RM и price). Кроме того, расположим ценовые уровни по возрастанию.
df_price = df.groupby(by=['price_levels'],as_index=False)[['price','RM']].mean().sort_values(by='price_levels', ascending=True)
df_price
С увеличением числа комнат повышается стоимость жилплощади. Но что если мы не хотим ограничиваться вычислением среднего, минимального и максимального значений? В таком случае нам потребуется функция apply . Выясним диапазон значений столбцов CRIM и LSTAT, имена которых соответственно означают уровень преступности на душу населения по городу и процент граждан с низким социальным статусом.
df_price = df.groupby(by=['price_levels'],as_index=False)[['CRIM','LSTAT']].apply(lambda v: v.max()-v.min())
df_price
Как видно, уровень преступности и процент населения с низким социальным положением уменьшаются с ростом цен на жилплощадь. Представим это соотношение в виде гистограммы:
import plotly.express as px
fig = px.bar(df_price, x='price_levels', y='CRIM', labels=)
fig.show()
3. Множественные агрегации
С целью выполнения множественных агрегаций для нескольких столбцов потребуется функция agg , позволяющая применить среднее, минимальное и максимальное значения, а также стандартное отклонение для каждого выбранного столбца. Результаты сгруппируем по типу цены.
df_price = df.groupby(by=['price_levels'],as_index=False)[['price','AGE','CRIM']].agg(['mean','min','max','std'])
df_price
Как видно, при добавлении функции agg аргумент as_index более не работает. Во избежание использования метки группы в качестве индекса можно в конце поместить функцию reset_index :
df_price = df.groupby(by=['price_levels'])[['price','AGE','CRIM']].agg(['mean','min','max','std']).reset_index()
df_price
Теперь все признаки агрегированы, но при работе с этим датасетом могут возникнуть проблемы. Выбирая конкретный признак, вы должны указывать двойные индексы:
df_price.columns
Работа с двойными индексами может стать обременительной. Чтобы не создавать лишних сложностей, можно заменить имена столбцов следующим образом:
stats = ['mean','min','max','std']
df_price.columns = ['price_levels']+['price_<>'.format(stat) for stat in stats]+['age_<>'.format(stat) for stat in stats]+['age_<>'.format(stat) for stat in stats]
df_price
С таким DataFrame работается уже намного легче.
Передача словаря в функцию agg позволит применять различные статистики в зависимости от столбца. В этом случае ключами будут имена столбцов, а значениями — агрегированная функция, подлежащая измерению. На этот раз для группировки датасета нам понадобятся два признака:
df_price = df.groupby(by=['price_levels','RM_levels'],as_index=False).agg()
df_price
Отразим новые итоги обработки данных в гистограмме. Сначала поменяем метки датасета, а затем посмотрим на результаты для жилья с наибольшим числом комнат.
import plotly.express as px
stats = ['count','mean','max','min']
df_price.columns = ['price_levels','RM_levels']+['CRIM_<>'.format(stat) for stat in stats]+['price_mean']
df_price
fig = px.bar(df_price[df_price.RM_levels=='high'], x='price_levels', y='CRIM_count', labels=)
fig.show()
Очевидно, что число домов увеличивается с ростом цены, поскольку мы выбрали большое количество комнат. Налицо прямая зависимость между числом комнат и ценой дома.
Выводы
В статье мы рассмотрели шаги по освоению функций для разделения датасета на группы. Надеюсь, что предложенные примеры помогли закрепить полученные знания.
Благодарю за внимание!
Полезные ссылки
- https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html
- https://pandas.pydata.org/docs/reference/api/pandas.cut.html
- https://www.google.com/search?q=groupby+pandas&oq=groupby+pandas&aqs=chrome..69i57j0j0i20i263j0l2j69i60j69i61j69i60.5962j0j7&sourceid=chrome&ie=UTF-8
- https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.agg.html
- 7 полезных операций в Pandas при работе с DataFrame
- Новая библиотека превосходит Pandas по производительности
- 7 трюков pandas для науки о данных
Работа с групповыми статистиками в pandas GroupBy

Работа с данными часто требует группировки и агрегации информации. В Python библиотека pandas предоставляет удобный и мощный инструмент для этого — метод groupby() . Он позволяет группировать данные по одному или нескольким столбцам и вычислять различные статистики для каждой группы.
Рассмотрим простой пример. Пусть имеется следующий датафрейм:
import pandas as pd data = df = pd.DataFrame(data)
Допустим, требуется найти среднее значение столбцов ‘D’ и ‘E’ для каждой комбинации значений в столбцах ‘A’ и ‘B’. Для этого можно использовать метод groupby() :
df.groupby(['A', 'B']).mean()
В результате получается датафрейм, в котором индексами являются уникальные комбинации значений столбцов ‘A’ и ‘B’, а значениями — средние значения столбцов ‘D’ и ‘E’ для каждой группы.
Однако, помимо среднего значения, часто требуется знать количество элементов в каждой группе. В pandas это можно сделать с помощью метода size() :
df.groupby(['A', 'B']).size()
Этот код вернет серию, где индексами будут уникальные комбинации значений столбцов ‘A’ и ‘B’, а значениями — количество строк в каждой группе.
Если требуется добавить эту информацию в исходный датафрейм, можно использовать метод merge() :
grouped = df.groupby(['A', 'B']).size().reset_index(name='counts') df = pd.merge(df, grouped, on=['A', 'B'], how='left')
В результате в исходном датафрейме появится новый столбец ‘counts’, содержащий количество строк в каждой группе.
Таким образом, с помощью метода groupby() можно легко вычислять различные статистики для каждой группы данных.