Обрабатываем данные и строим графики с pandas и python 3

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

Я покажу вам как работать с большим набором данных с помощью функций groupby() и pivot_table() из pandas, а в итоге мы еще и визуализируем результат.

Если вы еще не знакомы с это крутой библиотекой, то добро пожаловать)

В данном руководстве я покажу вам как работать с pandas как на локальном компьютере, так и на удаленном сервере. Работа с большими данными требует много памяти, а именно в данном случае нужно примерно 2 Гб оперативки.

Ну а работать мы будем в Jupyter Notebook. Если у вас его нет, то вы должны прочитать учебник, чтобы установить и настроить Jupyter Notebook для Python 3.

Получаем данные

В этом уроке мы будем работать с данными  детских имен в США, которые доступны на веб-сайте Social Security в виде файла формата zip  8MB.

Для начала нужно активировать нашу виртуальную среду на локальном компьютере, ну или на сервере (в зависимости от того что у вас):

sammy@ubuntu:~$ cd environments

sammy@ubuntu:~/environments$ . my_env/bin/activate

Теперь давайте создадим новый каталог для нашего проекта. Мы можем назвать его names, а затем переместиться в сам каталог:

(my_env)sammy@ubuntu:~$ mkdir names

(my_env)sammy@ubuntu:~$ cd names

В этот каталог мы можем скачать zip-файл с веб-сайта Social Security с помощью команды curl:

(my_env)sammy@ubuntu:~$ curl -O https://www.ssa.gov/oact/babynames/names.zip

Когда убедитесь что данные с сайта загрузились – проверьте все ли библиотеки, которыми мы  будем пользоваться в дальнейшем, у вас установлены:

– numpy – нужен для работы с многомерными массивами

– matplotlib – необходим для визуализации данных

– pandas – собственно для анализа данных

– seaborn – сделает нашу статистическую графику matplotlib более эстетичной

Если у вас нет каких-либо пакетов, то установите из при помощи pip:

(my_env)sammy@ubuntu:~$ pip install pandas

(my_env)sammy@ubuntu:~$ pip install matplotlib

(my_env)sammy@ubuntu:~$ pip install seaborn

Если выполнить эти команды numpy тоже установится.

Теперь мы можем запустить Jupyter Notebook:

(my_env)sammy@ubuntu:~$ jupyter notebook

Когда вы начнете работать в веб-интерфейсе Jupyter Notebook, вы увидите там файл names.zip.

Чтобы создать новый файл, выберите New > Python 3  в выпадающем меню в правом верхнем углу:

Create a new Python 3 notebook

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

import numpy as np
import matplotlib.pyplot as pp
import pandas as pd
import seaborn

Мы можем запустить этот код и перейти в новый блок кода,  просто набрав ALT + ENTER.

Также нужно сказать Ipython Notebook, что мы хотим, чтобы наши графики были встроенными:

matplotlib inline

Давайте запустим код и продолжим дальше просто нажав ALT + ENTER.

Затем мы перейдем к распаковке zip-архива, загрузке CSV-набора данных в pandas, а затем к конкатенации DataFrames.

Распаковываем архив zip

Чтобы распаковать zip-архив в текущий каталог, мы сначала должны импортировать модуль zipfile и затем вызвать функцию ZipFile с именем файла (в нашем случае имя файла –  names.zip):

import zipfile
zipfile.ZipFile(‘names.zip‘).extractall(‘.’)

Запустите код при помощи ALT + ENTER и продолжим дальше.

Теперь посмотрите в свою директорию с names.zip и там  у вас будут .txt файлы в формате CSV. Они то нам и нужны. Эти файлы будут соответствовать годам с 1881 по 2015 год. Каждый из этих файлов соответствует аналогичному соглашению об именах. Например файл с именами 2015 года называется  yob2015.txt, а файл 1927 года – yob1927.txt.

Давайте  посмотрим на  формат одного из этих файлов. Ну а сделаем это конечно же при помощи Python(откроем и отобразим 5 верхних строк):

open(‘yob2015.txt’,’r’).readlines()[:5]

Теперь запускайте код и смотрите что получилось.

Output
[‘Emma,F,20355\n’,
‘Olivia,F,19553\n’,
‘Sophia,F,17327\n’,
‘Ava,F,16286\n’,
‘Isabella,F,15504\n’]

Можно увидеть, что данные в таком формате: первым  идет имя(как в Emma или Olivia), затем пол( F для женского имени и M для мужского имени), а затем число младенцев, родившихся в этом году с таким именем (было 20,355 Дети по имени Эмма, родившиеся в 2015 году).

Ну а теперь пришло время загружать данные в pandas.

Загружаем CSV данные в pandas

Чтобы загрузить данные, где значения разделены запятыми(формат CSV),  в pandas, мы будем использовать функцию pd.read_csv (), в которую передадим имя текстового файла, а также имена столбцов, которые мы выбираем. Все это мы присвоим переменной names2015, поскольку мы используем данные из файла 2015 года рождения.

names2015 = pd.read_csv(‘yob2015.txt’, names = [‘Name’, ‘Sex’, ‘Babies’])

Запускайте код и мы будем продолжать.

Чтобы убедиться, что у нас все получилось выведите верхнюю часть таблицы:

names2015.head()

Если вы все правильно сделали, то когда вы запустите код вы должны увидеть следующее:

names2015-head-w

Теперь наша таблица содержит информацию об именах, поле ребенка и числе младенцев, рожденных с этим именем.

Конкатенация объектов pandas

Объединение объектов pandas позволит нам работать со всеми отдельными текстовыми файлами в одном каталоге имен.

Чтобы объединить наши отдельные файлы нам нужно сначала объявить переменную, которая будет нашим списком:

all_years = []

Как  только вы  объявили список  можно переходить следующему шагу. Нам нужен цикл, который будет проходить по нашим файлам, а у нас их немало(с 1880 года по 2015 год).  Мы добавим 1 к 2015 году, чтобы включить его в перебор цикла.

all_years = []
for year in range(1880, 2015+1):

Так как у нас много файлов, а соответственно и имен, то перебирать их в цикле будем при помощи метода format.

А передавить значения в метод будем при помощи переменной year. Ну и конечно не забудьте указать столбцы для Name, Sex и числа Babies:

all_years = []
for year in range(1880, 2015+1):
      all_years.append(pd.read_csv(‘yob{}.txt’.format(year),
               names = [‘Name’, ‘Sex’,’Babies’]))

Ну и плюс ко всему мы создадим еще один столбец в конце нашей таблицы, где мы будем хранить года:

all_years = []
for year in range(1880, 2015+1):
      all_years.append(pd.read_csv(‘yob{}.txt’.format(year),
               names = [‘Name’, ‘Sex’,’Babies’]))
all_years[-1][‘Year’] = year

В итоге нужно все это добавить в объект pandas и сделаем мы это при помощи функции pd.concat (). Ну а для хранения всей этой информации мы будем использовать переменную all_names.

all_years = []
for year in range(1880, 2015+1):
all_years.append(pd.read_csv(‘yob{}.txt’.format(year),
names = [‘Name’, ‘Sex’,’Babies’]))
all_years[-1][‘Year’] = year
all_names = pd.concat(all_years)

 

Ну а теперь исполняйте код и выводите последние строки при помощи метода tail():

all_names.tail()

all_names.tail outputl

Наш набор данных собран в одном месте и готов для дополнительной работы с ним.

Группировка данных

С pandas вы можете группировать данные по столбцам при помощи функции .groupby(). Используем переменную all_names с полным набором наших данных и функцию groupby () для их разделения на разные сегменты.

Давайте сгруппируем набор данных по полу и году. Мы можем сделать это следующим образом:

group_name = all_names.groupby([‘Sex’, ‘Year’])

Запускайте код ALT + ENTER и продолжим.

На данный момент, если вы попытаетесь вывести  результат переменной group_name, то получите такой результат:

<pandas.core.groupby.DataFrameGroupBy object at 0x000000000B8BBE80>

Это говорит нам о том, что это объект DataFrameGroupBy. Этот объект содержит инструкции о том как надо группировать данные, но не о том как надо их отображать.Для отображения значений нам нужно будет дать инструкции. Мы можем вычислить .size(), .<strong>mean()</strong>, и .sum().

Давайте начнем с .size():

group_name.size()

Когда мы запустим код  с ALT + ENTER, наш вывод будет выглядеть так:

Sex Year
F 1880 942
1881 938
1882 1028
1883 1054
1884 1172

Все конечно круто, но эти данные не читабельны. Мы можем сделать так, чтобы это выглядело намного лучше просто добавив функцию .unstack:

group_name.size().unstack()

Теперь все выглядит намного лучше:

group_name.size().unstack() output

Эти данные показываю нам, сколько женских и мужских имен было в каждый год. В 1889 году, например, было 1479 женских имен и 1111 мужских имен. В 2015 году было 18 993 женских и 13 959 мужских имен. Это говорит  нам о том, что с течением времени наблюдается большее разнообразие имен.

Если мы хотим получить общее число рожденных детей, мы можем использовать функцию .sum (). Давайте поэкспериментируем с набором данных names2015, который мы создали ранее:

names2015.groupby([‘Sex’]).sum()

Результат:

names2015.groupby(['Sex']).sum() output

Это покажет нам общее число младенцев мужского и женского пола, родившихся в 2015 году, хотя в наборе данных учитываются только дети, чье имя использовалось не менее 5 раз в этом году.

Сводные таблицы

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

В pandas для создания сводных таблиц используется функция pivot_table () .

В этом примере мы будем работать с данными, которые находятся в переменной all_names и показывать данные Babies, где name – это столбцы, а year – строки:

pd.pivot_table(all_names, ‘Babies’, ‘Name’, ‘Year’)

Мы увидим следующий результат:

pd.pivot_table(all_names, 'Babies', 'Name', 'Year') output

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

Визуализируем данные

Используя pandas в связке с такими библиотеками как matplotlib мы можем визуализировать наши данные.

Мы будем визуализировать популярность одного имени на протяжении многих лет.

Чтобы это сделать, нам нужно установить и отсортировать индексы,  которые позволят нам увидеть изменение популярности определенного имени.

Пакет pandas позволяет нам осуществлять иерархическое или многоуровневое индексирование, которое позволяет нам хранить и манипулировать данными с произвольным числом измерений.

Мы должны проиндексировать наши данные информацией о поле, имени и  годе. Мы также должны отсортировать индекс:

all_names_index = all_names.set_index([‘Sex‘,’Name‘,’Year‘]).sort_index()

Запускайте и переходе к нашей следующей строке, где у нас будет отображаться новый индексированный DataFrame:

all_names_index

Результат должен выглядеть примерно так:

all_names_index output

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

def name_plot(sex, name):

Теперь мы создадим переменную data, для хранения таблицы, которую мы создали. Нам понадобится loc чтобы выбрать нашу строку по значению индекса.

Запишем эту конструкцию в нашу функцию:

def name_plot(sex, name):
data = all_names_index.loc[sex, name]

В итоге(я же обещал графики) мы построим их с помощью matplotlib.pyplot, который мы импортировали как pp.

def name_plot(sex, name):
data = all_names_index.loc[sex, name]
pp.plot(data.index, data.values)

Теперь можете испробовать вашу функцию. Просто вызовите ее с аргументами F (женский пол) и именем Danica:

name_plot(‘F’, ‘Danica’)

Когда вы исполните код, вы получите это:

Danica Name Plot output

Глядя на визуализацию  вы можете видеть, что женское имя Danica немного увеличилось в популярности примерно в 1990 году и достигло максимума непосредственно перед 2010 годом.

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

pp.figure(figsize = (18, 8))

Ну а дальше создаем список с именами, которые мы будем строить:

pp.figure(figsize = (18, 8))
names = [‘Sammy‘, ‘Jesse‘, ‘Drew‘, ‘Jamie‘]

Теперь мы можем выполнить итерацию по нашему списку  names с помощью цикла for и строить график для каждого имени:

pp.figure(figsize = (18, 8))
names = [‘Sammy‘, ‘Jesse‘, ‘Drew‘, ‘Jamie‘]
for name in names:
name_plot(‘F’, name)

Чтобы лучше понимать что к чему,  давайте добавим легенду:

pp.figure(figsize = (18, 8))
names = [‘Sammy‘, ‘Jesse‘, ‘Drew‘, ‘Jamie‘]
for name in names:
name_plot(‘F‘, name)
pp.legend(names)

Ну а теперь исполняйте код и  смотрите на результат:

Name plot, female names output

По графику в принципе видно, не все имена были одинаково популярны. Только одно имя – Jamie в 1980 году взлетело на пик популярности.

Можно еще нарисовать графики, где можно посмотреть популярность мужских имен:

pp.figure(figsize = (18, 8))
names = [‘Sammy‘, ‘Jesse‘, ‘Drew‘, ‘Jamie‘]
for name in names:
name_plot(‘M‘, name)
pp.legend(names)

График должен получиться таким:

Name plot, male names output

Что касается мужских имен, то все которые мы выбрали очень популярны среди населения, но Джесси самое популярное имя. Особенно в 1980-х и 1990-х годах.