In [1]:
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

Leer datos¶

In [2]:
df = pd.read_pickle("data/datos_covid_ene19_todos.pkl")

Filtrar¶

  • Sólo queremos los casos confirmados
  • Vamos a tomar pacientes de 100 años o menos
In [3]:
df['CLASIFICACION_FINAL'].unique()
Out[3]:
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)
In [4]:
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()
Out[4]:
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

Grupos de edad¶

Vamos agrupar los datos en intervalos de 5 años. Primero etiquetamos cada fila da acuerdo al intervalo al que pertenece el paciente

In [5]:
intervalos_edad = list(range(0,106, 5))
df['Grupo de Edad'] = pd.cut(df['EDAD'], intervalos_edad)
df.head()
Out[5]:
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

Columnas Hombre - Mujer¶

In [6]:
df = pd.concat([df, pd.get_dummies(df['SEXO']).rename(columns={'1':'MUJER', '2':'HOMBRE'})], axis=1)
df.head()
Out[6]:
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

Columnas Ambulatorio - Hospitalizado¶

In [7]:
df = pd.concat([df, pd.get_dummies(df['TIPO_PACIENTE'])], axis=1)
df.head()
Out[7]:
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¶

In [8]:
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()
Out[8]:
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

Casos confirmados por grupo de edad¶

Primero calculamos los totales nacionales

In [9]:
agregados_nacionales = agregados.groupby(['FECHA_SINTOMAS', 'Grupo de Edad']).sum().reset_index()
agregados_nacionales.head()
Out[9]:
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
In [10]:
fig = px.line(agregados_nacionales, x='FECHA_SINTOMAS', y='Casos Confirmados', color='Grupo de Edad')
fig.show()

CFR por grupo de edad¶

El CFR es el cociente entre el número de defunciones y al total de casos confirmados

In [11]:
agregados_nacionales['CFR'] = agregados_nacionales["DEFUNCION"] / agregados_nacionales["Casos Confirmados"]
agregados_nacionales.head()
Out[11]:
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

In [12]:
fig = px.line(agregados_nacionales, x='FECHA_SINTOMAS', y='CFR', color='Grupo de Edad')
fig.show()

Facet de CFR y defunciones¶

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

In [15]:
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
Out[15]:
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

In [16]:
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?

Hospitalizaciones contra defunciones¶

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

In [19]:
agregados_nacionales['Hospitalizaciones/Defunciones'] = agregados_nacionales['DEFUNCION'] / agregados_nacionales['HOSPITALIZADO']
agregados_nacionales.head()
Out[19]:
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

In [20]:
fig = px.line(agregados_nacionales, x='FECHA_SINTOMAS', y='Hospitalizaciones/Defunciones', color='Grupo de Edad')
fig.show()
In [ ]: