Introducción

La Secretaría de Salud (SSA) de México publica muchos de sus datos abiertos a través de cubos dinámicos. Si bien los cubos dinámicos tienen la ventaja de que permiten a los usuarios construir consultas complejas, lo cierto es que la tecnologia que usa la SSA es muy antigua y hace prácticamente inaccesibles los datos. Según la guía de instalación, es necesario usar el navegador Internet Explorer (que ya fue descontinuado por Microsoft) y además instalar al menos 4 programas y complementos en el navegador. El proceso es simplemente anacrónico.

Para facilitar el acceso a los cubos y poder obtener un acceso a los datos mucho más detallado, me dí a la tarea de buscar una forma de conectarme directamente a los cubos sin necesidad de usar el tedioso proceso de instalación. A continuación detallo los pasos de cómo lo logré usando Python.

Pasos para la conexión

Primero que todo, es necesario instalar adobapi

pip install adodbapi
import pandas as pd
import adodbapi


def rows_to_df(rows) -> pd.DataFrame:
    """ 
    Convierte los resultados de una consulta de adobdapi a un DataFrame de Pandas.
    rows: resultado de la consulta
    """
    df = pd.DataFrame(data=dict(zip(rows.columnNames.keys(), rows.ado_results)))\
        .assign(_id=lambda x: range(len(x)))
    return df
  • Es posible que también haya que instalar los drivers que especifica la páginas de DGIS en las instrucciones de instalación (a mí me funcionó sin estos).
  • Usamos adodbapi para establecer una conexión con el servidor
  • Es necesario especificar la base de datos que se va a consultar, por ejemplo, este es la de muertes maternas.
cubo = 'MATERNAS_2020'
conn = adodbapi.connect('Provider=MSOLAP.8;Password=Temp123!;Persist Security Info=True;User ID=SALUD\DGIS15;'
                        f'Data Source=pwidgis03.salud.gob.mx;Update Isolation Level=2;Initial Catalog={cubo}')
cursor = conn.cursor()

Una vez establecida la conexión, se pueden hacer queries usando el lenguaje MDX, que usa algunos comando básicos de SQL aunque es mucho más difícil.

La siguiente query permite conocer todas las bases de datos disponibles:

cursor.execute("""SELECT [catalog_name] FROM $system.DBSCHEMA_CATALOGS""")
rows = cursor.fetchall()
list(rows)
[<SQLrow={catalog_name:'CLUES_2019'}>,
 <SQLrow={catalog_name:'CLUES_2019_C'}>,
 <SQLrow={catalog_name:'CONAPO_NACIMIENTOS'}>,
 <SQLrow={catalog_name:'Cubo solo sinba 2020'}>,
 <SQLrow={catalog_name:'Cubo solo sinba 2021'}>,
 <SQLrow={catalog_name:'Cubo solo sinba 2022'}>,
 <SQLrow={catalog_name:'cubo_lesiones_2017'}>,
 <SQLrow={catalog_name:'cubo_lesiones_2018'}>,
 <SQLrow={catalog_name:'Cubo_Lesiones2019'}>,
 <SQLrow={catalog_name:'Cubo_pobla_2019_DH'}>,
 <SQLrow={catalog_name:'CuboLesiones2020'}>,
 <SQLrow={catalog_name:'CuboLesiones2021'}>,
 <SQLrow={catalog_name:'CuboLesiones2022'}>,
 <SQLrow={catalog_name:'CuboSec_18_20'}>,
 <SQLrow={catalog_name:'CuboSectorial_18_19'}>,
 <SQLrow={catalog_name:'CuboSectorial_18_20'}>,
 <SQLrow={catalog_name:'Defunciones_hist'}>,
 <SQLrow={catalog_name:'DEFUNCIONES_PC_2020'}>,
 <SQLrow={catalog_name:'DERECHOHABIENCIA'}>,
 <SQLrow={catalog_name:'Detecciones'}>,
 <SQLrow={catalog_name:'EGRESOS'}>,
 <SQLrow={catalog_name:'egresos_procedimientos_sinba'}>,
 <SQLrow={catalog_name:'egresos_productos_sinba'}>,
 <SQLrow={catalog_name:'egresos_sinba'}>,
 <SQLrow={catalog_name:'EGRESOS2018'}>,
 <SQLrow={catalog_name:'Egresos2019_19'}>,
 <SQLrow={catalog_name:'Egresos2020'}>,
 <SQLrow={catalog_name:'Egresos2021'}>,
 <SQLrow={catalog_name:'Egresos2022'}>,
 <SQLrow={catalog_name:'IND_DEM_PROY'}>,
 <SQLrow={catalog_name:'Lesiones'}>,
 <SQLrow={catalog_name:'LESIONES_Historico'}>,
 <SQLrow={catalog_name:'lesiones_SINBA'}>,
 <SQLrow={catalog_name:'Maternas_2019'}>,
 <SQLrow={catalog_name:'Maternas_2020'}>,
 <SQLrow={catalog_name:'NACIMIENTOS_2018'}>,
 <SQLrow={catalog_name:'NACIMIENTOS_2019'}>,
 <SQLrow={catalog_name:'NACIMIENTOS_2020'}>,
 <SQLrow={catalog_name:'NACIMIENTOS_2021'}>,
 <SQLrow={catalog_name:'NACIMIENTOS_2022'}>,
 <SQLrow={catalog_name:'Personal_Salud'}>,
 <SQLrow={catalog_name:'Pob_2018_aseg_nov15'}>,
 <SQLrow={catalog_name:'POB_MIT_PROYECCIONES'}>,
 <SQLrow={catalog_name:'Poblacion'}>,
 <SQLrow={catalog_name:'PROCEDIMIENTOS'}>,
 <SQLrow={catalog_name:'PROCEDIMIENTOS_2019'}>,
 <SQLrow={catalog_name:'PROY_DEF_EDAD'}>,
 <SQLrow={catalog_name:'Recursos'}>,
 <SQLrow={catalog_name:'recursos_sector_ok_2'}>,
 <SQLrow={catalog_name:'Reporte_Diario'}>,
 <SQLrow={catalog_name:'saeh_sector_hist'}>,
 <SQLrow={catalog_name:'saeh2011'}>,
 <SQLrow={catalog_name:'saeh2012'}>,
 <SQLrow={catalog_name:'saeh2013'}>,
 <SQLrow={catalog_name:'saeh2016'}>,
 <SQLrow={catalog_name:'saeh2017'}>,
 <SQLrow={catalog_name:'saeh2018'}>,
 <SQLrow={catalog_name:'saeh2019'}>,
 <SQLrow={catalog_name:'SALUD_MENTAL'}>,
 <SQLrow={catalog_name:'SALUD_MENTAL_18'}>,
 <SQLrow={catalog_name:'seed2013'}>,
 <SQLrow={catalog_name:'seed2016'}>,
 <SQLrow={catalog_name:'seed2017_cierre'}>,
 <SQLrow={catalog_name:'SICUENTAS'}>,
 <SQLrow={catalog_name:'sinac_2015'}>,
 <SQLrow={catalog_name:'sinac_2016'}>,
 <SQLrow={catalog_name:'SINAC_SINBA_2017'}>,
 <SQLrow={catalog_name:'sinac2017'}>,
 <SQLrow={catalog_name:'SINERHIAS'}>,
 <SQLrow={catalog_name:'SIS_2017_NEW'}>,
 <SQLrow={catalog_name:'SIS_2018_NEW2'}>,
 <SQLrow={catalog_name:'SIS_2019_2'}>,
 <SQLrow={catalog_name:'SIS_SECTORIAL'}>,
 <SQLrow={catalog_name:'sis2010'}>,
 <SQLrow={catalog_name:'sis2011'}>,
 <SQLrow={catalog_name:'sis2012'}>,
 <SQLrow={catalog_name:'SIS2014'}>,
 <SQLrow={catalog_name:'sis2015'}>,
 <SQLrow={catalog_name:'sis2016'}>,
 <SQLrow={catalog_name:'TEF_NAC_PROYECCIONES'}>,
 <SQLrow={catalog_name:'Urgencias_SINBA'}>,
 <SQLrow={catalog_name:'urgencias2011'}>,
 <SQLrow={catalog_name:'urgencias2012'}>,
 <SQLrow={catalog_name:'urgencias2013'}>,
 <SQLrow={catalog_name:'URGENCIAS2017'}>,
 <SQLrow={catalog_name:'URGENCIAS2018'}>,
 <SQLrow={catalog_name:'Urgencias2019'}>,
 <SQLrow={catalog_name:'Urgencias2020'}>,
 <SQLrow={catalog_name:'Urgencias2021'}>,
 <SQLrow={catalog_name:'Urgencias2022'}>]

En nuestro caso, ya escogimos la base de datos "MATERNAS_2020" con los datos de mortalidad materna del año 2022.

La siguiente query sirve para conocer los cubos y dimensiones de esta base de datos:

cursor.execute("""
SELECT [CATALOG_NAME] as [DATABASE],
CUBE_NAME AS [CUBE], DIMENSION_CAPTION AS [DIMENSION]
FROM $system.MDSchema_Dimensions
WHERE DIMENSION_CAPTION='Measures'
""")
rows = cursor.fetchall()
df_cubos_maternas = rows_to_df(rows)
df_cubos_maternas
database cube dimension _id
0 Maternas_2020 $AFILIACION Measures 0
1 Maternas_2020 $AÑO CERTIFICACION Measures 1
2 Maternas_2020 $AÑO DEFUNCION Measures 2
3 Maternas_2020 $AÑO REGISTRO Measures 3
4 Maternas_2020 $ASISTENCIA MEDICA Measures 4
5 Maternas_2020 $CAUSA BASICA Measures 5
6 Maternas_2020 $CERTIFICO Measures 6
7 Maternas_2020 $EDAD Measures 7
8 Maternas_2020 $EDADD Measures 8
9 Maternas_2020 $ENTIDAD DEFUNCION Measures 9
10 Maternas_2020 $ENTIDAD RESIDENCIA Measures 10
11 Maternas_2020 $ESCOLARIDAD Measures 11
12 Maternas_2020 $ESTADO CIVIL Measures 12
13 Maternas_2020 $MES CERTIFICACION Measures 13
14 Maternas_2020 $MES DEFUNCION Measures 14
15 Maternas_2020 $MES REGISTRO Measures 15
16 Maternas_2020 $PRINCIPALES CAUSAS Measures 16
17 Maternas_2020 $RAZON MORTALIDAD Measures 17
18 Maternas_2020 $SITIO OCURRENCIA Measures 18
19 Maternas_2020 $TAMAÑO LOCALIDAD Measures 19
20 Maternas_2020 MUERTES MATERNAS Measures 20
  • de estos cubos, casi todos empiezan con el símbolo $ \$ $, excepto uno. Lo que he notado es que este cubo es el que tenemos que usar para hacer las consultas de los datos de interés. En este caso, por ejemplo, se trata del cubo "MUERTES MATERNAS"
df_cubos_maternas['cube'].loc[lambda x: ~ x.str.contains(r'\$')]
20    MUERTES MATERNAS
Name: cube, dtype: object

MDX permite hacer consultas con gran nivel de detalle, pero a mí me parece complicado. Considero más fácil descargar toda la información y luego hacer las agregaciones usando Python:

cursor.execute("""
SELECT *
FROM [MUERTES MATERNAS].[Measures]
""")
rows = cursor.fetchall()
df = rows_to_df(rows)
df
[totales].[$afiliacion.afiliación derechohabiencia] [totales].[$año certificacion.año de certificación] [totales].[$año defuncion.año de la defunción] [totales].[$año registro.año de registro] [totales].[$asistencia medica.asistencia médica] [totales].[$entidad defuncion.entidad de defunción] [totales].[$entidad residencia.entidad de residencia] [totales].[$escolaridad.escolaridad] [totales].[$estado civil.estado civil] [totales].[$mes certificacion.mes de la certificación] ... [totales].[$certifico.quién certificó] [totales].[$razon mortalidad.razón mortalidad materna] [totales].[$sitio ocurrencia.sitio de ocurrencia] [totales].[$tamaño localidad.tamaño de localidad] [totales].[$edad.edad quinquenal] [totales].[$edadd.edad] [totales].[$causa basica.cve causa 4] [totales].[$principales causas.clave pcm] [totales].[muertes maternas] _id
0 NO ESPECIFICADA 2019 2019 2019 NO ESPECIFICADO CHIAPAS CHIAPAS NO ESPECIFICADA SE IGNORA SEPTIEMBRE ... NO ESPECIFICADO MUERTES MATERNAS PARA LA RAZÓN DE MORTALIDAD M... OTRO LUGAR 1000 A 1999 HABITANTES 30 a 34 años None O432 PLACENTA ANORMALMENTE ADHERIDA 43G 1 0
1 SEGURO POPULAR 2019 2019 2019 SIN ATENCION MEDICA CHIHUAHUA CHIHUAHUA NINGUNA UNION LIBRE OCTUBRE ... MEDICO LEGISTA MUERTES MATERNAS PARA LA RAZÓN DE MORTALIDAD M... VIA PUBLICA 1 A 999 HABITANTES 30 a 34 años None O720 HEMORRAGIA DEL TERCER PERÍODO DEL PARTO 43K 1 1
2 IMSS 2019 2019 2019 CON ATENCION MEDICA JALISCO JALISCO BACHILLERATO O PREPARATORIA COMPLETA CASADO ABRIL ... OTRO MEDICO MUERTES MATERNAS PARA LA RAZÓN DE MORTALIDAD M... IMSS 1000000 A 1499999 HABITANTES 35 a 39 años None O021 ABORTO RETENIDO 43C 1 2
3 SEGURO POPULAR 2019 2019 2019 CON ATENCION MEDICA JALISCO JALISCO SECUNDARIA COMPLETA CASADO MARZO ... OTRO MEDICO MUERTES MATERNAS EXCLUIDAS PARA LA RAZÓN DE MO... OTRO LUGAR 10000 A 14999 HABITANTES 20 a 24 años None O961 MUERTE POR CAUSA OBSTÉTRICA INDIRECTA QUE... 4543 1 3
4 SEGURO POPULAR 2019 2019 2019 CON ATENCION MEDICA JALISCO JALISCO SECUNDARIA COMPLETA CASADO ABRIL ... MEDICO TRATANTE MUERTES MATERNAS PARA LA RAZÓN DE MORTALIDAD M... SECRETARIA DE SALUD 10000 A 14999 HABITANTES 30 a 34 años None O223 FLEBOTROMBOSIS PROFUNDA EN EL EMBARAZO 43N 1 4
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
21119 NO ESPECIFICADA 2020 2020 2020 CON ATENCION MEDICA SINALOA SINALOA PROFESIONAL CASADO JULIO ... OTRO MEDICO MUERTES MATERNAS PARA LA RAZÓN DE MORTALIDAD M... IMSS 500000 A 999999 HABITANTES 20 a 24 años None O985 OTRAS ENFERMEDADES VIRALES QUE COMPLICAN ... 45 1 21119
21120 IMSS 2020 2020 2020 CON ATENCION MEDICA SINALOA SINALOA PROFESIONAL SE IGNORA OCTUBRE ... OTRO MEDICO MUERTES MATERNAS PARA LA RAZÓN DE MORTALIDAD M... IMSS 500000 A 999999 HABITANTES 30 a 34 años None O996 ENFERMEDADES DEL SISTEMA DIGESTIVO QUE CO... 45 1 21120
21121 IMSS 2020 2020 2020 CON ATENCION MEDICA SINALOA SINALOA BACHILLERATO O PREPARATORIA COMPLETA CASADO ENERO ... MEDICO LEGISTA MUERTES MATERNAS PARA LA RAZÓN DE MORTALIDAD M... IMSS 250000 A 499999 HABITANTES 35 a 39 años None O721 OTRAS HEMORRAGIAS POSTPARTO INMEDIATAS 43K 1 21121
21122 IMSS 2020 2020 2020 CON ATENCION MEDICA SINALOA SINALOA BACHILLERATO O PREPARATORIA COMPLETA UNION LIBRE SEPTIEMBRE ... MEDICO TRATANTE MUERTES MATERNAS PARA LA RAZÓN DE MORTALIDAD M... IMSS 250000 A 499999 HABITANTES 25 a 29 años None O985 OTRAS ENFERMEDADES VIRALES QUE COMPLICAN ... 45 1 21122
21123 OTRA 2020 2020 2020 CON ATENCION MEDICA SINALOA SINALOA PRIMARIA COMPLETA UNION LIBRE JUNIO ... OTRO MEDICO MUERTES MATERNAS PARA LA RAZÓN DE MORTALIDAD M... SECRETARIA DE SALUD 1 A 999 HABITANTES 35 a 39 años None O432 PLACENTA ANORMALMENTE ADHERIDA 43G 1 21123

21124 rows × 22 columns

  • Los datos que obtuvimos son los 21124 registros individuales de mortalidad materna en 2020, para cada uno de ellos tenemos datos como el lugar de ocurrencia, causa, escolaridad, etc.

  • revisamos los nombres de columnas del DataFrame

df.columns
Index(['[totales].[$afiliacion.afiliación derechohabiencia]',
       '[totales].[$año certificacion.año de certificación]',
       '[totales].[$año defuncion.año de la defunción]',
       '[totales].[$año registro.año de registro]',
       '[totales].[$asistencia medica.asistencia médica]',
       '[totales].[$entidad defuncion.entidad de defunción]',
       '[totales].[$entidad residencia.entidad de residencia]',
       '[totales].[$escolaridad.escolaridad]',
       '[totales].[$estado civil.estado civil]',
       '[totales].[$mes certificacion.mes de la certificación]',
       '[totales].[$mes defuncion.mes de la defunción]',
       '[totales].[$mes registro.mes de registro]',
       '[totales].[$certifico.quién certificó]',
       '[totales].[$razon mortalidad.razón mortalidad materna]',
       '[totales].[$sitio ocurrencia.sitio de ocurrencia]',
       '[totales].[$tamaño localidad.tamaño de localidad]',
       '[totales].[$edad.edad quinquenal]', '[totales].[$edadd.edad]',
       '[totales].[$causa basica.cve causa 4]',
       '[totales].[$principales causas.clave pcm]',
       '[totales].[muertes maternas]', '_id'],
      dtype='object')
  • Podemos hacer cruces entre variables o graficxar la distribución de las variables:
pd.crosstab(df.iloc[:, 0], df.iloc[:, 4])
[totales].[$asistencia medica.asistencia médica] CON ATENCION MEDICA NO ESPECIFICADO SIN ATENCION MEDICA
[totales].[$afiliacion.afiliación derechohabiencia]
IMSS 3918 118 130
IMSS OPORTUNIDADES 84 7 18
IMSS PROSPERA 26 3 1
ISSSTE 578 26 16
NINGUNA 6013 235 977
NO ESPECIFICADA 1383 280 284
OTRA 354 4 30
PEMEX 36 2 2
SECRETARIA DE LA DEFENSA NACIONAL 98 2 2
SECRETARIA DE MARINA 82 2 8
SEGURO POPULAR 5673 272 460
df['[totales].[$afiliacion.afiliación derechohabiencia]'].value_counts().plot.barh()
<AxesSubplot:>
df['[totales].[$escolaridad.escolaridad]'].value_counts().plot.barh()
<AxesSubplot:>
df['[totales].[$edad.edad quinquenal]'].value_counts().plot.barh()
<AxesSubplot:>
  • Con MDX podemos hacer consultas seleccionando filtrando algunas columnas con SELECT y o también filtrando filas con WHERE:
cursor.execute("""
SELECT [$mes registro.mes de registro], [$afiliacion.afiliación derechohabiencia]
FROM [MUERTES MATERNAS].[MEASURES]
WHERE [$mes registro.mes de registro]="MAYO"
""")
rows = cursor.fetchall()
rows_to_df(rows)
[totales].[$mes registro.mes de registro] [totales].[$afiliacion.afiliación derechohabiencia] _id
0 MAYO SEGURO POPULAR 0
1 MAYO SEGURO POPULAR 1
2 MAYO SEGURO POPULAR 2
3 MAYO IMSS PROSPERA 3
4 MAYO SEGURO POPULAR 4
... ... ... ...
1686 MAYO OTRA 1686
1687 MAYO NINGUNA 1687
1688 MAYO IMSS 1688
1689 MAYO IMSS 1689
1690 MAYO SEGURO POPULAR 1690

1691 rows × 3 columns

Segundo ejemplo: cubo de nacimientos

  • De la lista de cubos disponibles, seleccionamos el de NACIMIENTOS_2022
cubo2 = 'NACIMIENTOS_2022'
conn2 = adodbapi.connect('Provider=MSOLAP.8;Password=Temp123!;Persist Security Info=True;User ID=SALUD\DGIS15;'
                        f'Data Source=pwidgis03.salud.gob.mx;Update Isolation Level=2;Initial Catalog={cubo2}')
cursor2 = conn2.cursor()
  • revisamos las dimensiones que tiene este cubo:
cursor2.execute("""
SELECT [CATALOG_NAME] as [DATABASE],
CUBE_NAME AS [CUBE], DIMENSION_CAPTION AS [DIMENSION]
FROM $system.MDSchema_Dimensions
""")
rows = cursor2.fetchall()
df_cubos_nacimiento = rows_to_df(rows)
df_cubos_nacimiento
database cube dimension _id
0 NACIMIENTOS_2022 $01 UNIDADMEDICA PARTO 01 UNIDADMEDICA PARTO 0
1 NACIMIENTOS_2022 $01 UNIDADMEDICA PARTO Measures 1
2 NACIMIENTOS_2022 $02 FECHA DE NACIMIENTO 02 FECHA DE NACIMIENTO 2
3 NACIMIENTOS_2022 $02 FECHA DE NACIMIENTO Measures 3
4 NACIMIENTOS_2022 $APGARH APGARH 4
... ... ... ... ...
124 NACIMIENTOS_2022 NACIMIENTOS_2022 SOBREVIVIO PARTO 124
125 NACIMIENTOS_2022 NACIMIENTOS_2022 TAMIZ Y VACUNAS 125
126 NACIMIENTOS_2022 NACIMIENTOS_2022 TRABAJA ACTUALMENTE 126
127 NACIMIENTOS_2022 NACIMIENTOS_2022 TRIMESTRE PRIMER CONSULTA 127
128 NACIMIENTOS_2022 NACIMIENTOS_2022 VIVE HIJO ANTERIOR 128

129 rows × 4 columns

  • el cubo de interés es "NACIMIENTOS_2022"
df_cubos_nacimiento.query('cube=="NACIMIENTOS_2022"')
database cube dimension _id
86 NACIMIENTOS_2022 NACIMIENTOS_2022 01 UNIDAD MEDICA PARTO 86
87 NACIMIENTOS_2022 NACIMIENTOS_2022 02 FECHA DE NACIMIENTO 87
88 NACIMIENTOS_2022 NACIMIENTOS_2022 APGARH 88
89 NACIMIENTOS_2022 NACIMIENTOS_2022 ATENCION PRENATAL 89
90 NACIMIENTOS_2022 NACIMIENTOS_2022 CERTIFICADO POR 90
91 NACIMIENTOS_2022 NACIMIENTOS_2022 CODIGO CIE 91
92 NACIMIENTOS_2022 NACIMIENTOS_2022 CONDICIONHIJOANTERIOR 92
93 NACIMIENTOS_2022 NACIMIENTOS_2022 CUENTA CON CURP 93
94 NACIMIENTOS_2022 NACIMIENTOS_2022 DERECHOHABIENCIA 94
95 NACIMIENTOS_2022 NACIMIENTOS_2022 EDAD DE LA MADRE 95
96 NACIMIENTOS_2022 NACIMIENTOS_2022 EDAD GESTACIONAL 96
97 NACIMIENTOS_2022 NACIMIENTOS_2022 ENTIDAD DE CAPTURA 97
98 NACIMIENTOS_2022 NACIMIENTOS_2022 ENTIDAD_NACIMIENTO_MADRE 98
99 NACIMIENTOS_2022 NACIMIENTOS_2022 ENTIDAD_PARTO 99
100 NACIMIENTOS_2022 NACIMIENTOS_2022 ENTIDAD_RESIDENCIA 100
101 NACIMIENTOS_2022 NACIMIENTOS_2022 ESCOLARIDAD 101
102 NACIMIENTOS_2022 NACIMIENTOS_2022 ESTADO CONYUGAL 102
103 NACIMIENTOS_2022 NACIMIENTOS_2022 GRUPO DE PESO 103
104 NACIMIENTOS_2022 NACIMIENTOS_2022 GRUPO EDAD 104
105 NACIMIENTOS_2022 NACIMIENTOS_2022 GRUPO EDAD GESTACIONAL 105
106 NACIMIENTOS_2022 NACIMIENTOS_2022 GRUPO TALLA 106
107 NACIMIENTOS_2022 NACIMIENTOS_2022 HIJOS NACIDOS MUERTOS 107
108 NACIMIENTOS_2022 NACIMIENTOS_2022 HIJOS NACIDOS VIVOS 108
109 NACIMIENTOS_2022 NACIMIENTOS_2022 HIJOS SOBREVIVIENTES 109
110 NACIMIENTOS_2022 NACIMIENTOS_2022 LUGAR NACIMIENTO 110
111 NACIMIENTOS_2022 NACIMIENTOS_2022 Measures 111
112 NACIMIENTOS_2022 NACIMIENTOS_2022 NUMERO TOTAL DE CONSULTAS 112
113 NACIMIENTOS_2022 NACIMIENTOS_2022 NUMERO_EMBARAZOS 113
114 NACIMIENTOS_2022 NACIMIENTOS_2022 OCUPACION_HABITUAL 114
115 NACIMIENTOS_2022 NACIMIENTOS_2022 ORDEN NACIMIENTO 115
116 NACIMIENTOS_2022 NACIMIENTOS_2022 PAIS_ORIGEN 116
117 NACIMIENTOS_2022 NACIMIENTOS_2022 PERSONAL ATENDIO 117
118 NACIMIENTOS_2022 NACIMIENTOS_2022 PESO 118
119 NACIMIENTOS_2022 NACIMIENTOS_2022 PRINCIPALES CAUSAS LM 119
120 NACIMIENTOS_2022 NACIMIENTOS_2022 PRODUCTO 120
121 NACIMIENTOS_2022 NACIMIENTOS_2022 SE CONSIDERA INDIGENA 121
122 NACIMIENTOS_2022 NACIMIENTOS_2022 SEXO 122
123 NACIMIENTOS_2022 NACIMIENTOS_2022 SILVERMAN 123
124 NACIMIENTOS_2022 NACIMIENTOS_2022 SOBREVIVIO PARTO 124
125 NACIMIENTOS_2022 NACIMIENTOS_2022 TAMIZ Y VACUNAS 125
126 NACIMIENTOS_2022 NACIMIENTOS_2022 TRABAJA ACTUALMENTE 126
127 NACIMIENTOS_2022 NACIMIENTOS_2022 TRIMESTRE PRIMER CONSULTA 127
128 NACIMIENTOS_2022 NACIMIENTOS_2022 VIVE HIJO ANTERIOR 128
  • Si intentamos seleccionar todos los datos del cubo, nos aparece un error porque parece que la carga es mucha y el tiempo de consulta se agota.
cursor2.execute("""
SELECT *
FROM [NACIMIENTOS_2022].[Measures]
""")
rows = cursor2.fetchall()
rows_to_df(rows)
---------------------------------------------------------------------------
com_error                                 Traceback (most recent call last)
~\anaconda3\lib\site-packages\adodbapi\adodbapi.py in _execute_command(self)
    681             else: #pywin32
--> 682                 recordset, count = self.cmd.Execute()
    683             # ----- ------------------------------- ---

~\anaconda3\lib\site-packages\win32com\client\dynamic.py in Execute(self, RecordsAffected, Parameters, Options)

~\anaconda3\lib\site-packages\win32com\client\dynamic.py in _ApplyTypes_(self, dispid, wFlags, retType, argTypes, user, resultCLSID, *args)
    286         def _ApplyTypes_(self, dispid, wFlags, retType, argTypes, user, resultCLSID, *args):
--> 287                 result = self._oleobj_.InvokeTypes(*(dispid, LCID, wFlags, retType, argTypes) + args)
    288                 return self._get_good_object_(result, user, resultCLSID)

com_error: (-2147352567, 'Ocurrió una excepción.', (0, 'Microsoft OLE DB Provider for Analysis Services.', 'XML for Analysis parser: The XML for Analysis request timed out before it was completed.', None, 0, -2147467259), None)

During handling of the above exception, another exception occurred:

DatabaseError                             Traceback (most recent call last)
<ipython-input-16-01a61da22894> in <module>
----> 1 cursor2.execute("""
      2 SELECT *
      3 FROM [NACIMIENTOS_2022].[Measures]
      4 """)
      5 rows = cursor2.fetchall()

~\anaconda3\lib\site-packages\adodbapi\adodbapi.py in execute(self, operation, parameters)
    873         if verbose > 3:
    874             print('Params=', format_parameters(self.cmd.Parameters, True))
--> 875         self._execute_command()
    876 
    877     def executemany(self, operation, seq_of_parameters):

~\anaconda3\lib\site-packages\adodbapi\adodbapi.py in _execute_command(self)
    688                                                             format_parameters(self.cmd.Parameters, True))
    689             klass = self.connection._suggest_error_class()
--> 690             self._raiseCursorError(klass, _message)
    691         try:
    692             self.rowcount = recordset.RecordCount

~\anaconda3\lib\site-packages\adodbapi\adodbapi.py in _raiseCursorError(self, errorclass, errorvalue)
    561         if eh is None:
    562             eh = api.standardErrorHandler
--> 563         eh(self.connection, self, errorclass, errorvalue)
    564 
    565     def build_column_info(self, recordset):

~\anaconda3\lib\site-packages\adodbapi\apibase.py in standardErrorHandler(connection, cursor, errorclass, errorvalue)
     55             cursor.messages.append(err)
     56         except: pass
---> 57     raise errorclass(errorvalue)
     58 
     59 # Note: _BaseException is defined differently between Python 2.x and 3.x

DatabaseError: (-2147352567, 'Ocurrió una excepción.', (0, 'Microsoft OLE DB Provider for Analysis Services.', 'XML for Analysis parser: The XML for Analysis request timed out before it was completed.', None, 0, -2147467259), None)
Command:

SELECT *
FROM [NACIMIENTOS_2022].[Measures]

Parameters:
[]
  • Para poder hacer la consulta, la opción es hacer varias consultas más pequeñas, por ejemplo, una para cada entidad federativa. Revisemos las entidades federativas disponibles
cursor2.execute("""
SELECT entidadresidenciad
FROM [NACIMIENTOS_2022].[$ENTIDAD_RESIDENCIA]
""")
rows = cursor2.fetchall()
rows_to_df(rows)
[$entidad_residencia].[entidadresidenciad] _id
0 00 NO ESPECIFICADO 0
1 01 AGUASCALIENTES 1
2 02 BAJA CALIFORNIA 2
3 03 BAJA CALIFORNIA SUR 3
4 04 CAMPECHE 4
5 05 COAHUILA DE ZARAGOZA 5
6 06 COLIMA 6
7 07 CHIAPAS 7
8 08 CHIHUAHUA 8
9 09 CIUDAD DE MEXICO 9
10 10 DURANGO 10
11 11 GUANAJUATO 11
12 12 GUERRERO 12
13 13 HIDALGO 13
14 14 JALISCO 14
15 15 MEXICO 15
16 16 MICHOACAN DE OCAMPO 16
17 17 MORELOS 17
18 18 NAYARIT 18
19 19 NUEVO LEON 19
20 20 OAXACA 20
21 21 PUEBLA 21
22 22 QUERETARO 22
23 23 QUINTANA ROO 23
24 24 SAN LUIS POTOSI 24
25 25 SINALOA 25
26 26 SONORA 26
27 27 TABASCO 27
28 28 TAMAULIPAS 28
29 29 TLAXCALA 29
30 30 VERACRUZ DE IGNACIO DE LA LLAVE 30
31 31 YUCATAN 31
32 32 ZACATECAS 32
33 88 NO APLICA 33
34 99 SE IGNORA 34
  • entonces ahora solo seleccionamos la de un estado en particular, por ejemplo, Chiapas
cursor2.execute("""
SELECT *
FROM [NACIMIENTOS_2022].[Measures]
WHERE [$ENTIDAD_RESIDENCIA.entidadresidenciad]="07 CHIAPAS"
""")
rows = cursor2.fetchall()
df_nacimientos_chis = rows_to_df(rows)
df_nacimientos_chis
[nacimientos].[$atencion prenatal.atencion prenatal] [nacimientos].[$certificado por.certificado por] [nacimientos].[$condicionhijoanterior.condicion hijo anterior] [nacimientos].[$producto.productoembarazo] [nacimientos].[$entidad de captura.entidad de captura] [nacimientos].[$estado conyugal.situacion conyugal] [nacimientos].[$se considera indigena.seconsideraindigena] [nacimientos].[$lugar nacimiento.lugar de nacimiento] [nacimientos].[$personal atendio.personal que atendio] [nacimientos].[$tamiz y vacunas.tamizauditivo] ... [nacimientos].[$ocupacion_habitual.ocupacion habitual] [nacimientos].[$entidad_parto.entidadfederativaparto] [nacimientos].[$entidad_residencia.entidadresidencia] [nacimientos].[$entidad_nacimiento_madre.entidadnacimiento] [nacimientos].[$edad gestacional.edad gestacional] [nacimientos].[$derechohabiencia.derechohabiencia] [nacimientos].[$escolaridad.escolaridad] [nacimientos].[$pais_origen.pais de origen] [nacimientos].[nacimientos] _id
0 SI OTRO MÉDICO NO HA TENIDO OTROS HIJOS(AS) ÚNICO 07 CHIAPAS UNION LIBRE NO SECRETARÍA DE SALUD MÉDICO NO ... NO REMUNERADO, AMA DE CASA 0001 TUXTLA GUTIERREZ 0062 061 40 NINGUNA BACHILLERATO O PREPARATORIA COMPLETA MEXICO 1 0
1 SI OTRO MÉDICO VIVO ÚNICO 07 CHIAPAS UNION LIBRE NO SECRETARÍA DE SALUD MÉDICO NO ... NO REMUNERADO, AMA DE CASA 0001 TUXTLA GUTIERREZ 0001 003 41 NINGUNA PRIMARIA COMPLETA MEXICO 1 1
2 SI OTRO MÉDICO NO HA TENIDO OTROS HIJOS(AS) ÚNICO 07 CHIAPAS UNION LIBRE NO HOGAR PARTERA NO ... NO REMUNERADO, AMA DE CASA 0001 MOTOZINTLA DE MENDOZA 0001 070 40 NINGUNA BACHILLERATO O PREPARATORIA COMPLETA MEXICO 1 2
3 SI PERSONA AUTORIZADA POR LA SECRETARÍA DE SALUD VIVO ÚNICO 07 CHIAPAS UNION LIBRE NO IMSS BIENESTAR MÉDICO NO ... NO REMUNERADO, AMA DE CASA 0001 MOTOZINTLA DE MENDOZA 0063 057 39 SE IGNORA BACHILLERATO O PREPARATORIA COMPLETA MEXICO 1 3
4 SI OTRO MÉDICO VIVO ÚNICO 07 CHIAPAS UNION LIBRE SI SECRETARÍA DE SALUD MÉDICO NO ... NO ESPECIFICADO 0001 CHALCHIHUITAN 0043 022 39 NINGUNA SECUNDARIA COMPLETA MEXICO 1 4
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
35819 SI OTRO MÉDICO VIVO ÚNICO 07 CHIAPAS UNION LIBRE NO SECRETARÍA DE SALUD MÉDICO NO ... NO REMUNERADO, AMA DE CASA 0001 TUXTLA GUTIERREZ 1017 020 40 NINGUNA BACHILLERATO O PREPARATORIA COMPLETA MEXICO 1 35819
35820 SI OTRO MÉDICO VIVO ÚNICO 07 CHIAPAS UNION LIBRE NO SECRETARÍA DE SALUD MÉDICO NO ... NO REMUNERADO, AMA DE CASA 0001 HUIXTLA 0225 071 39 NINGUNA SECUNDARIA COMPLETA MEXICO 1 35820
35821 SI MÉDICO PEDIATRA VIVO ÚNICO 07 CHIAPAS UNION LIBRE NO SECRETARÍA DE SALUD MÉDICO NO ... NO REMUNERADO, AMA DE CASA 0001 TAPACHULA DE CORDOVA Y ORDOÑEZ 0001 997 40 NINGUNA PRIMARIA COMPLETA GUATEMALA 1 35821
35822 SI MÉDICO PEDIATRA NO HA TENIDO OTROS HIJOS(AS) ÚNICO 07 CHIAPAS CASADO(A) NO SECRETARÍA DE SALUD MÉDICO NO ... NO REMUNERADO, AMA DE CASA 0001 COMITAN DE DOMINGUEZ 0085 104 38 NINGUNA BACHILLERATO O PREPARATORIA COMPLETA MEXICO 1 35822
35823 SI PERSONA AUTORIZADA POR LA SECRETARÍA DE SALUD VIVO ÚNICO 07 CHIAPAS UNION LIBRE NO IMSS BIENESTAR MÉDICO NO ... NO REMUNERADO, AMA DE CASA 0001 MOTOZINTLA DE MENDOZA 0130 089 39 SE IGNORA PRIMARIA COMPLETA MEXICO 1 35823

35824 rows × 44 columns

df_nacimientos_chis.columns
Index(['[nacimientos].[$atencion prenatal.atencion prenatal]',
       '[nacimientos].[$certificado por.certificado por]',
       '[nacimientos].[$condicionhijoanterior.condicion hijo anterior]',
       '[nacimientos].[$producto.productoembarazo]',
       '[nacimientos].[$entidad de captura.entidad de captura]',
       '[nacimientos].[$estado conyugal.situacion conyugal]',
       '[nacimientos].[$se considera indigena.seconsideraindigena]',
       '[nacimientos].[$lugar nacimiento.lugar de nacimiento]',
       '[nacimientos].[$personal atendio.personal que atendio]',
       '[nacimientos].[$tamiz y vacunas.tamizauditivo]',
       '[nacimientos].[$sexo.sexo]',
       '[nacimientos].[$sobrevivio parto.sobrevivio al parto]',
       '[nacimientos].[$trabaja actualmente.trabaja actualmente]',
       '[nacimientos].[$trimestre primer consulta.trimestre primer consulta]',
       '[nacimientos].[$hijos nacidos muertos.hijosnacidosmuertosc]',
       '[nacimientos].[$hijos sobrevivientes.hijos sobrevivientes]',
       '[nacimientos].[$hijos nacidos vivos.hijos nacidos vivos]',
       '[nacimientos].[$02 fecha de nacimiento.fechanacimiento]',
       '[nacimientos].[$numero_embarazos.numerodeembarazos]',
       '[nacimientos].[$edad de la madre.edad de la madre]',
       '[nacimientos].[$principales causas lm.cie causa]',
       '[nacimientos].[$grupo edad.grupoedad]', '[nacimientos].[$peso.peso]',
       '[nacimientos].[$grupo de peso.grupopeso]',
       '[nacimientos].[$grupo edad gestacional.grupoedadgestacional]',
       '[nacimientos].[$grupo talla.grupotalla]',
       '[nacimientos].[$orden nacimiento.ordendenacimiento]',
       '[nacimientos].[$apgarh.apgar]', '[nacimientos].[$silverman.silverman]',
       '[nacimientos].[$01 unidad medica parto.clues]',
       '[nacimientos].[$vive hijo anterior.vive hijo anterior]',
       '[nacimientos].[$codigo cie.codigocieanomalia1]',
       '[nacimientos].[$cuenta con curp.cuenta con curp]',
       '[nacimientos].[$numero total de consultas.numero total de consultas]',
       '[nacimientos].[$ocupacion_habitual.ocupacion habitual]',
       '[nacimientos].[$entidad_parto.entidadfederativaparto]',
       '[nacimientos].[$entidad_residencia.entidadresidencia]',
       '[nacimientos].[$entidad_nacimiento_madre.entidadnacimiento]',
       '[nacimientos].[$edad gestacional.edad gestacional]',
       '[nacimientos].[$derechohabiencia.derechohabiencia]',
       '[nacimientos].[$escolaridad.escolaridad]',
       '[nacimientos].[$pais_origen.pais de origen]',
       '[nacimientos].[nacimientos]', '_id'],
      dtype='object')

En este último caso quizá convenga aprender mejor MDX para hacer consultas más detalladas y con menos resultados. La verdad a mí me pareció un lenguaje complicado, pero es necesario entenderlo un poco para poder usar los cubos dinámicos. Yo tuve muchísimos errores antes de poder dar con las consultas correctas que hice arriba.