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.


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                80               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 (80 ú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)

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   : 83.54  
##  3rd Qu.:65.00   3rd Qu.:187.0   3rd Qu.:102.00  
##  Max.   :79.00   Max.   :199.0   Max.   :250.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 250 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: 6.72"
## [1] "Skew: 1.43"

Interpretación de las Variables Númericas Discretas:

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

  • En el caso de age, la distribución es relativamente uniforme a lo largo del rango de edades, con un sesgo cercano a cero (-0.0378) y una curtosis negativa (-1.1698) que indica colas más ligeras que las de una distribución normal.

  • Para height_cm, también se observa una distribución homogénea con un sesgo prácticamente nulo (0.0103) y una curtosis negativa (-1.1921), lo que sugiere una menor concentración en los extremos y mayor dispersión en torno a la media.

  • En cambio, weight_kg presenta un patrón distinto, con un sesgo positivo (1.4291) que indica una concentración de valores hacia el extremo inferior y una cola más larga hacia la derecha, lo que coincide con la presencia de valores atípicos altos; además, su curtosis positiva elevada (6.7219) revela colas pesadas y una mayor presencia de valores extremos en comparación con la normalidad.

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.


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 1:3) {
  
  nombre_col = names(df)[i]
  media = mean(df[[nombre_col]])
  desviacion = sd(df[[nombre_col]])
  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") + 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:  36.5 %"

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

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

Interpretación de las Variables Númericas Discretas:

  • En el caso de la variable age, con un coeficiente de variación del 36.5%, se observa una dispersión moderada respecto a su media, sin presencia aparente de valores atípicos, lo que indica una distribución relativamente homogénea.

  • Para height_cm, el coeficiente de variación es de 8.23%, lo que evidencia una variabilidad baja y una mayor concentración de los valores en torno al promedio, sin indicios de outliers.

  • En contraste, weight_kg presenta un coeficiente de variación de 30.95%, revelando una dispersión moderada-alta, además de la presencia de varios valores atípicos hacia el extremo superior, lo que sugiere la existencia de casos particulares con pesos significativamente mayores al resto de la población.

Estos patrones de dispersión y variabilidad son relevantes para comprender la homogeneidad y las posibles anomalías dentro de cada variable antes de realizar análisis estadísticos más complejos.

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.000   Min.   : 0.000   
##  1st Qu.: 62.10   1st Qu.:109.7   1st Qu.: 6.500   1st Qu.: 2.547   
##  Median : 70.25   Median :120.0   Median : 7.500   Median : 5.065   
##  Mean   : 70.29   Mean   :119.9   Mean   : 7.513   Mean   : 5.035   
##  3rd Qu.: 78.42   3rd Qu.:129.8   3rd Qu.: 8.600   3rd Qu.: 7.470   
##  Max.   :118.60   Max.   :171.2   Max.   :12.000   Max.   :10.000   
##                                   NA's   :160                       
##  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:

  • En cuanto a la frecuencia cardíaca (heart_rate) se ubica entre 45 y 118.6 latidos por minuto, siendo más habitual encontrar valores entre 62.1 y 78.4 lpm.

  • La presión arterial (blood_pressure) oscila entre 90 y 171.2 mmHg, concentrándose principalmente entre 109.7 y 129.8 mmHg.

  • En cuanto a las horas de sueño (sleep_hours), se registran entre 4 y 12 horas diarias, con la mayoría de los casos entre 6.5 y 8.6 horas, aunque con 160 valores perdidos.

  • La calidad de la nutrición (nutrition_quality) varía de 0 a 10 puntos, con mayor frecuencia entre 2.547 y 7.470 puntos.

  • Finalmente, el índice de actividad física (activity_index) presenta valores entre 1 y 4.99, siendo más habitual encontrarlos entre 2.038 y 3.950 puntos.

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.13"
## [1] "Skew: 0.15"

## [1] "Kurtosis: -0.27"
## [1] "Skew: 0.1"

## [1] "Kurtosis: -0.21"
## [1] "Skew: -0.02"

## [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.

  • Frecuencia Cardíaca (heart_rate): La distribución de la frecuencia cardíaca se asemeja a una campana de Gauss (distribución normal). Esto se evidencia por la concentración de datos en el centro y una disminución gradual hacia los extremos. Con un valor de asimetría (Skew) de 0.15, la distribución está ligeramente sesgada a la derecha, lo que significa que hay una pequeña “cola” de valores más altos. La curtosis de -0.13, cercana a cero, indica una curva mesocúrtica (similar a la normal), lo que sugiere que la forma del pico y las colas no son inusualmente pronunciadas ni planas.

  • Presión Arterial (blood_pressure): Al igual que la frecuencia cardíaca, esta variable muestra una distribución que se aproxima a una distribución normal. El coeficiente de asimetría de 0.1 es muy cercano a cero, indicando una distribución casi simétrica. La curtosis de -0.27 también está cerca de cero, lo que refuerza la idea de una forma de campana normal (mesocúrtica), sin picos ni colas particularmente extremos.

  • Horas de Sueño (sleep_hours): La distribución de las horas de sueño es una campana de Gauss bastante clara, con la mayoría de las personas durmiendo entre 6 y 8 horas. El coeficiente de asimetría de -0.02 es extremadamente cercano a cero, lo que indica una distribución prácticamente simétrica. La curtosis de -0.21 también sugiere una distribución mesocúrtica, similar a la normal.

  • Índice de Actividad (activity_index): El histograma del índice de actividad muestra una distribución más uniforme, sin un pico central claro. Los datos se distribuyen de manera más equitativa a lo largo de las categorías. Un coeficiente de asimetría (Skew) de 0.04 está muy cerca de cero, lo que confirma que la distribución es simétrica. Sin embargo, la curtosis de -1.16 es notablemente negativa, lo que indica una curva platicúrtica. Esto significa que la distribución es más “plana” que una distribución normal, con menos datos concentrados en el centro y colas más ligeras.

Calidad Nutricional (nutrition_quality): Al igual que el índice de actividad, esta variable presenta una distribución que se acerca a una distribución uniforme. El coeficiente de asimetría de 0.01 indica una simetría casi perfecta. La curtosis de -1.2, al ser un valor negativo y relativamente grande en magnitud, confirma que la distribución es platicúrtica; esto quiere decir que los datos están distribuidos de manera más homogénea a lo largo de todo el rango, sin una alta concentración en un punto específico.

En resumen, las variables de frecuencia cardíaca, presión arterial y horas de sueño siguen una distribución que se aproxima a la normal (con una forma de campana y una simetría alta), lo que sugiere que la mayoría de los individuos se encuentran en un rango promedio. Por otro lado, el índice de actividad y la calidad nutricional tienen distribuciones más planas o uniformes (platicúrticas), indicando una mayor variabilidad y una ausencia de un valor central predominante en la población estudiada.


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.85 %"

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

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

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

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

Interpretación de las Variables Númericas Continuas:

  • Presión Arterial (12.16%): Muestra la menor variabilidad. Los datos están muy concentrados, lo que podría indicar una población con niveles de presión arterial bastante estables. Sin embargo, los valores atípicos sugieren que hay individuos con lecturas inusualmente altas.

  • Frecuencia Cardíaca (16.85%): También presenta una dispersión baja. La mayoría de los datos se encuentran en un rango estrecho, pero al igual que la presión arterial, hay valores atípicos que podrían ser de personas con frecuencias cardíacas más elevadas de lo normal.

  • Horas de Sueño (19.99%): Presenta una dispersión moderada, lo cual es de esperar ya que los patrones de sueño pueden variar considerablemente entre individuos. El gráfico no muestra atípicos, lo que sugiere que todos los participantes tienen un rango de horas de sueño dentro de una distribución relativamente normal.

  • Índice de Actividad (37.89%): Tiene una dispersión alta, lo que indica que el nivel de actividad física es muy diverso en la población estudiada, con personas que varían mucho en su nivel de ejercicio.

  • Calidad Nutricional (56.88%): Es la variable con la mayor dispersión. Este alto coeficiente de variación sugiere una heterogeneidad significativa en los hábitos alimenticios o en la calidad de la nutrición dentro de la muestra, con algunos individuos teniendo una dieta mucho mejor que otros. El gráfico de caja muestra un rango intercuartílico amplio, reflejando esta variabilidad.

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 primer gráfico (smokes vs is_fit) ilustra una correlación significativa entre el hábito de fumar y el estado de forma. Un impresionante 53.3% de las personas que no fuman están en buen estado de forma. Por el contrario, este porcentaje cae drásticamente a solo el 23.6% entre las personas que sí fuman. Esta diferencia subraya la fuerte asociación negativa entre el tabaquismo y el estado físico, donde las personas que no fuman tienen más del doble de probabilidades de estar en buen estado de forma que las que sí lo hacen.

El segundo gráfico (gender vs is_fit) muestra que el 47.2% de los hombres (M) están en buen estado de forma, en comparación con solo el 33.1% de las mujeres (F). Esto indica una diferencia modesta, sugiriendo que, dentro de esta muestra, los hombres tienen una mayor probabilidad de estar en buen estado de forma física que las mujeres.

La muestra analizada indica que los hombres están en mejor estado físico que las mujeres y, de manera más contundente, las personas que no fuman tienen un estado de forma significativamente mejor que las personas fumadoras.


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 age vs is_fit muestra una tendencia descendente en el porcentaje de personas en buen estado de forma a medida que aumenta la edad. Las personas más jóvenes, en el rango de 18-23 años, tienen el porcentaje más alto de buen estado de forma, superando el 60%. Este porcentaje disminuye progresivamente hasta alcanzar su punto más bajo en los rangos de edad más avanzada, lo que sugiere que la capacidad física o la dedicación al ejercicio tiende a disminuir con los años.

  • El gráfico weight_kg vs is_fit ilustra una relación interesante con el peso. Se observa que el porcentaje de personas en buen estado de forma aumenta significativamente en los rangos de peso intermedios, alcanzando su pico entre los 60 y 95 kg. Sin embargo, en los rangos de peso más bajos (30-40 kg) y más altos (100+ kg), el porcentaje de personas en buen estado de forma disminuye considerablemente, lo que podría indicar que tanto el bajo peso como el sobrepeso/obesidad están asociados negativamente con el buen estado físico.

  • El gráfico height_cm vs is_fit muestra una tendencia poco pronunciada de aumento en el porcentaje de personas en buen estado de forma a medida que la altura incrementa. A partir de los 170 cm, el porcentaje se mantiene relativamente alto y estable, alcanzando su punto máximo en los rangos de altura de 180-195 cm.

En resumen, el análisis de las variables demográficas y antropométricas revela que el buen estado de forma está más asociado con los individuos más jóvenes, con un peso en el rango intermedio y una altura superior al promedio. Estas tendencias sugieren la importancia de la edad, el peso y la altura como factores predictivos del estado físico en la población estudiada.


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:

  • Frecuencia Cardíaca (heart_rate): En el gráfico heart_rate vs is_fit, se observa que el porcentaje de personas en buen estado de forma (color azul) es significativamente mayor en el rango de frecuencias cardíacas más bajas, especialmente entre las categorías de 50 y 80. A medida que la frecuencia cardíaca aumenta, el porcentaje de personas en buen estado de forma disminuye, y el porcentaje de personas que no están en forma (color salmón) se hace dominante. Esto sugiere una fuerte asociación inversa: una frecuencia cardíaca cercana al promedio está relacionada con un mejor estado físico.

  • Presión Arterial (blood_pressure): El gráfico blood_pressure vs is_fit muestra un patrón similar. Las personas en buen estado de forma (azul) representan un porcentaje mayor en los rangos de presión arterial más bajos (alrededor de 100-120). A partir de la categoría 125, el porcentaje de personas que no están en buen estado de forma (salmón) comienza a aumentar y se vuelve dominante en los rangos de presión arterial más altos. Esto indica que una presión arterial cercana al promedio se asocia positivamente con un buen estado de forma.

  • Horas de Sueño (sleep_hours): En el gráfico sleep_hours vs is_fit, el porcentaje de personas en buen estado de forma (azul) es más alto en las categorías de horas de sueño intermedias, especialmente alrededor de 7 a 9 horas. En los extremos, tanto en rangos de sueño muy bajos (4-5 horas) como en rangos altos (10-12 horas), el porcentaje de personas que no están en buen estado de forma (salmón) aumenta. Esto sugiere que dormir una cantidad adecuada de horas es más propicio para un buen estado físico que dormir muy poco o en exceso.

  • Índice de Actividad (activity_index): El gráfico activity_index vs is_fit muestra una correlación muy clara. El porcentaje de personas en buen estado de forma (azul) aumenta drásticamente a medida que el índice de actividad sube, especialmente a partir de la categoría 3. En las categorías de índice de actividad más bajas (1 y 2), la gran mayoría de las personas no están en buen estado de forma. Esto establece una fuerte relación directa: a mayor índice de actividad, mayor es la probabilidad de estar en buen estado de forma.

  • Calidad Nutricional (nutrition_quality): Por último, el gráfico nutrition_quality vs is_fit revela una tendencia similar a la del índice de actividad. El porcentaje de personas en buen estado de forma (azul) incrementa notablemente a medida que la calidad nutricional mejora (índices más altos). En los rangos más bajos (0 a 3), el porcentaje de personas en buen estado de forma es mínimo, mientras que en los rangos más altos (8 a 10) se convierte en la categoría predominante. Esto demuestra una clara asociación positiva: una mejor calidad nutricional está fuertemente ligada a un buen estado físico.

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

  • Estado físico (is_fit): En el gráfico de correlaciones se observa una correlación positiva moderada (0.35) entre (is_fit) y (activity_index), así como una correlación positiva moderada (0.26) entre (is_fit) y (nutrition_quality). Esto sugiere que las personas con un mayor nivel de actividad física y una mejor calidad nutricional tienden a ser clasificadas como fit. Asimismo, se aprecia una correlación negativa débil (-0.22) con (age), lo que indica que, a mayor edad, existe una ligera tendencia a presentar un menor estado físico.

  • (Correlaciones casi nulas:): Horas de sueño (sleep_hours), presión arterial (blood_pressure), frecuencia cardiaca (heart_rate), peso (weight_kg) y altura (height_cm) presentan correlaciones muy cercanas a 0 con la mayoria de las variables, por lo que no hay relaciones lineales fuertes entre ellas.

Conclusiones

Calidad de los datos

  • Se corrigieron inconsistencias en (smokes) (unificada a “yes”/“no”) y (is_fit) (convertida a factor).

Sleep_hours

  • Presenta 8% de valores faltantes; demás variables completas.

Distribución de variables clave

  • (is_fit) está desbalanceada (60% “no” vs 40% “yes”), lo que podría afectar modelos predictivos.

  • (Weight_kg) muestra sesgo positivo (+1.43) y alta curtosis (6.72), con valores atípicos en pesos altos.

  • Variables como (heart_rate), (blood_pressure) y (sleep_hours) siguen distribución normal, mientras (activity_index) y (nutrition_quality) son más uniformes.

Relaciones relevantes con el target (is_fit)

  • Fumadores (smokes=yes) tienen solo 23.6% de probabilidad de estar en forma vs 53.3% en no fumadores.

  • Hombres (gender=M) muestran mayor tasa de condición física (47.2%) que mujeres (33.1%).

  • (activity_index) y (nutrition_quality) correlacionan positivamente con is_fit (0.35 y 0.26), siendo los predictores más fuertes.

  • Edad avanzada y peso extremo (bajo/alto) reducen la probabilidad de estar en forma.

Hallazgos multivariados

  • Correlación positiva moderada (0.26) entre (activity_index) y (nutrition_quality), sugiriendo que actividad física y buena alimentación coexisten.

  • Variables como (blood_pressure) y (sleep_hours) no muestran correlación lineal fuerte con la variable objetivo, pero su distribución indica que valores extremos se asocian a menor condición física.