Uso de la API del Covid19 con Pandas

Origen de los datos

En esta práctica, se va a usar la API COVID-19, desarrollada por Kyle Redelinghuys y basada en los datos que proporciona la Universidad Johns Hopkins.

Instalación de las librerías con las que se va a trabajar

En primer lugar, es necesario instalar la librería para proyectar los datos que se obtengan sobre la incidencia de la COVID-19. Para ello, se emplea el comando pip install pandas. Pandas es una librería estándar de Python que se encarga del manejo de datos. Para especificar que lo que se está realizando es ejecutar un comando bash, se indica con el signo ! al inicio de la línea.

A continuación, se instala Pandas con el siguiente comando:

In [11]:
!pip install pandas 
Requirement already satisfied: pandas in /usr/local/lib/python3.8/dist-packages (1.3.1)
Requirement already satisfied: pytz>=2017.3 in /usr/local/lib/python3.8/dist-packages (from pandas) (2020.4)
Requirement already satisfied: python-dateutil>=2.7.3 in /usr/local/lib/python3.8/dist-packages (from pandas) (2.8.1)
Requirement already satisfied: numpy>=1.17.3 in /usr/local/lib/python3.8/dist-packages (from pandas) (1.21.1)
Requirement already satisfied: six>=1.5 in /usr/lib/python3/dist-packages (from python-dateutil>=2.7.3->pandas) (1.14.0)

Configuración de Pandas

Tras este paso, se importa el proyecto. Para ello, se utiliza la función import y se importa la librería Pandas como pd, de forma abreviada, tal y como sigue a continuación.

In [22]:
import pandas as pd 

Creación de la variable

Ya se ha instalado la librería Pandas y se ha importado. Ahora se comienza con la creación de las variables necesarias para, posteriormente, realizar los gráficos con los datos de la API. Primero se define la variable url, que será la que le diga a Python el origen de los datos:

In [20]:
 url = 'https://api.covid19api.com/countries'

Creación de DataFrame

Cuando se tiene el enlace donde se encuentran los datos, los mostramos. Primero se crea un DataFrame (una tabla con filas y columnas en la que aparecerán los datos). Para ello, se utiliza la función de Pandas que lee el formato .json, en el que están los datos de la API: pd.read_json(url). Al poner (url), se le señala a Pandas cuáles son los datos que tiene que leer.

In [23]:
df = pd.read_json(url)

Se ejecuta el df para visualizar que todo está correcto:

In [15]:
df
Out[15]:
Country Slug ISO2
0 French Polynesia french-polynesia PF
1 Indonesia indonesia ID
2 Malta malta MT
3 Sao Tome and Principe sao-tome-and-principe ST
4 Haiti haiti HT
... ... ... ...
243 Myanmar myanmar MM
244 Turkey turkey TR
245 Western Sahara western-sahara EH
246 Pitcairn pitcairn PN
247 Tanzania, United Republic of tanzania TZ

248 rows × 3 columns

Exploración de la tabla

Como se observa, al componerse de 248 filas, solo se muestra un extracto, las primeras y las últimas filas. A continuación, se sigue explorando la tabla.

Por ejemplo, se van a visualizar las 6 primeras filas. Para ello, a través de head, se le dice que muestre el DataFrame con las X primeras filas, que en este caso se ha indicado que sean 6:

In [24]:
df.head(6)
Out[24]:
Country Slug ISO2
0 Chad chad TD
1 Guinea-Bissau guinea-bissau GW
2 Hong Kong, SAR China hong-kong-sar-china HK
3 New Caledonia new-caledonia NC
4 Nigeria nigeria NG
5 Sierra Leone sierra-leone SL

Por consiguiente, sucede lo mismo pero al contrario, empezando por el final. Para decirle que se quiere comenzar por el final, en lugar de head se utiliza tail, con el mismo formato: entre paréntesis se señala el X número de filas que se quiere visualizar.

Se muestran las últimas entradas de la tabla (tail); el 2 indica cuántas filas se exponen:

In [18]:
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 248 entries, 0 to 247
Data columns (total 3 columns):
 #   Column   Non-Null Count  Dtype 
---  ------   --------------  ----- 
 0   Country  248 non-null    object
 1   Slug     248 non-null    object
 2   ISO2     248 non-null    object
dtypes: object(3)
memory usage: 5.9+ KB

Si se quieren conocer más datos técnicos acerca de la composición de lo que se está usando, se puede usar info. Esto indica información sobre el DataFrame entre la que incluye el índice (RangeIndex) con el número de entradas, el número de columnas que hay (Data Columns), el nombre de las columnas (Colum), la cantidad de datos nulos que posee la tabla (Non-Null Count), el tipo de variable de las columnas (Dtype) y el tamaño (memory usage).

Se muestra la información de la tabla como objecto de Pandas:

In [17]:
df.tail(2)
Out[17]:
Country Slug ISO2
246 Pitcairn pitcairn PN
247 Tanzania, United Republic of tanzania TZ

Si se quiere filtrar por columnas para ver los datos de alguna en particular, se sigue utilizando df pero ahora, entre corchetes, se pone el nombre de la columna que se quiera visualizar. Por ejemplo, para ver una lista (abreviada) de países, se emplea 'Country'.

A continuación, se muestra la columna 'Country':

In [19]:
df['Country']
Out[19]:
0                  French Polynesia
1                         Indonesia
2                             Malta
3             Sao Tome and Principe
4                             Haiti
                   ...             
243                         Myanmar
244                          Turkey
245                  Western Sahara
246                        Pitcairn
247    Tanzania, United Republic of
Name: Country, Length: 248, dtype: object

Además de esto, también se pueden cruzar filas con columnas. Es decir, saber qué valor tiene la fila X en la columna Y. Para ello, se incluye otro corchete a los que se tiene especificando el número de la fila. En este caso, se quiere saber qué país está codificado en la fila número 66 de la tabla o DataFrame.

Se muestra la columna 'Country' y la fila número 66:

In [20]:
df['Country'][66]
Out[20]:
'Somalia'

Tiempo real

https://api.covid19api.com/country/spain/status/confirmed/live

Una vez se han testeado algunas formas de visualización del DataFrame de los países, se centra en algunos territorios concretos para ver su evolución en tiempo real de los casos confirmados.

Para hacer esto, se tiene que definir de nuevo el enlace desde el que Pandas leerá los datos y crear, a partir de este, el DataFrame. En esta ocasión, se observarán los datos de España, por lo que se obtienen desde la API los casos confirmados en tiempo real para el país y se definen. Posteriormente, se muestran para ver que todo ha ido bien.

In [25]:
url_es = 'https://api.covid19api.com/country/spain/status/confirmed/live'
df_es = pd.read_json(url_es)
df_es
Out[25]:
Country CountryCode Province City CityCode Lat Lon Cases Status Date
0 Spain ES 40.46 -3.75 0 confirmed 2020-01-22 00:00:00+00:00
1 Spain ES 40.46 -3.75 0 confirmed 2020-01-23 00:00:00+00:00
2 Spain ES 40.46 -3.75 0 confirmed 2020-01-24 00:00:00+00:00
3 Spain ES 40.46 -3.75 0 confirmed 2020-01-25 00:00:00+00:00
4 Spain ES 40.46 -3.75 0 confirmed 2020-01-26 00:00:00+00:00
... ... ... ... ... ... ... ... ... ... ...
815 Spain ES 40.46 -3.75 11662214 confirmed 2022-04-16 00:00:00+00:00
816 Spain ES 40.46 -3.75 11662214 confirmed 2022-04-17 00:00:00+00:00
817 Spain ES 40.46 -3.75 11662214 confirmed 2022-04-18 00:00:00+00:00
818 Spain ES 40.46 -3.75 11736893 confirmed 2022-04-19 00:00:00+00:00
819 Spain ES 40.46 -3.75 11736893 confirmed 2022-04-20 00:00:00+00:00

820 rows × 10 columns

Al igual que se realizó anteriormente, se observan diferentes datos de la tabla. Para empezar, se listan las columnas existentes para saber qué datos contiene cada una. Esto se realiza mostrando el DataFrame de España y añadiendo columns.

Se muestran los nombres de las columnas de la tabla:

In [22]:
df_es.columns
Out[22]:
Index(['Country', 'CountryCode', 'Province', 'City', 'CityCode', 'Lat', 'Lon',
       'Cases', 'Status', 'Date'],
      dtype='object')

De la misma manera, se pueden cruzar filas con las columnas para saber un dato concreto. Por ejemplo, para saber cuál es el dato que se encuentra en la primera fila, empezando por el principio, se filtra por la columna 'Date', y con .head(x) se indica el número que se quiere ver.

Se muestra la primera fila (1) por la cabecera (head) de la columna 'Date' de la tabla:

In [23]:
df_es['Date'].head(1)
Out[23]:
0   2020-01-22 00:00:00+00:00
Name: Date, dtype: datetime64[ns, UTC]

Se muestra la primera fila (1) por el final de la tabla (tail) de la columna 'Date' de la tabla:

In [24]:
df_es['Date'].tail(1)
Out[24]:
802   2022-04-03 00:00:00+00:00
Name: Date, dtype: datetime64[ns, UTC]

Se utiliza info para exponer información sobre el DataFrame entre la que incluye el índice (RangeIndex) con el número de entradas, el número de columnas que hay (Data Columns), el nombre de las columnas (Colum), la cantidad de datos nulos que posee la tabla (Non-Null Count), el tipo de variable de las columnas (Dtype) y el tamaño (memory usage).

In [25]:
df_es.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 803 entries, 0 to 802
Data columns (total 10 columns):
 #   Column       Non-Null Count  Dtype              
---  ------       --------------  -----              
 0   Country      803 non-null    object             
 1   CountryCode  803 non-null    object             
 2   Province     803 non-null    object             
 3   City         803 non-null    object             
 4   CityCode     803 non-null    object             
 5   Lat          803 non-null    float64            
 6   Lon          803 non-null    float64            
 7   Cases        803 non-null    int64              
 8   Status       803 non-null    object             
 9   Date         803 non-null    datetime64[ns, UTC]
dtypes: datetime64[ns, UTC](1), float64(2), int64(1), object(6)
memory usage: 62.9+ KB

Si se profundiza en cuestiones estadísticas, se pueden conocer, por ejemplo, datos como la media, la desviación estándar, los diferentes cuartiles y el valor mínimo y máximo. Para ello, se emplea describe.

Muestra variables estadísticas de la tabla (de las columnas numéricas):

In [26]:
df_es.describe()
Out[26]:
Lat Lon Cases
count 8.030000e+02 803.00 8.030000e+02
mean 4.046000e+01 -3.75 3.242248e+06
std 7.109856e-15 0.00 3.109442e+06
min 4.046000e+01 -3.75 0.000000e+00
25% 4.046000e+01 -3.75 3.186710e+05
50% 4.046000e+01 -3.75 3.149012e+06
75% 4.046000e+01 -3.75 4.920388e+06
max 4.046000e+01 -3.75 1.155157e+07

A continuación se vuelve a mostrar el DataFrame completo, aunque aparece de forma abreviada por cuestión de espacio, sin filtrar por columna o tipo de datos.

In [26]:
df_es
Out[26]:
Country CountryCode Province City CityCode Lat Lon Cases Status Date
0 Spain ES 40.46 -3.75 0 confirmed 2020-01-22 00:00:00+00:00
1 Spain ES 40.46 -3.75 0 confirmed 2020-01-23 00:00:00+00:00
2 Spain ES 40.46 -3.75 0 confirmed 2020-01-24 00:00:00+00:00
3 Spain ES 40.46 -3.75 0 confirmed 2020-01-25 00:00:00+00:00
4 Spain ES 40.46 -3.75 0 confirmed 2020-01-26 00:00:00+00:00
... ... ... ... ... ... ... ... ... ... ...
815 Spain ES 40.46 -3.75 11662214 confirmed 2022-04-16 00:00:00+00:00
816 Spain ES 40.46 -3.75 11662214 confirmed 2022-04-17 00:00:00+00:00
817 Spain ES 40.46 -3.75 11662214 confirmed 2022-04-18 00:00:00+00:00
818 Spain ES 40.46 -3.75 11736893 confirmed 2022-04-19 00:00:00+00:00
819 Spain ES 40.46 -3.75 11736893 confirmed 2022-04-20 00:00:00+00:00

820 rows × 10 columns

Por consiguiente, se coloca como referencia la fecha de los datos, obteniéndose así una imagen general de la evolución de los datos. Para ello, se fija con set_index el nombre de la columna que se quiere colocar como referencia, en este caso es la fecha ('Date').

Se muestra la tabla según la columna 'Date':

In [28]:
df_es.set_index('Date')
Out[28]:
Country CountryCode Province City CityCode Lat Lon Cases Status
Date
2020-01-22 00:00:00+00:00 Spain ES 40.46 -3.75 0 confirmed
2020-01-23 00:00:00+00:00 Spain ES 40.46 -3.75 0 confirmed
2020-01-24 00:00:00+00:00 Spain ES 40.46 -3.75 0 confirmed
2020-01-25 00:00:00+00:00 Spain ES 40.46 -3.75 0 confirmed
2020-01-26 00:00:00+00:00 Spain ES 40.46 -3.75 0 confirmed
... ... ... ... ... ... ... ... ... ...
2022-03-30 00:00:00+00:00 Spain ES 40.46 -3.75 11508309 confirmed
2022-03-31 00:00:00+00:00 Spain ES 40.46 -3.75 11508309 confirmed
2022-04-01 00:00:00+00:00 Spain ES 40.46 -3.75 11551574 confirmed
2022-04-02 00:00:00+00:00 Spain ES 40.46 -3.75 11551574 confirmed
2022-04-03 00:00:00+00:00 Spain ES 40.46 -3.75 11551574 confirmed

803 rows × 9 columns

Para ceñirnos estrictamente a los datos que se quieren ver, y así dejar fuera los que son innecesarios, como el país, la latitud y la longitud o el código del país, entre corchetes se indican la(s) columna(s) que se quieren mostrar, además de la de la fecha.

In [32]:
df_es.set_index('Date')['Cases']
Out[32]:
Date
2020-01-22 00:00:00+00:00           0
2020-01-23 00:00:00+00:00           0
2020-01-24 00:00:00+00:00           0
2020-01-25 00:00:00+00:00           0
2020-01-26 00:00:00+00:00           0
                               ...   
2022-04-16 00:00:00+00:00    11662214
2022-04-17 00:00:00+00:00    11662214
2022-04-18 00:00:00+00:00    11662214
2022-04-19 00:00:00+00:00    11736893
2022-04-20 00:00:00+00:00    11736893
Name: Cases, Length: 820, dtype: int64

Representación gráfica

Manteniendo lo anterior, es decir, los datos que se quieren visualizar (fecha y número de casos, para ver la evolución temporal), se añade plot para crear el gráfico y mediante title se da un título al gráfico recién creado, dentro de los paréntesis a partir de plot.

In [30]:
df_es.set_index('Date')['Cases'].plot(title="Casos de Covid19 en España")
Out[30]:
<AxesSubplot:title={'center':'Casos de Covid19 en España'}, xlabel='Date'>

Datos de Italia

Se repite el mismo proceso que se ha hecho hasta ahora, pero con otro país (Italia). Nos centramos solo en la representación visual de los datos:

In [30]:
url_it = 'https://api.covid19api.com/country/italy/status/confirmed/live'
df_it = pd.read_json(url_it)
df_it.set_index('Date')['Cases'].plot()
Out[30]:
<AxesSubplot:xlabel='Date'>

Comparación entre países

Una vez que se han visto diversos países por separados, es el momento de unir todos sus datos para poder compararlos y ver cómo han ido evolucionando sus casos confirmados de coronavirus a lo largo de un periodo temporal. Para ello, se crea una nueva variable por cada país que contenga solo los datos que se han ido necesitando y trabajando anteriormente: fecha y número de casos confirmados.

Se comparan estos dos países en un solo gráfico:

In [31]:
casos_es = df_es.set_index('Date')['Cases']
casos_it = df_it.set_index('Date')['Cases']

Se concatenan los casos de los dos países en una misma tabla:

In [33]:
vs = pd.concat([casos_es,casos_it],axis=1)
vs
Out[33]:
Cases Cases
Date
2020-01-22 00:00:00+00:00 0 0
2020-01-23 00:00:00+00:00 0 0
2020-01-24 00:00:00+00:00 0 0
2020-01-25 00:00:00+00:00 0 0
2020-01-26 00:00:00+00:00 0 0
... ... ...
2022-03-30 00:00:00+00:00 11508309 14567990
2022-03-31 00:00:00+00:00 11508309 14642354
2022-04-01 00:00:00+00:00 11551574 14719394
2022-04-02 00:00:00+00:00 11551574 14790806
2022-04-03 00:00:00+00:00 11551574 14845815

803 rows × 2 columns

Ahora, se da nombre a las columnas para organizar bien los datos y el gráfico. Una vez hecho esto, se muestra el gráfico con plot y se elige el tipo de gráfico, que en este caso, será de área.

In [34]:
vs.columns =['España', 'Italia']
vs.plot(title="España vs Italia")
Out[34]:
<AxesSubplot:title={'center':'España vs Italia'}, xlabel='Date'>

Se exporta la nueva variable a un archivo separado por comas (.csv), con la función to_csv.

In [35]:
vs.to_csv('esvit.csv')

Se comprueba que el archivo se ha guardado ejecutando el comando bash ls:

In [36]:
!ls
api-pandas-folium.ipynb  phyton-pruebas.ipynb		Shared_Resources
escit.png		 python-api-covid-pandas.ipynb	Untitled.ipynb
esvit.csv		 seaborn-data

Para guardar el gráfico como imagen PNG, tenemos que importar la librería pyplot de matplotlib, que ya se encuentra instalada. Se genera el gráfico de nuevo y con plt.savefig('nombre-grafico.png') se crea la imagen.

In [37]:
import matplotlib.pyplot as plt
vs.plot()
plt.savefig('escit.png')