18 min read

Manejar datos de COVID-19 con R

Uso responsable de los datos

En esta entrada presentamos los pasos necesarios para descargar, actualizar y explorar con R datos sobre el COVID-19. Utilizaremos los datos elaborados y mantenidos por el Center for Systems Science and Engineering de la Universidad John Hopkins. Estos datos se han usado para crear visualizaciones que han tenido alguna trascendencia pública, sobre todo de las trayectorias de diferentes países con respecto a casos confirmados, defunciones y altas.

Disponer de datos actualizados sobre un evento relevante al mismo tiempo que este está ocurriendo es un avance desde el punto de vista de la ciencia abierta y libertad de los datos. De hecho esta entrada busca aportar a la accesibilidad de estos datos abiertos. Pero con gran poder viene gran responsabilidad y vale la pena hacer algunas consideraciones.

  1. La ciencia necesita datos, los datos necesitan ciencia. La ciencia dispone de teorías y modelos específicos de un campo de estudio que superaron pruebas de replicación y utilidad. Estos existen en los papers y, sobre todo, en la experiencia de las personas que hacen el trabajo, que han errado y aprendido de errores propios y ajenos. No sería razonable esperar un “descubrimiento” aplicando algún algoritmo exótico sobre estos datos. Al respecto, es excelente esta entrada de Rob Hyndman.

XKCD de turno

  1. En condiciones de epidemia la información es muy valiosa y la desinformación especialmente peligrosa. Es fácil analizar los datos de manera sesgada o inexacta (voluntaria o involuntariamente) y generar alarma en un momento delicado. A los gráficos de esta entrada les agregué la nota “Elaboración amateur” para aclarar que no consulté a expertos o expertas con competencias en el tema.

  2. Los datos que compila CSSE-JHU son datos oficiales. En este dataset no hay datos de mejor calidad que los que las autoridades hacen públicos día a día. De hecho son esos mismos datos, solo que reunidos y sistematizados por cuestiones de conveniencia. No son evidencia de una conspiración o el hilo negro de nada.

  3. Consulte la documentación y condiciones de uso del provedor de los datos. Desde la web del repostorio podemos acceder a los archivos README.md, con detalles sobre los datos, fuentes y formato en los que se ofrecen.

Dicho esto, para la población nerd en cuarentena, los pasos para descargar, actualizar y manejar los datos del COVID-19.

Descargar los datos con git

Los datos COVID-19 CSSE-JHS están disponibles en un repositorio Git alojado en GitHub. La forma más simple y eficiente de descargarlos y manterlos actualizados es clonando el repositorio en una carpeta local de nuestra computadora, usando el software git. git está muy vinculado con el desarrollo de Linux y no es llamativo que desde Linux sea extremadamente fácil. En otros sistemas operativos podría ser más complicado.

En Linux abrimos una terminal, y nos dirigimos a la carpeta raíz en la que se ubicará el repositorio. En mi caso es la carpeta ~/Documentos/ejercicios, así que:

cd Documentos/ejercicios

Y luego introducir el comando

git clone https://github.com/CSSEGISandData/COVID-19.git

Un minuto para que se descarguen y ya está.

Actualizar los datos

En la ubicación en la que están los datos y desde la terminal introducir

git pull

Una par de segundos y ya tenemos los datos acutualizados.

Importante no hay manera razonable de dañar los datos originales del repositorio usando git. Copie, borre, modifique: los datos en el servidor están a salvo ya que no podrá modificarlos sin autorización expresa de quienes mantienen el repositorio. Si su carperta de datos se daña o corrompe simplemente clonelo otra vez en otra carpeta.

OS != Linux

En otros sistemas operativos es el mismo procedimiento desde una terminal, sólo que antes es necesario instalar git.

Los archivos de datos

Con el repositorio clonado podemos ver su estructura de directorios:

├── archived_data
│   ├── archived_daily_case_updates
│   └── archived_time_series
├── csse_covid_19_data
│   ├── csse_covid_19_daily_reports
│   └── csse_covid_19_time_series
└── who_covid_19_situation_reports
    ├── who_covid_19_sit_rep_pdfs
    └── who_covid_19_sit_rep_time_series

Los datos que nos interesan están en ../../csse_covid_19_data/daily_reports. Los demás son reportes o datos antiguos que se conservan por cuestiones de compatibilidad y fueron integrados a a los datos vigentes o los mismos datos en formato de serie de tiempo.

¿Qué hay ahí en daily_reports?

1-22-2020.csv  01-30-2020.csv  02-07-2020.csv  02-15-2020.csv  02-23-2020.csv  03-02-2020.csv  03-10-2020.csv  03-18-2020.csv
01-23-2020.csv  01-31-2020.csv  02-08-2020.csv  02-16-2020.csv  02-24-2020.csv  03-03-2020.csv  03-11-2020.csv  03-19-2020.csv
01-24-2020.csv  02-01-2020.csv  02-09-2020.csv  02-17-2020.csv  02-25-2020.csv  03-04-2020.csv  03-12-2020.csv  03-20-2020.csv
01-25-2020.csv  02-02-2020.csv  02-10-2020.csv  02-18-2020.csv  02-26-2020.csv  03-05-2020.csv  03-13-2020.csv  03-21-2020.csv
01-26-2020.csv  02-03-2020.csv  02-11-2020.csv  02-19-2020.csv  02-27-2020.csv  03-06-2020.csv  03-14-2020.csv  README.md
01-27-2020.csv  02-04-2020.csv  02-12-2020.csv  02-20-2020.csv  02-28-2020.csv  03-07-2020.csv  03-15-2020.csv
01-28-2020.csv  02-05-2020.csv  02-13-2020.csv  02-21-2020.csv  02-29-2020.csv  03-08-2020.csv  03-16-2020.csv
01-29-2020.csv  02-06-2020.csv  02-14-2020.csv  02-22-2020.csv  03-01-2020.csv  03-09-2020.csv  03-17-2020.csv

Un archivo .csv con los datos de cada día y un archivo MarkDown vacio. No hay una base de datos unificada, así que vamos a tener que leer y reunir los datos con R.

Manejo en R

El primer paso es crear un proyecto en la carpeta en la que están los datos. Desde Rstudio vamos al menú File > New Project > Existing Directory e indicamos en qué carpeta se clonaron los datos. No indicamos la carpeta en la que están los archivos .csv, sino la raíz. El proyecto debería abrirse en el lugar correcto.

Con el proyecto funcionando es buena idea abrir un script nuevo para hacer la carga de datos.

Listar los archivos

Como son muchos archivos separados vamos a usar un iterador para leerlos a todos de una vez. Primero tenemos que crear la lista de archivos a leer, que será el input del iterador. Usamos la función list.files(). Es importante que el directorio de trabajo de R esté en la raíz del proyecto, podemos verificarlo con getwd().

list.files(path = "./csse_covid_19_data/csse_covid_19_daily_reports/")
##  [1] "01-22-2020.csv" "01-23-2020.csv" "01-24-2020.csv" "01-25-2020.csv"
##  [5] "01-26-2020.csv" "01-27-2020.csv" "01-28-2020.csv" "01-29-2020.csv"
##  [9] "01-30-2020.csv" "01-31-2020.csv" "02-01-2020.csv" "02-02-2020.csv"
## [13] "02-03-2020.csv" "02-04-2020.csv" "02-05-2020.csv" "02-06-2020.csv"
## [17] "02-07-2020.csv" "02-08-2020.csv" "02-09-2020.csv" "02-10-2020.csv"
## [21] "02-11-2020.csv" "02-12-2020.csv" "02-13-2020.csv" "02-14-2020.csv"
## [25] "02-15-2020.csv" "02-16-2020.csv" "02-17-2020.csv" "02-18-2020.csv"
## [29] "02-19-2020.csv" "02-20-2020.csv" "02-21-2020.csv" "02-22-2020.csv"
## [33] "02-23-2020.csv" "02-24-2020.csv" "02-25-2020.csv" "02-26-2020.csv"
## [37] "02-27-2020.csv" "02-28-2020.csv" "02-29-2020.csv" "03-01-2020.csv"
## [41] "03-02-2020.csv" "03-03-2020.csv" "03-04-2020.csv" "03-05-2020.csv"
## [45] "03-06-2020.csv" "03-07-2020.csv" "03-08-2020.csv" "03-09-2020.csv"
## [49] "03-10-2020.csv" "03-11-2020.csv" "03-12-2020.csv" "03-13-2020.csv"
## [53] "03-14-2020.csv" "03-15-2020.csv" "03-16-2020.csv" "03-17-2020.csv"
## [57] "03-18-2020.csv" "03-19-2020.csv" "03-20-2020.csv" "03-21-2020.csv"
## [61] "README.md"

Tenemos la lista de archivos, excepto que sobra uno: README.md. Podemos eliminarlo de la lista usando una expresión regular como segundo argumento de la función. Esta será muy simple y capturará a todos los archivos cuyo nombre tenga la cadena “csv”. Además queremos trabajar con path completo del archivo, así que pasamos el argumento full.names = TRUE.1

list.files(path = "./csse_covid_19_data/csse_covid_19_daily_reports/", pattern = "csv", full.names = TRUE)
##  [1] "../../csse_covid_19_data/csse_covid_19_daily_reports//01-22-2020.csv"
##  [2] "../../csse_covid_19_data/csse_covid_19_daily_reports//01-23-2020.csv"
##  [3] "../../csse_covid_19_data/csse_covid_19_daily_reports//01-24-2020.csv"
##  [4] "../../csse_covid_19_data/csse_covid_19_daily_reports//01-25-2020.csv"
##  [5] "../../csse_covid_19_data/csse_covid_19_daily_reports//01-26-2020.csv"
##  [6] "../../csse_covid_19_data/csse_covid_19_daily_reports//01-27-2020.csv"
##  [7] "../../csse_covid_19_data/csse_covid_19_daily_reports//01-28-2020.csv"
##  [8] "../../csse_covid_19_data/csse_covid_19_daily_reports//01-29-2020.csv"
##  [9] "../../csse_covid_19_data/csse_covid_19_daily_reports//01-30-2020.csv"
## [10] "../../csse_covid_19_data/csse_covid_19_daily_reports//01-31-2020.csv"
## [11] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-01-2020.csv"
## [12] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-02-2020.csv"
## [13] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-03-2020.csv"
## [14] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-04-2020.csv"
## [15] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-05-2020.csv"
## [16] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-06-2020.csv"
## [17] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-07-2020.csv"
## [18] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-08-2020.csv"
## [19] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-09-2020.csv"
## [20] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-10-2020.csv"
## [21] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-11-2020.csv"
## [22] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-12-2020.csv"
## [23] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-13-2020.csv"
## [24] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-14-2020.csv"
## [25] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-15-2020.csv"
## [26] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-16-2020.csv"
## [27] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-17-2020.csv"
## [28] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-18-2020.csv"
## [29] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-19-2020.csv"
## [30] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-20-2020.csv"
## [31] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-21-2020.csv"
## [32] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-22-2020.csv"
## [33] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-23-2020.csv"
## [34] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-24-2020.csv"
## [35] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-25-2020.csv"
## [36] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-26-2020.csv"
## [37] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-27-2020.csv"
## [38] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-28-2020.csv"
## [39] "../../csse_covid_19_data/csse_covid_19_daily_reports//02-29-2020.csv"
## [40] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-01-2020.csv"
## [41] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-02-2020.csv"
## [42] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-03-2020.csv"
## [43] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-04-2020.csv"
## [44] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-05-2020.csv"
## [45] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-06-2020.csv"
## [46] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-07-2020.csv"
## [47] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-08-2020.csv"
## [48] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-09-2020.csv"
## [49] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-10-2020.csv"
## [50] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-11-2020.csv"
## [51] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-12-2020.csv"
## [52] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-13-2020.csv"
## [53] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-14-2020.csv"
## [54] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-15-2020.csv"
## [55] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-16-2020.csv"
## [56] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-17-2020.csv"
## [57] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-18-2020.csv"
## [58] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-19-2020.csv"
## [59] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-20-2020.csv"
## [60] "../../csse_covid_19_data/csse_covid_19_daily_reports//03-21-2020.csv"

Leer y reunir los datos en un data.frame

El siguiente paso es usar un iterador para que lea cada archivo como un data.frame separado y los reúna en una lista Usaremos map() de la librería purr, pero lapply() estaría bien también.

library(tidyverse)  #Carga la librería purr

list.files(path = "./csse_covid_19_data/csse_covid_19_daily_reports/", pattern = "csv", full.names = TRUE) %>% 
  map(read_csv) %>% 
  .[11:12]           #Muestro solamente los data.frame 11 y 12 de la lista
## [[1]]
## # A tibble: 67 x 6
##    `Province/State` `Country/Region` `Last Update`  Confirmed Deaths Recovered
##    <chr>            <chr>            <chr>              <dbl>  <dbl>     <dbl>
##  1 Hubei            Mainland China   2/1/2020 11:53      7153    249       168
##  2 Zhejiang         Mainland China   2/1/2020 10:53       599      0        21
##  3 Guangdong        Mainland China   2/1/2020 14:23       535      0        14
##  4 Henan            Mainland China   2/1/2020 1:52        422      2         3
##  5 Hunan            Mainland China   2/1/2020 11:03       389      0         8
##  6 Anhui            Mainland China   2/1/2020 13:33       297      0         5
##  7 Jiangxi          Mainland China   2/1/2020 1:52        286      0         9
##  8 Chongqing        Mainland China   2/1/2020 8:43        247      1         3
##  9 Sichuan          Mainland China   2/1/2020 1:52        207      1         3
## 10 Shandong         Mainland China   2/1/2020 7:51        206      0         3
## # … with 57 more rows
## 
## [[2]]
## # A tibble: 67 x 6
##    `Province/State` `Country/Region` `Last Update`       Confirmed Deaths
##    <chr>            <chr>            <dttm>                  <dbl>  <dbl>
##  1 Hubei            Mainland China   2020-02-02 23:43:02     11177    350
##  2 Zhejiang         Mainland China   2020-02-02 18:03:05       661      0
##  3 Guangdong        Mainland China   2020-02-02 18:03:05       632      0
##  4 Henan            Mainland China   2020-02-02 18:03:05       493      2
##  5 Hunan            Mainland China   2020-02-02 18:03:05       463      0
##  6 Anhui            Mainland China   2020-02-02 18:03:05       340      0
##  7 Jiangxi          Mainland China   2020-02-02 18:03:05       333      0
##  8 Chongqing        Mainland China   2020-02-02 23:33:02       300      2
##  9 Jiangsu          Mainland China   2020-02-02 18:03:05       236      0
## 10 Sichuan          Mainland China   2020-02-02 18:03:05       231      1
## # … with 57 more rows, and 1 more variable: Recovered <dbl>

Hay dos problemas.

  1. Entre el 1 y 2 de febrero hubo un cambio en el formato de fecha de la variable Last Update. A partir del 2020-02-02 se utiliza un dato de fecha/hora en formato ISO 8601, en los anteriores se usa el más coloquial (en EEUU) mes/día/año. Es necesario arreglarlo antes de poder unir todas columnas. Como sabemos que el problema lo tienen los 11 primeros data.frame de la lista directamente los modificamos y llevamos a formato de fecha. Usamos la función mdy_hm() de la librería lubridate. Se leería como mesdíaaño_horaminuto y nos regresa un elemento de las clases POSIXct y POSIXt , es decir, de fecha y hora.

  2. Los nombres tienen espacios y caracteres reservados por R como /, así que nos van a dar trabajo para escribirlos en la consola. Usamos clean_names() de la librería janitor para arreglarlos.

library(lubridate)
library(janitor)

list.files("./csse_covid_19_data/csse_covid_19_daily_reports", 
           "csv",     
           full.names = TRUE) %>%
  map(read_csv) %>%
  map(clean_names) %>%     #también uso map para que lo haga a cada elemento de la lista que entrega %>% 
  modify_at(1:11,          #los primeros 11 archivos tienen un formato de fecha diferente al resto 
            ~mutate(.x, last_update = mdy_hm(last_update))) %>%   #Los paso a fecha/hora, será iguales a los siguientes.
  .[11:12]                                                        #Muestro solo los datos problemáticos
## [[1]]
## # A tibble: 67 x 6
##    province_state country_region last_update         confirmed deaths recovered
##    <chr>          <chr>          <dttm>                  <dbl>  <dbl>     <dbl>
##  1 Hubei          Mainland China 2020-02-01 11:53:00      7153    249       168
##  2 Zhejiang       Mainland China 2020-02-01 10:53:00       599      0        21
##  3 Guangdong      Mainland China 2020-02-01 14:23:00       535      0        14
##  4 Henan          Mainland China 2020-02-01 01:52:00       422      2         3
##  5 Hunan          Mainland China 2020-02-01 11:03:00       389      0         8
##  6 Anhui          Mainland China 2020-02-01 13:33:00       297      0         5
##  7 Jiangxi        Mainland China 2020-02-01 01:52:00       286      0         9
##  8 Chongqing      Mainland China 2020-02-01 08:43:00       247      1         3
##  9 Sichuan        Mainland China 2020-02-01 01:52:00       207      1         3
## 10 Shandong       Mainland China 2020-02-01 07:51:00       206      0         3
## # … with 57 more rows
## 
## [[2]]
## # A tibble: 67 x 6
##    province_state country_region last_update         confirmed deaths recovered
##    <chr>          <chr>          <dttm>                  <dbl>  <dbl>     <dbl>
##  1 Hubei          Mainland China 2020-02-02 23:43:02     11177    350       295
##  2 Zhejiang       Mainland China 2020-02-02 18:03:05       661      0        32
##  3 Guangdong      Mainland China 2020-02-02 18:03:05       632      0        15
##  4 Henan          Mainland China 2020-02-02 18:03:05       493      2        10
##  5 Hunan          Mainland China 2020-02-02 18:03:05       463      0        16
##  6 Anhui          Mainland China 2020-02-02 18:03:05       340      0         7
##  7 Jiangxi        Mainland China 2020-02-02 18:03:05       333      0        12
##  8 Chongqing      Mainland China 2020-02-02 23:33:02       300      2         7
##  9 Jiangsu        Mainland China 2020-02-02 18:03:05       236      0         7
## 10 Sichuan        Mainland China 2020-02-02 18:03:05       231      1        11
## # … with 57 more rows

Resuelto el problema podemos reunir los datos en un data.frame con la función bind_rows

list.files("./csse_covid_19_data/csse_covid_19_daily_reports", 
           "csv",     
           full.names = TRUE) %>%
  map(read_csv) %>%
  map(clean_names) %>%     
  modify_at(1:11, ~mutate(.x, last_update = mdy_hm(last_update))) %>%
  bind_rows() -> covid19   #asigno nombre a los datos

Finalmente tenemos los datos cargados. ¿Qué forma tienen?

covid19
## # A tibble: 7,617 x 8
##    province_state country_region last_update         confirmed deaths recovered
##    <chr>          <chr>          <dttm>                  <dbl>  <dbl>     <dbl>
##  1 Anhui          Mainland China 2020-01-22 17:00:00         1     NA        NA
##  2 Beijing        Mainland China 2020-01-22 17:00:00        14     NA        NA
##  3 Chongqing      Mainland China 2020-01-22 17:00:00         6     NA        NA
##  4 Fujian         Mainland China 2020-01-22 17:00:00         1     NA        NA
##  5 Gansu          Mainland China 2020-01-22 17:00:00        NA     NA        NA
##  6 Guangdong      Mainland China 2020-01-22 17:00:00        26     NA        NA
##  7 Guangxi        Mainland China 2020-01-22 17:00:00         2     NA        NA
##  8 Guizhou        Mainland China 2020-01-22 17:00:00         1     NA        NA
##  9 Hainan         Mainland China 2020-01-22 17:00:00         4     NA        NA
## 10 Hebei          Mainland China 2020-01-22 17:00:00         1     NA        NA
## # … with 7,607 more rows, and 2 more variables: latitude <dbl>, longitude <dbl>

Cada columna es un atributo: lugar, fecha, casos confirmados, defunciones y recuperados. Cada fila una observación de todos estos atributos. Si nos interesan los datos por países debemos considerar que algunos publican datos desagregados por unidades territoriales y otros solamente el total nacional. Es importante agregar esos datos si queremos hacer comparaciones a nivel nacional. No lo vamos a desarrollar porque los países que nos interesan no tienen datos desagregados, pero así podríamos resolverlo:

covid19 %>% 
  group_by(country_region, last_update) %>% 
  summarise_at(vars(confirmed:recovered), ~sum(.)) %>% #Listo, lo que sigue es un test
  filter(country_region == "Mainland China") %>%       #reviso datos China que están desagregados
  arrange(desc(last_update))                           #Un registro por día a nivel nacional
## # A tibble: 687 x 5
## # Groups:   country_region [1]
##    country_region last_update         confirmed deaths recovered
##    <chr>          <dttm>                  <dbl>  <dbl>     <dbl>
##  1 Mainland China 2020-03-11 02:18:29         0      0         0
##  2 Mainland China 2020-03-11 02:18:28         0      0         0
##  3 Mainland China 2020-03-10 23:53:02       709      6       678
##  4 Mainland China 2020-03-10 15:13:05     67760   3024     47743
##  5 Mainland China 2020-03-10 14:33:13       125      2        88
##  6 Mainland China 2020-03-10 10:33:02      1215      1      1191
##  7 Mainland China 2020-03-10 10:13:25       136      3       131
##  8 Mainland China 2020-03-10 10:13:17       539      3       478
##  9 Mainland China 2020-03-10 09:13:07      1353      8      1274
## 10 Mainland China 2020-03-10 08:53:02       245      1       227
## # … with 677 more rows

Graficar datos de América Latina

Uno de las visualizaciones más interesantes que han circulado es el gráfico del Financial Times presentando el crecimiento en casos confirmados en diferentes países tomando como orígen el día en el que en cada país se confirmó el caso número 100.

Vamos a reproducir ese gráfico de manera muy básica para algunos países de América Latina:

paises <- c("Mexico", "Argentina", "Brazil", "Uruguay", "Bolivia", 
            "Ecuador", "Chile", "Cuba", "Peru", "Colombia", "Venezuela", "Paraguay")

covid19 %>% 
  filter(country_region %in% paises) %>%    #ME quedo solo con los países que me interesan, definidos más arriba en un vector
  group_by(country_region) %>%              #Agrupo por país, modifica como funciona el filtro que sigue
  filter(confirmed >= 10) %>%               #Dentro de cada país me quedo solamente con las filas en las que hay 10 o más casos confirmados
  mutate(indice = 1:n()) %>%                #Creo un índice consecutivo de días desde la primera observación, aprovecho que los datos están ordenados.
  
#Hago el gráfico
  ggplot(aes(x = indice,
             y = confirmed, 
             color = country_region)) + 
  geom_point() +                            #Solo uso puntos
  labs( 
       x = "Días transcurridos de el caso confirmado número 10", 
       y = "Casos confirmados", 
       color = "País") + 
  theme_minimal()                           #Rayo desStatizador

El gráfico es elocuente, pero algo difícil de leer. Como el interés es lúdico vamos a “mejorarlo” agregando líneas y un emoji con la bandera de cada país en línea en reemplazo de la clave de color. Pasos:

  1. Crear una tabla relacional de nombres de país y emoji de bandera.
#No está en CRAN, instalar con 
# install.packages("devtools")
#devtools::install_github("hadley/emo")

library(emo)    #Para manejar emojis

# La función ji()
tibble(country_region = paises, 
       bandera = map_chr(paises, ~emo::ji(.x))
       )  -> emo_país

emo_país
## # A tibble: 12 x 2
##    country_region bandera
##    <chr>          <chr>  
##  1 Mexico         🇲🇽     
##  2 Argentina      🇦🇷     
##  3 Brazil         🇧🇷     
##  4 Uruguay        🇺🇾     
##  5 Bolivia        🇧🇴     
##  6 Ecuador        🇪🇨     
##  7 Chile          🇨🇱     
##  8 Cuba           🇨🇺     
##  9 Peru           🇵🇪     
## 10 Colombia       🇨🇴     
## 11 Venezuela      🇻🇪     
## 12 Paraguay       🇵🇾
  1. Ubicamos el último día de serie de cada país, así el emoji aparece solamente ahí:
covid19 %>% 
  clean_names() %>% 
  inner_join(emo_país) %>%        #Filtro los países que me interesan con un join, de paso agrego las banderas
  group_by(country_region) %>%       
  filter(confirmed >= 10) %>% 
  mutate(indice = 1:n() -1) %>% 
  filter(indice == max(indice)   #Conservo solamente el valor más alto
         ) -> ultima_data        #Los usaré para para los emojis...

ultima_data
## # A tibble: 12 x 10
## # Groups:   country_region [12]
##    province_state country_region last_update         confirmed deaths recovered
##    <chr>          <chr>          <dttm>                  <dbl>  <dbl>     <dbl>
##  1 <NA>           Brazil         2020-03-21 17:43:03      1021     15         2
##  2 <NA>           Chile          2020-03-21 14:43:03       537      0         6
##  3 <NA>           Ecuador        2020-03-21 20:43:02       506      7         3
##  4 <NA>           Peru           2020-03-21 20:43:02       318      5         1
##  5 <NA>           Mexico         2020-03-21 12:13:19       203      2         4
##  6 <NA>           Colombia       2020-03-21 17:43:03       196      0         1
##  7 <NA>           Argentina      2020-03-21 15:13:44       158      4         3
##  8 <NA>           Uruguay        2020-03-21 12:13:19       110      0         0
##  9 <NA>           Venezuela      2020-03-21 20:43:03        70      0         0
## 10 <NA>           Cuba           2020-03-21 02:13:32        21      1         0
## 11 <NA>           Bolivia        2020-03-21 12:43:08        19      0         0
## 12 <NA>           Paraguay       2020-03-21 12:43:08        18      1         0
## # … with 4 more variables: latitude <dbl>, longitude <dbl>, bandera <chr>,
## #   indice <dbl>
  1. ggplot2
covid19 %>% 
  clean_names() %>% 
  inner_join(emo_país) %>% 
  group_by(country_region) %>% 
  filter(confirmed >= 10) %>% 
  mutate(indice = 1:n() -1) %>% 
  ggplot(aes(x=indice, 
             y = confirmed, 
             color = country_region)) +
    geom_point(size = 4, alpha = 0.4) +
    geom_line() +       #Agrego una línea

    geom_text(data = ultima_data,     #Otra serie de datos, la que creamos ad hoc con cada último registro
              aes(x = indice, 
                  y = confirmed, 
                  label = bandera), 
              size = 8) +           #tamaño del emoji
    theme_minimal() + 
    labs(x = "Días trascurridos desde que confirmaron 10 casos", 
         y = "Casos confirmados", 
         caption = "Elaboración amateur con datos JHU-CSSE actualizados al 21-03-2020") +
    theme(legend.position = "none") +
  scale_x_continuous(breaks = seq(0, 30, 2))

El crecimiento de los casos confirmados de Argentina parecería un poco más plano que el de algunos de sus vecinos. Es difícil comparar directamente con Brasil o Ecuador, que tuvieron su décimo caso confirmado tres días antes. Tres días pueden hacer mucha diferencia y en estos casos el crecimiento no es lineal. En todo caso no sería respondable hacer un pronóstico.

  1. Un poco más solemne
library(ggrepel)
covid19 %>% 
  clean_names() %>% 
  inner_join(emo_país) %>% 
  group_by(country_region) %>% 
  filter(confirmed >= 10) %>% 
  mutate(indice = 1:n() -1) %>% 
  ggplot(aes(x=indice, 
             y = confirmed, 
             linetype = country_region), 
         alpha = 0.5) +
    geom_point() +
    geom_line()   +    #Agrego una línea

    geom_label_repel(data = ultima_data,     #Otra serie de datos, la que creamos ad hoc
                     aes(x = indice, 
                         y = confirmed, 
                         label = country_region), 
                     size = 4) +           #tamaño de la etiqueta
    theme_minimal() + 
    labs(x = "Días trascurridos desde que confirmaron 10 casos", 
         y = "Casos confirmados", 
         caption = "Elaboración amateur con datos JHU-CSSE actualizados al 21-03-2020") +
    theme(legend.position = "none") +
  scale_x_continuous(breaks = seq(0, 30, 2)) 

Cartograma

Otra forma de presentar la información es a través de un cartograma. Los datos prácticamente están ahí. La estructura para_emoji ya tiene

  1. Filtrado el último dato de cada país.
  2. La cantidad de casos confirmados.
  3. Datos de latitud y longitud.
ultima_data %>% 
  ggplot(aes(x= longitude, y = latitude)) + 
  geom_point(aes(size = confirmed))

No es muy claro. Ayudaría poner una capa debajo con los polígonos de los países a los que corresponden, para dar claves visuales. La librería sf nos facilita mucho la manipulación y visualización de polígonos y desde rnaturalearth podemos descargar polígonos básicos, como un mapa del mundo.

También podría ayudar modificar la escala de los puntos, para hacer más visibles las diferencias de tamaño.

En cualquier caso un cartograma estático no es una buena visualización de estos datos: no es informativo sobre el transcurso del tiempo y al tener dos escalas de tamaños (de superficie del país y de diámetro conteo de casos) en un mismo gráfico dificulta la comparación. Por ejemplo, Ecuador tiene menos casos confirmados que Brasil, pero se ve “rodeado”.

library(sf)
library(rnaturalearth) 

# Shapefile mundial

latam <- ne_countries(scale = "medium", returnclass = "sf") %>% 
  filter(region_wb %in% c("Latin America & Caribbean")) 

ggplot(latam) + 
  geom_sf(fill = "white") + 
  geom_point(data = ultima_data, 
             aes(x = longitude, y = latitude, size = confirmed), 
             alpha = 0.7,                 #una ligera transparencia
             color = "orange") + 
  theme_minimal() + 
  theme(legend.position = c(0.2, 0.4)) +  #Aprovecho el espacio ubicando la legenda en Pacífico 
  scale_size_area(max_size = 18, 
                  breaks = c(50, 100, 500, 1000, 2000)) + 
  labs (title = "Un cartograma no permite visualizar el cambio en el tiempo",
        x= NULL, 
        y = NULL, 
        size = "Casos confirmados", 
        caption = "Elaboración amateur con datos JHU-CSSE actualizados al 21-03-2020")


  1. Hay una ligera divergencia entre el path que debería usar en su computadora y el que uso en este ejemplo. Dado que escribo esto desde el repositorio que controla el sitio web que está viendo y por cuestiones de las carpetas que usa Hugo debo ubicarme dos carpetas detrás para encontrar los datos, por eso el path se antecede por ../../. Si no lo notó no se preocupe, si lo notó seguramente entiende cual es el problema.