Información del Dataset

Dataset: https://www.kaggle.com/datasets/muhammedderric/fitness-classification-dataset-synthetic?select=fitness_dataset.csv

Lista de Variables

  • age: Edad del individuo en años.
  • height_cm: Altura en centímetros.
  • weight_kg: Peso en kilogramos.
  • heart_rate: Frecuencia cardíaca en reposo en latidos por minuto.
  • blood_pressure: Presión arterial sistólica en mmHg.
  • sleep_hours: Promedio de horas de sueño al día.
  • nutrition_quality: Puntuación de calidad nutricional diaria entre 0 y 10.
  • activity_index: Puntuación del nivel de actividad física entre 1 y 5.
  • smokes: ¿fuma?
  • gender: Género del individuo.
  • is_fit: Variable objetivo (la persona está en forma o no).

Importar Librerías

Se importan las librerías esenciales para facilitar el análisis que abarca la carga de datos, la evaluación estadística, la visualización, la transformación de datos, la fusión y la unión.

require(dplyr)
## Cargando paquete requerido: dplyr
## Warning: package 'dplyr' was built under R version 4.4.3
## 
## Adjuntando el paquete: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
require(tibble)
## Cargando paquete requerido: tibble
require(stringr)
## Cargando paquete requerido: stringr
## Warning: package 'stringr' was built under R version 4.4.3
require(ggplot2)
## Cargando paquete requerido: ggplot2
## Warning: package 'ggplot2' was built under R version 4.4.3
require(ggpubr)
## Cargando paquete requerido: ggpubr
## Warning: package 'ggpubr' was built under R version 4.4.3
require(e1071)
## Cargando paquete requerido: e1071
## Warning: package 'e1071' was built under R version 4.4.3
require(psych)
## Cargando paquete requerido: psych
## Warning: package 'psych' was built under R version 4.4.3
## 
## Adjuntando el paquete: 'psych'
## The following objects are masked from 'package:ggplot2':
## 
##     %+%, alpha
library(reshape2)
## Warning: package 'reshape2' was built under R version 4.4.1
library(Hmisc)
## Warning: package 'Hmisc' was built under R version 4.4.3
## 
## Adjuntando el paquete: 'Hmisc'
## The following object is masked from 'package:psych':
## 
##     describe
## The following object is masked from 'package:e1071':
## 
##     impute
## The following objects are masked from 'package:dplyr':
## 
##     src, summarize
## The following objects are masked from 'package:base':
## 
##     format.pval, units
library(Amelia)
## Warning: package 'Amelia' was built under R version 4.4.3
## Cargando paquete requerido: Rcpp
## ## 
## ## Amelia II: Multiple Imputation
## ## (Version 1.8.3, built: 2024-11-07)
## ## Copyright (C) 2005-2025 James Honaker, Gary King and Matthew Blackwell
## ## Refer to http://gking.harvard.edu/amelia/ for more information
## ##

Lectura de Datos

df = read.csv("C:/Users/johan/Downloads/data_eda/fitness_dataset.csv", sep=",", header=TRUE, fileEncoding = "UTF-8")

Se muestran las 5 primeras filas del DataFrame.

head(df)
##   age height_cm weight_kg heart_rate blood_pressure sleep_hours
## 1  56       152        65       69.6          117.0          NA
## 2  69       186        95       60.8          114.8         7.5
## 3  46       192       103       61.4          116.4          NA
## 4  32       189        83       60.2          130.1         7.0
## 5  60       175        99       58.1          115.8         8.0
## 6  25       172        85       81.2          119.2         7.7
##   nutrition_quality activity_index smokes gender is_fit
## 1              2.37           3.97     no      F      1
## 2              8.77           3.19      0      F      1
## 3              8.20           2.03      0      F      0
## 4              6.18           3.68      0      M      1
## 5              9.95           4.83    yes      F      1
## 6              7.35           4.08    yes      M      0

Se muestran las 5 últimas filas del DataFrame.

tail(df)
##      age height_cm weight_kg heart_rate blood_pressure sleep_hours
## 1995  41       158       113       79.6          104.8        11.5
## 1996  52       173        98       60.7          106.1          NA
## 1997  61       186        74       51.4          123.8         9.4
## 1998  77       198        89       76.7          103.6         8.3
## 1999  62       190        63       80.7          115.9         6.7
## 2000  51       166        78       89.3          101.8         8.3
##      nutrition_quality activity_index smokes gender is_fit
## 1995              3.96           1.85    yes      M      0
## 1996              1.54           3.25      1      M      1
## 1997              8.63           3.15     no      M      1
## 1998              1.98           3.36    yes      M      0
## 1999              9.21           2.39      1      F      0
## 2000              4.42           1.02      1      M      0

Se presenta un resumen detallado de todas las variables del dataset, incluyendo el tipo de dato (entero, punto flotante o carácter) lo que permite evaluar la integridad y estructura de los datos para su posterior procesamiento.

glimpse(df)
## Rows: 2,000
## Columns: 11
## $ age               <int> 56, 69, 46, 32, 60, 25, 78, 38, 56, 75, 36, 40, 28, …
## $ height_cm         <int> 152, 186, 192, 189, 175, 172, 193, 188, 164, 198, 15…
## $ weight_kg         <int> 65, 95, 103, 83, 99, 85, 83, 57, 108, 55, 63, 55, 90…
## $ heart_rate        <dbl> 69.6, 60.8, 61.4, 60.2, 58.1, 81.2, 79.6, 81.2, 70.1…
## $ blood_pressure    <dbl> 117.0, 114.8, 116.4, 130.1, 115.8, 119.2, 132.5, 110…
## $ sleep_hours       <dbl> NA, 7.5, NA, 7.0, 8.0, 7.7, 7.4, 6.6, 9.1, 8.1, 5.9,…
## $ nutrition_quality <dbl> 2.37, 8.77, 8.20, 6.18, 9.95, 7.35, 2.16, 8.47, 4.15…
## $ activity_index    <dbl> 3.97, 3.19, 2.03, 3.68, 4.83, 4.08, 3.42, 4.96, 2.06…
## $ smokes            <chr> "no", "0", "0", "0", "yes", "yes", "yes", "0", "no",…
## $ gender            <chr> "F", "F", "F", "M", "F", "M", "F", "M", "F", "F", "M…
## $ is_fit            <int> 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0…

Interpretación del Dataset:

El dataset presentado contiene 2,000 observaciones y 11 variables, que incluyen una combinación de tipos de datos numéricos (enteros y de punto flotante) y de tipo carácter.

  • Las variables numéricas abarcan características como edad (age), altura (height_cm), peso (weight_kg), frecuencia cardíaca (heart_rate), presión arterial (blood_pressure), horas de sueño (sleep_hours), calidad de nutrición (nutrition_quality) e índice de actividad (activity_index), donde algunas presentan valores faltantes (NA) en sleep_hours.

  • Las variables cualitativas sonsmokes (hábito de fumar) y gender (género), mientras que la variable objetivo is_fit (indicador binario de condición física) está codificada como entero.

La estructura sugiere que el dataset está diseñado para analizar relaciones entre factores de salud y estilo de vida, aunque se observan inconsistencias en smokes (valores posiblemente erróneos), lo que podría requerir limpieza previa al análisis.


Se calcula el número de valores faltantes para cada variable del DataFrame.

colSums(is.na(df))
##               age         height_cm         weight_kg        heart_rate 
##                 0                 0                 0                 0 
##    blood_pressure       sleep_hours nutrition_quality    activity_index 
##                 0               160                 0                 0 
##            smokes            gender            is_fit 
##                 0                 0                 0
missmap(df, col = c("red", "blue"), legend = TRUE)

Interpretación de los Valores Faltantes:

El análisis de valores faltantes en el dataset revela que la variable sleep_hours presenta 160 NA (8% del total de observaciones de esa columna), lo que indica una proporción significativa de datos ausentes que podría afectar los análisis relacionados con patrones de sueño.

El resto de las variables, incluyendo indicadores de salud como presión arterial (blood_pressure), calidad nutricional (nutrition_quality), y variables demográficas como género (gender), no contienen valores faltantes.

Esta estructura sugiere que el dataset está mayormente completo, excepto por la variable de sueño, lo que podría requerir estrategias como imputación o exclusión controlada para evitar sesgos en estudios posteriores.

Tratamiento de datos faltantes

Como nuestra variable tiene un porcentaje de datos faltantes de 8%, podemos imputar por la media (en caso de que siga una distribución normal) o mediana (si no tiene una distribución normal). Antes de realizar la imputacion vamos a revisar la normalidad de nuestros datos para saber que metodo vamos a utilizar.

df_antes <- df

shapiro_antes <- shapiro.test(df_antes$sleep_hours)
print(shapiro_antes)
## 
##  Shapiro-Wilk normality test
## 
## data:  df_antes$sleep_hours
## W = 0.99655, p-value = 0.0003575

Como el p-valor obtenido es 0.0003575<0.05, se rechaza la hipotesis nula de que nuestro conjunto de datos es normal, por lo que debemos realizar la imputación por medio de la mediana.

median_sleep <- median(df$sleep_hours, na.rm = TRUE)

print(paste("La mediana de horas de sueño es:", round(median_sleep, 2)))
## [1] "La mediana de horas de sueño es: 7.5"
na_count_before <- sum(is.na(df_antes$sleep_hours))


df <- df %>%
  mutate(sleep_hours = ifelse(is.na(sleep_hours), median_sleep, sleep_hours))

missmap(df, col = c("red", "blue"), legend = TRUE)

Análisis de Conservación de la Distribución Post-Imputación

Realizaremos un histograma y una prueba de normalidad para ver si luego de hacer la imputación de los datos se mantiene la distribucion.

par(mfrow = c(1, 2))
hist(df_antes$sleep_hours, main = "Antes de imputación", 
     xlab = "Horas de sueño", col = "orange")
hist(df$sleep_hours, main = "Después de imputación", 
     xlab = "Horas de sueño", col = "lightgreen")

shapiro_despues <- shapiro.test(df$sleep_hours)
print(shapiro_despues)
## 
##  Shapiro-Wilk normality test
## 
## data:  df$sleep_hours
## W = 0.99405, p-value = 3.307e-07

Vemos que el p-valor es 3.307e-07<0.05, por lo que usaremos una prueba no parametrica para comprobar si mantiene o no la distribuciónn.

Prueba no Parametrica para Sleep_hours- Mediana

Para evaluar si la imputación mantuvo la forma original de la distribución de las variables, se emplea la prueba de Kolmogorov–Smirnov, la cual permite comparar estadísticamente dos muestras y determinar si provienen de la misma distribución.

Hipótesis nula: ambas muestras provienen de la misma distribución.

Hipótesis alternativa: las muestras provienen de distribuciones diferentes.

suppressWarnings({
  
ks.test(df_antes$sleep_hours, df$sleep_hours)

  })
## 
##  Asymptotic two-sample Kolmogorov-Smirnov test
## 
## data:  df_antes$sleep_hours and df$sleep_hours
## D = 0.039565, p-value = 0.09953
## alternative hypothesis: two-sided

Como el p-valor es mayor que el nivel de significancia, no hay evidencia significativa para rechazar H_0, por lo que ambas muestras provienen de la misma distribución, esto es, despues de la imputación no se afecto la distribución.


colSums(is.na(df))
##               age         height_cm         weight_kg        heart_rate 
##                 0                 0                 0                 0 
##    blood_pressure       sleep_hours nutrition_quality    activity_index 
##                 0                 0                 0                 0 
##            smokes            gender            is_fit 
##                 0                 0                 0

Ahora ejecutamos nuevamente el comando para ver los datos faltantes por variables y efectivamente, nuestra variable sleep_hours tiene 0 datos faltantes


Se calcula el número de valores únicos para cada variable del DataFrame.

sapply(df, function(x) length(unique(x)))
##               age         height_cm         weight_kg        heart_rate 
##                62                50                80               484 
##    blood_pressure       sleep_hours nutrition_quality    activity_index 
##               563                79               866               398 
##            smokes            gender            is_fit 
##                 4                 2                 2

Interpretación de los Valores únicos:

  • Variables continuas: heart_rate (484 únicos), blood_pressure (563), nutrition_quality (866) y activity_index (398) presentan alta granularidad, lo que sugiere mediciones precisas o decimales. sleep_hours (79 únicos) también refleja variabilidad, aunque con menos detalle.

  • Variables discretas: age (62 únicos), height_cm (50) y weight_kg (80) indican rangos demográficos y físicos esperados en una muestra de 2,000 observaciones.

  • Variables categóricas: smokes tiene 4 categorías (posiblemente incluyendo errores), gender y is_fit son binarias (2 únicos cada una), lo que confirma su naturaleza dicotómica.

Esta estructura resalta la adecuación del dataset para análisis estadísticos, aunque es necesario revisar las categorías de smokes para corregir inconsistencias. La alta cardinalidad en variables continuas sugiere riqueza de datos para modelado predictivo.


La variable smokes tiene 4 valores únicos, lo cual es inesperado, ya que se espera que sea una variable binaria que represente si una persona fuma o no. Entonces, se identifican los valores inesperados.

unique(df$smokes)
## [1] "no"  "0"   "yes" "1"

Se estandarizan las variables smokes e is_fit para mejorar la legibilidad y la coherencia de los datos.

  • La variable is_fit se encontraba codificada con los valores 0 y 1, que se reemplazan por “no” y “yes”, respectivamente, para hacerla más comprensible.

  • La variable smokes contenía cuatro valores únicos: 0, 1, “no” y “yes”. Para consolidar esta información y evitar errores en el análisis, se unifican todos los valores a dos categorías: 0 se reemplaza por “no” y 1 por “yes”.

df$smokes = str_replace(df$smokes, "0", "no")
df$smokes = str_replace(df$smokes, "1", "yes")
df$is_fit = str_replace(df$is_fit, "0", "no")
df$is_fit = str_replace(df$is_fit, "1", "yes")

Se transforman las variables tipo carácter a factor.

df$smokes = as.factor(df$smokes)
df$gender = as.factor(df$gender)
df$is_fit = as.factor(df$is_fit)

Analisis de Datos Atipicos

Aplicaremos la técnica de capping basada en percentiles para atenuar el efecto de valores atípicos extremos, reemplazándolos por los límites de los cuartiles y preservando así la estructura general de los datos sin eliminarlos.

capping_iqr <- function(x) {
  Q1 <- quantile(x, 0.25, na.rm = TRUE)
  Q3 <- quantile(x, 0.75, na.rm = TRUE)
  IQR_val <- Q3 - Q1
  
  lower <- Q1 - 1.5 * IQR_val
  upper <- Q3 + 1.5 * IQR_val
  
  x[x < lower] <- lower
  x[x > upper] <- upper
  return(x)
}

variables_predictores <- setdiff(names(df), "is_fit")

df_melted <- melt(df[, variables_predictores])
## Using smokes, gender as id variables
ggplot(df_melted, aes(x = "", y = value)) +
  geom_boxplot(fill = "#FBD000", color = "red") +
  theme_minimal() +
  labs(title = "Boxplot inicial", x = "", y = "Valor") +
  facet_wrap(~variable, scales = "free_y")

variables_numericas <- variables_predictores[sapply(df[variables_predictores], is.numeric)]

for (col in variables_numericas) {
  df[[col]] <- capping_iqr(df[[col]])
}

df_melted_final <- melt(df[, variables_predictores])
## Using smokes, gender as id variables
ggplot(df_melted_final, aes(x = "", y = value)) +
  geom_boxplot(fill = "#FBD000", color = "red") +
  theme_minimal() +
  labs(title = "Boxplot después de capping por IQR", x = "", y = "Valor") +
  facet_wrap(~variable, scales = "free_y")

Tras aplicar el capping, los boxplots muestran una distribución más uniforme y sin valores atípicos extremos, lo que facilita un análisis más estable y representativo de las variables.


Análisis Univariado

El Análisis Univariado es la primera fase del análisis exploratorio de datos. Se enfoca en el estudio individual de cada variable para entender su distribución, características y valores atípicos. Esto permite identificar patrones y la calidad de los datos antes de un análisis más complejo.

Variables Binarias

Se genera un doble gráfico para cada variable binaria, permitiendo comparar su distribución mediante:

  1. Gráfico de barras:
    • Muestra el conteo absoluto de cada categoría (0 y 1).
    • Ideal para comparar magnitudes visualmente.
  2. Gráfico de Torta:
    • Muestra la proporción porcentual de cada categoría.
    • Útil para entender el balance/imbalance entre clases.
color = (c("orange", "lightblue"))
fig1 = ggplot(data=df, aes(x=smokes)) + 
  geom_bar(fill = color) + 
  labs(title = "Frecuencia de smokes", y = "Frecuencia", x = "Categorias") + 
  theme_bw() + 
  theme(plot.title = element_text(hjust = 0.5)) + 
  geom_text(aes(label = ..count..), stat = "count", vjust = 2, colour = "black")

fig2 = ggplot(data=df, aes(x=gender)) + 
  geom_bar(fill = color) + 
  labs(title = "Frecuencia de gender", y = "Frecuencia", x = "Categorias") + 
  theme_bw() + 
  theme(plot.title = element_text(hjust = 0.5)) + 
  geom_text(aes(label = ..count..), stat = "count", vjust = 2, colour = "black")

fig3 = ggplot(data=df, aes(x=is_fit)) + 
  geom_bar(fill = color) + 
  labs(title = "Frecuencia de is_fit", y = "Frecuencia", x = "Categorias") + 
  theme_bw() + 
  theme(plot.title = element_text(hjust = 0.5)) + 
  geom_text(aes(label = ..count..), stat = "count", vjust = 2, colour = "black")

ggarrange(fig1,fig2, fig3, ncol=3 ,nrow=1) 
## Warning: The dot-dot notation (`..count..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(count)` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.


fig1 = ggplot(data = df, aes(x = "", fill = smokes)) +  
  geom_bar(width = 1, color = "white") + 
  coord_polar(theta = "y") + 
  labs(title = "Distribucion de smokes", x = NULL, y = NULL) + theme_bw() +
  theme(plot.title = element_text(hjust = 0.5)) + 
  geom_text(aes(label = paste0(round(..count.. / sum(..count..) * 100, 1), "%)")),stat = "count", position = position_stack(vjust = 0.5), color = "black") + 
  scale_fill_manual(values = color)

fig2 = ggplot(data = df, aes(x = "", fill = gender)) +  
  geom_bar(width = 1, color = "white") + 
  coord_polar(theta = "y") + 
  labs(title = "Distribucion de gender", x = NULL, y = NULL) + theme_bw() +
  theme(plot.title = element_text(hjust = 0.5)) + 
  geom_text(aes(label = paste0(round(..count.. / sum(..count..) * 100, 1), "%)")),stat = "count", position = position_stack(vjust = 0.5), color = "black") + 
  scale_fill_manual(values = color)

fig3 = ggplot(data = df, aes(x = "", fill = is_fit)) +  
  geom_bar(width = 1, color = "white") + 
  coord_polar(theta = "y") + 
  labs(title = "Distribucion de is_fit", x = NULL, y = NULL) + theme_bw() +
  theme(plot.title = element_text(hjust = 0.5)) + 
  geom_text(aes(label = paste0(round(..count.. / sum(..count..) * 100, 1), "%)")),stat = "count", position = position_stack(vjust = 0.5), color = "black") + 
  scale_fill_manual(values = color)

ggarrange(fig1, fig2, fig3, ncol=3, nrow=1) 

Interpretación de las Variables Binarias:

  • En cuanto al hábito de fumar, se observa que el 54.9% de las personas no fuma, frente al 45.1% que sí lo hace, con una diferencia moderada entre ambos grupos (1,099 vs. 901 individuos).

  • En la variable género, la distribución es bastante equilibrada, con un 51.5% de mujeres y un 48.5% de hombres (1,030 vs. 970 personas).

  • Por último, en cuanto a la condición física, un 60.1% de los participantes no se considera en forma, mientras que el 40% sí lo está (1,201 vs. 799 personas), evidenciando una mayor diferencia entre categorías respecto a las otras variables.

En conjunto, estos resultados permiten apreciar que las proporciones se mantienen cercanas al equilibrio en smokes y gender, pero en is_fit predomina claramente la categoría “no”.

Variables Númericas Discretas

Se visualiza algunos estadísticos descriptivos para las variables númericas discretas.

summary(df[, -c(4:11)])
##       age          height_cm       weight_kg     
##  Min.   :18.00   Min.   :150.0   Min.   : 30.00  
##  1st Qu.:34.00   1st Qu.:162.0   1st Qu.: 64.00  
##  Median :49.00   Median :174.0   Median : 83.00  
##  Mean   :49.11   Mean   :174.5   Mean   : 82.91  
##  3rd Qu.:65.00   3rd Qu.:187.0   3rd Qu.:102.00  
##  Max.   :79.00   Max.   :199.0   Max.   :159.00

Interpretación de las Variables Númericas Discretas:

  • En cuanto a age, la edad de los participantes oscila entre 18 y 79 años, con la mayoría concentrada entre 34 y 65 años.

  • La estatura (height_cm) varía entre 150 y 199 cm, siendo más común observar valores en el rango de 162 a 187 cm.

  • Finalmente, el peso (weight_kg) presenta un rango amplio de 30 a 159 kg, con mayor concentración entre 64 y 102 kg.

Estos resultados ofrecen una visión general del rango y concentración de cada variable.


Se genera un histograma para visualizar la distribución de frecuencias y la dispersión de los datos. Además se evalua:

  • Asimetría:

    • skew = 0: Distribución simétrica (valores aceptables skew \(\in (-1,1)\)).
    • skew > 0: Mayor peso en la cola izquierda de la distribución (sesgo positivo).
    • skew < 0: Mayor peso en la cola derecha de la distribución (sesgo negativo).
  • Kurtosis: Determina si una distribución tiene colas gruesas con respecto a la distribución normal. Proporciona información sobre la forma de una distribución de frecuencias.

    • kurtosis = 3: se denomina mesocúrtica (distribución normal).
    • kurtosis < 3: se denomina platicúrtica (distribución con colas menos gruesas que la normal).
    • kurtosis > 3: se denomina leptocúrtica (distribución con colas más gruesas que la normal) y significa que trata de producir más valores atípicos que la distribución normal.
for (i in 1:3) {
  
  nombre_col = names(df)[i]
  a = kurtosis(df[[nombre_col]])
  b = skew(df[[nombre_col]])
  
  print(paste("Kurtosis:", round(a, 2)))  
  print(paste("Skew:", round(b, 2)))  
  
  fig1 =  ggplot(data = df, aes(x = .data[[nombre_col]])) +
    geom_bar(fill = "lightblue", color = "white") +
    labs(title = paste("Frecuencias de", nombre_col), x = "Categorias", y = "Frecuencias")+ theme_bw() +  theme(plot.title = element_text(hjust = 0.5))
  
  print(fig1)
}
## [1] "Kurtosis: -1.17"
## [1] "Skew: -0.04"

## [1] "Kurtosis: -1.19"
## [1] "Skew: 0.01"

## [1] "Kurtosis: -0.26"
## [1] "Skew: 0.22"

Interpretación de las Variables Númericas Discretas:

El análisis de las distribuciones para age, height_cm y weight_kg muestra comportamientos diferenciados.

  • El gráfico muestra que la distribución de la edad es plana y casi simétrica. La curtosis de -1.17 indica que la distribución es platicúrtica (más aplanada que una distribución normal), sin un pico claro. La asimetría (skew) de -0.04, que está muy cerca de cero, confirma que no hay una inclinación significativa hacia edades más jóvenes o mayores. En general, las edades están distribuidas de manera uniforme, sin una concentración notable en ningún rango.

  • El gráfico de frecuencias de altura (height_cm) muestra una distribución plana y simétrica. La curtosis de -1.19 indica que es platicúrtica (más aplanada que una distribución normal), sin un pico claro. La asimetría (skew) de 0.01, muy cercana a cero, confirma que la distribución es simétrica, sin inclinación hacia valores más altos o más bajos. En conclusión, no hay una altura dominante en los datos.

  • El gráfico de peso (weight_kg) presenta una distribución platicúrtica con una ligera asimetría positiva. La curtosis de -0.26 indica que la distribución es más plana que una curva normal. La asimetría (skew) de 0.22, un valor positivo, muestra una ligera inclinación hacia valores más altos, impulsada por un valor atípico (outlier) alrededor de los 160 kg. En general, el peso está distribuido de manera uniforme, con la excepción de este dato extremo.

Estos resultados sugieren que, mientras la edad y la estatura se distribuyen de forma más equilibrada, el peso muestra asimetría y alta concentración en torno a ciertos valores, con casos extremos que podrían influir en análisis posteriores.


Variables Númericas Continuas

Se visualiza algunos estadísticos descriptivos para las variables númericas continuas.

summary(df[, c(4:8)])
##    heart_rate     blood_pressure   sleep_hours    nutrition_quality
##  Min.   : 45.00   Min.   : 90.0   Min.   : 4.00   Min.   : 0.000   
##  1st Qu.: 62.10   1st Qu.:109.7   1st Qu.: 6.60   1st Qu.: 2.547   
##  Median : 70.25   Median :120.0   Median : 7.50   Median : 5.065   
##  Mean   : 70.27   Mean   :119.9   Mean   : 7.51   Mean   : 5.035   
##  3rd Qu.: 78.42   3rd Qu.:129.8   3rd Qu.: 8.40   3rd Qu.: 7.470   
##  Max.   :102.91   Max.   :159.9   Max.   :11.10   Max.   :10.000   
##  activity_index 
##  Min.   :1.000  
##  1st Qu.:2.038  
##  Median :2.980  
##  Mean   :2.999  
##  3rd Qu.:3.950  
##  Max.   :4.990

Interpretación de las Variables Númericas Continuas:

  • Los resúmenes estadísticos revelan que todas las variables —ritmo cardíaco, presión sanguínea, horas de sueño, calidad de la nutrición e índice de actividad— tienen distribuciones altamente simétricas, lo que se evidencia en la proximidad de los valores de la media y la mediana en cada caso. El ritmo cardíaco (media 70.27, mediana 70.25), la presión sanguínea (media 119.9, mediana 120.0), las horas de sueño (media 7.51, mediana 7.50), la calidad de la nutrición (media 5.035, mediana 5.065) y el índice de actividad (media 2.999, mediana 2.980) muestran una consistencia notable en sus valores centrales. Esta uniformidad sugiere que los datos no están sesgados hacia valores extremos y que los valores atípicos, de existir, no tienen un impacto significativo en la tendencia central de las variables, lo que indica que los datos son homogéneos y representativos.

Estos resultados ofrecen una visión general del rango y concentración de cada variable.


Se genera un histograma para visualizar la distribución de frecuencias y la dispersión de los datos. Además se evalua:

  • Asimetría:

    • skew = 0: Distribución simétrica (valores aceptables skew \(\in (-1,1)\)).
    • skew > 0: Mayor peso en la cola izquierda de la distribución (sesgo positivo).
    • skew < 0: Mayor peso en la cola derecha de la distribución (sesgo negativo).
  • Kurtosis: Determina si una distribución tiene colas gruesas con respecto a la distribución normal. Proporciona información sobre la forma de una distribución de frecuencias.

    • kurtosis = 3: se denomina mesocúrtica (distribución normal).
    • kurtosis < 3: se denomina platicúrtica (distribución con colas menos gruesas que la normal).
    • kurtosis > 3: se denomina leptocúrtica (distribución con colas más gruesas que la normal) y significa que trata de producir más valores atípicos que la distribución normal.
for (i in 4:8) {

  nombre_col = names(df)[i]
  a = kurtosis(df[[nombre_col]], na.rm = TRUE)
  b <- skew(df[[nombre_col]], na.rm = TRUE)
  
  print(paste("Kurtosis:", round(a, 2)))  
  print(paste("Skew:", round(b, 2)))  
  
  fig1 = ggplot(data = df, aes(x = .data[[nombre_col]])) + geom_histogram(fill = "lightblue", bins = 30, color = "white", na.rm = TRUE) + labs(title = paste("Histograma de", nombre_col), x = "Categorias", y = "Frecuencia") + theme_bw() + theme(plot.title = element_text(hjust = 0.5))
  
  print(fig1)
}
## [1] "Kurtosis: -0.26"
## [1] "Skew: 0.11"

## [1] "Kurtosis: -0.34"
## [1] "Skew: 0.08"

## [1] "Kurtosis: -0.06"
## [1] "Skew: -0.05"

## [1] "Kurtosis: -1.2"
## [1] "Skew: 0.01"

## [1] "Kurtosis: -1.16"
## [1] "Skew: 0.04"

Interpretación de las Variables Númericas Continuas:

Los histogramas y las medidas de forma (kurtosis y asimetría) indican que las variables analizadas presentan distribuciones cercanas a la normalidad, aunque con algunas particularidades.

  • El histograma del ritmo cardíaco (heart_rate) muestra una distribución platicúrtica con una ligera asimetría positiva. El valor de la curtosis de -0.26, que es negativo, indica una distribución más aplanada que una curva normal, lo que significa que los datos no están tan concentrados en un único pico. Esto se puede apreciar visualmente en la forma ancha y redondeada de la distribución. Por otro lado, la asimetría (skew) de 0.11 es un valor pequeño y positivo, lo que sugiere un sesgo muy leve hacia la derecha. Aunque la mayoría de los datos se agrupan alrededor del centro, la cola de la distribución se extiende ligeramente hacia valores más altos (ritmos cardíacos más rápidos). En resumen, los ritmos cardíacos están distribuidos de manera bastante uniforme, con una ligera tendencia hacia valores más altos.

  • El histograma de presión sanguínea (blood_pressure) muestra una distribución platicúrtica con una asimetría positiva muy leve. La curtosis de -0.34, siendo un valor negativo, indica que la distribución es más plana que una curva normal, lo cual se observa en el gráfico por la falta de un pico pronunciado. Por su parte, la asimetría (skew) de 0.08, un valor cercano a cero, sugiere una distribución casi simétrica con un sesgo muy sutil hacia valores más altos. En esencia, la presión sanguínea está distribuida de manera bastante uniforme en el rango central, sin una concentración excesiva, y presenta una cola muy ligera que se extiende hacia valores más altos.

  • El histograma de horas de sueño (sleep_hours) presenta una distribución mesocúrtica y simétrica. A diferencia de los gráficos anteriores, la curtosis de -0.06 está muy cerca de cero, lo que indica que la distribución es similar a una curva normal en términos de su pico y colas, aunque ligeramente más aplanada. La asimetría (skew) de -0.05 también está muy cerca de cero, confirmando que la distribución es casi perfectamente simétrica. Visualmente, se observa un pico claro en el centro (alrededor de 7-8 horas de sueño) y una caída gradual y equilibrada hacia ambos extremos. Esto sugiere que la mayoría de las personas duermen una cantidad de horas estándar, con una cantidad similar de individuos que duermen menos y más de lo recomendado.

  • El histograma del índice de actividad (activity_index) muestra una distribución platicúrtica y simétrica. La curtosis de -1.16, que es un valor negativo, indica que la distribución es más plana que una curva normal, lo que significa que los datos están dispersos en un rango amplio en lugar de concentrarse en un pico. El valor de la asimetría (skew) de 0.04 está muy cerca de cero, lo que confirma que la distribución es casi perfectamente simétrica, sin un sesgo notable hacia valores más bajos o más altos. Visualmente, el gráfico es bastante plano y uniforme, lo que sugiere que el índice de actividad está distribuido de manera homogénea.

  • El histograma de calidad de la nutrición (nutrition_quality) muestra una distribución platicúrtica y simétrica. La curtosis de -1.2, al ser un valor negativo, indica que la distribución es más plana que una curva normal, lo que significa que los datos no se concentran en un único punto. La asimetría (skew) de 0.01 está muy cerca de cero, lo que confirma que la distribución es prácticamente simétrica, sin un sesgo notable hacia valores bajos o altos. Visualmente, el gráfico es bastante uniforme, sin picos pronunciados. Esto sugiere que la calidad de la nutrición en la población estudiada está distribuida de manera homogénea en un amplio rango de valores.


Se genera un un boxplot para visualizar la distribución e identificar outliers (puntos fuera de los bigotes) y la dispersión de los datos. Además se evalua:

  • Coeficiente de Variación: es una medida estadística que se utiliza para evaluar la variabilidad relativa de una muestra o población en relación con su media. Se calcula como la desviación estándar de los datos dividida por la media, y se expresa como un porcentaje multiplicado por 100 para facilitar su interpretación.
for (i in 4:8) {

  nombre_col = names(df)[i]
  media = mean(df[[nombre_col]], na.rm = TRUE)
  desviacion = sd(df[[nombre_col]], na.rm = TRUE)
  coef_variacion = (desviacion / media) * 100
  print(paste("El coeficiente de variacion es: ", round(coef_variacion, 2), "%"))
  
  fig2 = ggplot(data = df, aes(x = .data[[nombre_col]])) +
    geom_boxplot(fill = "orange", color = "black", na.rm = TRUE) + labs(title = paste("Dispersion de", nombre_col), x = "Categorias") + theme_bw() + theme(plot.title = element_text(hjust = 0.5))
  
  print(fig2)
}
## [1] "El coeficiente de variacion es:  16.79 %"

## [1] "El coeficiente de variacion es:  12.13 %"

## [1] "El coeficiente de variacion es:  19.09 %"

## [1] "El coeficiente de variacion es:  56.88 %"

## [1] "El coeficiente de variacion es:  37.89 %"

Interpretación de las Variables Númericas Continuas:

  • El coeficiente de variación del ritmo cardíaco es de 16.79%, lo que indica una baja dispersión de los datos. Esto significa que la mayoría de los valores de ritmo cardíaco están muy cerca de la media. El diagrama de caja y bigotes corrobora esto, mostrando una caja estrecha y la ausencia de valores atípicos, lo que confirma que los datos son muy consistentes.

  • El coeficiente de variación para la presión sanguínea es de 12.13%, lo que indica una baja dispersión y una alta consistencia en los datos. El diagrama de caja y bigotes corrobora esto, mostrando que la mitad de los datos se agrupan en un rango estrecho y que no hay valores atípicos, lo que refuerza la idea de que la presión sanguínea es bastante uniforme en esta población.

  • El coeficiente de variación para las horas de sueño es de 19.09%, lo que indica una baja dispersión en los datos. El diagrama de caja confirma esta consistencia, mostrando una caja relativamente estrecha y la ausencia de valores atípicos, lo que sugiere que la mayoría de los individuos duermen una cantidad de horas similar, con poca variación.

  • El coeficiente de variación para la calidad de la nutrición es de 56.88%. Este valor, al ser superior a 50%, indica una dispersión moderada-alta en los datos. A diferencia de los gráficos anteriores, la caja del diagrama de caja es más ancha, lo que visualmente confirma que el 50% de los datos centrales tienen un rango más amplio. Esto sugiere que hay una mayor variabilidad en la calidad de la nutrición entre los individuos, con valores más dispersos alrededor de la media y la mediana.

  • El coeficiente de variación del índice de actividad es de 37.89%. Este valor indica una dispersión moderada de los datos, ya que es significativamente mayor que los de las variables fisiológicas (ritmo cardíaco, presión sanguínea, horas de sueño), pero menor que el de la calidad de la nutrición. El diagrama de caja y bigotes corrobora esto, mostrando una caja más ancha que las primeras variables, lo que sugiere que hay una mayor variabilidad en los niveles de actividad entre los individuos. Sin embargo, no se aprecian valores atípicos que distorsionen la distribución.

Análisis Bivariado

El análisis bivariado es la segunda fase del análisis exploratorio de datos. Se enfoca en las relaciones entre dos variables para obtener datos estadísticos sobre sus influencias mutuas.


Se generan graficos de barras para analizar la relación entre el conjunto de variables binarias y la variable estado de forma con el fin de identificar qué variables binarias están asociadas en mayores proporciones.

for (i in 9:10) {
  
  nombre_col <- names(df)[i]
  
  df_summary <- df %>%
    group_by(.data[[nombre_col]]) %>%
    summarise(
      pct_yes = mean(is_fit == "yes") * 100, 
      .groups = "drop"
    )
  
  fig <- ggplot(df_summary, aes(x = .data[[nombre_col]], y = pct_yes, fill = .data[[nombre_col]])) +
    geom_bar(stat = "identity", width = 0.8, color = "black") +  
    scale_fill_manual(values = c("orange", "lightblue")) +
    labs(
      title = paste(nombre_col, "vs is_fit"), x = "Categorias",
      y = "Porcentaje en buen estado de forma (%)",
      fill = nombre_col
    ) +
    theme_bw() +
    theme(plot.title = element_text(hjust = 0.5)) +
    geom_text(
      aes(label = sprintf("%.1f%%", pct_yes)),  
      vjust = 2,
      color = "black"
    ) +
    scale_y_continuous(limits = c(0, 100))  
  
  print(fig)
}

Interpretación:

Estos gráficos de barras comparan el porcentaje de personas en buen estado de forma (is_fit) con dos variables categóricas: género (gender) y si fuman (smokes).

El gráfico de barras muestra la relación entre fumar y estar en buena forma física. La barra naranja, que representa a los no fumadores (“no”), indica que el 53.3% de este grupo está en buena forma física. En contraste, la barra azul, que representa a los fumadores (“yes”), muestra que solo el 23.6% de ellos está en buena forma física. Esto sugiere que hay una relación negativa entre fumar y el buen estado de forma, ya que el porcentaje de personas en buena forma física es significativamente más alto entre los no fumadores que entre los fumadores.

El gráfico de barras muestra una clara relación entre fumar y el estado de forma física. El 53.3% de las personas que no fuman están en buen estado de forma, mientras que solo el 23.6% de las personas que sí fuman lo están. Esto indica que hay una asociación negativa significativa: la probabilidad de estar en buen estado físico es más del doble entre los no fumadores que entre los fumadores.


Se generan graficos de barras para analizar la relación entre variables númericas discretas y el estado de forma en la población estudiada. Con el fin de identificar qué variables presentan mayores proporciones.

for (i in 1:3) {
  nombre_col <- names(df)[i]  
  
  min_val <- floor(min(df[[nombre_col]]))
  max_val <- ceiling(max(df[[nombre_col]]))
  breaks <- seq(min_val, max_val, by = 5)
  
  if (max_val > tail(breaks, 1)) {
    breaks <- c(breaks, tail(breaks, 1) + 5)
  }
  
  df_summary <- df %>%
    mutate(
      grupo = cut(
        .data[[nombre_col]],
        breaks = breaks,
        include.lowest = TRUE,
        right = FALSE,
        labels = paste0(breaks[-length(breaks)], "-", breaks[-1])
      )
    ) %>%
    group_by(grupo) %>%
    summarise(
      pct_yes = mean(is_fit == "yes") * 100,
      .groups = "drop"
    ) %>%
    mutate(
      centro = sapply(strsplit(as.character(grupo), "-"), function(x) mean(as.numeric(x)))
    )
  

  fig <- ggplot(df_summary, aes(x = grupo, y = pct_yes)) +

    geom_col(fill = "lightblue", color = "white", width = 0.7) +

    geom_line(aes(group = 1), color = "orange", linewidth = 1) +  
    labs(
      title = paste(nombre_col,"vs is_fit"),
      x = "Rangos de 5 unidades",
      y = "Porcentaje en buen estado de forma(%)"
    ) +
    theme_bw() +
    theme(
      plot.title = element_text(hjust = 0.5),
      axis.text.x = element_text(angle = 45, hjust = 1)
    ) +
    scale_y_continuous(limits = c(0, 100))
  
  print(fig)
}

Interpretación:

  • El gráfico revela una relación inversa entre la edad y el estado de forma. El porcentaje de personas en buen estado físico es más alto en los rangos de edad jóvenes (cerca del 60% entre 18-23 años) y disminuye progresivamente a medida que la edad aumenta (cayendo a menos del 25% entre 78-83 años).

  • La línea de tendencia naranja se mantiene relativamente plana y no muestra una disminución o aumento constante. El porcentaje de personas en buen estado de forma fluctúa entre un 30% y 45% en casi todos los rangos de altura. Esto sugiere que no existe una relación significativa entre la altura y la probabilidad de estar en buen estado de forma física.

  • El gráfico de peso vs. estado de forma muestra una relación curvilínea. La probabilidad de estar en forma es baja en los pesos extremos (muy bajos o muy altos). El porcentaje de personas en buen estado de forma aumenta hasta alcanzar un pico entre los 60 y 95 kg, donde se encuentra cerca del 50%, y luego disminuye a medida que el peso se incrementa.


Se generan histogramas para analizar la relación entre variables númericas continuas y el estado de forma en la población estudiada. Con el fin de identificar qué variables presentan mayores proporciones.

for (i in 4:8) {
  nombre_col <- names(df)[i]
  
  df_filtered <- df %>% filter(!is.na(.data[[nombre_col]]))
  
  fig <- ggplot(df_filtered, aes(x = .data[[nombre_col]], fill = is_fit)) +
    geom_histogram(aes(y = after_stat(count)/sum(after_stat(count))*100),
                 position = "identity", bins = 30, color = "white") +
    labs(title = paste(nombre_col, "vs is_fit"), 
         x = "Categorias", y = "Porcentaje (%)") +
    theme_bw() +
    theme(plot.title = element_text(hjust = 0.5))
  
  print(fig)
}

Interpretación:

  • El gráfico muestra que existe una relación inversa entre el ritmo cardíaco y el estado de forma. Las personas en buen estado físico (“yes”) se concentran en los rangos de ritmo cardíaco más bajos (por debajo de 80), mientras que a medida que el ritmo cardíaco aumenta, el porcentaje de personas en buen estado de forma disminuye considerablemente.

  • El gráfico muestra que existe una relación inversa entre la presión sanguínea y el estado de forma física. Las personas en buen estado físico (“yes”) se concentran en los rangos de presión sanguínea más bajos y normales. A medida que la presión sanguínea aumenta, el porcentaje de personas en buen estado de forma disminuye notablemente, siendo más predominantes las personas “no” en forma.

  • El gráfico muestra que existe una relación positiva entre las horas de sueño y el estado de forma física. El mayor porcentaje de personas en buen estado físico (“yes”) se concentra en quienes duermen entre 7 y 8 horas. Los porcentajes de personas en buen estado físico disminuyen drásticamente en los extremos, es decir, en quienes duermen muy poco o demasiado.

  • El gráfico muestra que existe una relación positiva entre la calidad de la nutrición y el estado de forma física. Las personas con baja calidad nutricional están predominantemente “no” en forma, mientras que las que tienen alta calidad nutricional están en su mayoría “yes” en forma. Esto indica que una mejor nutrición está directamente asociada a un mejor estado físico.

  • El gráfico muestra una fuerte relación positiva entre el índice de actividad y el estado de forma física. A medida que el índice de actividad aumenta, el porcentaje de personas en buen estado de forma también lo hace. Las personas con un índice de actividad bajo están predominantemente “no” en forma, mientras que aquellas con un índice alto están en su mayoría “yes” en forma.

Análisis multivariado

El análisis multivariado nos permite examinar simultáneamente las relaciones entre múltiples variables, revelando patrones complejos que no son evidentes en los análisis univariados o bivariados.


Se genera un heatmap de correlación de Pearson el cual nos permitira visualizar relaciones de dos o mas variables de manera compacta, destacando asociaciones lineales fuertes (positivas o negativas) entre las variables numéricas del dataset, a demas, nos permitira detectar posibles multicolinealidades y o entender la estructura de los datos.

df_cor <- df %>%
  select(age, height_cm, weight_kg, heart_rate, 
         blood_pressure, sleep_hours, 
         nutrition_quality, activity_index, is_fit) %>%
  mutate(is_fit = ifelse(is_fit == "yes", 1, 0))

cor_matrix <- cor(df_cor, use = "complete.obs")

ggplot(melt(cor_matrix), aes(Var1, Var2, fill = value)) +
  geom_tile(color = "white", linewidth = 0.7) +
  geom_text(aes(label = round(value, 2)), 
            color = "black", size = 3.5) +
  scale_fill_gradient2(low = "blue", high = "red", mid = "white",
                      midpoint = 0, limit = c(-1, 1)) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1),
        panel.grid = element_blank()) +
  labs(title = "Matriz de Correlación",
       x = "",
       y = "",
       fill = "Correlación") +
  coord_fixed()

Interpretación

La relación más fuerte con el estado de forma física (is_fit) se da con las siguientes variables:

  • Correlación positiva: Las variables que tienen la relación positiva más fuerte con estar en buena forma física son activity_index (0.34) y nutrition_quality (0.26). Esto significa que un mayor índice de actividad y una mejor calidad de la nutrición están asociados con una mayor probabilidad de estar en buena forma.
  • Correlación negativa: La variable que tiene la relación negativa más fuerte con estar en buena forma física es la edad (-0.21). Esto significa que, a mayor edad, la probabilidad de estar en buena forma física disminuye.

Las demás variables (altura, peso, ritmo cardíaco, horas de sueño y presión sanguínea) muestran correlaciones muy débiles con el estado de forma física, con valores cercanos a cero. Esto indica que no hay una relación lineal significativa entre estas variables y si una persona está en buena forma física o no.

Conclusiones

A continuación se presenta un resumen de los hallazgos principales del análisis estadístico y de gráficos:

Análisis de Distribución y Dispersión:

  • Las variables de edad, altura, ritmo cardíaco y presión sanguínea mostraron una distribución relativamente plana y simétrica (platicúrtica).
  • El peso tuvo una ligera asimetría positiva.
  • Las horas de sueño tuvieron una distribución más concentrada y simétrica (mesocúrtica).
  • La dispersión fue baja para el ritmo cardíaco, la presión sanguínea y las horas de sueño, pero moderada-alta para la calidad de la nutrición y el índice de actividad.

Relación con el Estado de Forma Física (is_fit):

  • Correlaciones fuertes y significativas:
  • Positiva: El estado de forma se asocia fuertemente con el índice de actividad y la calidad de la nutrición.
  • Negativa: El estado de forma se asocia negativamente con la edad y el consumo de tabaco.
  • Relaciones no lineales:
  • El peso tiene una relación curvilínea: la mejor forma física se encuentra en un rango de peso intermedio.
  • El ritmo cardíaco y la presión sanguínea tienen una relación inversa; valores más bajos se asocian con un mejor estado de forma.
  • Las horas de sueño tienen una relación positiva; la mayor probabilidad de estar en forma se encuentra en el rango óptimo de 7-8 horas.
  • La altura no mostró una relación significativa con el estado de forma.

En resumen, los factores más importantes para un buen estado de forma física son un alto índice de actividad, una buena nutrición, un peso en el rango óptimo y la ausencia de tabaquismo.