Exploración inicial
Antes de empezar la exploración, guardo las bases en variables y descargo las librerías que me van a permitir leer los datos.
Buscamos las dimensiones de las 2 bases de datos provistas (2020 y 2022)
## [1] 319795 18
## [1] 445132 40
## [1] "HeartDisease" "BMI" "Smoking" "AlcoholDrinking"
## [5] "Stroke" "PhysicalHealth" "MentalHealth" "DiffWalking"
## [9] "Sex" "AgeCategory" "Race" "Diabetic"
## [13] "PhysicalActivity" "GenHealth" "SleepTime" "Asthma"
## [17] "KidneyDisease" "SkinCancer"
## [1] "State" "Sex"
## [3] "GeneralHealth" "PhysicalHealthDays"
## [5] "MentalHealthDays" "LastCheckupTime"
## [7] "PhysicalActivities" "SleepHours"
## [9] "RemovedTeeth" "HadHeartAttack"
## [11] "HadAngina" "HadStroke"
## [13] "HadAsthma" "HadSkinCancer"
## [15] "HadCOPD" "HadDepressiveDisorder"
## [17] "HadKidneyDisease" "HadArthritis"
## [19] "HadDiabetes" "DeafOrHardOfHearing"
## [21] "BlindOrVisionDifficulty" "DifficultyConcentrating"
## [23] "DifficultyWalking" "DifficultyDressingBathing"
## [25] "DifficultyErrands" "SmokerStatus"
## [27] "ECigaretteUsage" "ChestScan"
## [29] "RaceEthnicityCategory" "AgeCategory"
## [31] "HeightInMeters" "WeightInKilograms"
## [33] "BMI" "AlcoholDrinkers"
## [35] "HIVTesting" "FluVaxLast12"
## [37] "PneumoVaxEver" "TetanusLast10Tdap"
## [39] "HighRiskLastYear" "CovidPos"
Podemos ver que la tabla del año 2022 es mucho más grande
Para combinar ambas tablas vamos a usar de base la tabla de menor cantidad de columnas
Para emparejar los datos, renombramos las columnas en la tabla 2022 para coincidir los nombres de las columnas con los mismos datos de la tabla 2020
Para hacer esto usamos la función _ rename_ del paquete “dplyr” previamente instalado
data_2022 <- rename(data_2022,
HeartDisease = HadHeartAttack,
Stroke = HadStroke,
Asthma = HadAsthma,
SkinCancer = HadSkinCancer,
KidneyDisease = HadKidneyDisease,
Diabetic = HadDiabetes,
PhysicalHealth = PhysicalHealthDays,
MentalHealth = MentalHealthDays,
DiffWalking = DifficultyWalking,
Race = RaceEthnicityCategory,
PhysicalActivity = PhysicalActivities,
GenHealth = GeneralHealth,
SleepTime = SleepHours,
Smoking = SmokerStatus,
AlcoholDrinking = AlcoholDrinkers
)Creamos un nuevo dataframe que contenga las filas de ambas tablas.
Para esto primero intersectamos las columnas, así separamos las que tienen igual nombre y las ponemos en nuevas variables
Compruebo que tenga +700.000 filas y 18 columnas
common_columns <- intersect(names(data_2020), names(data_2022))
combined_data<-bind_rows(select(data_2020, all_of(common_columns)),select(data_2022, all_of(common_columns)) )
dim(combined_data)## [1] 764927 18
Análisis inicial
Para analizar cada columna primero vamos a crear una variable aplicando la función “sapply” para conocer la clase de cada columna
Con “colSums” vamos a sumar los datos NA de cada coluna
Primero vamos a convertir los espacios vacíos en la tabla a na
Calculamos el porcentaje que representa esta suma en cada columna
Presentamos estos datos en un df
#tipo de dato de cada columna
data_types <- sapply(combined_data, class)
#convierto los vacios en na
combined_data[combined_data == ""] <- NA
#sumo los vacios de cada columna
missing_values <- colSums(is.na(combined_data))
#lo paso a porcentaje
percentage_missing <- round(colMeans(is.na(combined_data)) * 100,2)
#lo presento como tabla
result <- data.frame(
Data_type = data_types,
Missing_val = missing_values,
Percent_missing = percentage_missing
)
knitr::kable(result,caption="Result")| Data_type | Missing_val | Percent_missing | |
|---|---|---|---|
| HeartDisease | character | 3065 | 0.40 |
| BMI | numeric | 48806 | 6.38 |
| Smoking | character | 35462 | 4.64 |
| AlcoholDrinking | character | 46574 | 6.09 |
| Stroke | character | 1557 | 0.20 |
| PhysicalHealth | numeric | 10927 | 1.43 |
| MentalHealth | numeric | 9067 | 1.19 |
| DiffWalking | character | 24012 | 3.14 |
| Sex | character | 0 | 0.00 |
| AgeCategory | character | 9079 | 1.19 |
| Race | character | 14057 | 1.84 |
| Diabetic | character | 1087 | 0.14 |
| PhysicalActivity | character | 1093 | 0.14 |
| GenHealth | character | 1198 | 0.16 |
| SleepTime | numeric | 5453 | 0.71 |
| Asthma | character | 1773 | 0.23 |
| KidneyDisease | character | 1926 | 0.25 |
| SkinCancer | character | 3143 | 0.41 |
Limpieza de datos
combined_data<- select(combined_data, "Sex", "AgeCategory", "Race","BMI", "GenHealth", "PhysicalActivity", "PhysicalHealth",
"MentalHealth", "SleepTime", "DiffWalking", "Smoking", "AlcoholDrinking", "HeartDisease", "Stroke", "Asthma", "KidneyDisease", "SkinCancer", "Diabetic")## [1] 654934 18
Este tipo de limpieza es una práctica que conlleva una importante pérdida de datos presentes en las demás columnas y que aportaban información valiosa al conjunto de datos.
La eliminación de filas reduce el tamaño de la muestra, lo que puede afectar la significatividad estadística y la potencia de cualquier análisis realizado con los datos restantes.
Los modelos predictivos y estadísticos van a resultar menos generalizables y con un gran margen de error
Cambiar los NA por el promedio de los datos de esa columna, en lugar de eliminar toda la fila, sería una alternativa muy útil para mantener el tamaño original del data frame y conservar los datos de las demás columnas.
Con esta corrección, el valor y el promedio de la columna igualmente experimentan un cambio, pero no es tan significativo y refleja con mayor precisión los valores reales, lo que es útil para no variar los resultados y valores de los análisis; a diferencia de lo que sucede al eliminar filas de información.
## [1] 28215
Como podemos ver, tenemos más de 28.000 datos duplicados. Si bien, no significa un gran porcentaje de la tabla de datos, es un valor muy influyente en los cálculos generales y modelos estadísticos
La eliminación de los datos duplicados sería un error muy grande ya que, un duplicado representa un paciente con las mismas caraterísticas. Por esto, significaría una importante pérdida de datos que podría distorsionar totalmente las estadísticas descriptivas, como promedios, desviaciones estándar y correlaciones, se alteraría la distribución y la representación de los datos.
Limpieza de variables categóricas
“gsub” es una función en R que se usa para reemplazar todas las coincidencias de un patrón dentro de una cadena.
Para la limpieza usaremos alternativas que ofrece esta función, para cambiar patrones, eliminar caracteres de más o reemplazar el nombre de las variables
Simplificamos las variables de la columna “Race”
Compruebo cada tipo de variable presente en la columna
combined_data$Race <- gsub("White only, Non-Hispanic", "White", combined_data$Race)
combined_data$Race <- gsub("Black only, Non-Hispanic", "Black", combined_data$Race)
combined_data$Race <- gsub("Other race only, Non-Hispanic", "Other", combined_data$Race)
#verifico
unique(combined_data$Race)## [1] "White" "Black"
## [3] "Asian" "American Indian/Alaskan Native"
## [5] "Other" "Hispanic"
## [7] "Multiracial, Non-Hispanic"
Normalizar los datos de Categoría de Edad para que se resuman como “Edad inicial– Edad final”. Es decir, todos se deberían mostrar como “35-39” y no como “Age 35-39”.
Para esto vamos a cambiar las variables con la estructura incorrecta
#reemplazo
combined_data$AgeCategory<- gsub("Age (\\d+) to (\\d+)", "\\1-\\2", combined_data$AgeCategory)
combined_data$AgeCategory<- gsub("Age 80 or older", "80 or older", combined_data$AgeCategory)
#verifico
unique(combined_data$AgeCategory)## [1] "55-59" "80 or older" "65-69" "75-79" "40-44"
## [6] "70-74" "60-64" "50-54" "45-49" "18-24"
## [11] "35-39" "30-34" "25-29"
combined_data$Diabetic<- gsub("^Yes.*", "Yes", combined_data$Diabetic)
combined_data$Diabetic<- gsub("^No.*", "No", combined_data$Diabetic)
#verifico
unique(combined_data$Diabetic)## [1] "Yes" "No"
EDA (Exploratory Data Analysis)
Para realizar el EDA utilizaremos ggplot. Para ello, instalamos las librerias necesarias
##
## No Yes
## 609008 45926
Como podemos apreciar hay un desbalance significativo, ya que una mayor proporción de personas no experimentó un infarto
Si bien el enunciado nos dice que el 47% de la población cumple con al menos uno de los factores de riesgo, la tabla no muestra una inclinación de la población a sufrir infartos. Sin embargo, esta distribución podría reflejar la realidad observada en la población general.
Analizamos la relación de target con el sexo, actividad física, consumo de tabaco y alcohol
Vamos a visualizar esto individualmente y posteriormente en un gráfico donde podamos apreciar la superposición de las relaciones. Para esto, previamente cargamos las librerías necesarias
Target vs sexo Female Male
No 321930 287078 Yes 18077 27849
Target vs Actividad física
## No Yes
##
## No 130631 478377
## Yes 16861 29065
Target vs Consumo de tabaco
##
## No Yes
## 389493 265441
Target vs Consumo de alcohol
## No Yes
##
## No 411595 197413
## Yes 37524 8402
Gráfico de apresiación
La cantidad de casos con infartos es baja en todas las categorías de las variables.
La poca diferencia sugiere que, estas variables categóricas por sí solas no parecen tener una fuerte asociación con los enfermedades cardiacas. Es decir, no parecen representar factores importantes de riesgo
Podemos ver que el fator de la edad influye significativamente en la cantidad de casos, los cuales aumentan conforme también aumenta la edad En este caso, podemos aseguarar que la edad es un factor de riesgo
-Revición general de los campos de forma gráfica.
Podemos observar que el pico de la curva está corrido ligeramente por encima de los valores “normales”
## No Yes
##
## No 590991 18017
## Yes 37991 7935
Entre los que si sufieron infartos, la gran mayoria no realizaba actividad física. Por lo tanto, podemos concluir que es un factor de riesgo
MentalHealth
No se ve una relación
DiffWalking
## No Yes
##
## No 531929 77079
## Yes 28585 17341
Race
No hay relación
Diabetic
## No Yes
##
## No 533191 75817
## Yes 30405 15521
## `summarise()` has grouped output by 'GenHealth'. You can override using the
## `.groups` argument.
Podemos ver que la población que no tiene una buena salúd general es
mucho más propensa a sufrir infartos
## `summarise()` has grouped output by 'SleepTime'. You can override using the
## `.groups` argument.
Aunque los casos se vean más altos en las 7-8 horas de sueño, tiene
que ver con la cantidad mayor de población que duerme esa cantidad de
horas. Por lo tanto, las horas de sueño no reflejan una relación con los
casos de infartos
## No Yes
##
## No 524150 84858
## Yes 37604 8322
## No Yes
##
## No 588234 20774
## Yes 39843 6083
## No Yes
##
## No 558883 50125
## Yes 38332 7594
Análisis estadistico
numeric_summary <- combined_data %>%
summarise(across(where(is.numeric), list(mean = ~mean(.), sd = ~sd(.), median = ~median(.), min = ~min(.), max = ~max(.)), .names = "{col}_{fn}"))
print(numeric_summary)## BMI_mean BMI_sd BMI_median BMI_min BMI_max PhysicalHealth_mean
## 1 28.45071 6.442805 27.4 12.02 97.65 3.79821
## PhysicalHealth_sd PhysicalHealth_median PhysicalHealth_min PhysicalHealth_max
## 1 8.245473 0 0 30
## MentalHealth_mean MentalHealth_sd MentalHealth_median MentalHealth_min
## 1 4.126199 8.115427 0 0
## MentalHealth_max SleepTime_mean SleepTime_sd SleepTime_median SleepTime_min
## 1 30 7.059847 1.443783 7 1
## SleepTime_max
## 1 24
Media (mean): Representa el valor promedio de la variable. Es útil para entender el valor central de los datos.
Desviación Estándar (sd): Mide la dispersión de los datos alrededor de la media. Una desviación estándar más alta indica mayor variabilidad.
Mediana (median): Es el valor que se encuentra en el centro de un conjunto de datos ordenados. Es útil cuando los datos no siguen una distribución normal.
Mínimo (min) y Máximo (max): Representan los valores más pequeño y más grande en el conjunto de datos, respectivamente.
La media de MBI está dentro de los valores normales, por lo que podemos asumir que la mayor parte de la población está dentro del rango considerado sano. Sin embargo, como ya habiamos aclarado, no se ve una relación directa con las enfermedades cardiacas
La salud física es el dato donde existe una mayor desviación, lo cual indica una variabilidad muy alta en la población total´. Por esta razón vemos que la media se achica
La salúd mental no muestra una relación con los infartos, pero tenemos en cuenta que las conclusiones que podriamos sacar de estos datos son muy ambiguas puesto que no sabemos qué consideraciones se tomó para la medición y que, la salud mental abarca una variedad de factores muy amplia que podrian o no tener que ver con los paros cardiacos individualmente
Las horas de sueño son el factor en que mejores datos se obtuvieron. La media se encuentra dentro de los valores recomendado y hay una disviación muy baja. Aún así, no se ven relaciones con los paros cardiacos
Los datos “outliers” son valores que se desvían significativamente de la tendencia general del conjunto de datos. En la medición de factores de riesgo poblacional para enfermedades cardiacas, los outliers pueden afectar la precisión y la interpretación de los resultados.
Detectarlos es importante para garantizar que los análisis sean representativos. Para hacerlo vamos a utilizar el rango intercuartil (IQR). Los valores que se encuentran a una distancia mayor de 1.5 * IQR del primer y tercer cuartil son considerados outliers.
Analizamos estos datos gráficamente
En BMI vemos varios outliers mayores a 60. No se observa una clara
separación entre los casos con y sin enfermedad cardíaca en función del
BMI.
Los valores de Salud Mental se agrupan principalmente entre 0 y 30 días (considerando un mes). No hay outliers significativos en esta variable.
No hay outliers significativos en la variable de salúd física. La distribución de los casos con y sin enfermedad cardíaca es bastante uniforme, con algunos puntos más dispersos hacia la derecha
Hay algunos outliers con valores de sueño muy altos (más de 15 horas). No se observa una clara separación entre los casos con y sin enfermedad cardíaca en función del tiempo de sueño.
Pudimos apreciar que no hay una diferencia entre la distribución de las personas con y sin enfermedad cardiaca en las variables analizadas
Elimiar los outliers impata en el análisis de los datos, ya que influyen en las estadísticas descriptivas, los modelos predictivos y la interpretación general
Aunque la eliminación de estos datos puede mejorar la presición de algunos modelos, en los casos como este donde analizamos muestras y factores reales implica un perdida de información valiosa Igualmente se debe analizar que no se trate de pocos casos aislados que muevan significativamente el valor de los cálculos
Una mejor forma de trabajar esos casos es reemplazar estos valores por la media del conjunto de datos o, en lo posible, reemplazar por los valores más cercanos en un rango.
Conclusiones
Después del análisis de nuestros datos, concluimos que si bien todos los datos son considerados factores de riesgo por CDC, no todos ellos muestran una relación significativa con las enfermedades cardiacas.
La edad, la actividad física, la mala salud general y el BMI son los factores con la influencia más fuerte. Las personas mayores, aquellos que no realizan actividad física frecuente, no tienen una buena salud general o tienen un alto BMI, sufren un riesgo más alto de padecer infartos.
Muchos datos son ambiguos, esto quiere decir que, si bien son muy útiles, no permiten sacar conclusiones muy acertadas por la generalización que implican o el desconocimiento de lo que se tuvo en cuenta para la evaluación. Esto se puede apreciar en las variables de salud general y salud mental.
Al contrario de lo esperado, el consumo de alcohol y tabaco no mostró una relación. Esto no quiere decir que no representen un factor de riesgo, sino que, a partir de nuestra muestra, no se puede corroborar una relación.
Se cuenta con muchos datos que realmente no tienen ninguna influencia ni podrían implicar factores de riesgo. Sin embargo, se llevó a cabo un conteo de la presencia de enfermedad cardiaca, discriminando por estos factores. Así, podemos decir con seguridad que, el sexo, la raza, las horas de sueño, el asma, la diabetes o el cáncer de piel no representan un factor de riego.