Глядя на таблицу результатов футбольного сезона, я часто задаюсь вопросами:

  • Лидировала ли команда с начала сезона или совершила героический рывок в конце?
  • Как зимнее трансферное окно повлияло на результаты?
  • Доигрывали ли сезон команды в середине таблицы или играли в полную силу?

Статичная таблица не даёт ответов.

Поиски удачной визуализации сезона на просторах интернета закончились безуспешно, поэтому я начал экспериментировать сам.

Графики или быстро теряли читаемость с ростом количеством команд, или требовали нетривиальной концентрации при первом контакте. В итоге, всё закончилось тем, с чего началось — таблицей. Точнее, скриптом, который преобразовывает результаты сезона в интерактивную таблицу. Можно посмотреть результаты после любого тура или просто нажать на replay и наблюдать, как команды плавают вверх и вниз по таблице:

image

> Живая демонстрация

Входные данные


Найти результаты любого хоть сколь-нибудь популярного чемпионата не проблема, спасибо огромной букмекерской индустрии. Для демонстрации я возьму результаты матчей английской Премьер-лиги на Football Data:
Date Home Team Score Away Team Score
August 8, 2015 Bournemouth 0 Aston Villa 1
August 8, 2015 Chelsea 2 Swansea 2
August 8, 2015 Everton 2 Watford 2
... ... ... ... ...

Скрипт парсит csv-файл, преобразовывает данные в удобные для представления javascript-объекты и добавляет дополнительную информацию —например, количество побед, ничей и поражений после каждого тура.

Использование


  1. Подключаем стили в head:

    <link rel="stylesheet" type="text/css" href="cdn.jsdelivr.net/replay-table/latest/replay-table.css">

  2. Добавляем скрипт в конец body:

    <script type="text/javascript" src="//cdn.jsdelivr.net/replay-table/latest/replay-table.min.js"></script>

  3. Помещаем на страницу div с классом replayTable и ссылкой на csv-файл в атрибуте data-csv:

    <div class="replayTable" data-csv="https://s3-us-west-2.amazonaws.com/replay-table/csv/football/england/premier-league/2015-2016.csv" data-table-name="Premier League" data-input-type="listOfMatches" data-item-name="Team" data-use-rounds-numbers="true" </div>

Готово:

image

Настройка


Внимательный читатель уже заметил data- атрибуты. С их помощью таблица адаптируется под разные виды спорта, локализируется и меняет внешний вид. Подробная документация есть на Гитхабе.

Я постарался сделать так, чтобы скрипт без доработки можно было использовать для как можно большего числа видов спорта. На сайте таблиц есть примеры вместе с исходным кодом:


Лицензия


Используйте таблицы на любых сайтах, в том числе коммерческих.

> Код на Гитхабе.

Спасибо Даше за красоту.
Поделиться с друзьями
-->

Комментарии (22)


  1. Smi1e
    27.12.2016 13:29
    +8

    Мне нравится очень информативная графика изменения позиций пилотов на протяжении гонки в той же F1. Легко проследить динамику конкретного пилота.


    1. maxzuber
      28.12.2016 17:35

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


      1. antoniokov
        28.12.2016 22:49

        Чехарды в начале меньше, если расставить команды по результатам прошлого сезона. Но всё равно «обгонов» будет в разы больше, чем в любой формульной гонке.


      1. Smi1e
        29.12.2016 15:05

        Можно сделать побольше горизонтальный масштаб (расстояние между турами) и читаемость значительно повысится.
        Да и брать за отсечку не обязательно начало чемпионата. Расставить можно сразу по результатам 1-го тура, там даже будет правильнее.


  1. Writerim
    27.12.2016 13:51

    Идея хорошая, но я запомнил положение участников, потом они «сбрасываются» вниз и… я не помню кто куда переместился. Сделать бы изменения только тех позиций, которые реально менялись.


    1. antoniokov
      28.12.2016 22:51

      Не совсем понял. Можете привести пример или прикрепить гифку, пожалуйста?
      Сортировка в таблицах устойчивая, команды перемещаются только при изменении позиции.


  1. m_z
    27.12.2016 15:03

    Отлично. Недавно понадобилось сделать такую же анимацию, нашелся только старенький jquery-плагин.
    Единственное пожелание: добавьте файл лицензии (MIT или другую), чтобы можно было спокойно использовать в коммерческих проектах.


    1. antoniokov
      28.12.2016 22:55

      Добавил MIT


  1. ConceptDesigner
    28.12.2016 14:53

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


  1. vmm86
    28.12.2016 14:53

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


  1. Aw55
    28.12.2016 14:53

    Такие таблицы очень легко делаются с помощью d3.js, если вдруг кому понадобится.


    1. antoniokov
      28.12.2016 22:56

      Вполне вероятно, что и я перепишу анимацию на d3.js вместо React Flip Move.


  1. Singapura
    28.12.2016 14:53

    Добавите статистику по динамике кэфов от букмекеров для каждой из команд(в чемпионате) и будет Супер. Видеть надёжность команд в отрабатывании ожиданий, это ценно.


  1. MOTORIST
    28.12.2016 14:53

    Движения для всех команд тяжело для восприятия. Лучше показать движение одной команды.


    1. antoniokov
      28.12.2016 22:58

      Можно сфокусироваться на одной или нескольких командах через параметр data-focused-items.


  1. maxzuber
    28.12.2016 14:54
    +1

    Получается довольно любопытный виджет, спасибо. Благодатная тема для исследования и совершенствования, поскольку в каждом чемпионате свои нюансы при равенстве очков. Вижу естественным развитием вынесение конфигурации чемпионата в файл с данными. Причём, в формате JSON, а не CSV, всё-таки.

    В самой анимации смущает изменение номера места до перемещения. Может, лучше разбить на три этапа? Начисление очков, сортировка, обновление номера.


    1. antoniokov
      28.12.2016 23:03

      Всё так. Скорее всего, для начала добавлю параметр data-tie-breaking с заготовками для дополнительных показателей количество побед, забитые голы и т.п. Если будет не хватать, добавлю опцию конфигурационного JSON-файла.

      Я пробовал сделать обновление в несколько этапов — интуитивно казалось, что так хуже.


  1. 1ax
    03.01.2017 12:36

    Неплохо было бы разделать css на тот что относится именно к таблице и общий, относящийся к странице. Потому что если подключить все по инструкции в существующий сайт будет конфликт определения заголовков h2 и некоторых других общеупотребимых тегов.
    Так же идет ссылка на локально расположенные шрифты, про которых в инструкции ничего нет, что выдает ошибку валидатора.


    1. antoniokov
      03.01.2017 12:42

      Это очень хорошие замечания, спасибо.

      Мы сделали ошибку, не разделив скрипт и демонстрационный сайт, из-за чего вылезли проблемы с CSS и шрифтами. Сайт в новогоднюю ночь сел на Jekyll и переехал в отдельный репозиторий: https://github.com/TargetProcess/replayTable.com. В ближайшее время вычистим лишнее из файлов самого скрипта — и всё станет хорошо.


  1. Shvedov
    04.01.2017 05:28

    Такая задача как раз для OLAP кубов… Вот либа есть для отображения на js


  1. 1ax
    04.01.2017 10:55

    сформировал вот такой файлик исходных данных — чо-то не работает.

    2 Дивизион Центр
    Date,HomeTeam,FTHG,AwayTeam,FTAG
    20.07.2016, Арсенал-2,0, Зенит Пз,0
    20.07.2016, Торпедо М,2, Чертаново,1
    20.07.2016, Авангард К,2, Энергомаш,0
    20.07.2016, Сатурн,1, Металлург Лп,1
    20.07.2016, Орёл,1, Калуга,0
    20.07.2016, Витязь,1, Рязань,0
    28.07.2016, Торпедо М,2, Витязь,1
    28.07.2016, Зенит Пз,0, Орёл,0
    28.07.2016, Металлург Лп,2, Рязань,2
    28.07.2016, Сатурн,1, Авангард К,1
    28.07.2016, Энергомаш,1, Динамо Бр,0
    29.07.2016, Калуга,2, Чертаново,0
    04.08.2016, Арсенал-2,0, Энергомаш,6
    04.08.2016, Чертаново,1, Зенит Пз,1
    04.08.2016, Авангард К,1, Металлург Лп,0
    04.08.2016, Динамо Бр,0, Сатурн,1
    04.08.2016, Рязань,2, Торпедо М,1
    04.08.2016, Витязь,1, Калуга,0
    11.08.2016, Зенит Пз,0, Витязь,5
    11.08.2016, Металлург Лп,0, Торпедо М,2
    11.08.2016, Авангард К,1, Динамо Бр,0
    11.08.2016, Сатурн,1, Арсенал-2,1
    11.08.2016, Энергомаш,4, Орёл,0
    12.08.2016, Калуга,1, Рязань,0
    18.08.2016, Арсенал-2,1, Авангард К,2
    18.08.2016, Орёл,1, Сатурн,0
    18.08.2016, Чертаново,1, Энергомаш,4
    18.08.2016, Торпедо М,1, Калуга,1
    18.08.2016, Динамо Бр,1, Металлург Лп,0
    18.08.2016, Рязань,2, Зенит Пз,0
    25.08.2016, Авангард К,0, Орёл,0
    25.08.2016, Динамо Бр,5, Арсенал-2,0
    25.08.2016, Сатурн,1, Чертаново,2
    28.08.2016, Металлург Лп,1, Калуга,0
    28.08.2016, Зенит Пз,1, Торпедо М,1
    28.08.2016, Энергомаш,2, Витязь,0
    02.09.2016, Чертаново,0, Авангард К,1
    02.09.2016, Арсенал-2,1, Металлург Лп,3
    02.09.2016, Орёл,0, Динамо Бр,1
    02.09.2016, Рязань,1, Энергомаш,3
    02.09.2016, Витязь,2, Сатурн,1
    03.09.2016, Калуга,2, Зенит Пз,2
    09.09.2016, Арсенал-2,2, Орёл,2
    10.09.2016, Энергомаш,0, Торпедо М,0
    10.09.2016, Сатурн,1, Рязань,0
    10.09.2016, Динамо Бр,0, Чертаново,2
    10.09.2016, Металлург Лп,2, Зенит Пз,4
    11.09.2016, Авангард К,3, Витязь,0
    16.09.2016, Чертаново,0, Арсенал-2,1
    17.09.2016, Калуга,1, Энергомаш,1
    17.09.2016, Орёл,0, Металлург Лп,1
    17.09.2016, Витязь,1, Динамо Бр,0
    17.09.2016, Рязань,1, Авангард К,1
    18.09.2016, Торпедо М,1, Сатурн,2
    25.09.2016, Сатурн,1, Калуга,1
    25.09.2016, Динамо Бр,0, Рязань,0
    25.09.2016, Арсенал-2,1, Витязь,2
    25.09.2016, Орёл,1, Чертаново,3
    25.09.2016, Авангард К,0, Торпедо М,1
    26.09.2016, Энергомаш,5, Зенит Пз,1
    01.10.2016, Калуга,2, Авангард К,0
    01.10.2016, Торпедо М,1, Динамо Бр,1
    02.10.2016, Витязь,3, Орёл,2
    02.10.2016, Зенит Пз,0, Сатурн,1
    02.10.2016, Чертаново,1, Металлург Лп,2
    02.10.2016, Рязань,2, Арсенал-2,1
    09.10.2016, Арсенал-2,1, Торпедо М,2
    09.10.2016, Чертаново,3, Витязь,3
    09.10.2016, Динамо Бр,0, Калуга,0
    09.10.2016, Орёл,1, Рязань,1
    09.10.2016, Металлург Лп,0, Энергомаш,4
    09.10.2016, Авангард К,1, Зенит Пз,0
    15.10.2016, Калуга,1, Арсенал-2,0
    16.10.2016, Торпедо М,1, Орёл,1
    16.10.2016, Зенит Пз,1, Динамо Бр,3
    16.10.2016, Металлург Лп,0, Витязь,0
    16.10.2016, Рязань,2, Чертаново,2
    16.10.2016, Энергомаш,0, Сатурн,1
    23.10.2016, Чертаново,4, Калуга,0
    23.10.2016, Динамо Бр,1, Энергомаш,0
    23.10.2016, Авангард К,2, Сатурн,2
    23.10.2016, Витязь,1, Торпедо М,3
    23.10.2016, Орёл,3, Зенит Пз,1
    23.10.2016, Рязань,1, Металлург Лп,1
    29.10.2016, Калуга,2, Витязь,1
    30.10.2016, Торпедо М,1, Рязань,1
    30.10.2016, Металлург Лп,1, Авангард К,0
    30.10.2016, Энергомаш,4, Арсенал-2,0
    30.10.2016, Зенит Пз,3, Чертаново,0
    30.10.2016, Сатурн,1, Динамо Бр,0
    06.11.2016, Витязь,3, Зенит Пз,1
    06.11.2016, Арсенал-2,1, Сатурн,3
    06.11.2016, Динамо Бр,0, Авангард К,1
    06.11.2016, Торпедо М,1, Металлург Лп,0
    06.11.2016, Рязань,1, Калуга,2
    06.11.2016, Орёл,1, Энергомаш,1


    1. antoniokov
      06.01.2017 00:00

      И вправду, был баг, при котором таблица не работала, если команды сыграли разное количество туров.

      Выпустил новый релиз с фиксом и протестировал вашу таблицу: https://replaytable.com/tables/requests/2016/1ax-russian-second-division-centre.html. Кстати, локализировал её и добавил общее количество туров, чтобы прогресс-бар показывал правду — загляните в исходный код.

      В следующем релизе поправим стили, про которые вы писали сверху.