PASOS DE LA LIMPIEZA DE DATOS
Los datos limpios son un componente clave en el desarrollo de soluciones basadas en datos, ya que nos ayudan a aumentar la productividad, minimizar las molestias y brindar resultados de mayor calidad.
Carga de los datos
Se importan y organizan datos de diferentes fuentes para tener el conjunto inicial de datos.
Observaciones duplicadas
Los datos duplicados se eliminan solo si son producto de un error en la generación del conjunto de datos.
Estadísticas descriptivas básicas
Se calculan algunas medidas de tendencia central, dispersión y posición para identificar qué variables requieren algún tratamiento particular.
Variables no informativas
Con base en las estadísticas anteriores, se decide qué variables podrían ser no informativas.
Valores atípicos
Se identifican y eliminan o imputan valores extremos que pudieran afectar ajustes posteriores.
Registros no informados
Monitorear y trabajar la presencia de valores no informados es recomendable.
ACERCA DE LA LIMPIEZA DE DATOS
El éxito o fracaso de los modelos y análisis de datos que se llevan a cabo está determinado en gran medida por la calidad de los datos con los que se construyen. En base a esto, es importante tener en cuenta los criterios de selección y tratamiento de las variables a considerar en el estudio:
CONSIDERACIONES TEÓRICAS
Análisis univariado, descripción de variables
Distribución de las variables: se analiza la distribución de las variables para detectar valores no válidos, así como concentraciones en determinados valores.
Variables numéricas: se calcula la media, la mediana, la desviación estándar y la posición.
Variables categóricas: se calcula la moda y la mediana, y se analiza la frecuencia observada en cada valor de la variable.
Outliers: se trata de detectar valores extremos.
Faltantes: se analiza el porcentaje de casos no reportados que presenta cada una de las variables.
Análisis temporal
Distribución de las variables: se analiza la distribución de las variables para detectar la ausencia de información en un periodo de tiempo, así como cambios estructurales en las variables.
Variables numéricas: se calcula la media, la mediana, el percentil 5 y el percentil 95. Se analiza la tendencia de la variable, así como posibles valores extremos.
Variables categóricas: para cada semestre se calcula la distribución de la variable. Se analizan los cambios estructurales entre las variables que podrían ser objeto de modelos predictivos (en una fase posterior si los modelos no alcanzan una buena calidad de pronóstico).
Análisis de coherencia
- Se busca la consistencia de los datos, se analiza la calidad de la información y monitorea la integridad de la misma.
- Se procura que el porcentaje de faltantes no sea alto (reducir la incorporación de incertidumbre o ruido en el modelo a pronosticar).
- Se prioriza que las covariables representen una importante fuente de información (análisis de medidas de posición).
Análisis bivariado
- Procurar que las variables explicativas sean interesantes desde el punto de vista del contexto (fácil acceso e interés).
- Para variables continuas, que la tendencia de la variable sea clara (teniendo en cuenta que puede variar en función de los tramos muestreados).
- En el caso de las variables alfanuméricas se prioriza que no exista una alta concentración en un valor y que se puedan realizar discriminaciones con diferentes comportamientos frente a la variable objetivo.
Tratamiento de los datos faltantes
Se admite que las variables no sean informadas en algunos momentos, y el tratamiento depende del porcentaje de registros no informados presente, generalmente los criterios giran en torno a las siguientes consideraciones:
- Si hay más del 20% de datos faltantes → Se elimina la variable de estudio.
- Si hay menos del 20% → Se procesan a partir de otros instantes de la variable o en función de su comportamiento e interacción con otras variables para los segmentos reportados, es decir, se imputan ya sea por regresión o por el valor promedio correspondiente al ciclo estacional anterior.
- Cuando hay menos del 5% de datos no reportados, los registros pueden ser borrados, sin embargo, esto afecta la secuencia temporal de la serie y es preferible hacer imputaciones (si se trabaja con series temporales).
En ocasiones, aunque el porcentaje de registros no reportados es alto, es necesario modelar incluyéndolos, para esto se genera una variable dummy que refleje este efecto (Sujeto a consideración porque se podría considerar simular la variable, pero se requiere tiempo para procesar las variables o trabajarlas en tramos).
PASO 1. CARGA DE LOS DATOS
En el repositorio, se pueden encontrar todos los scripts asociados a esta sección. Se trabaja con datos vinculados a registros de vehículos en circulación, delincuencia, población y algunos indicadores económicos de distintas regiones de Chile, en la siguiente tabla se resumen los tópicos y enlaces para descargar los datos desde sus fuentes originales.
N° | Tópico | Descripción | Periodicidad | Fecha (años) | Fuente |
---|---|---|---|---|---|
1 | Población | Población nacional total, INE (número de personas). | Anual | 2005 - 2022 | Click |
2 | Siniestros de transito | Siniestros de transito en rutas de Chile (número de personas involucrados en los siniestros). | Mensual | 2015 - 2022 | Click |
3 | Vehiculos en circulación | Parque de vehículos en circulación, por tipo, segun región (número de vehículos). | Anual | 2008 - 2021 | Click |
4 | Registro de empresas y sociedades | Número de constituciones en registro de empresas y sociedades a nivel regional (Cantidad). | Mensual | 2013 - 2022 | Click |
5 | Consumo de hogares e IPSFL | Consumo de hogares e IPSFL por región , volumen a precios del año anterior encadenado, referencia 2018 (miles de millones de pesos encadenados). | Anual | 2013 - 2021 | Click |
6 | Producto Interno Bruto | Producto interno bruto por región, volumen a precios del año anterior encadenado, series empalmadas, referencia 2018 (miles de millones de pesos encadenados). | Anual | 2013 - 2021 | Click |
7 | Fuerza de trabajo | Fuerza de trabajo (Miles de personas), Ocupados (Miles de personas) y Tasa de desocupación (%). | Mensual | 2010 - 2022 | Click |
8 | Delitos | Hechos delictivos que registraron las policías durante el período (Cantidad de registros). | Mensual | 2005 - 2022 | Click |
Se descargaron todos los registros de la fuente original y se guardaron en una carpeta para ser procesados de manera local. Para el proceso de carga y visualización inicial de los datos se implementaron las siguientes herramientas:
# Libraries ############################################################################################################
import os
import glob
import pandas as pd
import numpy as np
from datetime import datetime
# Local path ##########################################################################################################
# Directorio local donde se encuentran los datos descargados
path: str = './DataDrivenCulture/'
path_poblacion = path + 'Banco/Poblacion.xlsx'
path_empresa = path + 'Banco/Empresas.xlsx'
path_consumo = path + 'Banco/ConsumoHogares.xlsx'
path_PIB = path + 'Banco/PIB.xlsx'
path_trabajo = path + 'Banco/Fuerza_Trabajo.xlsx'
path_delito = path + 'Delincuencia/Delincuencia_Regiones.csv'
path_cars = [path + 'Transito/2021.xlsx',
path + 'Transito/2020.xlsx',
path + 'Transito/2019.xlsx',
path + 'Transito/2018.xlsx',
path + 'Transito/2017.xlsx',
path + 'Transito/2016.xlsx',
path + 'Transito/2015.xlsx',
path + 'Transito/2014.xlsx',
path + 'Transito/2013.xlsx',
path + 'Transito/2012.xls',
path + 'Transito/2011.xls',
path + 'Transito/2010.xls',
path + 'Transito/2009.xls',
path + 'Transito/2008.xls']
# URL #################################################################################################################
# En caso de preferir descargar los datos desde la URL se toma este caso como ejemplo.
accident_root: str = 'https://opendata.arcgis.com/api/v3/datasets/'
url_accident = [
accident_root + '8eb8d87f868840e6aba4a0018ccab140_0/downloads/data?format=csv&spatialRefId=4326&where=1%3D1',
accident_root + 'deeda8796b00416b8db0759afb29f028_0/downloads/data?format=csv&spatialRefId=4326&where=1%3D1',
accident_root + '51207ebf95db4d5f98e312dd3222bce8_0/downloads/data?format=csv&spatialRefId=4326&where=1%3D1'
]
Se crearon 8 funciones para cargar los diferentes DataFrames disponibles, una función por cada tópico (población, empresa, consumo, delito, trabajo, PIB, accidentes, cars) y otra función para unir todos los DataFrames en uno solo. La idea es abarcar la mayor cantidad de datos posibles en un solo archivo e ir tomando secciones del mismo dependiendo del análisis que se quiere realizar, esto se debe a que con este DataFrame consolidado se realizan varios ajustes en distintas secciones del sitio web.
Así mismo, se crearon dos funciones para la estandarización de los datos, se abrevian los nombres de las regiones y se colocan en mayúsculas
N° | Nombre de la Región | Abreviación en el dataframe |
---|---|---|
1 | Región de Arica y Parinacota | AP |
2 | Región de Tarapacá | TA |
3 | Región de Antofagasta | AN |
4 | Región de Atacama | AT |
5 | Región de Coquimbo | CO |
6 | Región de Valparaíso | VA |
7 | Región Metropolitana de Santiago | RM |
8 | Región del Libertador General Bernardo O'Higgins | LI |
9 | Región del Maule | ML |
10 | Región de Ñuble | NB |
11 | Región del Biobío | BI |
12 | Región de La Araucanía | AR |
13 | Región de Los Ríos | LR |
14 | Región de Los Lagos | LL |
15 | Región de Aysén del General Carlos Ibáñez del Campo | AI |
16 | Región de Magallanes y de la Antártica Chilena | MA |
# Tools definitions ###################################################################################################
def clean_accents(text):
"""
Return the text without accents.
"""
accents = {'á': 'a', 'é': 'e', 'í': 'i', 'ó': 'o', 'ú': 'u',
'Á': 'A', 'É': 'E', 'Í': 'I', 'Ó': 'O', 'Ú': 'U'}
for acc in accents:
if acc in text:
text = text.replace(acc, accents[acc])
else:
pass
return text
def replace_region(df) -> object:
"""
Retorna el dataframe asociado con los nombres de regiones estandarizados.
:rtype: DataFrame
"""
df = df.str.upper().str.strip()
df = pd.Series([clean_accents(i) for i in list(df)])
df = df.str.replace("REGION ANTOFAGASTA", "AN") \
.str.replace("REGION ARAUCANIA", "AR") \
.str.replace("REGION ARICA Y PARINACOTA", "AP") \
.str.replace("REGION ATACAMA", "AT") \
.str.replace("REGION AYSEN DEL GRL. CARLOS IBAÑEZ DEL CAMPO", "AI") \
.str.replace("REGION BIO BIO", "BI") \
.str.replace("REGION COQUIMBO", "CO") \
.str.replace("REGION DE LOS RIOS", "LR") \
.str.replace("REGION LIB.B.O'HIGGINS", "LI") \
.str.replace("REGION LOS LAGOS", "LL") \
.str.replace("REGION MAGALLANES Y ANTARTICA CHILENA", "MA") \
.str.replace("REGION MAULE", "ML") \
.str.replace("REGION TARAPACA", "TA") \
.str.replace("REGION VALPARAISO", "VA") \
.str.replace("REGION ÑUBLE", "NB") \
.str.replace("REGION DE ARICA Y PARINACOTA", "AP") \
.str.replace("REGION DE TARAPACA", "TA") \
.str.replace("REGION DE ANTOFAGASTA", "AN") \
.str.replace("REGION DE ATACAMA", "AT") \
.str.replace("REGION DE COQUIMBO", "CO") \
.str.replace("REGION DE VALPARAISO", "VA") \
.str.replace("REGION DEL LIBERTADOR GENERAL BERNARDO O'HIGGINS", "LI") \
.str.replace("REGION DEL MAULE", "ML") \
.str.replace("REGION DEL BIOBIO", "BI") \
.str.replace("REGION DE ÑUBLE", "NB") \
.str.replace("REGION DE LA ARAUCANIA", "AR") \
.str.replace("REGION DE LOS LAGOS", "LL") \
.str.replace("REGION DE AYSEN DEL GENERAL CARLOS IBAÑEZ DEL CAMPO", "AI") \
.str.replace("REGION DE MAGALLANES Y LA ANTARTICA CHILENA", "MA") \
.str.replace("REGION DEL LIBERTADOR GENERAL BERNARDO OHIGGINS", "LI") \
.str.replace("REGION DE MAGALLANES Y DE LA ANTARTICA CHILENA", "MA") \
.str.replace("REGION DE MAULE", "ML") \
.str.replace("REGION DEL LIBERTADOR GENERAL BERNARDO O'HIGGINS", "LI") \
.str.replace("I DE TARAPACA", "TA") \
.str.replace("II DE ANTOFAGASTA", "AN") \
.str.replace("III DE ATACAMA", "AT") \
.str.replace("IV DE COQUIMBO", "CO") \
.str.replace("V DE VALPARAISO", "VA") \
.str.replace("VI DE O'HIGGINS", "LI") \
.str.replace("VII DEL MAULE", "ML") \
.str.replace("VIII DEL BIOBIO", "BI") \
.str.replace("IX DE LA ARAUCANIA", "AR") \
.str.replace("X DE LOS LAGOS", "LL") \
.str.replace("XI DE AYSEN", "AI") \
.str.replace("XII DE MAGALLANES Y DE LA ANTARTICA CHILENA", "MA") \
.str.replace("XIV DE LOS RIOS", "LR") \
.str.replace("XV DE ARICA Y PARINACOTA", "AP") \
.str.replace("XI AYSEN", "AI") \
.str.replace("XII MAGALLANES Y LA ANTARTICA", "MA") \
.str.replace("XV ARICA Y PARINACOTA", "AP") \
.str.replace("XI AISEN", "AI") \
.str.replace("XII MAGALLANES Y ANTARTICA", "MA") \
.str.replace("VIII DEL BIO-BIO", "BI") \
.str.replace("REGION DEL LIB. BERNARDO OHIGGINS", "LI") \
.str.replace("REGION DE AYSEN", "AI") \
.str.replace("REGION DE MAGALLANES", "MA") \
.str.replace("REGION METROPOLITANA DE SANTIAGO", "RM") \
.str.replace("XIII METROPOLITANA", "RM") \
.str.replace("METROPOLITANA DE SANTIAGO", "RM") \
.str.replace("REGION METROPOLITANA", "RM")
return df
2. Datos de población
def population_load():
"""
Return the data frame associated with population.
"""
df_pop = pd.read_excel(path_poblacion, skiprows=2)
# Se pivotea la tabla
df_pop = pd.melt(df_pop,
id_vars=df_pop.columns[[1]],
value_vars=df_pop.columns[2:],
value_name='Population')
# Se define el formato y cambio de nombre
df_pop['variable'] = pd.to_datetime(df_pop['variable']).dt.strftime('%Y')
df_pop.rename(columns={'variable': 'Year', 'Descripción series': 'Region'},
inplace=True)
# La funcion replace_region estandariza el formato de los nombres de las regiones
df_pop['Region'] = replace_region(df_pop['Region'])
df_pop['Year'] = pd.to_datetime(df_pop['Year']).dt.strftime('%Y')
return df_pop
2. Datos de siniestros de transito
def accident_load():
"""
Return the data frame associated with car accidents.
"""
df_accident: DataFrame = pd.DataFrame()
# Se itera sobre la lista de enlaces web
for i in range(len(url_accident)):
df_load = pd.read_csv(url_accident[i],
usecols=['Fecha', 'Región',
'Fallecidos', 'Graves',
'Menos_Grav', 'Leves'],
parse_dates=['Fecha'])
df_accident = df_accident.append(df_load)
# Se aplica np.nansum para calcular el total de lesionados.
df_accident['Lesionados_total'] = df_accident[
['Graves', 'Menos_Grav', 'Leves']].apply(np.nansum, axis=1)
df_accident.rename(columns={'Región': 'Region'}, inplace=True)
df_accident['Month'] = df_accident['Fecha'].dt.strftime('%m')
df_accident['Year'] = df_accident['Fecha'].dt.strftime('%Y')
df_accident = df_accident.reset_index(drop=True)
# Se aplica groupby().sum() ya que los datos estan dados en formato
# dd/mm/yyyy HH:MM y se quieren mensuales.
df_accident = df_accident.groupby(['Region', 'Year', 'Month'],
as_index=False).sum()
# La funcion replace_region estandariza el formato de los nombres de las regiones
df_accident['Region'] = replace_region(df_accident['Region'])
return df_accident
3. Datos de vehiculos en circulación
def car_load():
"""
Return the data frame associated with car population.
"""
# A year counter is created since the data does not have the corresponding
# column.
counter = 2021
# The titles of the tables of the source of origin are identified, not all
# the tables have the same format, so several lists of titles must be created.
Column_0 = ['Año', 'Sector', 'Localidad', 'Total', 'Motorizados', 'No_motorizados', 'Automóvil_StatioWagon',
'Todo_Terreno', 'Furgón', 'Minibús', 'Camioneta', 'Motocicleta_y_Similares', 'Otros_con_Motor1',
'Otros_sin_Motor', 'Taxi_Básico', 'Taxi_Colectivo', 'Taxi_Turismo', 'Minibús_Transporte_Colectivo',
'Minibús_furgón escolar_y_trabajadores', 'Taxi_bus', 'Bus_Transporte_Colectivo',
'Bus_transporte_escolar_y_trabajadores', 'Camión_Simple', 'Tractocamión', 'Tractor_Agrícola',
'Otros_con_Motor2', 'Remolque_Semirremolque']
Column_1 = ['Año', 'Sector', 'Localidad', 'Total', 'Motorizados', 'No_motorizados', 'Automóvil_StatioWagon',
'Todo_Terreno', 'Furgón', 'Minibús', 'Camioneta', 'Motocicleta_y_Similares', 'Otros_con_Motor1',
'Otros_sin_Motor', 'Taxi_Básico', 'Taxi_Colectivo', 'Taxi_Turismo', 'Minibús_Transporte_Colectivo',
'Minibús_furgón_escolar_y_trabajadores', 'Bus_Transporte_Colectivo',
'Bus_transporte_escolar_y_trabajadores', 'Camión_Simple', 'Tractocamión', 'Tractor_Agrícola',
'Otros_con_Motor2', 'Remolque_Semirremolque']
Column_2 = ['Año', 'Sector', 'Localidad', 'Total', 'Automóvil_StatioWagon', 'Todo_Terreno', 'Furgón', 'Minibús',
'Camioneta', 'Motocicleta_y_Similares', 'Otros_con_Motor1', 'Otros_sin_Motor', 'Taxi_Básico',
'Taxi_Colectivo', 'Taxi_Turismo', 'Minibús_Transporte_Colectivo',
'Minibús_furgón_escolar_y_trabajadores', 'Bus_Transporte_Colectivo',
'Bus_transporte_escolar_y_trabajadores', 'Camión_Simple', 'Tractocamión', 'Tractor_Agrícola',
'Otros_con_Motor2', 'Remolque_Semirremolque']
Column_3 = ['Año', 'Sector', 'Localidad', 'Total', 'Automóvil_StatioWagon', 'Todo_Terreno', 'Furgón', 'Minibús',
'Camioneta', 'Motocicleta_y_Similares', 'Otros_con_Motor1', 'Otros_sin_Motor', 'Taxi_Básico',
'Taxi_Colectivo', 'Taxi_Turismo', 'Minibús_Transporte_Colectivo',
'Minibús_furgón_escolar_y_trabajadores', 'Bus_Transporte_Colectivo',
'Bus_transporte_escolar_y_trabajadores', 'Camión_Simple', 'Tractocamión', 'Tractor_Agrícola',
'Otros_con_Motor2', 'Remolque_Semirremolque']
Column_4 = ['Año', 'Sector', 'Localidad', 'Total', 'Automóvil_StatioWagon_TodoTerreno', 'Furgón', 'Minibús',
'Camioneta', 'Motocicleta_y_Similares', 'Otros_con_Motor1', 'Otros_sin_Motor', 'Taxi_Básico',
'Taxi_Colectivo', 'Taxi_Turismo', 'Minibús_Transporte_Colectivo', 'Bus_Transporte_Colectivo',
'Camión_Simple', 'Tractocamión', 'Tractor_Agrícola', 'Otros_con_Motor2', 'Remolque_Semirremolque']
Common_list: list[str | Any] = ['Año', 'Sector', 'Localidad', 'Automóvil_StatioWagon_TodoTerreno', 'Furgón',
'Minibús', 'Camioneta', 'Motocicleta_y_Similares', 'Otros_sin_Motor',
'Taxi_Básico', 'Taxi_Colectivo', 'Taxi_Turismo', 'Minibús_Transporte_Colectivo',
'Bus_Transporte_Colectivo', 'Camión_Simple', 'Tractocamión', 'Tractor_Agrícola',
'Remolque_Semirremolque']
df_car = pd.DataFrame()
for url in range(len(path_cars)):
if url < 4:
df_load = pd.read_excel(path_cars[url], sheet_name='4', skiprows=4)
df_load.columns = Column_4
elif url == 4:
df_load = pd.read_excel(path_cars[url], sheet_name='4', skiprows=3)
df_load.columns = Column_3
elif url == 5:
df_load = pd.read_excel(path_cars[url], sheet_name='Cuadro 2.2', skiprows=4)
df_load.columns = Column_2
elif 6 <= url < 10:
df_load = pd.read_excel(path_cars[url], sheet_name='Cuadro 2', skiprows=4)
df_load.columns = Column_1
elif url == 10:
df_load = pd.read_excel(path_cars[url], sheet_name='Cuadro 2', skiprows=9)
df_load.columns = Column_1
else:
df_load = pd.read_excel(path_cars[url], sheet_name='CUADRO 2', skiprows=9)
df_load.columns = Column_0
# Asignar el DataFrame a una variable diferente en cada iteración
df_load[df_load.columns[0]] = counter
# Se remueven caracteres los identificados en el dataframe que causan errores en la lectura
# de los datos
df_load.replace(['-',
' ',
' -',
' -',
' -',
' -',
' -'],
[np.nan, '', '', '', '', '', ''],
inplace=True)
if url > 3:
df_load['Automóvil_StatioWagon_TodoTerreno'] = pd.to_numeric(df_load['Automóvil_StatioWagon']) \
+ pd.to_numeric(df_load['Todo_Terreno'])
else:
pass
df_load = df_load[Common_list]
df_car = df_car.append(df_load)
df_car['Localidad'] = df_car['Localidad'].str.upper().str.strip()
counter -= 1
df_car = df_car.reset_index(drop=True)
df_car['Localidad'] = df_car['Localidad'].str.upper().str.strip()
df_car['Sector'] = df_car['Sector'].replace('Región', 'Region')
lista_region_2 = list(df_car[(df_car['Sector'] == 'Region')]['Localidad'].unique())
# Se toma solo la información de las regiones
df_car = df_car[df_car.Localidad.isin(lista_region_2)]
df_car.rename(columns={'Localidad': 'Region',
'Año': 'Year'}, inplace=True)
df_car.drop('Sector', axis=1, inplace=True)
df_car.iloc[:, 2:] = df_car.iloc[:, 2:].astype('float32')
df_car['Year'] = pd.to_datetime(df_car['Year'].astype('str')).dt.strftime('%Y')
df_car['Region'] = df_car['Region'].str.upper().str.strip()
df_car['Region'] = [clean_accents(i) for i in list(df_car['Region'])]
df_car['Region'] = replace_region(df_car['Region']).values
# Se consolidan los diferentes tipos de transportes de carga en uno solo para
# reducir la presencia de nan
df_car['Transporte_de_carga'] = df_car[['Camión_Simple', 'Tractocamión', 'Tractor_Agrícola',
'Remolque_Semirremolque']].apply(np.nansum, axis=1)
return df_car.drop(['Camión_Simple', 'Tractocamión', 'Tractor_Agrícola', 'Remolque_Semirremolque'], axis=1)
4. Datos de registro de empresas y sociedades
def empresa_load():
"""
Return the data frame associated with Registration of companies.
"""
df_empresas = pd.read_excel(path_empresa, skiprows=2)
# Transpose the columns you want
df_empresas = pd.melt(df_empresas,
id_vars=df_empresas.columns[[1]],
value_vars=df_empresas.columns[2:],
value_name='Reg_Empresas_Sociedades')
# The text of the variable Year is extracted
df_empresas.rename(columns={'variable': 'Date', 'Descripción series': 'Region'},
inplace=True)
df_empresas['Date'] = pd.to_datetime(df_empresas['Date'])
df_empresas['Year'] = df_empresas['Date'].dt.strftime('%Y')
df_empresas['Month'] = df_empresas['Date'].dt.strftime('%m')
df_empresas['Region'] = replace_region(df_empresas['Region'])
return df_empresas.drop('Date', axis=1)
5. Datos del consumo de hogares
def consumo_load():
"""
Return the data frame associated with Registration of companies.
"""
df_consumo = pd.read_excel(path_consumo, skiprows=2)
# Transpose the columns you want
df_consumo: DataFrame = pd.melt(df_consumo,
id_vars=df_consumo.columns[[1]],
value_vars=df_consumo.columns[2:],
value_name='Consumo_Hogares_IPSFL')
# The text of the variable Year is extracted
df_consumo.rename(columns={'variable': 'Date', 'Descripción series': 'Region'},
inplace=True)
df_consumo['Date'] = pd.to_datetime(df_consumo['Date'])
df_consumo['Year'] = df_consumo['Date'].dt.strftime('%Y')
df_consumo['Region'] = replace_region(df_consumo['Region'])
return df_consumo.drop('Date', axis=1)
6. Datos del Producto Interno Bruto
def PIB_load():
"""
Return the data frame associated with Registration of companies.
"""
df_PIB: DataFrame = pd.read_excel(path_PIB, skiprows=2)
# Transpose the columns you want
df_PIB = pd.melt(df_PIB,
id_vars=df_PIB.columns[[1]],
value_vars=df_PIB.columns[2:],
value_name='PIB')
# The text of the variable Year is extracted
df_PIB.rename(inplace=True,
columns={'variable': 'Date', 'Descripción series': 'Region'})
df_PIB['Date'] = pd.to_datetime(df_PIB["Date"])
df_PIB['Year'] = df_PIB['Date'].dt.strftime('%Y')
df_PIB['Region'] = replace_region(df_PIB['Region'])
return df_PIB.drop('Date', axis=1)
7. Datos de la fuerza de trabajo
def fuerza_trabajo_load():
"""
Return the data frame associated with Labor force.
"""
df_Trabajo = pd.read_excel(path_trabajo)
df_Trabajo['Year'] = pd.to_datetime(df_Trabajo['Year'])
df_Trabajo['Month'] = df_Trabajo['Year'].dt.strftime('%m')
df_Trabajo['Year'] = df_Trabajo['Year'].dt.strftime('%Y')
return df_Trabajo
8. Datos de hechos delictivos
def delito_load():
"""
Return the data frame associated with crimes.
"""
df_delito = pd.read_csv(path_delito, index_col=0, thousands=',', encoding='latin-1').reset_index()
df_delito[['Region', 'Year']] = df_delito[['Region', 'Year']].astype('str')
df_delito['Year'] = pd.to_datetime(df_delito['Year']).dt.strftime('%Y')
df_delito['Month'] = df_delito['Month'].astype('str').str.rjust(2, '0')
df_delito['Region'] = replace_region(df_delito['Region'])
return df_delito
Dataframe consolidado
def df_merge(df_accident: DataFrame = accident_load(),
df_population: DataFrame = population_load(),
df_car: DataFrame = car_load(),
df_delito: DataFrame = delito_load(),
df_trabajo: DataFrame = fuerza_trabajo_load(),
df_PIB: DataFrame = PIB_load(),
df_Consumo: DataFrame = consumo_load(),
df_Empresa: DataFrame = empresa_load()
) -> DataFrame:
"""
Returns the consolidated dataframe.
"""
# The union of the first two df is made (by 'Year' and 'Region')
df_Region_I = pd.merge(pd.merge(
pd.merge(df_population, df_car, on=['Year', 'Region'], how='outer', suffixes=('', '_drop')),
df_PIB, on=['Year', 'Region'], how='outer', suffixes=('', '_drop')),
df_Consumo, on=['Year', 'Region'], how='outer', suffixes=('', '_drop'))
df_Region_II = pd.merge(pd.merge(
pd.merge(df_delito, df_accident, on=['Year', 'Region', 'Month'], how='outer', suffixes=('', '_drop')),
df_trabajo, on=['Year', 'Region', 'Month'], how='outer', suffixes=('', '_drop')),
df_Empresa, on=['Year', 'Region', 'Month'], how='outer', suffixes=('', '_drop'))
df_Region = pd.merge(df_Region_II, df_Region_I, on=['Year', 'Region'], how='outer', suffixes=('', '_drop'))
df_Region.drop([col for col in df_Region.columns if 'drop' in col], axis=1, inplace=True)
Regiones_list = ['AP', 'TA', 'AN', 'AT', 'CO', 'VA', 'RM', 'LI',
'ML', 'NB', 'BI', 'AR', 'LR', 'LL', 'AI', 'MA']
df_Region = df_Region[df_Region['Region'].isin(Regiones_list)]
# Ordenamos un poco las columnas
df_Region = df_Region.loc[:,['Region', 'Year', 'Month', 'Population', 'Fallecidos', 'Graves', 'Menos_Grav',
'Leves', 'Lesionados_total', 'Automóvil_StatioWagon_TodoTerreno', 'Furgón', 'Minibús',
'Camioneta', 'Motocicleta_y_Similares', 'Otros_sin_Motor', 'Taxi_Básico',
'Taxi_Colectivo', 'Taxi_Turismo', 'Minibús_Transporte_Colectivo',
'Bus_Transporte_Colectivo', 'Transporte_de_carga',
'Grupo de delitos de mayor connotación social',
'Homicidios', 'Hurtos', 'Lesiones leves', 'Lesiones menos graves graves o gravísimas',
'Otros robos con fuerza', 'Robo con violencia o intimidación',
'Robo de objetos de o desde vehículo', 'Robo de vehículo motorizado',
'Robo en lugar habitado', 'Robo en lugar no habitado', 'Robo por sorpresa',
'Violaciones', 'Grupo de Infracción a ley de armas', 'Abandono de armas',
'Hallazgo de armas o explosivos', 'Otros ley de armas', 'Porte de armas',
'Tenencia ilegal de armas o explosivos', 'Grupo de Incivilidades', 'Amenazas',
'Comercio ambulante o clandestino', 'Consumo alcohol vía pública', 'Daños',
'Desórdenes', 'Ebriedad', 'Otras incivilidades', 'Riña pública', 'Ruidos molestos',
'Abigeato', 'Abusos sexuales y otros delitos sexuales',
'Grupo de Violencia intrafamiliar', 'Violencia intrafamiliar a adulto mayor',
'Violencia intrafamiliar a hombre', 'Violencia intrafamiliar a mujer',
'Violencia intrafamiliar a niño', 'Violencia intrafamiliar no clasificado',
'Receptación', 'Robo frustrado', 'PIB', 'Consumo_Hogares_IPSFL',
'Fuerza_de_trabajo_(Miles_de_personas)', 'Ocupados_(Miles_de_personas)',
'Tasa_de_desocupación_(%)', 'Reg_Empresas_Sociedades']]
df_Region.columns = df_Region.columns.str.replace(' ', '_')
return df_Region
Una vez cargadas todas las funciones, se usa la función df_merge para unirlos, la unión demora unos 8.51 segundos en generarse, el DataFrame resultante contiene 3456 filas y 69 variables.
# Se define el DataFrame ya consolidado como df_Region:
start_time = datetime.now()
df_Region = df_merge(df_accident=accident_load(),
df_population=population_load(),
df_car=car_load(),
df_delito= delito_load(),
df_trabajo=fuerza_trabajo_load(),
df_PIB=PIB_load(),
df_Consumo=consumo_load(),
df_Empresa=empresa_load())
print('df_Empresa: Processing time (hh:mm:ss.ms) {}'.format(datetime.now() - start_time))
df_Region
N° | Region | Year | Month | Population | Fallecidos | Graves | Menos_Grav | Leves | Lesionados_total | Automóvil_StatioWagon_TodoTerreno | Furgón | Minibús | Camioneta | Motocicleta_y_Similares | Otros_sin_Motor | Taxi_Básico | Taxi_Colectivo | Taxi_Turismo | Minibús_Transporte_Colectivo | Bus_Transporte_Colectivo | Camión_Simple | Tractocamión | Tractor_Agrícola | Remolque_Semirremolque | Grupo de delitos de mayor connotación social | Homicidios | Hurtos | Lesiones leves | Lesiones menos graves graves o gravísimas | Otros robos con fuerza | Robo con violencia o intimidación | Robo de objetos de o desde vehículo | Robo de vehículo motorizado | Robo en lugar habitado | Robo en lugar no habitado | Robo por sorpresa | Violaciones | Grupo de Infracción a ley de armas | Abandono de armas | Hallazgo de armas o explosivos | Otros ley de armas | Porte de armas | Tenencia ilegal de armas o explosivos | Grupo de Incivilidades | Amenazas | Comercio ambulante o clandestino | Consumo alcohol vía pública | Daños | Desórdenes | Ebriedad | Otras incivilidades | Riña pública | Ruidos molestos | Abigeato | Abusos sexuales y otros delitos sexuales | Grupo de Violencia intrafamiliar | Violencia intrafamiliar a adulto mayor | Violencia intrafamiliar a hombre | Violencia intrafamiliar a mujer | Violencia intrafamiliar a niño | Violencia intrafamiliar no clasificado | Receptación | Robo frustrado | PIB | Consumo_Hogares_IPSFL | Fuerza_de_trabajo_(Miles_de_personas) | Ocupados_(Miles_de_personas) | Tasa_de_desocupación_(%) | Reg_Empresas_Sociedades | FIELD71 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | AP | 2005 | 01 | 201749.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 444.0 | 1.0 | 93.0 | 116.0 | 30.0 | 6.0 | 44.0 | 27.0 | 26.0 | 54.0 | 27.0 | 17.0 | 3.0 | 7.0 | 0.0 | 1.0 | 0.0 | 5.0 | 1.0 | 435.0 | 40.0 | 30.0 | 49.0 | 93.0 | 22.0 | 178.0 | 23.0 | 0.0 | 0.0 | 1.0 | 6.0 | 125.0 | 0.0 | 7.0 | 115.0 | 3.0 | 0.0 | 7.0 | 7.0 | NaN | NaN | NaN | NaN | NaN | NaN | |
1 | AP | 2005 | 02 | 201749.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 403.0 | 0.0 | 115.0 | 92.0 | 26.0 | 5.0 | 31.0 | 26.0 | 14.0 | 51.0 | 30.0 | 13.0 | 0.0 | 11.0 | 0.0 | 0.0 | 0.0 | 11.0 | 0.0 | 410.0 | 57.0 | 18.0 | 42.0 | 76.0 | 25.0 | 164.0 | 26.0 | 0.0 | 2.0 | 1.0 | 9.0 | 112.0 | 2.0 | 12.0 | 95.0 | 3.0 | 0.0 | 4.0 | 10.0 | NaN | NaN | NaN | NaN | NaN | NaN | |
2 | AP | 2005 | 03 | 201749.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 433.0 | 0.0 | 119.0 | 89.0 | 25.0 | 11.0 | 28.0 | 35.0 | 32.0 | 59.0 | 22.0 | 11.0 | 2.0 | 10.0 | 0.0 | 0.0 | 0.0 | 9.0 | 1.0 | 464.0 | 55.0 | 27.0 | 72.0 | 74.0 | 20.0 | 171.0 | 43.0 | 0.0 | 2.0 | 0.0 | 8.0 | 116.0 | 2.0 | 12.0 | 98.0 | 4.0 | 0.0 | 2.0 | 20.0 | NaN | NaN | NaN | NaN | NaN | NaN | |
3 | AP | 2005 | 04 | 201749.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 411.0 | 0.0 | 95.0 | 88.0 | 22.0 | 6.0 | 35.0 | 36.0 | 21.0 | 68.0 | 28.0 | 10.0 | 2.0 | 7.0 | 0.0 | 0.0 | 0.0 | 5.0 | 2.0 | 437.0 | 40.0 | 11.0 | 64.0 | 80.0 | 27.0 | 193.0 | 22.0 | 0.0 | 0.0 | 0.0 | 9.0 | 127.0 | 6.0 | 11.0 | 105.0 | 5.0 | 0.0 | 3.0 | 12.0 | NaN | NaN | NaN | NaN | NaN | NaN | |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | |
3452 | MA | 2022 | 09 | 181143.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 156.0 | 0.0 | 43.0 | 56.0 | 19.0 | 1.0 | 4.0 | 4.0 | 5.0 | 11.0 | 8.0 | 0.0 | 5.0 | 2.0 | 0.0 | 0.0 | 1.0 | 1.0 | 0.0 | 240.0 | 86.0 | 0.0 | 15.0 | 99.0 | 1.0 | 8.0 | 31.0 | 0.0 | 0.0 | 1.0 | 13.0 | 102.0 | 1.0 | 19.0 | 79.0 | 3.0 | 0.0 | 2.0 | 0.0 | NaN | NaN | 98.307092 | 92.424925 | 5.983462 | 132.0 | |
3453 | MA | 2022 | 10 | 181143.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 98.360188 | 93.000401 | 5.449142 | 124.0 | |
3454 | MA | 2022 | 11 | 181143.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 98.831505 | 93.493908 | 5.400704 | 114.0 | |
3455 | MA | 2022 | 12 | 181143.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 99.737670 | 95.788979 | 3.959076 | 113.0 |
3456 rows x 69 columns
Finalmente, se guarda el archivo en un formato más liviano para procesarlo en el Paso 2. A continuación se muestran tres formatos para guardar el DataFrame, el más liviano y rápido de procesar es el parquet.
# Formato tradicional
df_Region.to_csv("./df_Region.csv")
# Otras opciones de guardado
df_Region.to_pickle("./df_Region.pkl")
df_Region.to_feather("./df_Region.feather")
# Formato mas liviano (OPTIMO)
df_Region.to_parquet("./df_Region.parquet")
Si deseas descargar el DataFrame df_Region puedes hacerlo ingresando aquí.
PASO 2. OBSERVACIONES DUPLICADAS
El método pandas.DataFrame.drop_duplicates() se usa para eliminar filas duplicadas; en nuestro ejemplo, mantenemos las filas del DataFrame anterior con la dimensión 3456 filas y 69 columnas porque no se detectaron filas duplicadas.
# Using DataFrame.drop_duplicates() to keep first duplicate row
df = df.drop_duplicates(keep='first')
df.shape
PASO 3. ESTADÍSTICAS DESCRIPTIVAS BÁSICAS
Las estadísticas descriptivas resumen o describen las características de un conjunto de datos, generalmente se dividen en:
- Medidas de tendencia central: describen el centro del conjunto de datos (media, mediana, moda),
- Medidas de variabilidad: describen la dispersión de los datos (varianza, rango, desviación estándar, asimetría, curtosis) y
- Medidas de posición: se usan para clasificar una observación dentro de la población o muestra (cuartiles, deciles y percentiles).
Las estadísticas descriptivas suelen representarse de forma gráfica y/o tabular, en esta etapa solo utilizaremos la forma tabular, en secciones posteriores se implementarán gráficos. Con la función describe_all se calculan algunas medidas de tendencia central, dispersión y posición del DataFrame guardado en la sección anterior.
Con esta función se incorporan algunas métricas a la matriz de salida de cálculo de estadísticas descriptivas de Pandas pd.describe(), tales como TYPE, NAN, NAN_PROPORTION, y DECISION, para llevar un conteo de los valores no informados, la proporción de los mismos, el tipo de variable contenida en el DataFrame y la decisión que se pudiera tomar a partir de la cantidad de registros no informados.
Antes de implementarla, se cargan los datos guardados en la sección anterior y se toma un segmento de los mismos, esto debido a que no se trabajará con todos en esta sección debido a que tienen periodicidades diferentes. Se toma el periodo que va desde el año 2015 hasta el año 2021, continuación se muestra la tabla de salida:
def describe_all(df, p=0.10, save=True):
"""
The describe_all function is used to describe a variable and identify the decision to make given the presence of
NaN, the criteria is, if there is more than 50% NaN, the decision is to remove the variable
df: column of the data frame to process
p: proportion of NaN to consider for exclusion == True
save: bool
:type df: DataFrame
:type p: float
:type save: bool
"""
describe_df = df.describe(include='all')
describe_df.loc['type'] = df.dtypes
describe_df.loc['NaN'] = df['Year'].count() - describe_df.loc['count']
describe_df.loc['NaN_proportion'] = (describe_df.loc['NaN'] / df['Year'].count())
describe_df.loc['interquartile_range'] = describe_df.loc['75%']-describe_df.loc['25%']
describe_df.loc['Decision_p'] = 'Remove'
describe_df.loc['Decision_IQR'] = 'Remove'
Describe_df = pd.DataFrame(describe_df.T)
Describe_df.loc[Describe_df['NaN_proportion'] <= p, 'Decision_p'] = 'Work it'
Describe_df.loc[Describe_df['interquartile_range'] != 0, 'Decision_IQR'] = 'Work it'
consequence = ['Fallecidos','Graves','Menos_Grav', 'Leves']
Describe_df.loc[consequence,'Decision_IQR']='Work it'
if save:
Describe_df.to_csv(r'./Descriptivo.csv',
index=True, header=True)
else:
pass
return Describe_df
# Se cargan los datos.
df_Region = pd.read_csv("./df_Region.csv")
# Se describe la parte de los datos con los que se quiere trabajar.
describe_all(df_Region[(df_Region['Year']>'2014') &
(df_Region['Year']<='2021')])
69 rows x 17 columns
En la tabla se puede observar que de las 69 variables disponibles en el DataFrame 3 son cualitativas y 66 cuantitativas, asimismo se puede observar que del total de 1344 registros esperados en cada variable, en algunas se contabilizan menor cantidad de registros, por lo que hay presencia de valores faltantes, la variable que presenta mayor número de valores faltantes es Tractor_Agrícola, así mismo se observan algunos valores faltantes en las variables asociadas al tipo de lesión en siniestros de tránsito. Con relación a las medidas de posición y tendencia central se puede observar que las variables presentan sesgos importantes y en consecuencia una desviación estándar bastante alta. En la siguiente etapa se identificarán las variables que pueden ser excluidas del análisis y como se tratarán los valores faltantes.
Variable | count | unique | top | freq | mean | std | min | 25% | 50% | 75% | max | type | NaN | NaN_proportion | interquartile_range | Decision_p | Decision_IQR |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Region | 1344 | 16 | AP | 84 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | object | 0 | 0.0 | NaN | Work it | Work it |
Year | 1344 | 7 | 2015 | 192 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | object | 0 | 0.0 | NaN | Work it | Work it |
Month | 1344 | 12 | 01 | 112 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | object | 0 | 0.0 | NaN | Work it | Work it |
Population | 1344.0 | NaN | NaN | NaN | 1174580.857143 | 1768084.362532 | 104097.0 | 325497.0 | 735916.5 | 1031659.75 | 8242459.0 | float64 | 0.0 | 0.0 | 706162.75 | Work it | Work it |
Fallecidos | 1301.0 | NaN | NaN | NaN | 4.421214 | 4.004149 | 0.0 | 1.0 | 3.0 | 7.0 | 21.0 | float64 | 43.0 | 0.031994 | 6.0 | Work it | Work it |
Graves | 1301.0 | NaN | NaN | NaN | 11.348194 | 9.825214 | 0.0 | 4.0 | 9.0 | 16.0 | 63.0 | float64 | 43.0 | 0.031994 | 12.0 | Work it | Work it |
Menos_Grav | 1301.0 | NaN | NaN | NaN | 6.468101 | 5.798403 | 0.0 | 2.0 | 5.0 | 10.0 | 30.0 | float64 | 43.0 | 0.031994 | 8.0 | Work it | Work it |
Leves | 1301.0 | NaN | NaN | NaN | 58.40123 | 49.898008 | 0.0 | 18.0 | 47.0 | 84.0 | 265.0 | float64 | 43.0 | 0.031994 | 66.0 | Work it | Work it |
Lesionados_total | 1301.0 | NaN | NaN | NaN | 76.217525 | 63.358015 | 0.0 | 25.0 | 63.0 | 108.0 | 336.0 | float64 | 43.0 | 0.031994 | 83.0 | Work it | Work it |
Automóvil_StatioWagon_TodoTerreno | 1308.0 | NaN | NaN | NaN | 224362.125 | 339702.84375 | 18694.0 | 60909.0 | 114871.0 | 201954.0 | 1569108.0 | float32 | 36.0 | 0.026786 | 141045.0 | Work it | Work it |
Furgón | 1308.0 | NaN | NaN | NaN | 12966.632812 | 24352.710938 | 492.0 | 1603.0 | 5198.0 | 12264.0 | 114705.0 | float32 | 36.0 | 0.026786 | 10661.0 | Work it | Work it |
Minibús | 1308.0 | NaN | NaN | NaN | 1739.91748 | 1649.870361 | 174.0 | 567.0 | 965.0 | 2496.0 | 7399.0 | float32 | 36.0 | 0.026786 | 1929.0 | Work it | Work it |
Camioneta | 1308.0 | NaN | NaN | NaN | 60416.824219 | 60968.617188 | 7875.0 | 18852.0 | 46989.0 | 76979.0 | 285490.0 | float32 | 36.0 | 0.026786 | 58127.0 | Work it | Work it |
Motocicleta_y_Similares | 1308.0 | NaN | NaN | NaN | 12213.59668 | 23390.869141 | 462.0 | 2116.0 | 4738.0 | 12301.0 | 123575.0 | float32 | 36.0 | 0.026786 | 10185.0 | Work it | Work it |
Otros_sin_Motor | 1308.0 | NaN | NaN | NaN | 2277.54126 | 2324.756592 | 134.0 | 522.0 | 1591.0 | 2915.0 | 10997.0 | float32 | 36.0 | 0.026786 | 2393.0 | Work it | Work it |
Taxi_Básico | 1308.0 | NaN | NaN | NaN | 2172.550537 | 5458.430664 | 194.0 | 390.0 | 613.0 | 846.0 | 24461.0 | float32 | 36.0 | 0.026786 | 456.0 | Work it | Work it |
Taxi_Colectivo | 1308.0 | NaN | NaN | NaN | 3853.816406 | 3483.369141 | 236.0 | 1320.0 | 3379.0 | 4590.0 | 14432.0 | float32 | 36.0 | 0.026786 | 3270.0 | Work it | Work it |
Taxi_Turismo | 1308.0 | NaN | NaN | NaN | 587.715576 | 944.708435 | 11.0 | 139.0 | 267.0 | 595.0 | 4322.0 | float32 | 36.0 | 0.026786 | 456.0 | Work it | Work it |
Minibús_Transporte_Colectivo | 1308.0 | NaN | NaN | NaN | 2361.0 | 3440.179688 | 170.0 | 699.0 | 1244.0 | 2750.0 | 20145.0 | float32 | 36.0 | 0.026786 | 2051.0 | Work it | Work it |
Bus_Transporte_Colectivo | 1308.0 | NaN | NaN | NaN | 3442.651367 | 3796.36792 | 192.0 | 1233.0 | 2375.0 | 3807.0 | 17975.0 | float32 | 36.0 | 0.026786 | 2574.0 | Work it | Work it |
Camión_Simple | 1308.0 | NaN | NaN | NaN | 9806.761719 | 10918.499023 | 1414.0 | 3505.0 | 7124.0 | 12302.0 | 49584.0 | float32 | 36.0 | 0.026786 | 8797.0 | Work it | Work it |
Tractocamión | 1308.0 | NaN | NaN | NaN | 3170.477051 | 3118.336914 | 218.0 | 1137.0 | 2006.0 | 4090.0 | 14446.0 | float32 | 36.0 | 0.026786 | 2953.0 | Work it | Work it |
Tractor_Agrícola | 1296.0 | NaN | NaN | NaN | 543.777771 | 573.47229 | 1.0 | 48.25 | 376.5 | 876.75 | 2174.0 | float32 | 48.0 | 0.035714 | 828.5 | Work it | Work it |
Remolque_Semirremolque | 1308.0 | NaN | NaN | NaN | 4979.981445 | 4655.186035 | 374.0 | 2009.0 | 3432.0 | 6980.0 | 20923.0 | float32 | 36.0 | 0.026786 | 4971.0 | Work it | Work it |
Grupo_de_delitos_de_mayor_connotación_social | 1344.0 | NaN | NaN | NaN | 2632.417411 | 4540.314393 | 82.0 | 707.75 | 1342.5 | 2164.0 | 24181.0 | float64 | 0.0 | 0.0 | 1456.25 | Work it | Work it |
Homicidios | 1344.0 | NaN | NaN | NaN | 3.322917 | 6.51511 | 0.0 | 0.0 | 1.0 | 3.0 | 56.0 | float64 | 0.0 | 0.0 | 3.0 | Work it | Work it |
Hurtos | 1344.0 | NaN | NaN | NaN | 776.970982 | 1153.382969 | 22.0 | 202.25 | 421.5 | 796.0 | 6841.0 | float64 | 0.0 | 0.0 | 593.75 | Work it | Work it |
Lesiones_leves | 1344.0 | NaN | NaN | NaN | 293.747024 | 423.122425 | 13.0 | 87.75 | 196.0 | 277.5 | 2479.0 | float64 | 0.0 | 0.0 | 189.75 | Work it | Work it |
Lesiones_menos_graves_graves_o_gravísimas | 1344.0 | NaN | NaN | NaN | 80.200893 | 110.131187 | 4.0 | 26.0 | 50.0 | 76.0 | 708.0 | float64 | 0.0 | 0.0 | 50.0 | Work it | Work it |
Otros_robos_con_fuerza | 1344.0 | NaN | NaN | NaN | 32.761905 | 61.919963 | 0.0 | 7.0 | 15.0 | 30.0 | 381.0 | float64 | 0.0 | 0.0 | 23.0 | Work it | Work it |
Robo_con_violencia_o_intimidación | 1344.0 | NaN | NaN | NaN | 348.811756 | 914.5507 | 0.0 | 45.0 | 99.0 | 160.0 | 6249.0 | float64 | 0.0 | 0.0 | 115.0 | Work it | Work it |
Robo_de_objetos_de_o_desde_vehículo | 1344.0 | NaN | NaN | NaN | 276.765625 | 635.719974 | 0.0 | 47.0 | 109.0 | 175.0 | 3678.0 | float64 | 0.0 | 0.0 | 128.0 | Work it | Work it |
Robo_de_vehículo_motorizado | 1344.0 | NaN | NaN | NaN | 136.067708 | 289.03014 | 0.0 | 21.0 | 45.0 | 112.25 | 1704.0 | float64 | 0.0 | 0.0 | 91.25 | Work it | Work it |
Robo_en_lugar_habitado | 1344.0 | NaN | NaN | NaN | 273.689732 | 347.357691 | 3.0 | 82.75 | 162.5 | 318.0 | 2149.0 | float64 | 0.0 | 0.0 | 235.25 | Work it | Work it |
Robo_en_lugar_no_habitado | 1344.0 | NaN | NaN | NaN | 232.179315 | 327.298568 | 2.0 | 67.0 | 139.0 | 239.25 | 3037.0 | float64 | 0.0 | 0.0 | 172.25 | Work it | Work it |
Robo_por_sorpresa | 1344.0 | NaN | NaN | NaN | 160.24628 | 352.318099 | 0.0 | 24.0 | 56.0 | 104.0 | 1841.0 | float64 | 0.0 | 0.0 | 80.0 | Work it | Work it |
Violaciones | 1344.0 | NaN | NaN | NaN | 17.653274 | 26.895712 | 0.0 | 5.0 | 10.0 | 18.0 | 231.0 | float64 | 0.0 | 0.0 | 13.0 | Work it | Work it |
Grupo_de_Infracción_a_ley_de_armas | 1344.0 | NaN | NaN | NaN | 69.067708 | 112.904017 | 0.0 | 19.0 | 35.0 | 64.0 | 887.0 | float64 | 0.0 | 0.0 | 45.0 | Work it | Work it |
Abandono_de_armas | 1344.0 | NaN | NaN | NaN | 0.05878 | 0.330117 | 0.0 | 0.0 | 0.0 | 0.0 | 5.0 | float64 | 0.0 | 0.0 | 0.0 | Work it | Remove |
Hallazgo_de_armas_o_explosivos | 1344.0 | NaN | NaN | NaN | 1.901786 | 3.168516 | 0.0 | 0.0 | 1.0 | 2.0 | 21.0 | float64 | 0.0 | 0.0 | 2.0 | Work it | Work it |
Otros_ley_de_armas | 1344.0 | NaN | NaN | NaN | 16.190476 | 27.808826 | 0.0 | 3.0 | 8.0 | 18.0 | 439.0 | float64 | 0.0 | 0.0 | 15.0 | Work it | Work it |
Porte_de_armas | 1344.0 | NaN | NaN | NaN | 47.177827 | 88.679401 | 0.0 | 9.0 | 21.0 | 42.0 | 654.0 | float64 | 0.0 | 0.0 | 33.0 | Work it | Work it |
Tenencia_ilegal_de_armas_o_explosivos | 1344.0 | NaN | NaN | NaN | 3.738839 | 5.958365 | 0.0 | 1.0 | 2.0 | 4.0 | 41.0 | float64 | 0.0 | 0.0 | 3.0 | Work it | Work it |
Grupo_de_Incivilidades | 1344.0 | NaN | NaN | NaN | 2590.508929 | 3555.286328 | 99.0 | 777.75 | 1750.0 | 2694.75 | 21583.0 | float64 | 0.0 | 0.0 | 1917.0 | Work it | Work it |
Amenazas | 1344.0 | NaN | NaN | NaN | 634.510417 | 975.564708 | 32.0 | 171.0 | 384.5 | 598.25 | 5262.0 | float64 | 0.0 | 0.0 | 427.25 | Work it | Work it |
Comercio_ambulante_o_clandestino | 1344.0 | NaN | NaN | NaN | 161.916667 | 438.495314 | 0.0 | 7.0 | 35.0 | 114.0 | 3739.0 | float64 | 0.0 | 0.0 | 107.0 | Work it | Work it |
Consumo_alcohol_vía_pública | 1344.0 | NaN | NaN | NaN | 631.606399 | 983.813929 | 0.0 | 132.0 | 310.0 | 740.25 | 7688.0 | float64 | 0.0 | 0.0 | 608.25 | Work it | Work it |
Daños | 1344.0 | NaN | NaN | NaN | 577.102679 | 761.356865 | 36.0 | 185.0 | 363.5 | 607.25 | 4344.0 | float64 | 0.0 | 0.0 | 422.25 | Work it | Work it |
Desórdenes | 1344.0 | NaN | NaN | NaN | 23.251488 | 59.620898 | 0.0 | 3.0 | 8.0 | 20.0 | 1117.0 | float64 | 0.0 | 0.0 | 17.0 | Work it | Work it |
Ebriedad | 1344.0 | NaN | NaN | NaN | 243.110863 | 264.128878 | 2.0 | 89.75 | 163.0 | 316.0 | 1921.0 | float64 | 0.0 | 0.0 | 226.25 | Work it | Work it |
Otras_incivilidades | 1344.0 | NaN | NaN | NaN | 254.438244 | 319.243688 | 8.0 | 67.0 | 145.5 | 265.0 | 1917.0 | float64 | 0.0 | 0.0 | 198.0 | Work it | Work it |
Riña_pública | 1344.0 | NaN | NaN | NaN | 19.974702 | 21.215821 | 0.0 | 6.0 | 13.0 | 24.0 | 128.0 | float64 | 0.0 | 0.0 | 18.0 | Work it | Work it |
Ruidos_molestos | 1344.0 | NaN | NaN | NaN | 44.59747 | 55.031374 | 0.0 | 3.0 | 26.0 | 64.0 | 316.0 | float64 | 0.0 | 0.0 | 61.0 | Work it | Work it |
Abigeato | 1344.0 | NaN | NaN | NaN | 18.185268 | 20.804928 | 0.0 | 2.0 | 11.0 | 28.0 | 125.0 | float64 | 0.0 | 0.0 | 26.0 | Work it | Work it |
Abusos_sexuales_y_otros_delitos_sexuales | 1344.0 | NaN | NaN | NaN | 59.799851 | 88.620986 | 2.0 | 18.0 | 33.0 | 61.0 | 687.0 | float64 | 0.0 | 0.0 | 43.0 | Work it | Work it |
Grupo_de_Violencia_intrafamiliar | 1344.0 | NaN | NaN | NaN | 634.730655 | 827.521407 | 46.0 | 229.0 | 389.0 | 671.25 | 4973.0 | float64 | 0.0 | 0.0 | 442.25 | Work it | Work it |
Violencia_intrafamiliar_a_adulto_mayor | 1344.0 | NaN | NaN | NaN | 14.626488 | 25.221203 | 0.0 | 3.0 | 8.0 | 14.0 | 196.0 | float64 | 0.0 | 0.0 | 11.0 | Work it | Work it |
Violencia_intrafamiliar_a_hombre | 1344.0 | NaN | NaN | NaN | 94.97247 | 130.806075 | 4.0 | 31.75 | 56.0 | 102.0 | 783.0 | float64 | 0.0 | 0.0 | 70.25 | Work it | Work it |
Violencia_intrafamiliar_a_mujer | 1344.0 | NaN | NaN | NaN | 487.218006 | 620.42136 | 31.0 | 174.75 | 306.5 | 521.25 | 3618.0 | float64 | 0.0 | 0.0 | 346.5 | Work it | Work it |
Violencia_intrafamiliar_a_niño | 1344.0 | NaN | NaN | NaN | 24.290923 | 36.911753 | 0.0 | 7.0 | 14.0 | 25.0 | 240.0 | float64 | 0.0 | 0.0 | 18.0 | Work it | Work it |
Violencia_intrafamiliar_no_clasificado | 1344.0 | NaN | NaN | NaN | 13.622768 | 25.706948 | 0.0 | 3.0 | 7.0 | 14.0 | 332.0 | float64 | 0.0 | 0.0 | 11.0 | Work it | Work it |
Receptación | 1344.0 | NaN | NaN | NaN | 44.630952 | 71.569621 | 0.0 | 12.0 | 25.0 | 41.0 | 476.0 | float64 | 0.0 | 0.0 | 29.0 | Work it | Work it |
Robo_frustrado | 1344.0 | NaN | NaN | NaN | 45.310268 | 81.377702 | 0.0 | 9.0 | 21.0 | 40.0 | 512.0 | float64 | 0.0 | 0.0 | 31.0 | Work it | Work it |
PIB | 1344.0 | NaN | NaN | NaN | 10407.497467 | 17784.675871 | 1050.441136 | 2373.04526 | 5316.867697 | 8917.322293 | 82857.681622 | float64 | 0.0 | 0.0 | 6544.277034 | Work it | Work it |
Consumo_Hogares_IPSFL | 1344.0 | NaN | NaN | NaN | 7112.840091 | 14568.218439 | 479.389591 | 1509.786 | 3753.810556 | 4357.701281 | 71604.390215 | float64 | 0.0 | 0.0 | 2847.915281 | Work it | Work it |
Fuerza_de_trabajo_(Miles_de_personas) | 1344.0 | NaN | NaN | NaN | 572.320604 | 914.33451 | 55.487947 | 158.9883 | 341.414386 | 484.627522 | 4321.237051 | float64 | 0.0 | 0.0 | 325.639222 | Work it | Work it |
Ocupados_(Miles_de_personas) | 1344.0 | NaN | NaN | NaN | 528.178481 | 839.766771 | 49.969041 | 144.932392 | 314.448727 | 450.426301 | 3977.141045 | float64 | 0.0 | 0.0 | 305.493908 | Work it | Work it |
Tasa_de_desocupación_(%) | 1344.0 | NaN | NaN | NaN | 7.075024 | 2.345473 | 1.091505 | 5.638536 | 7.180471 | 8.314839 | 16.128285 | float64 | 0.0 | 0.0 | 2.676303 | Work it | Work it |
Reg_Empresas_Sociedades | 1300.0 | NaN | NaN | NaN | 574.432308 | 1028.449712 | 31.0 | 128.75 | 282.0 | 519.5 | 7463.0 | float64 | 44.0 | 0.032738 | 390.75 | Work it | Work it |
PASO 4. VARIABLES NO INFORMATIVAS
En esta sección se hace uso de las estadísticas descriptivas calculadas en el paso anterior. Las variables que tienen más del 10% de información faltante se cuentan y se excluyen del estudio. Es importante señalar que esta es una medida preestablecida al inicio del estudio, no es generalizable a todos los casos, es decisión del investigador sobre los criterios de limpieza que decida tomar.
Adicionalmente, si para las variables alfanuméricas existe una alta concentración en un valor y, a priori, no es posible observar que se pueden realizar discriminaciones con diferentes comportamientos frente a la variable objetivo y con una población suficiente, se eliminan del estudio.
En la última columna de la tabla se puede observar la decisión a tomar respecto a la exclusión de la variable con base en los criterios antes mencionados.
# Se cargan los datos.
df_Region = pd.read_parquet("./df_Region.parquet")
# Se describe la parte de los datos con los que se quiere trabajar.
describe_all(df_Region[(df_Region['Year']>'2014') &
(df_Region['Year']<='2021')])
Variable | count | unique | top | freq | mean | std | min | 25% | 50% | 75% | max | type | NaN | NaN_proportion | interquartile_range | Decision_p | Decision_IQR |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Fallecidos | 1301.0 | NaN | NaN | NaN | 4.421214 | 4.004149 | 0.0 | 1.0 | 3.0 | 7.0 | 21.0 | float64 | 43.0 | 0.031994 | 6.0 | Work it | Work it |
Graves | 1301.0 | NaN | NaN | NaN | 11.348194 | 9.825214 | 0.0 | 4.0 | 9.0 | 16.0 | 63.0 | float64 | 43.0 | 0.031994 | 12.0 | Work it | Work it |
Menos_Grav | 1301.0 | NaN | NaN | NaN | 6.468101 | 5.798403 | 0.0 | 2.0 | 5.0 | 10.0 | 30.0 | float64 | 43.0 | 0.031994 | 8.0 | Work it | Work it |
Leves | 1301.0 | NaN | NaN | NaN | 58.40123 | 49.898008 | 0.0 | 18.0 | 47.0 | 84.0 | 265.0 | float64 | 43.0 | 0.031994 | 66.0 | Work it | Work it |
Lesionados_total | 1301.0 | NaN | NaN | NaN | 76.217525 | 63.358015 | 0.0 | 25.0 | 63.0 | 108.0 | 336.0 | float64 | 43.0 | 0.031994 | 83.0 | Work it | Work it |
Automóvil_StatioWagon_TodoTerreno | 1308.0 | NaN | NaN | NaN | 224362.125 | 339702.84375 | 18694.0 | 60909.0 | 114871.0 | 201954.0 | 1569108.0 | float32 | 36.0 | 0.026786 | 141045.0 | Work it | Work it |
Furgón | 1308.0 | NaN | NaN | NaN | 12966.632812 | 24352.710938 | 492.0 | 1603.0 | 5198.0 | 12264.0 | 114705.0 | float32 | 36.0 | 0.026786 | 10661.0 | Work it | Work it |
Minibús | 1308.0 | NaN | NaN | NaN | 1739.91748 | 1649.870361 | 174.0 | 567.0 | 965.0 | 2496.0 | 7399.0 | float32 | 36.0 | 0.026786 | 1929.0 | Work it | Work it |
Camioneta | 1308.0 | NaN | NaN | NaN | 60416.824219 | 60968.617188 | 7875.0 | 18852.0 | 46989.0 | 76979.0 | 285490.0 | float32 | 36.0 | 0.026786 | 58127.0 | Work it | Work it |
Motocicleta_y_Similares | 1308.0 | NaN | NaN | NaN | 12213.59668 | 23390.869141 | 462.0 | 2116.0 | 4738.0 | 12301.0 | 123575.0 | float32 | 36.0 | 0.026786 | 10185.0 | Work it | Work it |
Otros_sin_Motor | 1308.0 | NaN | NaN | NaN | 2277.54126 | 2324.756592 | 134.0 | 522.0 | 1591.0 | 2915.0 | 10997.0 | float32 | 36.0 | 0.026786 | 2393.0 | Work it | Work it |
Taxi_Básico | 1308.0 | NaN | NaN | NaN | 2172.550537 | 5458.430664 | 194.0 | 390.0 | 613.0 | 846.0 | 24461.0 | float32 | 36.0 | 0.026786 | 456.0 | Work it | Work it |
Taxi_Colectivo | 1308.0 | NaN | NaN | NaN | 3853.816406 | 3483.369141 | 236.0 | 1320.0 | 3379.0 | 4590.0 | 14432.0 | float32 | 36.0 | 0.026786 | 3270.0 | Work it | Work it |
Taxi_Turismo | 1308.0 | NaN | NaN | NaN | 587.715576 | 944.708435 | 11.0 | 139.0 | 267.0 | 595.0 | 4322.0 | float32 | 36.0 | 0.026786 | 456.0 | Work it | Work it |
Minibús_Transporte_Colectivo | 1308.0 | NaN | NaN | NaN | 2361.0 | 3440.179688 | 170.0 | 699.0 | 1244.0 | 2750.0 | 20145.0 | float32 | 36.0 | 0.026786 | 2051.0 | Work it | Work it |
Bus_Transporte_Colectivo | 1308.0 | NaN | NaN | NaN | 3442.651367 | 3796.36792 | 192.0 | 1233.0 | 2375.0 | 3807.0 | 17975.0 | float32 | 36.0 | 0.026786 | 2574.0 | Work it | Work it |
Camión_Simple | 1308.0 | NaN | NaN | NaN | 9806.761719 | 10918.499023 | 1414.0 | 3505.0 | 7124.0 | 12302.0 | 49584.0 | float32 | 36.0 | 0.026786 | 8797.0 | Work it | Work it |
Tractocamión | 1308.0 | NaN | NaN | NaN | 3170.477051 | 3118.336914 | 218.0 | 1137.0 | 2006.0 | 4090.0 | 14446.0 | float32 | 36.0 | 0.026786 | 2953.0 | Work it | Work it |
Tractor_Agrícola | 1296.0 | NaN | NaN | NaN | 543.777771 | 573.47229 | 1.0 | 48.25 | 376.5 | 876.75 | 2174.0 | float32 | 48.0 | 0.035714 | 828.5 | Work it | Work it |
Remolque_Semirremolque | 1308.0 | NaN | NaN | NaN | 4979.981445 | 4655.186035 | 374.0 | 2009.0 | 3432.0 | 6980.0 | 20923.0 | float32 | 36.0 | 0.026786 | 4971.0 | Work it | Work it |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
Abandono_de_armas | 1344.0 | NaN | NaN | NaN | 0.05878 | 0.330117 | 0.0 | 0.0 | 0.0 | 0.0 | 5.0 | float64 | 0.0 | 0.0 | 0.0 | Work it | Remove |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | |
Reg_Empresas_Sociedades | 1300.0 | NaN | NaN | NaN | 574.432308 | 1028.449712 | 31.0 | 128.75 | 282.0 | 519.5 | 7463.0 | float64 | 44.0 | 0.032738 | 390.75 | Work it | Work it |
68 rows x 17 columns
El proceso se hace en varias fases, primero se eliminan las variables que presentan las siguientes condiciones:
Condición 1. Variables que cuentan con más del 10% de registros no informados. Ninguna variable cumple esta condición, el conteo de “Tractor_Agrícola” es quien presenta mayor cantidad de registros no informados, pero se informa una proporción del 1 – 0.035714 de los registros totales (3.57% de valores faltantes) por lo tanto, conservamos la variable.
Condición 2: Variables con rango intercuartílico (IQR) igual a cero, excluyendo las que están asociadas a variables objetivos tales como [‘Fallecidos’,’Leionados_total’]. Se puede observar que el IQR de la variable “Abandono_de_armas” es de 0, lo que indica que existe una alta concentración en torno al valor “0” y, a priori, no es posible realizar discriminaciones con diferentes comportamientos frente a las variables objetivo, por esta razón se elimina del estudio.
Luego de eliminar estas variables nos quedamos con 68 columnas en nuestro DataFrame. Posteriormente, considerando que se tienen muchas variables y pocas observaciones, se puede reducir la dimensionalidad implementando alguna técnica de agrupación multivariada, sin embargo, en el DataFrame se encuentran agrupadas desde la fuente las variables asociadas a delitos de la siguiente manera;
Grupo de delitos de mayor connotación social
- Homicidios.
- Hurtos.
- Lesiones leves.
- Lesiones menos graves graves o gravísimas.
- Otros robos con fuerza.
- Robo con violencia o intimidación.
- Robo de objetos de o desde vehículo.
- Robo de vehículo motorizado.
- Robo en lugar habitado.
- Robo en lugar no habitado.
- Robo por sorpresa.
- Violaciones.
Grupo de Violencia intrafamiliar
- Violencia intrafamiliar a adulto mayor.
- Violencia intrafamiliar a hombre.
- Violencia intrafamiliar a mujer.
- Violencia intrafamiliar no clasificado.
- Violencia intrafamiliar a niño.
Grupo de Infracción a ley de armas
- Abandono de armas.
- Hallazgo de armas o explosivos.
- Otros ley de armas.
- Porte de armas.
- Tenencia ilegal de armas o explosivos
Grupo de Incivilidades
- Amenazas.
- Comercio ambulante o clandestino.
- Consumo alcohol vía pública.
- Daños.
- Desórdenes.
- Ebriedad.
- Otras incivilidades.
- Riña pública.
- Ruidos molestos.
Abusos sexuales y otros delitos sexuales
Abigeato
Receptación
Robo frustrado
Muchas de estas variables se encuentran altamente correlacionadas, así que se tomarán solo los conteos por grupo delictual y los delitos se excluirán de esta fase, (podrían considerarse en procesos posteriores, pero de momento menos puede ser más dado el tamaño muestral), en este sentido se excluyen 31 variables asociadas a delitos y se conservan los grupos delictuales resaltados previamente.
Con relación a los registros de siniestros de tránsito, se tiene la variable “Lesionados_total“, la cual resulta de la suma de “Graves”, “Menos_Grav“ y “Leves”, por lo que se encuentran altamente correlacionadas y se pueden eliminar las últimas 3 que ya están contenidas en la primera.
# Eliminación de variables que cumplen con la condición 1
describe_df = display(describe_all(df_Region,p=0.10, save=True))
df_clean = df_Region.drop(list((describe_df[describe_df['Decision_p']=='Remove']).index),axis=1)
# Eliminación de variables que cumplen con la condición 2
df_clean = df_clean.drop(list((describe_df[describe_df['Decision_IQR']=='Remove']).index),axis=1)
# Variables duplicadas o altamente correlacionadas con otras que las contienen
df_Region.drop(['Homicidios', 'Hurtos', 'Lesiones_leves', 'Lesiones_menos_graves_graves_o_gravísimas',
'Otros_robos_con_fuerza', 'Robo_con_violencia_o_intimidación', 'Robo_de_objetos_de_o_desde_vehículo',
'Robo_de_vehículo_motorizado', 'Robo_en_lugar_habitado', 'Robo_en_lugar_no_habitado',
'Robo_por_sorpresa', 'Violaciones','Abandono_de_armas','Hallazgo_de_armas_o_explosivos',
'Otros_ley_de_armas', 'Porte_de_armas', 'Tenencia_ilegal_de_armas_o_explosivos', 'Amenazas',
'Comercio_ambulante_o_clandestino', 'Consumo_alcohol_vía_pública', 'Daños', 'Desórdenes', 'Ebriedad',
'Otras_incivilidades', 'Riña_pública', 'Ruidos_molestos','Violencia_intrafamiliar_a_adulto_mayor',
'Violencia_intrafamiliar_a_hombre', 'Violencia_intrafamiliar_a_mujer',
'Violencia_intrafamiliar_a_niño', 'Violencia_intrafamiliar_no_clasificado', 'Graves', 'Menos_Grav',
'Leves'],axis=1, inplace=True)
El DataFrame ahora contiene 35 columnas y 1344 filas. Se puede reducir aún más excluyendo las variables que no sostienen una relación fuerte con la objetivo (Lesionados_total ) pero ese proceso de descarte se realizara más adelante en la etapa de construcción de modelos.
PASO 5. VALORES ATÍPICOS
Los outliers o valores atípicos son esas observaciones con valores muy diferentes al resto de la muestra. Ellos pueden provenir de diferentes fuentes de errores o deberse a una probabilidad aleatoria inherente al proceso. En ocasiones estos valores pueden afectar los ajustes que se estén realizando de los datos, entre los problemas más comunes causados por outliers se tienen: el incremento de la varianza del error, incidir en el rechazo o aceptación de una prueba de hipótesis, causar sesgos en los ajustes, quebrantar supuestos de distribución. Es por ello que suele considerarse la posibilidad de eliminarlos o sustituirlos como opciones convenientes y previas a la realización de cálculos más avanzados. La consideración que toma cada investigador con relación al tratamiento de outliers está ligada a consecuencias tanto positivas como negativas, dependiendo de múltiples factores, algunos de los más importantes son:
- El tamaño de la muestra: cuando se tienen muestras muy pequeñas, reducirlas aún más al eliminar los datos atípicos puede ser contraproducente.
- La técnica que se desea implementar: en ocasiones, la dispersión que aportan los datos puede ser capturada por la técnica sin la necesidad de remover los outliers, quizás es conveniente hacer ajustes previos al tratamiento de outliers y posteriores a este para ver si el mismo realmente aporta un beneficio al estudio.
- La distribución de los datos: cuando se tienen datos distribuidos normalmente, usar el rango intercuartílico para identificar valores atípicos (tradicionalmente se utilizan boxplots) es una opción simple y puede ser conveniente cuando se ajustan, por ejemplo, modelos de regresión, sin embargo, ante la presencia de datos que no están normalmente distribuidos, resulta más conveniente utilizar otras técnicas de identificación de outliers, como los asociados a la minería de datos, mediante el análisis de agrupaciones o mediante los cálculos de distancias.
Tipos de outliers
Los valores atípicos suelen clasificarse en grupos, entre los más comunes se encuentran:
- Valores atípicos que surgen de errores en los datos: asociados a los procesos de codificación y/o lectura de los datos, normalmente se corrigen en las fases de lectura de las estadísticas descriptivas básicas, por ejemplo un valor máximo de 154 metros en la variable estatura de personas adultas (metros) es lógico interpretarlo como un error, se puede reemplazar en caso de identificarse cuál es el valor real, imputarse, marcarse como dato no informado o eliminarse.
- Valores atípicos que surgen de procesos aleatorios: hay observaciones extremas que ocurren como consecuencia de eventos extraordinarios, pero inherentes al proceso aleatorio, en estos casos se suele dejar el valor para incorporar la variabilidad que aporta al análisis, pero también se realizan experimentos con y sin los valores atípicos para medir su impacto en la precisión de los resultados.
- Valores atípicos que surgen de ausencias en el proceso: hay observaciones que provienen del resultado de la combinación de factores inherentes al proceso de consolidación de la base de datos y en ocasiones uno de esos factores se omite, esto suele ocurrir con frecuencia cuando se trabaja con modelos de series temporales cuyo insumo proviene de diversas fuentes y una de sus fuentes marca los valores ausentes con cifras como por ejemplo el valor -99.99 que se encuentra en los datos de la variable SOI. En estos casos conviene tener presente el momento de disponibilidad de la información para elaborar estrategias de consolidación de nuestras bases de datos que estén en armonía con la disponibilidad del insumo, en otros casos se extienden los análisis a crear técnicas para imputar los registros no informados mediante técnicas de pronóstico. A la final, la decisión que se tome con relación a estos valores atípicos depende de la habilidad del investigador para hacer uso de la información y los recursos disponibles.
Detección de outliers
En la actualidad existen diversas técnicas para identificar valores atípicos, estas se clasifican según el número de variables que intervienen en el proceso:
Detección univariante de outliers: bajo el paradigma univariante, la detección de outliers suele realizarse a partir de intervalos que de alguna manera se establecen a priori, o a partir de las desviaciones que presentan los datos con relación al rango intercuartílico, sin embargo, también hay procesos más elaborados como los diagramas de control de procesos estadísticos, conocidos como cartas de control, pero no serán implementadas acá, solo se utilizarán los métodos gráficos tradicionales (histogramas de frecuencias, dispersión, boxplots).
Detección multivariante de outliers: bajo este paradigma, los valores atípicos parten de la desviación que se detecta a partir de la interacción en un conjunto de variables, es decir, la detección de outliers mediante técnicas multivariantes implica el cálculo de relaciones entre las diferentes variables que intervienen en el proceso, este cálculo puede ser basado en medidas de distancias o correlaciones.
Uso de diagramas univariados para detectar outliers
Los diagramas de cajas son en esencia un método paramétrico basado en el principio de normalidad que identifica valores atípicos de acuerdo a la distancia que presenta el mismo outlier con relación al primer (Q1) o tercer cuartil (Q3) de la distribución de los datos (rango intercuartílico – prueba de Tukey). Cuando se usan Boxplot para identificar los outliers se observa cuando algunos valores no se corresponden con los restantes al encontrarse distantes a los intervalos de confianza. Por ejemplo, en la figura 1 se observa un Boxplot para los registros de personas lesionadas en el mes en siniestros de tránsito de Chile según la Región de ocurrencia, allí se observa la presencia de algunos valores atípicos, pero con excepción de los que se presentan en la Región de Antofagasta (AN) no parece evidenciarse un problema serio de outliers para la variable explicativa, sin embargo, se mostrará una posible forma de hacer la limpieza.
# Se toma la parte de los datos con los que se quiere trabajar.
df_sample = (df_Region[(df_Region['Year']>'2014') &
(df_Region['Year']<='2021')])
params = {'axes.titlesize':'26',
'xtick.labelsize':'20',
'ytick.labelsize':'22',
'xtick.color':'#222222',
'ytick.color':'#222222',
'figure.figsize': (30,8),
'figure.dpi': 100
}
plt.rcParams.update(params)
plt.figure(facecolor='#E7E7E7', alpha=0.8)
point = dict(markerfacecolor='#000000', marker='.')
plt.style.use('ggplot')
fig = df_sample.boxplot(column=['Lesionados_total'],
color='#000000',
flierprops=point,
by=['Region'],
rot = 90)
fig_axes = fig.get_figure()
fig_axes.set_facecolor("#E7E7E7")
fig_axes.suptitle('')
plt.ylabel('Frequencia',size=24, color='#222222')
plt.xlabel("Lesionados en el mes por Región", size=24, color='#222222')
plt.title('')
plt.savefig('./Output/boxplot_Lesionados.png', bbox_inches='tight')
plt.show()
Figura 1. Diagrama de cajas de frecuencias observadas del número de fallecidos en siniestros de tránsito según región.
Figura 2. Histograma de frecuencias observadas del número de fallecidos por siniestro de tránsito (considera todas las regiones).
Figura 3. Diagrama de dispersión del número de fallecidos por siniestro de tránsito
Cuando se va a realizar la limpieza de outliers conviene hacerse varias preguntas, algunas de estas son:
- ¿Los datos se encuentran sesgados?,
- ¿Se está trabajando con series temporales?,
- ¿Hay alguna agrupación que se debe considerar antes de aplicar algún filtro?.
Cuando se trabaja con series temporales tiene importancia la presencia de estacionalidad y autocorrelación de las series, por lo que la eliminación de registros no es lo más correcto para estos casos, ya que se altera el ciclo que acompaña a los datos, para estos casos es más conveniente la imputación. Por otro lado, cuando los datos tienen una estructura segmentada o responden a alguna agrupación asociada, puede ser conveniente tomar en cuenta la misma antes de aplicar cualquier filtro. En el caso de los registros de lesiones mensuales por Región, se deben aplicar los filtros por región porque la distribución puede variar por zona geográfica, esto es más claro si se comparan las distribuciones de frecuencias de los registros de lesiones mensuales por accidentes de tránsito para cada región:
Región de Tarapaca (TA)
Región de Antofagasta (AN)
Region de Atacama (AT)
Region de Coquimbo (CO)
Región de Valparaiso (VA)
Región del Libertador General Bernardo O'higgins (LI)
Region del Maule (ML)
Region del Biobio (BI)
Región de la Araucania (AR)
Región de Los Lagos (LL)
Region de Aysen del General Carlos Ibañez del Campo (AI)
Region de Magallanes y de la Antartica Chilena (MA)
Región Metropolitana de Santiago (MR)
Región de Los Rios (LR)
Region de Arica y Parinacota (AP)
Region de Ñuble (NB)
Se puede observar que la distribución de frecuencias es diferente cuando se trabajan los histogramas por región que cuando se consolidan los registros de todas las regiones en un solo histograma (Figura 2), se pudiera aplicar el rango intercuartílico en la mayoría de los casos para limpiar los outlier, para ello se puede implementar la función clean_outliers() y los boxplots por región quedarían de la siguiente manera:
def clean_outliers(df, var_clean='Lesionados_total'):
df_no_outliers = []
for i in df.Region.astype('object').unique():
Q = df[df['Region']== i ][var_clean].quantile([.25, .75])
LS = ((1.5*(Q.values[1]-Q.values[0]))+Q.values[1])
LI = (Q.values[0]-(0.5*(Q.values[1]-Q.values[0])))
df_filtered = df[(df['Region']== i) &
(df[var_clean]<=LS) &
(df[var_clean]>=LI)]
df_no_outliers.append(df_filtered)
df_no_outliers = pd.concat(df_no_outliers)
return df_no_outliers
Figura 5. Diagrama de cajas de frecuencias observadas del número de fallecidos en siniestros de tránsito según región, posterior al tratamiento de outliers.
PASO 6. REGISTROS NO INFORMADOS
Los datos faltantes son aquellos registros que no se encuentran informados en la base de datos. Las razones por las que se presentan datos faltantes pueden ser múltiples, pero suelen atribuirse a causas aleatorias o no aleatorias.
Tipos de registros no informados
- Registros no informados de manera aleatoria (MAR: Missing At Random): en estos casos la ausencia del registro se presenta debido a la presencia de los valores si informados de otras variables.
- Registros no informados de manera no aleatoria (MNAR: Missing Not At Random): ocurre cuando la ausencia del registro es producto del valor de los mismos registros no observados. Es el caso más complejo debido a que la ausencia del registro es producto de causas sistémicas que deben identificarse y ser tratadas.
En la práctica hay dos aproximaciones para tratar los registros no informados:
- Eliminar los casos no informados de la muestra o eliminar las variables que presentan los datos faltantes.
- Imputar los registros no informados, consiste en estimar dichos valores mediante la implementación de diversos métodos.
Métodos de imputación
Los métodos de imputación se basan en la estimación del registro no informado a partir del comportamiento de los registros si informados, bien sea de la misma variable o de las otras variables del estudio.
Entre los principales procedimientos para imputar se encuentran:
- Imputación por la media: se basa en sustituir los valores ausentes por la media de los valores válidos.
- Imputación por el vecino más cercano: se basa en sustituir los valores faltantes por la media de todos los valores situados a k-distancias del registro no informado.
- Imputación por regresión: se basa en estimar los registros no informados considerando la relación que sostienen los si informados de la variable con otras mediante Análisis de Regresión.
En términos generales, imputar registros puede también tener consecuencias negativas, como lo son el distorsionar la distribución de la variable, afectar la correlación
y la varianza de las mismas, sin embargo, también puede ayudar en el proceso de ajuste cuando no se cuenta con suficientes registros, ayuda a reducir el sesgo que se produce por la ausencia de los datos y hacer el procedimiento más consistente.
A continuación se tomarán los registros que quedaron de la matriz anterior (29 variables y 1344 filas) y se observarán los valores faltantes
# Se selecciona la parte de los datos con los que se quiere trabajar.
df_sample = (df_Region[(df_Region['Year']>'2014') &
(df_Region['Year']<='2021')])
# Se imprimen los nan
df_sample[df_sample.isna().values]
Region | Year | Month | Population | Fallecidos | Lesionados_total | Automóvil_StatioWagon_TodoTerreno | Furgón | Minibús | Camioneta | Motocicleta_y_Similares | Otros_sin_Motor | Taxi_Básico | Taxi_Colectivo | Taxi_Turismo | Minibús_Transporte_Colectivo | Bus_Transporte_Colectivo | Transporte_de_carga | Grupo_de_delitos_de_mayor_connotación_social | Grupo_de_Infracción_a_ley_de_armas | Grupo_de_Incivilidades | Abigeato | Abusos_sexuales_y_otros_delitos_sexuales | Grupo_de_Violencia_intrafamiliar | Receptación | Robo_frustrado | PIB | Consumo_Hogares_IPSFL | Fuerza_de_trabajo_(Miles_de_personas) | Ocupados_(Miles_de_personas) | Tasa_de_desocupación_(%) | Reg_Empresas_Sociedades | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
NB | 2015 | 01 | 493464.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 894.0 | 15.0 | 865.0 | 15.0 | 25.0 | 368.0 | 13.0 | 17.0 | 2087.852542 | 1567.86672 | 237.838431 | 220.145929 | 7.438874 | NaN | |
NB | 2015 | 01 | 493464.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 894.0 | 15.0 | 865.0 | 15.0 | 25.0 | 368.0 | 13.0 | 17.0 | 2087.852542 | 1567.86672 | 237.838431 | 220.145929 | 7.438874 | NaN | |
NB | 2015 | 01 | 493464.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 894.0 | 15.0 | 865.0 | 15.0 | 25.0 | 368.0 | 13.0 | 17.0 | 2087.852542 | 1567.86672 | 237.838431 | 220.145929 | 7.438874 | NaN | |
NB | 2015 | 01 | 493464.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 894.0 | 15.0 | 865.0 | 15.0 | 25.0 | 368.0 | 13.0 | 17.0 | 2087.852542 | 1567.86672 | 237.838431 | 220.145929 | 7.438874 | NaN | |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
En este caso no se implementará ningún método de imputación debido a que los valores faltantes se asocian a registros para la Región de Ñuble previos al año 2018, considerando que el decreto promulgatorio de la ley que crea la Región de Ñuble entró en vigencia en el año 2018, tiene sentido que estos valores no se encuentren informados.
# Se remueven los nan
df_sample.dropna(inplace=True)
# Se imprime la dimensión de la matriz final
df_sample.shape
Se remueven los registros no informados y se imprime la dimensión de la matriz final, la cual cuenta con 1293 filas y 29 columnas. Ahora, luego de hacer la limpieza de los registros, se continúa con la siguiente etapa, un análisis descriptivo más exhaustivo.
Si deseas descargar el DataFrame df_Region final puedes hacerlo ingresando aquí.