import os # hablar con el sistema operativo
import glob # listar directorios y ese tipo de operaciones
import itertools # herramientas para iterar objetos
from pathlib import Path # manipular rutas a directorios
import zipfile # comprimir y descomprimir archivos
import numpy as np # operaciones vectorizadas
import pandas as pd # DataFrames
from datetime import timedelta, date, datetime # Manejar fechas
import openpyxl # leer/escribir archivos de exel
import requests # Hablar con direcciones web
import plotly.express as px
import logging
df = pd.read_pickle("data/datos_covid_ene19_todos.pkl")
df['CLASIFICACION_FINAL'].unique()
array(['CASO SOSPECHOSO', 'NEGATIVO A SARS-COV-2', 'CASO DE SARS-COV-2 CONFIRMADO', 'NO REALIZADO POR LABORATORIO', 'CASO DE COVID-19 CONFIRMADO POR ASOCIACIÓN CLÍNICA EPIDEMIOLÓGICA', 'CASO DE COVID-19 CONFIRMADO POR COMITÉ DE DICTAMINACIÓN', 'INVÁLIDO POR LABORATORIO'], dtype=object)
valores_confirmados = ['CASO DE SARS-COV-2 CONFIRMADO',
'CASO DE COVID-19 CONFIRMADO POR ASOCIACIÓN CLÍNICA EPIDEMIOLÓGICA',
'CASO DE COVID-19 CONFIRMADO POR COMITÉ DE DICTAMINACIÓN']
df = df.query('EDAD <= 100')
df = df.loc[df["CLASIFICACION_FINAL"].isin(valores_confirmados)]
df.head()
FECHA_ACTUALIZACION | ID_REGISTRO | ORIGEN | SECTOR | ENTIDAD_UM | SEXO | ENTIDAD_NAC | ENTIDAD_RES | MUNICIPIO_RES | TIPO_PACIENTE | ... | TOMA_MUESTRA_ANTIGENO_BIN | MIGRANTE_BIN | UCI_BIN | DEFUNCION | AÑO_INGRESO | MES_INGRESO | DIA_SEMANA_INGRESO | SEMANA_AÑO_INGRESO | DIA_MES_INGRESO | DIA_AÑO_INGRESO | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
FECHA_SINTOMAS | |||||||||||||||||||||
2020-06-10 | 2022-01-18 | z54912 | 1 | 12 | YUCATÁN | 1 | YUCATÁN | YUCATÁN | TEKAX | AMBULATORIO | ... | 0 | 0 | 0 | 0 | 2020 | 6 | 2 | 24 | 10 | 162 |
2020-05-30 | 2022-01-18 | z552ac | 1 | 12 | AGUASCALIENTES | 1 | CIUDAD DE MÉXICO | AGUASCALIENTES | AGUASCALIENTES | AMBULATORIO | ... | 0 | 0 | 0 | 0 | 2020 | 5 | 5 | 22 | 30 | 151 |
2020-06-30 | 2022-01-18 | z59345 | 1 | 12 | YUCATÁN | 2 | YUCATÁN | YUCATÁN | TICUL | AMBULATORIO | ... | 0 | 0 | 0 | 0 | 2020 | 6 | 1 | 27 | 30 | 182 |
2020-08-05 | 2022-01-18 | z4887b | 2 | 12 | CHIHUAHUA | 1 | DURANGO | CHIHUAHUA | JUÁREZ | AMBULATORIO | ... | 0 | 0 | 0 | 0 | 2020 | 8 | 2 | 32 | 5 | 218 |
2020-09-23 | 2022-01-18 | z13788 | 1 | 12 | CIUDAD DE MÉXICO | 1 | CIUDAD DE MÉXICO | CIUDAD DE MÉXICO | CUAJIMALPA DE MORELOS | AMBULATORIO | ... | 0 | 0 | 0 | 0 | 2020 | 9 | 2 | 39 | 23 | 267 |
5 rows × 69 columns
Vamos agrupar los datos en intervalos de 5 años. Primero etiquetamos cada fila da acuerdo al intervalo al que pertenece el paciente
intervalos_edad = list(range(0,106, 5))
df['Grupo de Edad'] = pd.cut(df['EDAD'], intervalos_edad)
df.head()
FECHA_ACTUALIZACION | ID_REGISTRO | ORIGEN | SECTOR | ENTIDAD_UM | SEXO | ENTIDAD_NAC | ENTIDAD_RES | MUNICIPIO_RES | TIPO_PACIENTE | ... | MIGRANTE_BIN | UCI_BIN | DEFUNCION | AÑO_INGRESO | MES_INGRESO | DIA_SEMANA_INGRESO | SEMANA_AÑO_INGRESO | DIA_MES_INGRESO | DIA_AÑO_INGRESO | Grupo de Edad | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
FECHA_SINTOMAS | |||||||||||||||||||||
2020-06-10 | 2022-01-18 | z54912 | 1 | 12 | YUCATÁN | 1 | YUCATÁN | YUCATÁN | TEKAX | AMBULATORIO | ... | 0 | 0 | 0 | 2020 | 6 | 2 | 24 | 10 | 162 | (55, 60] |
2020-05-30 | 2022-01-18 | z552ac | 1 | 12 | AGUASCALIENTES | 1 | CIUDAD DE MÉXICO | AGUASCALIENTES | AGUASCALIENTES | AMBULATORIO | ... | 0 | 0 | 0 | 2020 | 5 | 5 | 22 | 30 | 151 | (55, 60] |
2020-06-30 | 2022-01-18 | z59345 | 1 | 12 | YUCATÁN | 2 | YUCATÁN | YUCATÁN | TICUL | AMBULATORIO | ... | 0 | 0 | 0 | 2020 | 6 | 1 | 27 | 30 | 182 | (35, 40] |
2020-08-05 | 2022-01-18 | z4887b | 2 | 12 | CHIHUAHUA | 1 | DURANGO | CHIHUAHUA | JUÁREZ | AMBULATORIO | ... | 0 | 0 | 0 | 2020 | 8 | 2 | 32 | 5 | 218 | (70, 75] |
2020-09-23 | 2022-01-18 | z13788 | 1 | 12 | CIUDAD DE MÉXICO | 1 | CIUDAD DE MÉXICO | CIUDAD DE MÉXICO | CUAJIMALPA DE MORELOS | AMBULATORIO | ... | 0 | 0 | 0 | 2020 | 9 | 2 | 39 | 23 | 267 | (65, 70] |
5 rows × 70 columns
df = pd.concat([df, pd.get_dummies(df['SEXO']).rename(columns={'1':'MUJER', '2':'HOMBRE'})], axis=1)
df.head()
FECHA_ACTUALIZACION | ID_REGISTRO | ORIGEN | SECTOR | ENTIDAD_UM | SEXO | ENTIDAD_NAC | ENTIDAD_RES | MUNICIPIO_RES | TIPO_PACIENTE | ... | DEFUNCION | AÑO_INGRESO | MES_INGRESO | DIA_SEMANA_INGRESO | SEMANA_AÑO_INGRESO | DIA_MES_INGRESO | DIA_AÑO_INGRESO | Grupo de Edad | MUJER | HOMBRE | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
FECHA_SINTOMAS | |||||||||||||||||||||
2020-06-10 | 2022-01-18 | z54912 | 1 | 12 | YUCATÁN | 1 | YUCATÁN | YUCATÁN | TEKAX | AMBULATORIO | ... | 0 | 2020 | 6 | 2 | 24 | 10 | 162 | (55, 60] | 1 | 0 |
2020-05-30 | 2022-01-18 | z552ac | 1 | 12 | AGUASCALIENTES | 1 | CIUDAD DE MÉXICO | AGUASCALIENTES | AGUASCALIENTES | AMBULATORIO | ... | 0 | 2020 | 5 | 5 | 22 | 30 | 151 | (55, 60] | 1 | 0 |
2020-06-30 | 2022-01-18 | z59345 | 1 | 12 | YUCATÁN | 2 | YUCATÁN | YUCATÁN | TICUL | AMBULATORIO | ... | 0 | 2020 | 6 | 1 | 27 | 30 | 182 | (35, 40] | 0 | 1 |
2020-08-05 | 2022-01-18 | z4887b | 2 | 12 | CHIHUAHUA | 1 | DURANGO | CHIHUAHUA | JUÁREZ | AMBULATORIO | ... | 0 | 2020 | 8 | 2 | 32 | 5 | 218 | (70, 75] | 1 | 0 |
2020-09-23 | 2022-01-18 | z13788 | 1 | 12 | CIUDAD DE MÉXICO | 1 | CIUDAD DE MÉXICO | CIUDAD DE MÉXICO | CUAJIMALPA DE MORELOS | AMBULATORIO | ... | 0 | 2020 | 9 | 2 | 39 | 23 | 267 | (65, 70] | 1 | 0 |
5 rows × 72 columns
df = pd.concat([df, pd.get_dummies(df['TIPO_PACIENTE'])], axis=1)
df.head()
FECHA_ACTUALIZACION | ID_REGISTRO | ORIGEN | SECTOR | ENTIDAD_UM | SEXO | ENTIDAD_NAC | ENTIDAD_RES | MUNICIPIO_RES | TIPO_PACIENTE | ... | MES_INGRESO | DIA_SEMANA_INGRESO | SEMANA_AÑO_INGRESO | DIA_MES_INGRESO | DIA_AÑO_INGRESO | Grupo de Edad | MUJER | HOMBRE | AMBULATORIO | HOSPITALIZADO | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
FECHA_SINTOMAS | |||||||||||||||||||||
2020-06-10 | 2022-01-18 | z54912 | 1 | 12 | YUCATÁN | 1 | YUCATÁN | YUCATÁN | TEKAX | AMBULATORIO | ... | 6 | 2 | 24 | 10 | 162 | (55, 60] | 1 | 0 | 1 | 0 |
2020-05-30 | 2022-01-18 | z552ac | 1 | 12 | AGUASCALIENTES | 1 | CIUDAD DE MÉXICO | AGUASCALIENTES | AGUASCALIENTES | AMBULATORIO | ... | 5 | 5 | 22 | 30 | 151 | (55, 60] | 1 | 0 | 1 | 0 |
2020-06-30 | 2022-01-18 | z59345 | 1 | 12 | YUCATÁN | 2 | YUCATÁN | YUCATÁN | TICUL | AMBULATORIO | ... | 6 | 1 | 27 | 30 | 182 | (35, 40] | 0 | 1 | 1 | 0 |
2020-08-05 | 2022-01-18 | z4887b | 2 | 12 | CHIHUAHUA | 1 | DURANGO | CHIHUAHUA | JUÁREZ | AMBULATORIO | ... | 8 | 2 | 32 | 5 | 218 | (70, 75] | 1 | 0 | 1 | 0 |
2020-09-23 | 2022-01-18 | z13788 | 1 | 12 | CIUDAD DE MÉXICO | 1 | CIUDAD DE MÉXICO | CIUDAD DE MÉXICO | CUAJIMALPA DE MORELOS | AMBULATORIO | ... | 9 | 2 | 39 | 23 | 267 | (65, 70] | 1 | 0 | 1 | 0 |
5 rows × 74 columns
agregados = (df.groupby([pd.Grouper(freq='W'), "ENTIDAD_RES", 'Grupo de Edad'])
.agg({'ID_REGISTRO':'size',
'MUJER':'sum',
'HOMBRE':'sum',
'DEFUNCION':'sum',
'AMBULATORIO': 'sum',
'HOSPITALIZADO': 'sum',
'UCI_BIN':'sum',
'INTUBADO_BIN':'sum',
'DIABETES_BIN':'sum',
'EPOC_BIN': 'sum',
'ASMA_BIN': 'sum',
'INMUSUPR_BIN':'sum',
'HIPERTENSION_BIN':'sum',
'CARDIOVASCULAR_BIN':'sum',
'OBESIDAD_BIN': 'sum',
'RENAL_CRONICA_BIN': 'sum',
'TABAQUISMO_BIN':'sum',
'OTRAS_COM_BIN':'sum'
})
.rename(columns={'ID_REGISTRO':'Casos Confirmados'}))
agregados.head()
Casos Confirmados | MUJER | HOMBRE | DEFUNCION | AMBULATORIO | HOSPITALIZADO | UCI_BIN | INTUBADO_BIN | DIABETES_BIN | EPOC_BIN | ASMA_BIN | INMUSUPR_BIN | HIPERTENSION_BIN | CARDIOVASCULAR_BIN | OBESIDAD_BIN | RENAL_CRONICA_BIN | TABAQUISMO_BIN | OTRAS_COM_BIN | |||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
FECHA_SINTOMAS | ENTIDAD_RES | Grupo de Edad | ||||||||||||||||||
2020-02-23 | AGUASCALIENTES | (0, 5] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
(5, 10] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ||
(10, 15] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ||
(15, 20] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ||
(20, 25] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Primero calculamos los totales nacionales
agregados_nacionales = agregados.groupby(['FECHA_SINTOMAS', 'Grupo de Edad']).sum().reset_index()
agregados_nacionales.head()
FECHA_SINTOMAS | Grupo de Edad | Casos Confirmados | MUJER | HOMBRE | DEFUNCION | AMBULATORIO | HOSPITALIZADO | UCI_BIN | INTUBADO_BIN | DIABETES_BIN | EPOC_BIN | ASMA_BIN | INMUSUPR_BIN | HIPERTENSION_BIN | CARDIOVASCULAR_BIN | OBESIDAD_BIN | RENAL_CRONICA_BIN | TABAQUISMO_BIN | OTRAS_COM_BIN | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2020-02-23 | (0, 5] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 2020-02-23 | (5, 10] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 2020-02-23 | (10, 15] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
3 | 2020-02-23 | (15, 20] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
4 | 2020-02-23 | (20, 25] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
fig = px.line(agregados_nacionales, x='FECHA_SINTOMAS', y='Casos Confirmados', color='Grupo de Edad')
fig.show()
El CFR es el cociente entre el número de defunciones y al total de casos confirmados
agregados_nacionales['CFR'] = agregados_nacionales["DEFUNCION"] / agregados_nacionales["Casos Confirmados"]
agregados_nacionales.head()
FECHA_SINTOMAS | Grupo de Edad | Casos Confirmados | MUJER | HOMBRE | DEFUNCION | AMBULATORIO | HOSPITALIZADO | UCI_BIN | INTUBADO_BIN | ... | EPOC_BIN | ASMA_BIN | INMUSUPR_BIN | HIPERTENSION_BIN | CARDIOVASCULAR_BIN | OBESIDAD_BIN | RENAL_CRONICA_BIN | TABAQUISMO_BIN | OTRAS_COM_BIN | CFR | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2020-02-23 | (0, 5] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | NaN |
1 | 2020-02-23 | (5, 10] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | NaN |
2 | 2020-02-23 | (10, 15] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | NaN |
3 | 2020-02-23 | (15, 20] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | NaN |
4 | 2020-02-23 | (20, 25] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | NaN |
5 rows × 21 columns
fig = px.line(agregados_nacionales, x='FECHA_SINTOMAS', y='CFR', color='Grupo de Edad')
fig.show()
La gráfica anterior parece mostrar que el CFR es relatívamente constante, lo que resulta contraintuitivo pensando en el efecto de la vacuniación, sin embargo también hay que tomar en cuenta el efecto del número de pruebas en la cantidad de casos confirmados. Para empezar a refinar más esta idea, veamos juntas las gráficas de total de defunciones (por semana) y el CFR.
Recordando del taller anterior, para hacer el facet que queremos, necesitamos tener una columna que nos distinga el CFR de las defunciones. Es decir, necesitamos hacer un melt de dichas columnas
cfr_defunciones = (agregados_nacionales[['FECHA_SINTOMAS', 'Grupo de Edad', 'DEFUNCION', 'CFR']]
.melt(id_vars=['FECHA_SINTOMAS', 'Grupo de Edad'], value_vars=['DEFUNCION', 'CFR']))
cfr_defunciones
FECHA_SINTOMAS | Grupo de Edad | variable | value | |
---|---|---|---|---|
0 | 2020-02-23 | (0, 5] | DEFUNCION | 0.0 |
1 | 2020-02-23 | (5, 10] | DEFUNCION | 0.0 |
2 | 2020-02-23 | (10, 15] | DEFUNCION | 0.0 |
3 | 2020-02-23 | (15, 20] | DEFUNCION | 0.0 |
4 | 2020-02-23 | (20, 25] | DEFUNCION | 0.0 |
... | ... | ... | ... | ... |
4237 | 2022-01-23 | (80, 85] | CFR | 0.0 |
4238 | 2022-01-23 | (85, 90] | CFR | 0.0 |
4239 | 2022-01-23 | (90, 95] | CFR | 0.0 |
4240 | 2022-01-23 | (95, 100] | CFR | 0.0 |
4241 | 2022-01-23 | (100, 105] | CFR | NaN |
4242 rows × 4 columns
fig = px.line(cfr_defunciones, x='FECHA_SINTOMAS', y='value', color='Grupo de Edad', facet_col='variable', facet_col_wrap=1)
fig.update_yaxes(matches=None)
fig.show()
Viendo estas gráficas, es claro que, aunque el CFR se mantiene relatívamente constante la cantidad de defunciones se ha reducido significativamente, eso nos indica que en realidad estamos viendo un efecto del número de pruebas en el CFR.
¿Cómo podríamos ver con más claridad el efecto de las vacunas?
Una primera aproximación es ver en qué medida los pacientes hospitalizados han fallecido a lo largo del tiempo. Hagamos una nueva columna con el cociente entre defunciones y pacientes hospitalizados
agregados_nacionales['Hospitalizaciones/Defunciones'] = agregados_nacionales['DEFUNCION'] / agregados_nacionales['HOSPITALIZADO']
agregados_nacionales.head()
FECHA_SINTOMAS | Grupo de Edad | Casos Confirmados | MUJER | HOMBRE | DEFUNCION | AMBULATORIO | HOSPITALIZADO | UCI_BIN | INTUBADO_BIN | ... | ASMA_BIN | INMUSUPR_BIN | HIPERTENSION_BIN | CARDIOVASCULAR_BIN | OBESIDAD_BIN | RENAL_CRONICA_BIN | TABAQUISMO_BIN | OTRAS_COM_BIN | CFR | Hospitalizaciones/Defunciones | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2020-02-23 | (0, 5] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | NaN | NaN |
1 | 2020-02-23 | (5, 10] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | NaN | NaN |
2 | 2020-02-23 | (10, 15] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | NaN | NaN |
3 | 2020-02-23 | (15, 20] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | NaN | NaN |
4 | 2020-02-23 | (20, 25] | 0 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | NaN | NaN |
5 rows × 22 columns
fig = px.line(agregados_nacionales, x='FECHA_SINTOMAS', y='Hospitalizaciones/Defunciones', color='Grupo de Edad')
fig.show()