Прогнозирование случаев COVID-19 с помощью нейронных сетей с помощью PyTorch

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

Из-за того, что в некоторых странах объявлена ​​изоляция, это также стало (даже более) горячей темой для обсуждения и сосредоточения внимания в наши дни.

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

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

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



Источники данных

Наш основной источник данных будет предоставлен Университетом Джона Хопкинса, размещенным в их Github Repo. Конкретно за основу возьмем их отчеты временных рядов.



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

  • Плотность населения: COVID-19 случаи заболевания не будут расти такими же темпами, если на 1 км² будет 1 человек вместо тысячи, верно?
  • Использование маски ежедневно. Имейте в виду, что мы предполагаем, что это повлияет на дела, но мы не знаем, насколько это будет зависеть от данных и модели!
  • Этническая группа: особенно азиатская. Это связано с наличием ACE2 в наших клетках, как утверждает ECDC.
  • Контрмеры, принимаемые правительствами.

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

  • Плотность населения: мы взяли последние данные, доступные в World Population Review для каждого необходимого местоположения.
  • Использование масок: для жителей Запада странно видеть людей, носящих маски на повседневной основе, но в некоторых восточных странах, таких как Япония, их использование является обычным явлением, которое может помочь замедлить распространение вируса. Источники по этому поводу можно найти в нашем специальном файле сведений о масках в репозитории.
  • Популяционный риск (Этническая группа): мы провели классификацию на основе следующего изображения (по правде говоря, я не знаю конкретного источника, то есть базы набор данных или публикация, в которой он был первоначально опубликован, как я выразился здесь), который разработан на основе Проекта 1000 геномов, как указано в подписи в конце.

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

Список контрмер выглядит следующим образом. Найдите в нашем репозитории Raw Government Measures Readme источники, где мы нашли эту информацию как для Lockdown, так и для границ.

  • Заключение дома (изоляция)
  • Страна границы закрыты

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

Предварительная обработка данных

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

COVID-19 случаи - это int переменные для каждого местоположения, но даже если они являются целочисленными значениями, их возможные значения настолько широки, что любая система ML будет чрезвычайно трудно обобщать в этих условиях, поэтому нам нужно применить некоторые ограничения к этим значениям! Представьте страну со 100 миллионами жителей. У вас может быть любое количество подтвержденных случаев от 0 до 100 миллионов. С этим не справиться ни одна система машинного обучения, по крайней мере, если у нас недостаточно обучающих выборок. И мы лечим новый вирус, поэтому, конечно же, не лечим! Чтобы решить эту проблему, мы собираемся дискретизировать эти значения:

  1. COVID-19 случаев.
  2. Для значений population density мы также сделаем их кратными 5 (значение в жителей / км²). Таким образом, это уменьшит диапазон возможностей и упростит схождение моделей. Затем мы применим некоторые гандикапы. В основном мы стремимся исправить некоторые данные, такие как, например, плотность населения Австралии, поскольку большая часть населения живет на меньшей площади по сравнению со всей земельной площадью страны. Этот вид исправления будет применяться как коэффициент x3 к исходной заданной плотности населения из-за того, что население сосредоточено на части территории страны. Наконец, мы собираемся еще больше сократить эту функцию. Мы стремимся установить три уровня плотности: низкий, средний и высокий. Мы разработаем соответствующий порог для каждой группы, а затем будем использовать в качестве функции только одну из этих трех категорий для каждого местоположения или страны.
  3. masks будет значением верно / неверно для каждой страны.
  4. Population risk данные будут подвергаться аналогичному подходу к population density (три уровня).

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

Наконец, мы не говорим о государственных контрмерах, потому что мы встроили их непосредственно в функции (handmade CSV для функций).

Особенности Определение

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

  • COVID-19 дел. Это единственная функция, которая не подвергнется нормализации между 0–1, а будет log нормализация. Таким образом, мы можем избежать проблем с исчезающим градиентом из-за того, что низкие значения слишком близки к нулю. Помните, что эта функция обрабатывается таким же образом, но ожидаемый результат будет обрабатываться одинаково, поскольку это одна и та же единица / величина.
  • Плотность населения: низкая / средняя / высокая. Но как нам достичь этой классификации? Что ж, давайте следовать этому пошаговому рецепту:
  1. Мы взяли population density, как указано в предыдущих разделах, из необработанных данных, хранящихся в CSV файле.
  2. Мы делаем эти значения кратными 5 и применяем гандикапы (фактически только для Австралии, поскольку ее обширные неиспользуемые земли искажают плотность населения в густонаселенных районах).
  3. Другой шаг - также удалить очень маленькие страны, такие как Гонконг, которые могут позже повлиять на границы из-за их высоких population density значений. Мы избавляемся от всех, у кого плотность населения превышает 10 000 человек / км². Не бойтесь, они будут автоматически классифицированы как верхний уровень плотности при дискретизации значений.
  4. Наконец, мы применяем преобразование к этой классификации: плотность населения - это непрерывные значения, которые могут сделать почти невозможным для модели изучить ее влияние с нашим очень ограниченным набором обучающих данных. Мы упрощаем его, группируя их по трем категориям: с низкой (1), средней (2) или высокой (3) плотностью.
  5. Но как поставить границу? Мы не хотим устанавливать ограничение каждой категории вручную, поэтому мы применяем здесь наш первый алгоритм: К-средние. Напомним, что этот метод будет пытаться разделить наши данные на K кластеров, поэтому мы применяем его для K = 3.

Эта кластеризация приводит к классификации для каждой страны со значениями от 1 самая низкая плотность до 3 самая высокая.

  • Маски: классифицируются как неиспользуемые (0) или используемые (1).
  • Популяционный риск (Этническая группа): классифицируется как низкий (1), средний (2) или высокий (3).

Мы разработали следующий процесс, чтобы классифицировать население каждой страны в одну группу:

  1. Рассчитайте взвешенный риск для каждой страны на основе предыдущего изображения из проекта «1000 геномов». Для стран с большим количеством иностранцев, таких как Япония, где проживает много китайских иммигрантов, мы приняли их во внимание.
  2. Если нет данных о стране, в которой представлены случаи, мы установили для них значение риска 50% как минимальное значение, которое можно увидеть в таблице для лиц европеоидной расы. Эти первые два шага были выполнены для получения взвешенного риска в листе Excel (обратите внимание, что в папке с исходными данными по ссылке есть два файла: сама книга Excel с расчетом, и файл CSV с результатами, которые будут использоваться на следующих этапах).
  3. Наконец, мы применяем преобразование к этой классификации: проценты - это непрерывные значения, которые могут сделать почти невозможным для модели изучить свой эффект с нашим очень ограниченным набором обучающих данных. Мы упрощаем его, группируя их по трем категориям: низкий (1), средний (2) или высокий (3) риск из-за взвешенного риска.
  4. Но к чему принадлежит население каждой страны? Мы не хотим устанавливать ограничение каждой категории вручную, поэтому мы снова применяем K-средние. Напомним, что этот метод будет пытаться разделить наши данные на K кластеров, поэтому мы применяем его для K = 3.

Результатом такой кластеризации является классификация каждой страны от 1 наименьшего риска до 3 наибольшего.

Наконец, у нас есть особенности правительственных мер. Они не были объяснены в предыдущем разделе Обработка данных, поскольку мы собрали и пометили их непосредственно на уровнях.

  • Блокировка: уровень от отсутствия блокировки (0) до строгой блокировки (2), как в Китае.
  • Границы закрыты: уровень от неприменения меры противодействия (0) до полного закрытия страны или выполнения ее чрезвычайно суровой (2), как это сделано в Китае или Южной Корее.

Помните, что после всего этого идет нормализация функций, поэтому все значения находятся в диапазоне от 0 до 1, за исключением случаев, которые будут log нормализованы.

Данные, используемые для обучения

После всего, что мы объяснили, мы должны кратко указать, какие данные мы собираемся использовать для обучения нашей системы. Да, еще до того, как сказать, какую модель или метод машинного обучения мы будем использовать. Вам может быть интересно ... Почему?

Что ж, примите во внимание, что это постоянная проблема (COVID-19 вопрос в целом), поэтому данные обновляются каждый день, добавляются новые затронутые страны и так далее. Если бы мы тренировались с использованием последних данных, нам также потребовалось бы обновить все связанные данные: новые страны предполагают большую плотность населения, которую нужно извлекать и добавлять к нашим функциям, продолжать отслеживать правительственные меры противодействия и так далее. Даже анализ классификации признаков нужно делать заново!

Теперь, когда мы объяснили все это, мы можем просто сказать, что мы будем брать в качестве данных данные с начала отчетов (22.01.2020) по 31 марта (31.03.2020).

Но! Примите во внимание, что в наборе данных есть страны, в которых подтвержденные случаи заболевания начались позже, чем в других. Это означает, что группа стран имеет группу дней подряд со значением 0 case (вы можете проверить исходный репозиторий, из которого мы берем наши данные) . Мы должны удалить их из наших образцов! В противном случае система не будет обучаться должным образом, поскольку она увидит оба примера, которые говорят, что для количества случаев 0 в один день на следующий день снова будет 0, и другие примеры, которые сообщают сети, что с количеством 0 однажды, на следующий день он поднимается (отправная точка подтвержденных случаев в этой стране). Подводя итог: рассмотрите в качестве первой выборки для каждой страны первый день с COVID-19 случаями.

Модель и структура

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

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

Где d субиндекс обозначает конкретный день, а c - конкретную страну. Но здесь мы проводим контролируемое обучение, поэтому мы знаем, что нам нужно обучить нашу модель на примере с желаемым результатом. Итак, каков результат для этого образца? Мы ожидаем прогноза - это подтвержденные случаи, но на следующий день!

Заметив, что у нас есть 6 входов, первый параметр нашей модели ясен. Пришло время выбрать, какую модель мы строим. Возможно, вы думаете о RNN, потому что они хорошо справляются с задачами регрессии. Это может быть полезно, если мы работаем с последовательным подходом к корреляции данных, чего мы не собираемся делать, поскольку мы хотим прогнозировать на основе последнего дня. Это заставляет нас выбрать более простой подход, и мы посмотрим, работает ли он: последовательная нейронная сеть. Почему?

Что ж, вот и весь ваш опыт, догадки и имеющиеся данные. Мы знаем, что статистические модели для эпидемий имеют форму функции Гаусса в случае активных случаев. Здесь мы будем работать с подтвержденными случаями, то есть с общим количеством случаев, мы не вычитаем ни выздоровевших, ни смертельных случаев. Для этого показателя он имеет сигмовидную форму: нормализованные случаи могут изменяться от 0 до 1, это означает, что, согласно сообщениям, все население пострадало от вируса. Таким образом, мы можем получить приблизительный подход к сложности, которую должна будет решить наша модель, так что это хорошая отправная точка для проектирования модели. Сложив достаточно (и не больше, из-за небольшого количества доступных данных и избежания чрезмерного подгонки) скрытых слоев подходящего размера, наша проблема имеет большие шансы быть решена или аппроксимирована с достаточной точностью.

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

Это будет реализовано в PyTorch с использованием MSELoss и применением mean уменьшения для каждого batch. Для тех, кому нужен быстрый обзор некоторых возможных показателей, здесь у вас есть краткая справка в Microsoft docs.

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

  • Модель A: скрытый слой размера Y, за которым следует второй скрытый слой размера Y / 2.

  • Модель B: скрытый слой размера Y, за которым следует второй скрытый слой размером Y * 2 + 1.

  • Модель C: скрытый слой размера Y, за которым следует второй скрытый слой размера Y * 2 + 1 и, наконец, третий слой размера Y.

И последнее, но не менее важное: обратите внимание, что мы собираемся реализовать это с помощью PyTorch, что позволит нам (среди других преимуществ) иметь хорошую производительность процессора. Не о чем беспокоиться с ограниченным объемом данных, верно? Но помните, что мы все равно захотим реализовать какой-то цикл обучения.

Обучение

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

  • Набор данных для обучения: мы используем 90% имеющихся данных для обучения. То есть: 90% того, что у нас есть с самого начала данных до 31 марта (после предварительной очистки).

Полезные образцы: 4395
Образцы до 31 марта: 3278
Образцы после 31 марта: 1117

  • Скорость обучения: 0.00001
  • Пакет: мы принимаем 48 в качестве размера пакета.
  • Эпохи: мы рассмотрим весь набор обучающих данных 50 раз.
  • Размер скрытого слоя: да, мы знаем, что чуть выше мы сказали, что это будет последовательная модель с 6, а затем 3 нейронами, но ... что, если это не лучший вариант? Для начала выберем 12 (затем 6 на втором слое).

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

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

  • Массив скорости обучения: [0.0001, 0.00001, 0.000001]
  • Массив размера партии: [8, 12, 48]
  • Массив эпох: [50, 100, 150, 200]
  • Массив размеров скрытого слоя модели: [3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25]

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

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

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

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

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

Благодаря нашему циклу обучения мы обучили тысячи моделей, поэтому мы собираемся просто придерживаться этих лучших моделей потерь val, чтобы увидеть, как они работают: имейте в виду, что они, вероятно, не лучший тот, который у нас есть!

Модель A: 200 эпох, размер пакета 8, lr 1e-4, скрытый размер Y = 25.

Окончательная валидационная потеря: 0,06039683411193148

Модель B: 200 эпох, размер пакета, 12, lr 1e-4, скрытый размер Y = 3.

Окончательная валидационная потеря: 0,05095066449471882

Модель C: 50 эпох, размер пакета 8, lr 1e-4, скрытый размер Y = 20.

Окончательная валидационная потеря: 0,04294525007376584

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

Во-первых, это может быть шкала оси Y, которая начинается с высокого значения и не позволяет нам увидеть меньшие улучшения в более поздние эпохи.

Во-вторых, вы, возможно, задаетесь вопросом… Почему нет переоборудования? Мы можем подумать об основных причинах этой проблемы, но простота нашей модели и функций может указывать на то, что модель изучила все, что доступно. Поскольку самой модели не хватает сложности, она не может больше учиться (по крайней мере, не забывая чего-то еще), поэтому такого переобучения никогда не происходит. Это будет означать ... Что, возможно, мы сможем расширить нашу модель, чтобы попытаться добиться лучших результатов!

Прогноз

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

Мы собираемся проверить это тремя разными способами!

Вы помните, о чем мы говорили в разделе Данные, используемые для обучения? Точнее, мы использовали в качестве данных до 31 марта. Что ж, мы начнем наши прогнозы и проверки с той же точки.

  1. Мы собираемся проанализировать фактические подтвержденные случаи, зарегистрированные каждой страной с 31 марта по 6 апреля. Почему? Потому что мы заполнили наши данные до сих пор, поэтому у нас есть функции, необходимые для прогнозирования в эти дни.
  2. Затем мы собираемся спрогнозировать на основе данных за каждый день (с 1 по 6 апреля) прогноз на следующий день и построить его в сравнении с предыдущим. Таким образом, мы можем увидеть ошибку или отклонение наших моделей от отчетных данных по странам.

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

Прежде чем продолжить, имейте в виду, что мы можем сделать это для всех стран, которые сообщили о случаях заражения вирусом. По крайней мере, это непрактично из-за количества стран. Мы выберем пару стран и повторим этот процесс с ними, чтобы было несколько сравнений, и да… Испания будет одной из них :)

Примеры прогнозов - Испания

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

Примеры прогнозов - Франция

Из этих трех моделей мы снова видим, что модель B является той, которая работает лучше всего (для обеих стран одновременно), демонстрируя очень низкие отклонения в прогнозах на некоторые дни.

Вывод

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

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

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

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

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

Будущие шаги

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

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

2. О нашем прогнозе или модели:

  • Как отмечалось выше, прежде чем показывать некоторые прогнозы, похоже, что более сложные модели (то есть больше, больше нейронов, больше слоев) смогут дальше учиться на нашем наборе данных. Почему бы не попробовать?
  • Мы провели исследование подтвержденных случаев. Его можно применить к смертельным случаям и вылеченным случаям (также, например, добавив новую функцию age).
  • Также можно попробовать другой тип сети, например, RNN с комментариями.

3. Есть возможности для технических улучшений:

  • Улучшение кода для производительности.
  • Добавление дополнительных настраиваемых параметров для сценария обучения.

Прочие соображения

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

Нам известно, что некоторые из используемых данных могут быть неточными: плотность населения, отчеты о подтвержденных случаях (из-за отчетов правительства…) и так далее. Мы изо всех сил старались использовать согласованную базу данных для упражнения, но мы понимаем и осознаем, что идеально смоделировать его невозможно!

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

Благодаря

Очень хочу поблагодарить:

  • Пабло Гомес, чьи усилия и помощь в сборе данных сделали возможным осуществление этого проекта.
  • Рикардо Вильялобос, который помог зарегистрировать наши источники данных в репозитории.
  • Javier VGD, который помог нам как в рецензировании этой истории, так и в решении некоторых проблем с кодированием.

Другие ссылки