Агрегация является одной из самых частых операций при анализе данных. Разные технологии предлагают нам кучу способов эффективно группировать и агрегировать интересующие нас поля(столбцы, признаки). В этой статье будет рассказано про реализацию агрегации в pandas.
По своей специализации я очень мало работаю с python, но часто слышу про плюсы и мощь этого языка, в особенности когда речь заходит про работу с данными. Поэтому я проведу здесь параллель операций с T-SQL и приведу некотрые примеры кода. В качестве данных я буду использовать наверное самый популярный data set — Ирисы Фишера.
Первое, что приходит в голову, это получить максимальное, минимальное или среднее значение по какому либо из параметров ириса и сгруппировать по видам этого растения, что на python с помощью pandas будет выглядить примерно следующим образом:
import pandas as pd
df = pd.read_csv('iris.csv', delimiter = ',')
print(df.groupby('variety').max()[['sepalLength']].to_markdown())
Результат:
| variety | sepal.length |
|:-----------|---------------:|
| Setosa | 5.8 |
| Versicolor | 7 |
| Virginica | 7.9 |
Либо так:
import pandas as pd
df = pd.read_csv('iris.csv', delimiter = ',')
print(df.groupby('variety').sepalLength.agg(
maxSepalLength = 'max',
minSepalLength = 'min',
).to_markdown())
Результат:
| variety | maxSepalLength | minSepalLength |
|:-----------|-----------------:|-----------------:|
| Setosa | 5.8 | 4.3 |
| Versicolor | 7 | 4.9 |
| Virginica | 7.9 | 4.9 |
Или с помощью лямбда выражений:
import pandas as pd
df = pd.read_csv('iris.csv', delimiter = ',')
print(df.groupby('variety').sepalLength.agg([
lambda x: x.max(),
lambda x: x.min()
]).to_markdown())
Результат:
| variety | <lambda_0> | <lambda_1> |
|:-----------|-------------:|-------------:|
| Setosa | 5.8 | 4.3 |
| Versicolor | 7 | 4.9 |
| Virginica | 7.9 | 4.9 |
Функция экземпляра DataFrame
to_markdown()
позволяет вывести в привычном(консольном) виде таблицу(DataFrame). На T-SQL такая операция выглядит приблизительно так:
select i.Variety, max(i.SepalLength) as maxSepalLength
from Iris i
group by i.Variety
Результат:
Setosa 5.8
Versicolor 7.0
Virginica 7.9
Но допустим, теперь мы хотим получить и максимальное и минимальное (если угодно среднее) значения по всем параметрам ириса, естественно для каждого вида растения, здесь саначала код на T-SQL:
select
i.Variety
,max(i.SepalLength) as maxSepalLength
,min(i.SepalLength) as minSepalLength
,max(i.SepalWidth) as maxSepalWidth
,min(i.SepalWidth) as minSepalWidth
,max(i.PetalLength) as maxPetalLength
,min(i.PetalLength) as mibPetalLength
,max(i.PetalWidth) as maxPetalWidth
,min(i.PetalWidth) as minPetalWidth
from Iris i
group by i.Variety
Результат:
Setosa 5.8 4.3 4.4 2.3 1.9 1.0 0.6 0.1
Versicolor 7.0 4.9 3.4 2.0 5.1 3.0 1.8 1.0
Virginica 7.9 4.9 3.8 2.2 6.9 4.5 2.5 1.4
В pandas возможность групповой агрегации появилась только в версии 0.25.0 от 18 июля 2019(что было делать раньше?) и тут есть несколько вариации, рассмотрим их:
import pandas as pd
df = pd.read_csv('iris.csv', delimiter = ',')
df.groupby('variety').agg(
maxSepalLength = pd.NamedAgg(column = 'sepalLength', aggfunc = 'max'),
minSepalLength = pd.NamedAgg(column = 'sepalLength', aggfunc = 'min'),
maxSepalWidth = pd.NamedAgg(column = 'sepalWidth', aggfunc = 'max'),
minSepalWidth = pd.NamedAgg(column = 'sepalWidth', aggfunc = 'min'),
maxPetalLength = pd.NamedAgg(column = 'petalLength', aggfunc = 'max'),
minPetalLength = pd.NamedAgg(column = 'petalLength', aggfunc = 'min'),
maxPetalWidth = pd.NamedAgg(column = 'petalWidth', aggfunc = 'max'),
minPetalWidth = pd.NamedAgg(column = 'petalWidth', aggfunc = 'min'),
)
Результат:
Setosa 5.8 4.3 4.4 2.3 1.9 1.0 0.6 0.1
Versicolor 7.0 4.9 3.4 2.0 5.1 3.0 1.8 1.0
Virginica 7.9 4.9 3.8 2.2 6.9 4.5 2.5 1.4
Функция
DataFrame.agg(self, func, axis=0, *args, **kwargs)
позволяет проводить агрегацию из нескольких операций над заданной осью. В качестве параметров функция получает **kwargs(именнованные аргументы, подробнее можно посмотреть в статье на habr), которые представляют собой столбец, над которым производится операция и собтсвенно имя функции агрегирования в одинарных кавычках. Запись выглядит довольно пространно. Идем дальше.
То же решение с применением лямбда выражений выглядит гораздо более лаконично и просто:
import pandas as pd
df = pd.read_csv('iris.csv', delimiter = ',')
df.groupby('variety').agg([
lambda x: x.max(),
lambda x: x.min()
])
Результат:
Setosa 5.8 4.3 4.4 2.3 1.9 1.0 0.6 0.1
Versicolor 7.0 4.9 3.4 2.0 5.1 3.0 1.8 1.0
Virginica 7.9 4.9 3.8 2.2 6.9 4.5 2.5 1.4
Мне часто доводится слышать о гораздо меньшем количестве написанного когда Python при решении однотипных задача в сравнении с другими языками. Здесь, в сравнении с T-SQL, с этим можно согласится, однако понятность и последовательность выражений лингвистических средств таких как SQL или T-SQL напрочь теряется(личное мнение).
Data set и код из статьи можной найти здесь
What’s new in 0.25.0 (July 18, 2019)
pandas