Работа с данными среднего размера в Python. Pandas и Seaborn

    Когда много работаешь с данными, нужно часто строить графики и делать разными преобразования над таблицами. Важно научиться делать это быстро и минимально напрягая мозг. Дело в том, что анализ данных во многом заключается в придумывании и проверке гипотез. Придумывать, конечно, интереснее, чем проверять. Но делать нужно и то и другое. Хорошие инструменты в тренированных руках помогают тратить на техническую работу минимальное количество времени и интеллектуальной энергии.

    Я попробовал много инструментов: Excel, Python+Matplotlib, R+ggplot, Python+ggplot, и остановился на связке Python+Pandas+Seaborn. Решил с их использованием уже много задач и хотел бы поделиться наблюдениями.



    Я покажу процесс исследования одного странного датасета. Речь пойдёт о расписании всех спортивных мероприятий с участием российских спортсменов за последние 8 лет. Кстати, если у кого-то после прочтения появятся идеи, что с этими данными делать, пожалуйста, напишите, потому что я ничего толкового так и не придумал.

    Захватывающий этап парсинга rst и doc-файликов с сайта Минспорта я пропускаю. Предположим, что все данные уже есть в виде DataFrame'a:
    data.head()
    


    Таблица не большая, но и не маленькая:

    len(data)
    
    76601
    

    Первым делом хотелось бы посмотреть, какие виды спорта представлены:

    table = data.groupby('section').size()
    table
    
    section
    R4                                                             11
    Авиамодельный спорт                                          1206
    Автомобильный спорт                                          2787
    Автомодельный спорт                                           160
    Айкидо                                                         26
    Айсшток                                                        49
    Академическая гребля                                          358
    Акробатический рок-н-ролл                                     293
    Альпинизм                                                     355
    Американский футбол                                            86
    Армейский рукопашный бой                                       15
    Армспорт                                                      159
    Бадминтон                                                     698
    Баскетбол                                                    1160
    Бейсбол                                                       176
    Биатлон                                                      1101
    Бильярдный спорт                                              442
    Бобслей                                                        53
    Бобслей (скелетон)                                            368
    Бодибилдинг                                                    94
    Бокс                                                         1374
    Борьба на поясах                                              144
    Боулинг                                                       134
    ...
    

    Отсортируем:

    table = data.groupby('section').size()
    table = table.sort(inplace=False, ascending=False)
    table
    
    section
    Автомобильный спорт                                          2787
    Спорт лиц с поражением ОДА                                   2469
    Фехтование                                                   2253
    Мотоциклетный спорт                                          2025
    Дзюдо                                                        2022
    Теннис                                                       1770
    Велоспорт-шоссе                                              1561
    Легкая атлетика                                              1511
    Футбол                                                       1432
    Парусный спорт                                               1418
    Бокс                                                         1374
    Конный спорт                                                 1297
    Спорт глухих                                                 1277
    Волейбол                                                     1208
    Авиамодельный спорт                                          1206
    Художественная гимнастика                                    1196
    ...
    

    Достаточно странный топ. Отсортируем не по числу мероприятий, а по числу участников:

    table = data.groupby('section').participants.sum()
    table = table.sort(inplace=False, ascending=False)
    table
    
    Футбол                                                       535305
    Баскетбол                                                    460100
    Автомобильный спорт                                          401397
    Танцевальный спорт                                           307731
    Дзюдо                                                        297023
    Самбо                                                        219013
    Легкая атлетика                                              217761
    Тхэквондо                                                    202859
    Киокусинкай                                                  191995
    Велоспорт-шоссе                                              190293
    Комплексные мероприятия                                      187294
    Спортивная борьба                                            177312
    Мотоциклетный спорт                                          176387
    Спортивный туризм                                            153417
    Бокс                                                         148654
    Хоккей                                                       136849
    Кикбоксинг                                                   136099
    ...
    

    Так лучше. Теперь нарисуем график для топ-30:

    table = data.groupby('section').participants.sum()
    table = table.sort(inplace=False, ascending=False)
    table.head(30).plot(kind='bar')
    


    Я не фанат супер-сурового инженерного дизайна, поэтому всегда импортирую Seaborn:
    import seaborn
    
    table = data.groupby('section').participants.sum()
    table = table.sort(inplace=False, ascending=False)
    table.head(30).plot(kind='bar')
    


    Идеально! Никакой другой функциональности Seaborn здесь использовано не будет. У меня чаще всего так и получается — Seaborn появляется только, чтобы графики стали красивыми. Это уже немало, но, вообще, библиотека умеет много чего другого.

    Теперь интересно посмотреть на изменение числа мероприятий во времени. Всё то же самое только группировать нужно по дате:

    table = data.groupby('start').size()
    table.plot()
    


    Почти готово. Нужно сделать детализацию, например, по неделям, а не по дням:
    table = data.groupby('start').size()
    table = table.resample('M', how='sum')
    table.plot()
    
    • Спортивная жизнь зимой наименее активна.
    • Тренд до последнего времени восходящий.
    • В 2010 был какой-то провал.


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

    table = data.pivot_table(index='start', columns='section', values='participants', aggfunc=len)
    table.head()
    


    И нарисовать:

    table.plot()
    


    Блестяще, ничего не понятно. Для начала уберём всё кроме топ-40 видов спорта, кстати, код для расчёта топа уже есть, заново не надо писать:
    table = data.groupby('section').size()
    table = table.sort(inplace=False, ascending=False)
    order = table.head(40).index
    
    table = data.pivot_table(index='start', columns='section', values='participants', aggfunc=len)
    table = table.reindex(columns=order)
    table.plot()
    


    Ладно, сделаем детализацию по неделям:
    table = data.groupby('section').size()
    table = table.sort(inplace=False, ascending=False)
    order = table.head(40).index
    
    table = data.pivot_table(index='start', columns='section', values='participants', aggfunc=len)
    table = table.reindex(columns=order)
    table = table.resample('M', how='sum')
    table.plot()
    


    Почти то, что надо, теперь расположим каждую линию на отдельном графике:
    table = data.groupby('section').size()
    table = table.sort(inplace=False, ascending=False)
    order = table.head(40).index
    
    table = data.pivot_table(index='start', columns='section', values='participants', aggfunc=len)
    table = table.reindex(columns=order)
    table = table.resample('M', how='sum')
    table.plot(subplots=True, layout=(8, 5), figsize=(20, 20))
    
    • Для некоторых видов наблюдаются тренды в числе мероприятий:
      • Сильно растёт спорт для инвалидов (лиц с поражением ОДА) и автомобильный спорт.
      • Слегка растёт мотоциклетный спорт, дзюдо, самбо, парусный спорт, волейбол, тхэквондо, бадминтон.
      • У футбола какой-интересный тренд.
      • Бокс перестал расти в 2012 году.
      • Надо отметить что падения числа мероприятий нигде не наблюдается.
    • Иногда есть периодичность в числе мероприятий, что не удивительно:
      • Парусный спорт, конные спорт, авиамодельный спорт, велоспорт замирают зимой.
      • Вообще зимой у многих видов затишье. Но бывает и наоборот, например, во фристайле.
      • Интересный рисунок у тенниса и лёгкой атлетики, который повторяется каждый год


    Что мне нравится в этом подходе? Функция plot очень простая, у неё всего несколько важных аргументов: kind, subplots, figsize. Результат зависит в основном от структуры данных, для которых вызывается plot. То есть не нужно осваивать отдельно инструмент для рисования графиков. Нужно научиться преобразовывать данные. С Pandas это сделать не очень сложно. Достаточно выучить несколько «кирпичиков»: groupby, sort, head, pivot_table, reindex, resample, и применять их в разных комбинациях.
    Метки:
    • +23
    • 29,7k
    • 3
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 3
    • 0
      Чем лучше/хуже Python+Matplotlib?
      • 0
        Простые графики, которые нужно строить часто, реализуются проще. Попробуй с matplotlib построить гистограмму с подписями-словами (первая картинка в тексте). А здесь table.plot(kind='bar'). При этом что-то сложное нужно делать через matplotlib. Scatter plot с точками разного размера и цвета через table.plot(...) не построишь
      • 0
        Не знал про seaborn, спасибо.

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