Анализ скорости распространения COVID-19 и публикация результатов на dstack.ai

от автора

Предисловие переводчика

Всем привет, давно ничего не писал на Хабр, но тут появился отличным повод. Я довольно много лет проработал в области анализа данных и самая важная вещь, которую я понял за это время — в анализе данных очень не хватает инструментов, самых разных инструментов. Меня беспокоило несколько вещей, одна из которых — это трудность, с которой сталкивается специалист по анализу данных при попытке поделиться результатами своей работы с менеджером или даже коллегой по “цеху”. Обычно здесь идут в ход любые подручные средства, email, IM, dropbox и т.д. Мы с Андреем и еще одним нашим другом решили попытаться сделать что-то осмысленное в этой области и сегодня я хочу рассказать вам о том, что же у нас получилось. В ситуации, в которой мы все оказались из-за вируса COVID-19 проблемы публикации и обсуждения результатов исследований стала пожалуй еще более актуальна чем когда бы то ни было раньше.

Итак, меня зовут Виталий, среди прочего я сооснователь dstack.ai и представляю вам перевод текста, который написал Андрей несколько дней назад. Он рассчитан скорее на начинающих, но даже продвинутые специалисты по анализу данных, особенно те, кто интересуются инструментами визуализации могут найти здесь кое что интересное, что может помочь им в повседневной работе.
Для того, чтобы быть совсем честным, код всех примеров написал я, так можно писать прямо мне на vitaly at dstack.ai.

Анализ скорости распространения COVID-19

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

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

Исходя из того, что сотрудничество кажется особенно важным именно в области анализа данных, я решил написать эту статью, чтобы показать как можно использовать Python с библиотеками для того, чтобы проанализировать данные о вирусе COVID-19 и внести вклад в общее усилие сделав свои результаты доступными для остального сообщества, ну и по ходу изучить основы анализа данных.
Эта статья расскажет как…

  • Использовать Python, pandas и plot.ly для анализа и визуализации данных. Это может быть интересно для тех кто начинает заниматься data science и интересуется Python в контексте анализа данных.
  • Использовать публичные данные для анализа влияния COVID-19.
  • Использовать Python dstack.ai API для того чтобы опубликовать результаты и сделать их доступными для других.

Внимание, примеры в данном ноутбуке преследуют учебные цели и не ставят целью проведение тщательного анализа. В случае, если вы нашли ошибку в анализе или у вас есть вопрос по коду или результатам, вы можете направить письмо по адресу andrey at dstack.ai.

В случае, если вы только изучаете Python для анализа данных и не имеете опыта с Jupyter ноутбуками, рекомендую взглянуть на Jupyter, как на основной способ работы с данным на Python.
Когда нужно анализировать данные, самое основное — это иметь хорошие библиотеки под рукой. В этой статье я буду использовать самые простые, но также самые важные и популярные библиотеки: pandas для работы с данными, plot.ly для создания интерактивных и красивых визуализаций.

На данный момент pandas — это стандарт de facto для работы с данными в Python, тогда как существуют более чем одна популярная библиотека (еще, как минимум, есть Matplotlib and Bokeh). Для простоты в статье будем использовать plotly.express — надстройка, которая оборачивает plotly до совсем простых примитивов, чтобы сделать код визуализации максимально простым (часть пакета plotly — примечание переводчика).
Так как мне бы хотелось опубликовать данные и визуализации на dstack.ai, то я буду использовать библиотеку dstack для Python (вариант для conda доступен по ссылкепримечание переводчика).

import pandas as pd import plotly.express as px from dstack import create_frame

Другая вещь без которой вы не можете анализировать данные, это, собственно, данные. В случае, если данные не чистые, вам необходимо их очистить самим, например используя pandas. Очистка данных — это интересная тема сама по себе, но мы оставим ее за пределами нашей статьи. В нашем случае я планирую использовать данные подтвержденных случаев COVID-19 собранные из различных источников и обновляемые Университетом Джонса Хопкинса.

Как вы сможете увидеть в выводе следующей ячейки, данные, которые я буду использовать содержат информацию о новых подтвержденных случаях COVID-19 с указанием провинции/штата, страны и даты. Здесь небольшое количество кода, которое позволяет загрузить данные из URL в датафрейм pandas, а также предпросмотр загруженных данных:

url = "https://data.humdata.org/hxlproxy/api/data-preview.csv?url=https%3A%2F%2Fraw.githubusercontent.com%2FCSSEGISandData%2FCOVID-19%2Fmaster%2Fcsse_covid_19_data%2Fcsse_covid_19_time_series%2Ftime_series_19-covid-Confirmed.csv&filename=time_series_2019-ncov-Confirmed.csv" df = pd.read_csv(url) # загружаем датафрейм из URL df.head() # отображаем первые 5 строк, чтобы понять что мы скачали

скаченные данные

(В оригинальной статье в качестве упрощения не используются подробные данные для тех стран, когда они доступны по регионам, например для Китая, США, Канады и Австралии, здесь мы также не будем визуализировать данные по этим странам и оставим это в качестве упражнения читателю — примечание переводчика).

Одна из вещей, которые могут нас заинтересовать — это как ситуация вокруг COVID-19 изменилась за последние два дня в каждой стране. Для того, чтобы получить эти данные воспользуемся pandas API для манипуляции с данными: выберем нужные нам колонки — страну и два последних дня и скопируем их в другой датафрейм (так как у нас еще есть данные по регионам, мы просто проследим за тем, чтобы в таблицу попали только данные целиком по стране — примечание переводчика):

# берем первую (2-ю с нуля) колонку и две последних cols = [df.columns[1]] + list(df.columns[-2:])  last_2_days = df[df["Province/State"].isnull()][cols].copy() # копируем last_2_days # напечатаем, что у нас получилось

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

d1 = last_2_days.columns[-1]  # последний день d2 = last_2_days.columns[-2]  # предпоследний день last_2_days["delta"] = last_2_days[d1] - last_2_days[d2] last_2_days["delta%"] = last_2_days["delta"] / last_2_days[d2] last_2_days

А сейчас представим себе, что вы хотите опубликовать интересные результаты в сети и сделать их доступными для других людей. Вы можете это сделать используя пакет dstack. Это пакет предоставляет API чтобы публиковать датайреймы pandas и визуализации, созданные с помощью plotly (среди прочих поддерживаемых библиотек визуализации).

Замечательная функция dstack — это то, что dstack позволяет публиковать данные и визуализации делая их интерактивными: например пользователь может изменить параметры и увидеть данные, соответствующие выбранным параметрам.

Однажды опубликованные данные на dstack.ai могу быть доступны по ссылке, другие люди смогут их увидеть и прокомментировать.

Для того, чтобы опубликовать датафреймы pandas или графики (фигуры) plotly из вашего кода, вам нужно создать фрейм dstack указав имя стека с которым вы работаете. Этот стек будет доступен позднее через URL следующего вида https://dstack.ai/<user>/<stack>. Каждый стек может иметь много фреймов. Фреймы — это просто версии тех данных, что вы публикуете. Каждый стек указывает на вершину — самый верхний фрейм стека. Каждый фрейм включает в себя список вложений (attachments). Каждое вложение может быть либо визуализацией либо датафреймом и должно иметь список параметров (если вложение одно, то список может быть пустым, в противном случае значения параметров должны быть уникальными для каждого вложения, чтобы как-то их отличать, таким образом можно рассматривать вложение как своего рода представление данных для заданного набора параметров — примечание переводчика).

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

  1. Ниже мы создаем фрейм в стеке covid19/speed (вам не надо создавать стек, он будет создан автоматически — примечание переводчика). Это значит, что данный фрейм будет опубликован в стеке и будет доступен по адресу: https://dstack.ai/<user>/covid19/speed.

Обратите внимание, имя пользователя конфигурируется через утилиту командной строки dstack.

dstack config --token <token> --user <user>

Таким образом вы сконфигурировали dstack так, чтобы токен и имя пользователя хранились в файле .dstack/config.yaml в текущем рабочем каталоге (т.е. там где вы вызвали эту команду — примечание переводчика). Вы можете узнать больше о том, как использовать инструменты командной строки на сайте docs.dstack.ai.

  1. И для абсолютного прироста числа заболевших и для доли новых случаев в процентном соотношении мы опубликуем два различных датафрейма (т.к. мы хотим отсортировать данные по соответствующей колонке в каждом случае — примечание переводчика), таким образом пользователь, получивший доступ сможет переключаться между двумя таблицами. Каждый датафрейм, будет добавлен во фрейм (commited) как отдельное вложение вместе с описанием и соответствующими значениями параметров.

  2. Затем отправим (push) фрейм вместе с вложениями:

min_cases = 50 # созданим фрейм в стеке top_speed_frame = create_frame("covid19/speed") # сортировки sort_by_cols = ["delta", "delta%"] for col in sort_by_cols:     top = last_2_days[last_2_days[last_2_days.columns[1]] > min_cases].                  sort_values(by=[col], ascending=False).head(50)     # добавим данные во фрейм     top_speed_frame.commit(top, f"Top 50 countries with the " \                                 f"fastest growing number of confirmed " \                                 f"Covid-19 cases (at least {min_cases})",                                  {"Sort by": col})  top_speed_frame.push()

Вы можете увидеть опубликованный стек здесь: https://dstack.ai/cheptsov/covid19/speed.

А теперь давайте визуализируем какие-нибудь данные, например новые подтвержденные случае для заданной страны во времени. Это упражнение отличное не только потому что оно показывает как визуализировать данные, оно также показывает еще несколько аспектов работы с данными.

Для того, чтобы визуализировать наши данные по новым случаям нам нужно немного изменить формат данных — транспонировать данные, для того чтобы сделать даты строками вместо колонок:

# транспонируем датафрейм cdf = df[(df["Country/Region"]=="Italy") & (df["Province/State"].isnull())][df.columns[4:]].T # дадим нормальное имя колонке cdf = cdf.rename(columns={cdf.columns[0]:"confirmed"}) 

После того, как наши данные подготовлены мы можем построить график для новых случаев. Мы построим его как линейный график, в котором по оси x будут отложены данные, а по оси y количество новых случаев:

fig = px.line(cdf, x=cdf.index, y="confirmed") fig.show()

Давайте сейчас попробуем что-нибудь более сложное. Как на счет визуализации прироста случаев от времени?

Для того, чтобы это сделать нам понадобится другой способ работы с датафреймом.
Мы сделаем датафрейм вычитанием общего количества подтвержденных случаев за день и случаев за предыдущей день. Вот как это можно сделать с помощью pandas API:

delta = (cdf.shift(-1) - cdf) delta.tail() # убедимся, что мы получили, что хотели

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

fig = px.line(delta, x=delta.index, y="confirmed") fig.show()

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

Функция ниже возвращает 3 графика (figures) plotly: всего подтвержденных случаев, абсолютное значение прироста, а также прирост в процентах:

def plots_by_country(country):     cdf = df[(df["Country/Region"]==country) & (df["Province/State"].isnull())][df.columns[4:]].T     cdf = cdf.rename(columns={cdf.columns[0]:"confirmed"})     cfig = px.line(cdf, x=cdf.index, y="confirmed")     delta = (cdf.shift(-1) - cdf).rename(columns={"confirmed": "confirmed per day"})     cdfig = px.line(delta, x=cdf.index, y="confirmed per day")     delta_p = ((cdf.shift(-1) - cdf) / cdf.shift(-1)).rename(columns={"confirmed": "confirmed per day %"})     cdpfig = px.line(delta_p, x=cdf.index, y="confirmed per day %")     return (cfig, cdfig, cdpfig)

Для того, чтобы протестировать эту функцию давайте вызовем ее для Австрии и отобразим графики один за одним:

(fig1, fig2, fig3) = plots_by_country("Austria") fig1.show() fig2.show() fig3.show()



Теперь давайте воспользуемся нашей функцией для для каждой из 30 стран с максимальным количеством подтвержденных новых случаях и опубликуем визуализации в одном стеке. Этот пример покажет как один стек может быть использован для организации интерактивных панелей с графиками (dashboards) с различным набором параметров. И так:

# возьмем 30 стран с самым большим количеством подтвержденных случаев countries = df[df["Province/State"].isnull()].sort_values(by=[df.columns[-1]], ascending=False)[["Country/Region"]].head(30)  # создадим новый фрейм и запишем туда все графики для каждой страны frame = create_frame("covid19/speed_by_country") for c in countries["Country/Region"].tolist():     print(c)     (fig1, fig2, fig3) = plots_by_country(c)     frame.commit(fig1, f"Confirmed cases in {c}", {"Country": c, "Chart": "All cases"})     frame.commit(fig2, f"New confirmed cases in {c}", {"Country": c, "Chart": "New cases"})     frame.commit(fig3, f"New confirmed cases in {c} in %", {"Country": c, "Chart": "New cases (%)"})  frame.push()

Опубликованный стек доступен по ссылке https://dstack.ai/cheptsov/covid19/speed_by_country.

Для того чтобы разобраться с другим, немного более продвинутым примером обработки данных, визуализации и публикации давайте визуализируем похожие данные, но теперь в дополнение к визуализации по каждой отдельной стране давайте также включим визуализацию с всеми странами одновременно (на самом деле возьмем top 10 — примечание переводчика) в один график.

В рамках этого упражнения мы увидим еще один интересный способ работы с данным: давайте для начала создадим датафрейм с новыми подтвержденными случаями для Италии каждый день (и добавим поле со страной в полученный датафрейм — примечание переводчика):

t1 = df[(df["Country/Region"]=="Italy") & (df["Province/State"].isnull())][df.columns[4:]].T t1 = t1.rename(columns={t1.columns[0]:"confirmed"}) t1.reset_index() # индекс теперь просто колонка t1["Country/Region"] = "Italy" # добавим колонку со страной t1.tail() # проверим, что получилось

Давайте обобщим код, который мы написали выше для данной страны, также добавив абсолютное датафреймы для абсолютного значения прироста а также значения в процентах:

def country_df(country):     cdf = df[(df["Country/Region"]==country) & (df["Province/State"].isnull())][df.columns[4:]].T     cdf = cdf.rename(columns={cdf.columns[0]:"confirmed"})     delta = (cdf.shift(-1) - cdf).rename(columns={"confirmed": "confirmed per day"})     delta.reset_index()     delta["Country/Region"] = country     delta_p = ((cdf.shift(-1) - cdf) / cdf.shift(-1)).rename(columns={"confirmed": "confirmed per day %"})     delta_p.reset_index()     delta_p["Country/Region"] = country     cdf.reset_index()     cdf["Country/Region"] = country     return (cdf, delta, delta_p)

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

# топ 10 стран по обычному критерию - количество подтвержденных случаев top10 = df[df["Province/State"].isnull()].sort_values(by=[df.columns[-1]], ascending=False)[["Country/Region"]].head(10)  # списки в которые будем добавлять датафреймы для стран top = [] top_delta = [] top_delta_p = [] for c in top10["Country/Region"].tolist():     (x, y, z) = country_df(c)     top.append(x)     top_delta.append(y)     top_delta_p.append(z)  test = pd.concat(top) # сделаем один датафрейм из списка # отобразим результата px.line(test, x=test.index, y="confirmed", color='Country/Region').show()

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

  • Общее количество случаев от времени для 10 стран с максимальным значением на последний день
  • Количество новых случаев от времени для 10 стран с максимальным значением на последний день
  • Количество новых случае в процентах для этих 10 стран
  • Количество всех случаев для 30 стран с максимальным значением этих случае отдельно по каждой стране
  • Тоже самое, что и в предыдущем пункте, только абсолютное значение прироста
  • Тоже самое, но в процентах

Вот здесь код для этого:

 frame = create_frame("covid19/speed_by_country_all")  top10df = pd.concat(top) fig = px.line(top10df, x=top10df.index, y="confirmed", color='Country/Region') frame.commit(fig, "Confirmed cases in top 10 countries", {"Country": "Top 10", "Chart": "All cases"})  top10df_delta = pd.concat(top_delta) fig = px.line(top10df_delta, x=top10df_delta.index, y="confirmed per day", color='Country/Region') frame.commit(fig, "New confirmed cases in top 10 countries", {"Country": "Top 10", "Chart": "New cases"})  top10df_delta_p = pd.concat(top_delta_p) fig = px.line(top10df_delta_p, x=top10df_delta_p.index, y="confirmed per day %", color='Country/Region') frame.commit(fig, "New confirmed cases in top 10 countries in %", {"Country": "Top 10", "Chart": "New cases (%)"})  for c in countries["Country/Region"].tolist():     print(c)     (fig1, fig2, fig3) = plots_by_country(c)     frame.commit(fig1, f"Confirmed cases in {c}", {"Country": c, "Chart": "All cases"})     frame.commit(fig2, f"New confirmed cases in {c}", {"Country": c, "Chart": "New cases"})     frame.commit(fig3, f"New confirmed cases in {c} in %", {"Country": c, "Chart": "New cases (%)"})  frame.push()

Опубликованный стек находится здесь: https://dstack.ai/cheptsov/covid19/speed_by_country_all.

На этом пока все. Надеюсь вам было интересно поработать с этими примерами и вы поняли, что анализ данных может быть простым. Исходный код этого ноутбука можно найти на GitHub (полученные результаты будут отличаться от того, что вы найдете в этой статье, так как данные постоянно обновляются, а код в этом ноутбуке предназначен для публикации самых актуальных данных — примечание переводчика) .

Здесь список ресурсов, который могут оказаться полезными:

ссылка на оригинал статьи https://habr.com/ru/post/493664/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *