.groupby()
и .agg()
¶Часто случается, что данные необходимо сгруппировать по какому-то признаку – по значениям определенной переменной. На входе имеется таблица (датафрейм), а на выходе хочется получить несколько таблиц: отдельная таблица для каждого значения. Давайте рассмотрим такой пример. У нас есть файл с результатами выборов 2012 года по всем избирательным участкам, и нам нужно сгруппировать данные по регионам.
Для начала импортируем библиотеку pandas
и загрузим файл с данными.
import pandas as pd
Загрузим файл elect.csv
:
df = pd.read_csv("elect.csv")
В таблице сохранены результаты выборов президента России 2012 года.
df.head()
link | uik | kom1 | kom2 | kom3 | kom4 | kom5 | 1 | 2 | 3 | ... | 18 | 19 | 20 | 21 | 22 | 23 | а | б | в | г | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | http://www.adygei.vybory.izbirkom.ru/region/ad... | 1 | Республика Адыгея (Адыгея) | Адыгейская | УИК №1 | NaN | NaN | 2383.0 | 2147.0 | 0.0 | ... | 0.0 | 24.0 | 382.0 | 28.0 | 71.0 | 1066.0 | NaN | NaN | NaN | NaN |
1 | http://www.adygei.vybory.izbirkom.ru/region/ad... | 2 | Республика Адыгея (Адыгея) | Адыгейская | УИК №2 | NaN | NaN | 2865.0 | 2586.0 | 0.0 | ... | 0.0 | 51.0 | 453.0 | 49.0 | 104.0 | 1174.0 | NaN | NaN | NaN | NaN |
2 | http://www.adygei.vybory.izbirkom.ru/region/ad... | 3 | Республика Адыгея (Адыгея) | Адыгейская | УИК №3 | NaN | NaN | 2821.0 | 2558.0 | 0.0 | ... | 0.0 | 36.0 | 481.0 | 24.0 | 107.0 | 1025.0 | NaN | NaN | NaN | NaN |
3 | http://www.adygei.vybory.izbirkom.ru/region/ad... | 4 | Республика Адыгея (Адыгея) | Адыгейская | УИК №4 | NaN | NaN | 2069.0 | 1868.0 | 0.0 | ... | 0.0 | 0.0 | 414.0 | 0.0 | 48.0 | 784.0 | NaN | NaN | NaN | NaN |
4 | http://www.adygei.vybory.izbirkom.ru/region/ad... | 5 | Республика Адыгея (Адыгея) | Адыгейская | УИК №5 | NaN | NaN | 777.0 | 705.0 | 0.0 | ... | 0.0 | 19.0 | 138.0 | 4.0 | 7.0 | 286.0 | NaN | NaN | NaN | NaN |
5 rows × 34 columns
df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 90003 entries, 0 to 90002 Data columns (total 34 columns): link 90003 non-null object uik 90003 non-null int64 kom1 90003 non-null object kom2 90003 non-null object kom3 89618 non-null object kom4 0 non-null float64 kom5 0 non-null float64 1 89994 non-null float64 2 89994 non-null float64 3 89994 non-null float64 4 89994 non-null float64 5 89994 non-null float64 6 89994 non-null float64 7 89994 non-null float64 8 89994 non-null float64 9 89994 non-null float64 10 89994 non-null float64 11 89994 non-null float64 12 89994 non-null float64 13 89994 non-null float64 14 89994 non-null float64 15 89994 non-null float64 16 89994 non-null float64 17 89994 non-null float64 18 89994 non-null float64 19 89994 non-null float64 20 89994 non-null float64 21 89994 non-null float64 22 89994 non-null float64 23 89994 non-null float64 а 0 non-null float64 б 0 non-null float64 в 0 non-null float64 г 0 non-null float64 dtypes: float64(29), int64(1), object(4) memory usage: 23.3+ MB
Таблица достаточно большая, поэтому давайте выберем те столбцы, которые понадобятся нам для работы. Какие именно? Столбцы в этой таблице имеют порядковый номер строки в таблице на сайте Центральной избирательной комиссии.
Выберем столбцы, которые соответствуют уровням комиссий, а также следующим показателям: общее число зарегистрированных избирателей, число недействительных бюллетеней, число действительных бюллетеней, число голосов за Жириновского, Зюганова, Миронова, Прохорова и Путина.
d = df[["kom1", "kom2", "kom3",
"1", "9", "10", "19",
"20", "21", "22", "23"]]
d.head()
kom1 | kom2 | kom3 | 1 | 9 | 10 | 19 | 20 | 21 | 22 | 23 | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | Республика Адыгея (Адыгея) | Адыгейская | УИК №1 | 2383.0 | 19.0 | 1571.0 | 24.0 | 382.0 | 28.0 | 71.0 | 1066.0 |
1 | Республика Адыгея (Адыгея) | Адыгейская | УИК №2 | 2865.0 | 29.0 | 1831.0 | 51.0 | 453.0 | 49.0 | 104.0 | 1174.0 |
2 | Республика Адыгея (Адыгея) | Адыгейская | УИК №3 | 2821.0 | 31.0 | 1673.0 | 36.0 | 481.0 | 24.0 | 107.0 | 1025.0 |
3 | Республика Адыгея (Адыгея) | Адыгейская | УИК №4 | 2069.0 | 0.0 | 1246.0 | 0.0 | 414.0 | 0.0 | 48.0 | 784.0 |
4 | Республика Адыгея (Адыгея) | Адыгейская | УИК №5 | 777.0 | 8.0 | 454.0 | 19.0 | 138.0 | 4.0 | 7.0 | 286.0 |
Теперь присвоим столбцам более информативные названия:
d.columns = ["region", "tik", "uik",
"total", "invalid", "valid",
"Zh", "Zu", "Mi", "Pr", "Pu"]
d.head() # опять посмотрим
region | tik | uik | total | invalid | valid | Zh | Zu | Mi | Pr | Pu | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | Республика Адыгея (Адыгея) | Адыгейская | УИК №1 | 2383.0 | 19.0 | 1571.0 | 24.0 | 382.0 | 28.0 | 71.0 | 1066.0 |
1 | Республика Адыгея (Адыгея) | Адыгейская | УИК №2 | 2865.0 | 29.0 | 1831.0 | 51.0 | 453.0 | 49.0 | 104.0 | 1174.0 |
2 | Республика Адыгея (Адыгея) | Адыгейская | УИК №3 | 2821.0 | 31.0 | 1673.0 | 36.0 | 481.0 | 24.0 | 107.0 | 1025.0 |
3 | Республика Адыгея (Адыгея) | Адыгейская | УИК №4 | 2069.0 | 0.0 | 1246.0 | 0.0 | 414.0 | 0.0 | 48.0 | 784.0 |
4 | Республика Адыгея (Адыгея) | Адыгейская | УИК №5 | 777.0 | 8.0 | 454.0 | 19.0 | 138.0 | 4.0 | 7.0 | 286.0 |
Посмотрим теперь, какие регионы есть в базе. Выбрать столбец region
в таком случае будет не совсем удачно, поскольку в нем будет много повторяющихся значений. Посмотрим только на уникальные:
d.region.unique() # метод unique - уникальные значения
array(['Республика Адыгея (Адыгея)', 'Республика Алтай', 'Республика Башкортостан', 'Республика Бурятия', 'Республика Дагестан', 'Ðåñïóáëèêà Äàãåñòàí', 'Республика Ингушетия', 'Кабардино-Балкарская Республика', 'Республика Калмыкия', 'Карачаево-Черкесская Республика', 'Республика Карелия', 'Республика Коми', 'Республика Марий Эл', 'Республика Мордовия', 'Республика Саха (Якутия)', 'Республика Северная Осетия - Алания', 'Республика Тыва', 'Удмуртская Республика', 'Республика Хакасия', 'Чувашская Республика - Чувашия', 'Алтайский край', 'Забайкальский край', 'Камчатский край', 'Краснодарский край', 'Красноярский край', 'Пермский край', 'Приморский край', 'Ставропольский край', 'Хабаровский край', 'Õàáàðîâñêèé êðàé', 'Амурская область', 'Архангельская область', 'Астраханская область', 'Белгородская область', 'Брянская область', 'Владимирская область', 'Волгоградская область', 'Вологодская область', 'Воронежская область', 'Ивановская область', 'Иркутская область', 'Калужская область', 'Кемеровская область', 'Кировская область', 'Костромская область', 'Курганская область', 'Курская область', 'Ленинградская область', 'Липецкая область', 'Магаданская область', 'Московская область', 'Мурманская область', 'Ìóðìàíñêàÿ îáëàñòü', 'Нижегородская область', 'Новгородская область', 'Новосибирская область', 'Омская область', 'Оренбургская область', 'Орловская область', 'Пензенская область', 'Псковская область', 'Ростовская область', 'Рязанская область', 'Самарская область', 'Саратовская область', 'Сахалинская область', 'Свердловская область', 'Смоленская область', 'Тамбовская область', 'Тверская область', 'Томская область', 'Тульская область', 'Тюменская область', 'Ульяновская область', 'Челябинская область', 'Город Москва', 'Город Санкт-Петербург', 'Ãîðîä Ñàíêò-Ïåòåðáóðã', 'Еврейская автономная область', 'Ненецкий автономный округ', 'Чукотский автономный округ', 'Ямало-Ненецкий автономный округ', 'Город Байконур (Республика Казахстан)', 'Территория за пределами РФ'], dtype=object)
Видно, что в этом массиве встречаются какие-то крокозябры — названия со странной кодировкой. Давайте уберем эти строки из датафрейма, потому что данные, соответствующие этим названиям, состоят из пропущенных значений NaN
. Найдем в этих крокозябрах общий символ и воспользуемся методом str.contains()
для строк:
# - или ~ для отрицания
d = d[~d.region.str.contains("à")]
Сгруппируем данные по регионам и посчитаем для каждого региона явку в процентах и процент голосов за каждого кандидата. Группировка осуществляется с помощью метода .groupby()
.
d.groupby("region") # пока ничего не увидели
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x11965d710>
Что выдает метод .groupby()
? На самом деле он делает следующее: создает список, состоящий из кортежей. Каждый кортеж – это пара название группы-соответствующий ей фрагмент датафрейма.
# посмотрим на все сразу
for g in d.groupby("region"):
print(g)
В таком виде метод .groupby()
дает нам немного. Мы же хотим не просто получать отдельные таблицы, а агрегировать данные по регионам – суммировать все показатели (число избирателей, бюллетеней, голосов) по каждому региону. Тут на помощь придет метод .agg()
, который выполняет агрегирование по группам.
d.groupby("region").agg("sum")
total | invalid | valid | Zh | Zu | Mi | Pr | Pu | |
---|---|---|---|---|---|---|---|---|
region | ||||||||
Алтайский край | 1961328.0 | 12004.0 | 1163426.0 | 97961.0 | 261665.0 | 45883.0 | 83778.0 | 674139.0 |
Амурская область | 662320.0 | 4708.0 | 394996.0 | 39717.0 | 67433.0 | 13594.0 | 23070.0 | 251182.0 |
Архангельская область | 988678.0 | 5522.0 | 569492.0 | 51169.0 | 91648.0 | 33223.0 | 60108.0 | 333344.0 |
Астраханская область | 769608.0 | 5107.0 | 427496.0 | 21918.0 | 67662.0 | 18595.0 | 21873.0 | 297448.0 |
Белгородская область | 1210590.0 | 10209.0 | 889764.0 | 59561.0 | 211079.0 | 35601.0 | 49807.0 | 533716.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
Хабаровский край | 1056125.0 | 8733.0 | 645264.0 | 68500.0 | 115436.0 | 31944.0 | 62145.0 | 367239.0 |
Челябинская область | 2757879.0 | 25366.0 | 1704033.0 | 97869.0 | 254542.0 | 88177.0 | 138907.0 | 1124538.0 |
Чувашская Республика - Чувашия | 954572.0 | 10465.0 | 692492.0 | 39707.0 | 144676.0 | 31201.0 | 38838.0 | 438070.0 |
Чукотский автономный округ | 35968.0 | 428.0 | 28909.0 | 2106.0 | 2651.0 | 633.0 | 2209.0 | 21310.0 |
Ямало-Ненецкий автономный округ | 358834.0 | 2669.0 | 332293.0 | 17456.0 | 18738.0 | 4979.0 | 7807.0 | 283313.0 |
80 rows × 8 columns
Сначала в .groupby()
мы указали переменную, по которой нужно выполнить группировку, затем в .agg()
мы указали функцию, которую нужно применить ко всем столбцам в каждой группе. В нашем случае это 'sum'
, поскольку нам нужно просто просуммировать все значения по столбцам в пределах одного региона. Применять можно и другие функции, например, считать среднее:
d.groupby("region").agg("mean").head() # mean - среднее
total | invalid | valid | Zh | Zu | Mi | Pr | Pu | |
---|---|---|---|---|---|---|---|---|
region | ||||||||
Алтайский край | 1053.344791 | 6.446831 | 624.825994 | 52.610634 | 140.529001 | 24.641783 | 44.993555 | 362.051020 |
Амурская область | 845.874840 | 6.012771 | 504.464879 | 50.724138 | 86.121328 | 17.361430 | 29.463602 | 320.794381 |
Архангельская область | 1004.754065 | 5.611789 | 578.752033 | 52.001016 | 93.138211 | 33.763211 | 61.085366 | 338.764228 |
Астраханская область | 1313.324232 | 8.715017 | 729.515358 | 37.402730 | 115.464164 | 31.732082 | 37.325939 | 507.590444 |
Белгородская область | 968.472000 | 8.167200 | 711.811200 | 47.648800 | 168.863200 | 28.480800 | 39.845600 | 426.972800 |
Или сразу несколько статистик. которые можно указать в .agg()
в виде списка.
d.groupby("region").agg(["mean", "median"]).head() # среднее и медиана
total | invalid | valid | Zh | Zu | Mi | Pr | Pu | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
mean | median | mean | median | mean | median | mean | median | mean | median | mean | median | mean | median | mean | median | |
region | ||||||||||||||||
Алтайский край | 1053.344791 | 823.0 | 6.446831 | 4.0 | 624.825994 | 495.0 | 52.610634 | 41.0 | 140.529001 | 109.5 | 24.641783 | 15.0 | 44.993555 | 22.0 | 362.051020 | 305.5 |
Амурская область | 845.874840 | 523.0 | 6.012771 | 4.0 | 504.464879 | 326.0 | 50.724138 | 31.0 | 86.121328 | 52.0 | 17.361430 | 9.0 | 29.463602 | 12.0 | 320.794381 | 224.0 |
Архангельская область | 1004.754065 | 581.5 | 5.611789 | 2.0 | 578.752033 | 332.5 | 52.001016 | 29.0 | 93.138211 | 44.0 | 33.763211 | 19.0 | 61.085366 | 20.5 | 338.764228 | 230.5 |
Астраханская область | 1313.324232 | 1283.5 | 8.715017 | 6.0 | 729.515358 | 692.5 | 37.402730 | 31.0 | 115.464164 | 100.5 | 31.732082 | 22.0 | 37.325939 | 22.0 | 507.590444 | 480.0 |
Белгородская область | 968.472000 | 802.0 | 8.167200 | 6.0 | 711.811200 | 633.0 | 47.648800 | 41.0 | 168.863200 | 140.5 | 28.480800 | 21.0 | 39.845600 | 22.0 | 426.972800 | 397.0 |
Кроме того, внутри .agg()
можно указывать свои функции. Например, нас интересует разница между максимальным и минимальным значением. Сначала напишем функцию my_diff
, которая будет определять такую разность:
def my_diff(x):
return max(x) - min(x)
Проверим, как она работает:
my_diff([4, 6, 8]) # все верно, 8 - 4 = 4
4
Теперь используем эту функцию внутри .agg()
:
d.groupby("region").agg(my_diff).head() # везде смотрим на первые 5 строк
total | invalid | valid | Zh | Zu | Mi | Pr | Pu | |
---|---|---|---|---|---|---|---|---|
region | ||||||||
Алтайский край | 3030.0 | 72.0 | 2389.0 | 379.0 | 573.0 | 131.0 | 351.0 | 1639.0 |
Амурская область | 2942.0 | 130.0 | 1773.0 | 267.0 | 404.0 | 92.0 | 197.0 | 1201.0 |
Архангельская область | 2953.0 | 76.0 | 1951.0 | 232.0 | 407.0 | 153.0 | 369.0 | 1205.0 |
Астраханская область | 2936.0 | 223.0 | 1862.0 | 209.0 | 411.0 | 157.0 | 234.0 | 1367.0 |
Белгородская область | 2998.0 | 71.0 | 2118.0 | 234.0 | 612.0 | 108.0 | 335.0 | 1268.0 |
Всё, что мы пока сделали, очень интересно, но есть проблема: все данные пока даны в абсолютных значениях, не в процентах. Это неудобно. Давайте сгруппируем данные по региону и добавим в базу с агрегированными данными новые столбцы: явка в процентах и проценты голосов за каждого кандидата.
Для этого необходимо вспомнить, как считается явка и проценты голосов. Явка считается так: суммируем число действительных и недействительных бюллетеней. Чтобы получить явку в процентах, делим явку на общее число зарегистрированных избирателей и домножаем на $100$, чтобы перевести долю в проценты. Проценты голосов за кандидатов считаем от явки, берем число голосов за кандидата, делим на явку и домножаем на $100$. Проделаем это поэтапно.
Сначала сохраним результат агрегирования в переменную regs
и добавим новый столбец для явки в абсолютных значениях (в голосах).
regs = d.groupby("region").agg("sum")
# новый столбец - сумма двух старых
regs["turnout"] = regs["invalid"] + regs["valid"]
regs.head(3)
total | invalid | valid | Zh | Zu | Mi | Pr | Pu | turnout | |
---|---|---|---|---|---|---|---|---|---|
region | |||||||||
Алтайский край | 1961328.0 | 12004.0 | 1163426.0 | 97961.0 | 261665.0 | 45883.0 | 83778.0 | 674139.0 | 1175430.0 |
Амурская область | 662320.0 | 4708.0 | 394996.0 | 39717.0 | 67433.0 | 13594.0 | 23070.0 | 251182.0 | 399704.0 |
Архангельская область | 988678.0 | 5522.0 | 569492.0 | 51169.0 | 91648.0 | 33223.0 | 60108.0 | 333344.0 | 575014.0 |
Теперь добавим столбец с явкой в процентах:
regs["turnout_perc"] = regs["turnout"] / regs["total"] * 100
regs.head(3)
total | invalid | valid | Zh | Zu | Mi | Pr | Pu | turnout | turnout_perc | |
---|---|---|---|---|---|---|---|---|---|---|
region | ||||||||||
Алтайский край | 1961328.0 | 12004.0 | 1163426.0 | 97961.0 | 261665.0 | 45883.0 | 83778.0 | 674139.0 | 1175430.0 | 59.930313 |
Амурская область | 662320.0 | 4708.0 | 394996.0 | 39717.0 | 67433.0 | 13594.0 | 23070.0 | 251182.0 | 399704.0 | 60.349076 |
Архангельская область | 988678.0 | 5522.0 | 569492.0 | 51169.0 | 91648.0 | 33223.0 | 60108.0 | 333344.0 | 575014.0 | 58.159886 |
Осталось проделать аналогичные операции для голосов за разных кандидатов. Но повторять одно и то же пять раз не хочется (а что бы мы делали, если бы кандидатов было больше?). Давайте напишем функцию, которая будет принимать на вход столбец, делить все его значения на значения из столбца turnout и переводить все в проценты.
def to_perc(x):
return x / regs["turnout"] * 100
А теперь выберем из базы данных столбцы с голосами за кандидатов и применим к ним нашу функцию – воспользуемся методом .apply()
.
# axis = 0 - по столбцам, не по строкам
perc = regs[["Zh", "Zu", "Mi",
"Pr", "Pu"]].apply(to_perc, axis = 0)
perc.head(3)
Zh | Zu | Mi | Pr | Pu | |
---|---|---|---|---|---|
region | |||||
Алтайский край | 8.334056 | 22.261215 | 3.903508 | 7.127434 | 57.352543 |
Амурская область | 9.936603 | 16.870734 | 3.401017 | 5.771771 | 62.842003 |
Архангельская область | 8.898740 | 15.938395 | 5.777772 | 10.453311 | 57.971458 |
Нужно переименовать столбцы в базе perc
. Давайте сделаем это по-умному: возьмем названия столбцов в perc
и приклеим к ним часть с _perc
, чтобы названия столбцов с показателями в процентах отличались от показателей в абсолютных числах.
# вместо цикла используем списковое включение с for
# доклеиваем _perc ко всем названиям столбцов
# заменяем значения .columns на новые
perc.columns = [c + "_perc" for c in perc.columns]
perc.head()
Zh_perc | Zu_perc | Mi_perc | Pr_perc | Pu_perc | |
---|---|---|---|---|---|
region | |||||
Алтайский край | 8.334056 | 22.261215 | 3.903508 | 7.127434 | 57.352543 |
Амурская область | 9.936603 | 16.870734 | 3.401017 | 5.771771 | 62.842003 |
Архангельская область | 8.898740 | 15.938395 | 5.777772 | 10.453311 | 57.971458 |
Астраханская область | 5.066539 | 15.640668 | 4.298398 | 5.056137 | 68.757729 |
Белгородская область | 6.618087 | 23.453926 | 3.955785 | 5.534277 | 59.303557 |
Ура! Последний аккорд: соединим нашу таблицу regs
с таблицей perc
, чтобы все показатели были в одном месте. Просто склеим две таблицы по столбцам с помощью метода .concat()
.
# axis = 1 - склеивание по столбцам (доклеиваем новую таблицу справа)
# axis = 0 - склеивание по строкам (доклеиваем новую таблицу снизу)
final = pd.concat([regs, perc], axis = 1)
final.head()
total | invalid | valid | Zh | Zu | Mi | Pr | Pu | turnout | turnout_perc | Zh_perc | Zu_perc | Mi_perc | Pr_perc | Pu_perc | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
region | |||||||||||||||
Алтайский край | 1961328.0 | 12004.0 | 1163426.0 | 97961.0 | 261665.0 | 45883.0 | 83778.0 | 674139.0 | 1175430.0 | 59.930313 | 8.334056 | 22.261215 | 3.903508 | 7.127434 | 57.352543 |
Амурская область | 662320.0 | 4708.0 | 394996.0 | 39717.0 | 67433.0 | 13594.0 | 23070.0 | 251182.0 | 399704.0 | 60.349076 | 9.936603 | 16.870734 | 3.401017 | 5.771771 | 62.842003 |
Архангельская область | 988678.0 | 5522.0 | 569492.0 | 51169.0 | 91648.0 | 33223.0 | 60108.0 | 333344.0 | 575014.0 | 58.159886 | 8.898740 | 15.938395 | 5.777772 | 10.453311 | 57.971458 |
Астраханская область | 769608.0 | 5107.0 | 427496.0 | 21918.0 | 67662.0 | 18595.0 | 21873.0 | 297448.0 | 432603.0 | 56.210824 | 5.066539 | 15.640668 | 4.298398 | 5.056137 | 68.757729 |
Белгородская область | 1210590.0 | 10209.0 | 889764.0 | 59561.0 | 211079.0 | 35601.0 | 49807.0 | 533716.0 | 899973.0 | 74.341685 | 6.618087 | 23.453926 | 3.955785 | 5.534277 | 59.303557 |
Приличную базу мы получили, можно перейти к чему-то более содержательному.
В прошлый раз мы познакомились с тем, как строить графики для переменных в датафрейме. Давайте посмотрим на диаграммы рассеяния – графики, которые позволяют увидеть совместное распределение пары количественных показателей.
import matplotlib # загружаем библиотеку для графиков
%matplotlib inline
А теперь сама диаграмма рассеяния (scatterplot) для показателей явка в процентах и процент за Зюганова:
final.plot.scatter("turnout_perc", "Zu_perc")
<matplotlib.axes._subplots.AxesSubplot at 0x11d613a50>
Можем привести график в порядок. Добавить заголовок и подписи к осям, плюс, изменить цвет точек. Для этого основной график сохраним в переменную ax
, а затем применим к ней методы, которые отвечают за добавление заголовка и названиям осей x и y.
# цвет red
ax = final.plot.scatter("turnout_perc", "Zu_perc", color = "red")
# заголовок для объекта ax
ax.set_title("Диаграмма рассеивания")
# подпись для оси x
ax.set_xlabel("Явка (в %)")
# подпись для оси y
ax.set_ylabel("Число голосов за Зюганова (в %)")
Text(0, 0.5, 'Число голосов за Зюганова (в %)')
По графику видно, что, в целом, чем выше явка, тем ниже процент голосов за Зюганова. Углубляться в разные настройки графиков и в статистику не будем, но познакомимся с примером графика средствами библиотеки pandas
. Построим матрицу диаграмм рассеяния (scatterplot matrix), сетку с диаграммами рассеяния для всех пар показателей.
Логично будет строить такой график для переменных в базе perc
.
# импортируем функцию
from pandas.plotting import scatter_matrix
# строим график
scatter_matrix(perc, diagonal = "hist", figsize = (10, 10))
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x118ed5fd0>, <matplotlib.axes._subplots.AxesSubplot object at 0x11defba10>, <matplotlib.axes._subplots.AxesSubplot object at 0x11df38d90>, <matplotlib.axes._subplots.AxesSubplot object at 0x11df6fa50>, <matplotlib.axes._subplots.AxesSubplot object at 0x11dfabdd0>], [<matplotlib.axes._subplots.AxesSubplot object at 0x11dfe3a90>, <matplotlib.axes._subplots.AxesSubplot object at 0x11e021d50>, <matplotlib.axes._subplots.AxesSubplot object at 0x11e055910>, <matplotlib.axes._subplots.AxesSubplot object at 0x11e05f490>, <matplotlib.axes._subplots.AxesSubplot object at 0x11e093e10>], [<matplotlib.axes._subplots.AxesSubplot object at 0x11e109cd0>, <matplotlib.axes._subplots.AxesSubplot object at 0x11e13f990>, <matplotlib.axes._subplots.AxesSubplot object at 0x11e17dd10>, <matplotlib.axes._subplots.AxesSubplot object at 0x11e3769d0>, <matplotlib.axes._subplots.AxesSubplot object at 0x11e6b4d50>], [<matplotlib.axes._subplots.AxesSubplot object at 0x11e6eba10>, <matplotlib.axes._subplots.AxesSubplot object at 0x11e72bd90>, <matplotlib.axes._subplots.AxesSubplot object at 0x11e762a50>, <matplotlib.axes._subplots.AxesSubplot object at 0x11e79fdd0>, <matplotlib.axes._subplots.AxesSubplot object at 0x11e7d6a90>], [<matplotlib.axes._subplots.AxesSubplot object at 0x11e814e10>, <matplotlib.axes._subplots.AxesSubplot object at 0x11e84bad0>, <matplotlib.axes._subplots.AxesSubplot object at 0x11e88ae50>, <matplotlib.axes._subplots.AxesSubplot object at 0x11e8c1b10>, <matplotlib.axes._subplots.AxesSubplot object at 0x11e8ffe90>]], dtype=object)
Аргумент diagonal
отвечает за тип графика, который будет находиться на диагонали (в нашем случае гистограмма – "hist"
), а аргумент figsize
– за размер графика (по горизонтали и по вертикали). На диагоналях также можно построить сглаженные графики плотности распределения показателей:
# kde - от kernel density estimation –
# график плотности – как сглаженный вариант гистограммы
scatter_matrix(perc, diagonal = "kde",
figsize = (10, 10))
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x11dec4710>, <matplotlib.axes._subplots.AxesSubplot object at 0x11ff023d0>, <matplotlib.axes._subplots.AxesSubplot object at 0x11ff837d0>, <matplotlib.axes._subplots.AxesSubplot object at 0x120116fd0>, <matplotlib.axes._subplots.AxesSubplot object at 0x120157810>], [<matplotlib.axes._subplots.AxesSubplot object at 0x12018bf90>, <matplotlib.axes._subplots.AxesSubplot object at 0x1201cc850>, <matplotlib.axes._subplots.AxesSubplot object at 0x12020b090>, <matplotlib.axes._subplots.AxesSubplot object at 0x12020bbd0>, <matplotlib.axes._subplots.AxesSubplot object at 0x12024c590>], [<matplotlib.axes._subplots.AxesSubplot object at 0x1202b98d0>, <matplotlib.axes._subplots.AxesSubplot object at 0x1202f6c50>, <matplotlib.axes._subplots.AxesSubplot object at 0x12032e910>, <matplotlib.axes._subplots.AxesSubplot object at 0x12036cc90>, <matplotlib.axes._subplots.AxesSubplot object at 0x1203a4950>], [<matplotlib.axes._subplots.AxesSubplot object at 0x1203e1cd0>, <matplotlib.axes._subplots.AxesSubplot object at 0x120417990>, <matplotlib.axes._subplots.AxesSubplot object at 0x120455d10>, <matplotlib.axes._subplots.AxesSubplot object at 0x12048b9d0>, <matplotlib.axes._subplots.AxesSubplot object at 0x1204cbd50>], [<matplotlib.axes._subplots.AxesSubplot object at 0x120503a10>, <matplotlib.axes._subplots.AxesSubplot object at 0x120540d90>, <matplotlib.axes._subplots.AxesSubplot object at 0x120576750>, <matplotlib.axes._subplots.AxesSubplot object at 0x1205a7f50>, <matplotlib.axes._subplots.AxesSubplot object at 0x1205e7790>]], dtype=object)
Поиграем с цветами. Изменим цвет точек на всех диаграммах рассеяния:
scatter_matrix(perc, diagonal = "hist",
figsize = (10, 10),
color = "green")
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x11ff0b9d0>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a22431c90>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a2247fa50>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a2265fdd0>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a22695a90>], [<matplotlib.axes._subplots.AxesSubplot object at 0x1a226d2e10>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a22708ad0>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a2274a310>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a2274ae50>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a22789810>], [<matplotlib.axes._subplots.AxesSubplot object at 0x1a227f5b50>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a22833ed0>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a22868b90>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a228a8f10>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a228dfbd0>], [<matplotlib.axes._subplots.AxesSubplot object at 0x1a2291ef50>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a22955c10>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a22990d90>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a229c3a50>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a22a03dd0>], [<matplotlib.axes._subplots.AxesSubplot object at 0x1a22a38a90>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a22a78e10>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a22aadad0>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a22aeee50>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a22b23b10>]], dtype=object)
А теперь цвет гистограмм. Это сделать чуть сложнее, поскольку нужно задавать значения аргуметов как *kwargs
, в виде словаря:
# заодно выставим число столбцов bins = 10
# и прозрачность 40% alpha = 0.4
# а также цвет границ столбцов edgecolor
scatter_matrix(perc, diagonal = "hist", figsize = (10, 10), color = "green",
hist_kwds = {"color": "red", "edgecolor" : "black",
"bins" : 10, "alpha" : 0.4})
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x1a22da3550>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a23786090>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a237b8c10>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a23959f90>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a2398ec50>], [<matplotlib.axes._subplots.AxesSubplot object at 0x1a239cffd0>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a23a02c90>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a23a424d0>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a23a4f050>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a23a819d0>], [<matplotlib.axes._subplots.AxesSubplot object at 0x1a23aedd10>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a23b2e550>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a23b63d50>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a23ba5590>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a23bd8d90>], [<matplotlib.axes._subplots.AxesSubplot object at 0x1a23c1a5d0>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a23c4cdd0>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a23c8e610>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a23cc2e10>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a23d04650>], [<matplotlib.axes._subplots.AxesSubplot object at 0x1a23d37e50>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a23d79690>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a23daee90>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a23dee6d0>, <matplotlib.axes._subplots.AxesSubplot object at 0x1a23e23ed0>]], dtype=object)
На этом пока всё.