Следующая тема: ИАД. Изучение срезов данных

Вернуться в раздел: Исследовательский анализ данных

Вернуться в оглавление: Я.Практикум

1. Введение

2. Знакомство с задачей

3. Сводные таблицы для расчета среднего

4. Применяем сводные таблицы

5. Есть ли проблема в данных?

6. Базовая проверка данных

7. Гистограмма

8. Гистограмма для двух кубиков

9. Распределения

10. Диаграмма размаха

11. Диаграмма размаха в Python

12. Описание данных

13. Заключение

14. Проверочные задания. Первые графики и выводы

Знакомство с задачей

Как исследовать поведение водителей на заправках, если вы — Яндекс? Обратиться к статистике Яндекс.Навигатора: узнать, на какую именно заправку заезжал водитель и сколько времени там провёл.

Ваши коллеги из Навигатора собрали необходимые данные и прислали их в таком виде:

  • Зашифрованное наименование сети АЗС (столбец name): вместо брендов — названия растений.
  • Уникальный идентификатор конкретной АЗС (столбец id) — в сети их много.
  • Время заезда на АЗС (столбец date_time) в формате ISO: 20190405T165358 означает, что водитель прибыл на заправку 5 апреля 2019 года в 16 часов 53 минуты 58 секунд по UTC.
  • Проведённое на АЗС время (столбец time_spent) в секундах.

Нужно ответить на вопрос, сколько в среднем времени тратят водители на заправку в каждой из сетей АЗС. image Выведем первые строки таблицы методом head():

import pandas as pd

data = pd.read_csv('/datasets/visits.csv')

print(data.head())

date_time\tid\ttime_spent\tname

0 20190405T165358\t76144fb2\t98.0\tВасилёк

1 20190403T173913\t76144fb2\t15.0\tВасилёк

2 20190402T172824\t76144fb2\t220.0\tВасилёк

3 20190406T070441\t76144fb2\t19.0\tВасилёк

4 20190403T132049\t76144fb2\t14.0\tВасилёк

Вот и первая проблема. Данные склеились в одну строку вместо того, чтобы разбиться по колонкам. Это произошло из-за разделителей в формате csv. Напомним, что csv — это Comma-Separated Values, или значения, разделённые запятыми. Действительно, в прошлых уроках вы работали с таблицами, где строку на колонки делили запятые. Однако вместо них могут быть точки с запятой, знаки табуляции или другие символы. Могут вносить путаницу и десятичные дроби, записанные с запятой. Какими символами разделять колонки и дроби, указывают в аргументах функции read_csv(). За разделитель колонок отвечает параметр sep (от англ. separate — «отделять, разделять»), а дробей — параметр decimal (пер. «десятичная дробь»):

 file = pd.read_csv('file.csv', sep=';', decimal=',') 

6 ноября 1974 года пуэрториканская обсерватория Аресибо отправила в космос радиосигнал, вошедший в историю под названием «Послание Аресибо».

Это адресованная инопланетянам информация, записанная двоичным кодом: числа от 1 до 10; химические элементы; строение и форма ДНК; количество людей на Земле, их средний рост и эскиз устройства человека; описание Солнечной системы; сведения об обсерватории Аресибо. Фрагмент послания выглядит так: image1 Само послание — последовательность нулей и единиц. Для расшифровки нужно разбить её на 73 строки по 23 символа. Вызовем функцию read_csv() — обратите внимание, что ей можно «скормить» файл в формате txt — и прочитаем послание Аресибо:

arecibo = pd.read_csv('arecibo.txt')
print(arecibo.head())

         date_time        id  time_spent     name
0  20180406T165358  76144fb2        98.0  Василёк
1  20180404T173913  76144fb2        15.0  Василёк
2  20180403T172824  76144fb2       220.0  Василёк
3  20180407T070441  76144fb2        19.0  Василёк
4  20180404T132049  76144fb2        14.0  Василёк

Когда что-то не клеится — это обычно нехорошо. Когда данные не склеиваются в одну строку — это, наоборот, здорово.

Сводные таблицы для расчёта среднего

Нужно узнать, сколько времени в среднем водители тратят на заправку в каждой сети АЗС. В каждой строке датафрейма есть название сети и время, проведённое на АЗС. Занимаясь предобработкой данных, вы применяли pivot_table() — метод для построения сводных таблиц.

Напомним его параметры:

  • index — столбец, значения которого становятся названиями строк (индексом);
  • columns — столбец, значения которого становятся названиями столбцов;
  • values — значения, по которым вы хотите увидеть сводную таблицу;
  • aggfunc — функция, применяемая к значениям.

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

Чтобы начать работать с данными о заправках, надо сначала научиться строить сводные таблицы. Для этого у вас будет новый набор данных — с информацией о матчах Национальной хоккейной лиги.

Нужно проанализировать среднюю посещаемость домашних матчей команд Национальной хоккейной лиги, начиная с сезона 2000–2001 гг. и заканчивая сезоном 2017–2018 гг. Посмотрим на первые пять строк таблицы, где для каждой команды указано количество домашних матчей и их суммарная посещаемость home_att:

nhl_att = pd.read_excel('nhl_attendance.xlsx')
print(nhl_att.head()) 

season    team    home_games    home_att
0    2017-2018    Chicago    41    887794
1    2017-2018    Montreal    41    873283
2    2017-2018    Philadelphia    41    800214
3    2017-2018    Detroit    41    800115
4    2017-2018    Toronto    41    786677

seasonteamhome_gameshome_attavg_home_att
0 2017-2018 Chicago 41 887794 21653.512195
1 2017-2018 Montreal 41 873283 21299.585366
2 2017-2018 Philadelphia 41 800214 19517.414634
3 2017-2018 Detroit 41 800115 19515.000000
4 2017-2018 Toronto 41 786677
 
Посчитаем среднюю сезонную посещаемость матчей. Эта метрика поможет проанализировать, насколько успешен был тот или иной сезон:
nhl_pivot = nhl_att.pivot_table(index='season', values='avg_home_att') 
# группируем таблицу по сезонам, для каждого находим среднюю посещаемость  
print(nhl_pivot) 
 
season    avg_home_att
2000-01    16559.268467
2001-02    16758.941463
2002-03    16590.563415
2003-04    16549.755285
2005-06    16954.608943
2006-07    16960.798374
2007-08    17307.805691
2008-09    17476.049593
2009-10    17072.668333
2010-11    17122.738211
2011-12    17455.410569
2012-13    17720.623611
2013-14    17587.389431
2014-15    17502.595122
2015-16    17576.266667
2016-17    17500.631707
2017-18    17446.312352
 

             time_spent
name
Агератум     337.802721
Амарант      132.760012
Аммобиум     256.708042
Арктотис      73.879984
Астильба     376.143149
Бальзамин    134.508411
Бархатцы     145.300328
Бегония      163.200647
Белоцветник  100.818966

Есть ли проблемы в данных?

Таблицу, которую получили в прошлом уроке, нельзя показывать менеджерам: они всё равно не поверят!

У сетей АЗС «Нарцисс», «Арктотис» и «Малопа» среднее время заправки около 70 секунд. Неправдоподобно мало.

Как вы думаете, всё ли в порядке с данными?

Всё правильно посчитано, в результатах не может быть ошибки.

Правильный ответ Всё может быть. Нужно проверить и данные, и расчёты.

Надо искать ошибку в расчётах.

Наверное, нам прислали неправильные данные.

Доверяй, но проверяй. В следующем уроке начнём проверять данные.

import pandas as pd

data = pd.read_csv ('/datasets/visits.csv', sep='\t')
#количество заездов на АЗС 
total_visits = data['id'].count()
print('Количество заездов:', total_visits)
import pandas as pd

data = pd.read_csv ('/datasets/visits.csv', sep='\t')
#количество заездов на АЗС 
total_visits = data['id'].count()
#print('Количество заездов:', total_visits)
total_stations = len(data['id'].unique())
print('Количество АЗС:', total_stations)
import pandas as pd

data = pd.read_csv ('/datasets/visits.csv', sep='\t')
#количество заездов на АЗС 
total_visits = data['id'].count()
print('Количество заездов:', total_visits)
total_stations = len(data['id'].unique())
print('Количество АЗС:', total_stations)
print(data['date_time'].min(), data['date_time'].max())
import pandas as pd

data = pd.read_csv ('/datasets/visits.csv', sep='\t')
#количество заездов на АЗС 
total_visits = data['id'].count()
print('Количество заездов:', total_visits)
total_stations = len(data['id'].unique())
print('Количество АЗС:', total_stations)
print(data['date_time'].min(), data['date_time'].max())
total_days = round(((pd.to_datetime(data['date_time'].max(), format='%Y%m%dT%H%M%S') - pd.to_datetime(data['date_time'].min(), format='%Y%m%dT%H%M%S'))/pd.Timedelta("1s")/60/60/24))
station_visits_per_day = total_visits/total_stations/total_days
print ('Количество заездов на АЗС в сутки:', station_visits_per_day)
import pandas as pd

data = pd.read_csv ('/datasets/visits.csv', sep='\t')
#количество заездов на АЗС 
total_visits = data['id'].count()
print('Количество заездов:', total_visits)
total_stations = len(data['id'].unique())
print('Количество АЗС:', total_stations)
print(data['date_time'].min(), data['date_time'].max())
total_days = round(((pd.to_datetime(data['date_time'].max(), format='%Y%m%dT%H%M%S') - pd.to_datetime(data['date_time'].min(), format='%Y%m%dT%H%M%S'))/pd.Timedelta("1s")/60/60/24))
station_visits_per_day = total_visits/total_stations/total_days
print ('Количество заездов на АЗС в сутки:', station_visits_per_day)
print(data['name'].value_counts().head(10))

Количество заездов: 317104
Количество АЗС: 471
20180402T000008 20180408T235957
Количество заездов на АЗС в сутки: 96.17955717318775
Календула      85648
Василёк        79006
Георгина       34356
Немезия        20138
Колокольчик    18835
Мальва         17386
Гейхера        14125
Доротеантус     6312
Нарцисс         3640
Амарант         3221
Name: name, dtype: int64

Эксперты рынка АЗС подтвердили, что сети «Календула» и «Василёк» — крупнейшие игроки. Другие сети, по словам специалистов, также распределились правдоподобно.
Поздравляем! С объёмом данных всё в порядке. Нужно разбираться дальше.

simpsons = pd.read_csv('simpsons.csv')
print(simpsons.head()) 

     id  title  season  \
0  10                                  Homer's Night Out       1   
1  12                                 Krusty Gets Busted       1   
2  14                                   Bart Gets an "F"       2   
3  17  Two Cars in Every Garage and Three Eyes on Eve...       2   
4  19                               Dead Putting Society       2   

   number_in_season  imdb_rating  
0                10          7.4  
1                12          8.3  
2                 1          8.2  
3                 4          8.1  
4                 6          8.0 

Методом value_counts() можно вывести количество серий, получивших тот или иной рейтинг:

print(simpsons['imdb_rating'].value_counts()) 

7.3    48
7.0    42
7.1    42
6.9    40
7.2    39
6.7    29
8.2    29
7.7    27
8.0    25
6.6    23
6.8    21
6.5    18
8.3    18
7.5    18
8.1    18
...

Теперь гистограмма соответствует действительности. Что, если количество шаров с разными номерами изменится? Скажем, будет два шара под номером 8 и ни одной «семёрки»? 

pd.Series([6, 8, 8, 9, 10, 11, 12, 13, 14, 15, 16]).hist(bins=11)
pd.Series([6, 8, 8, 9, 10, 11, 12, 13, 14, 100]).hist(range = (6, 14))
pd.Series([4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6]).hist(range=(0, 10))

льтат