Enunciat:
Carregueu el fitxer de dades “Findata.csv”.
Resposta:
fin_df <- fread(
"data/Findata.csv", # Camí al fitxer
dec = ".", # Caràcter decimal
data.table = FALSE # Data frame de R (no data.table)
)Enunciat:
Comproveu els tipus de dades de les variables.
Resposta:
## V1 Age
## "integer" "numeric"
## Gender Weight..kg.
## "character" "character"
## Height..m. Max_BPM
## "character" "numeric"
## Avg_BPM Resting_BPM
## "numeric" "numeric"
## Session_Duration..hours. Calories_Burned
## "numeric" "numeric"
## Workout_Type Fat_Percentage
## "character" "numeric"
## Water_Intake..liters. Workout_Frequency..days.week.
## "numeric" "numeric"
## Experience_Level BMI
## "integer" "numeric"
## Daily.meals.frequency Physical.exercise
## "numeric" "numeric"
## Carbs Proteins
## "numeric" "numeric"
## Fats Calories
## "numeric" "integer"
## meal_name meal_type
## "character" "character"
## diet_type sugar_g
## "character" "numeric"
## sodium_mg cholesterol_mg
## "numeric" "numeric"
## serving_size_g cooking_method
## "numeric" "character"
## prep_time_min cook_time_min
## "numeric" "numeric"
## rating Name.of.Exercise
## "numeric" "character"
## Sets Reps
## "numeric" "numeric"
## Benefit Burns.Calories..per.30.min.
## "character" "numeric"
## Target.Muscle.Group Equipment.Needed
## "character" "character"
## Difficulty.Level Body.Part
## "character" "character"
## Type.of.Muscle Workout
## "character" "character"
## BMI_calc cal_from_macros
## "numeric" "numeric"
## pct_carbs protein_per_kg
## "numeric" "numeric"
## pct_HRR pct_maxHR
## "numeric" "numeric"
## cal_balance lean_mass_kg
## "numeric" "numeric"
## expected_burn Burns.Calories..per.30.min._bc
## "numeric" "character"
## Burns_Calories_Bin
## "character"
Enunciat:
Elimineu els punts al final dels noms de les variables.
Resposta:
# Índexs dels noms de les variables amb punt al final
replacement_indexes <- which(grepl("\\.$", names(fin_df)))
# Mostra els noms de les variables abans d'eliminar el punt al final
names(fin_df)[replacement_indexes]## [1] "Weight..kg." "Height..m."
## [3] "Session_Duration..hours." "Water_Intake..liters."
## [5] "Workout_Frequency..days.week." "Burns.Calories..per.30.min."
# Elimina el punt al final dels noms de les variables
names(fin_df)[replacement_indexes] <- gsub(
"\\.$", "", names(fin_df)[replacement_indexes]
)
# Mostra els noms de les variables després d'eliminar el punt al final
names(fin_df)[replacement_indexes]## [1] "Weight..kg" "Height..m"
## [3] "Session_Duration..hours" "Water_Intake..liters"
## [5] "Workout_Frequency..days.week" "Burns.Calories..per.30.min"
Enunciat:
Substituïu els punts dins del nom de la variable per un guió baix. Si hi ha dos punts seguits, reemplaceu-los per un únic guió baix.
Resposta:
# Índexs dels noms de les variables amb un o dos punts al mig
replacement_indexes <- which(grepl("\\.{1,2}", names(fin_df)))
# Mostra els noms de les variables abans de la substitució
names(fin_df)[replacement_indexes]## [1] "Weight..kg" "Height..m"
## [3] "Session_Duration..hours" "Water_Intake..liters"
## [5] "Workout_Frequency..days.week" "Daily.meals.frequency"
## [7] "Physical.exercise" "Name.of.Exercise"
## [9] "Burns.Calories..per.30.min" "Target.Muscle.Group"
## [11] "Equipment.Needed" "Difficulty.Level"
## [13] "Body.Part" "Type.of.Muscle"
## [15] "Burns.Calories..per.30.min._bc"
# Fes la substitució
names(fin_df)[replacement_indexes] <- gsub(
"\\.{1,2}", "_", names(fin_df)[replacement_indexes],
perl = TRUE
)
# Mostra els noms de les variables després de la substitució
names(fin_df)[replacement_indexes]## [1] "Weight_kg" "Height_m"
## [3] "Session_Duration_hours" "Water_Intake_liters"
## [5] "Workout_Frequency_days_week" "Daily_meals_frequency"
## [7] "Physical_exercise" "Name_of_Exercise"
## [9] "Burns_Calories_per_30_min" "Target_Muscle_Group"
## [11] "Equipment_Needed" "Difficulty_Level"
## [13] "Body_Part" "Type_of_Muscle"
## [15] "Burns_Calories_per_30_min__bc"
Enunciat:
Substituïu els guions baixos seguits per un únic guió baix.
Resposta:
# Índexs dels noms de les variables amb dos o més guions baixos seguits
replacement_indexes <- which(grepl("_{2,}", names(fin_df)))
# Mostra els noms de les variables abans de la substitució
names(fin_df)[replacement_indexes]## [1] "Burns_Calories_per_30_min__bc"
# Fes la substitució
names(fin_df)[replacement_indexes] <- gsub(
"_{2,}", "_", names(fin_df)[replacement_indexes],
perl = TRUE
)
# Mostra els noms de les variables després de la substitució
names(fin_df)[replacement_indexes]## [1] "Burns_Calories_per_30_min_bc"
Enunciat:
Comproveu que no hi hagi dos guions baixos seguits.
Resposta:
## [1] FALSE
Weight_kg i Height_mConsulteu els tipus de dades de les variables Weight_kg
i Height_m. Si és necessari, apliqueu les transformacions
apropiades. Esbrineu possibles inconsistències en els valors de les
variables. En cas que existeixin inconsistències, corregiu-les. Definiu
les variables com numèriques
Nota: Si els valors de Weight_kg estan en lliures
s’han de passar a kg. Si els valors de Height_m estan en
peus i polzades s’han de passar a metres. Considereu els següents
factors de conversió: 1 kg = 2.20 lb i 1 mt = 39.37 polzades, 12
polzades = 1 peu.
Resposta:
## Weight_kg Height_m
## "character" "character"
# Conversió de Weight_kg de lliures a kg
fin_df$Weight_kg <- ifelse(
grepl("lb$", fin_df$Weight_kg),
as.numeric(gsub("lb$", "", fin_df$Weight_kg)) / 2.20,
fin_df$Weight_kg
)
# Conversió de Height_m de peus i polzades a metres
fin_df$Height_m <- ifelse(
grepl("'", fin_df$Height_m),
{
feet_inches <- strsplit(gsub('"$', "", fin_df$Height_m), "'")
sapply(feet_inches, function(x) {
feet <- as.numeric(x[1])
inches <- as.numeric(x[2])
total_inches <- (feet * 12) + inches
total_inches / 39.37
})
},
fin_df$Height_m
)
# Conversió de les variables Weight_kg i Height_m a numèriques
fin_df$Weight_kg <- as.numeric(fin_df$Weight_kg)
fin_df$Height_m <- as.numeric(fin_df$Height_m)
# Tipus de les variables Weight_kg i Height_m després de la conversió
sapply(fin_df[c("Weight_kg", "Height_m")], class)## Weight_kg Height_m
## "numeric" "numeric"
# Resum estadístic de les variables Weight_kg i Height_m
summary(fin_df[c("Weight_kg", "Height_m")])## Weight_kg Height_m
## Min. : 39.18 Min. :1.490
## 1st Qu.: 58.16 1st Qu.:1.620
## Median : 70.00 Median :1.710
## Mean : 73.90 Mean :1.723
## 3rd Qu.: 86.10 3rd Qu.:1.800
## Max. :130.77 Max. :2.010
Determineu el nombre de valors absents i el nombre de valors atípics
de les variables Weight_kg i Height_m.
Resposta:
# Nombre de valors absents
n_na_weight <- sum(is.na(fin_df$Weight_kg))
n_na_height <- sum(is.na(fin_df$Height_m))
kable(
data.frame(
Variable = c("Weight_kg", "Height_m"),
Valors_absents = c(n_na_weight, n_na_height)
),
caption = "Nombre de valors absents",
col.names = c("Variable", "Valors absents")
) |>
kable_styling(
"striped",
latex_options = c("striped", "scale_down", "repeat_header"),
position = "center",
full_width = FALSE
)| Variable | Valors absents |
|---|---|
| Weight_kg | 0 |
| Height_m | 0 |
Per determinar els valors atípics, primer relacionem l’altura i el pes amb l’índex de massa corporal (IMC), que es calcula com el pes en quilograms dividit per l’altura en metres al quadrat.
A través dels diagrames de caixa, visualitzem els valors atípics de l’IMC, de l’altura i del pes.
# Diagrama de caixa de l'IMC, de l'altura i del pes
par(mfrow = c(1, 3))
boxplot(
imc,
main = "Diagrama de caixa de l'IMC",
ylab = "IMC"
)
boxplot(
fin_df$Height_m,
main = "Diagrama de caixa de l'altura",
ylab = "Height_m (m)"
)
boxplot(
fin_df$Weight_kg,
main = "Diagrama de caixa del pes",
ylab = "Weight_kg (kg)"
)A continuació, calculem el nombre de valors atípics de l’IMC, de l’altura i del pes utilitzant el criteri de l’interval interquartílic (IQR). Un valor es considera atípic si és inferior a Q1 - 1.5 * IQR o superior a Q3 + 1.5 * IQR, on Q1 i Q3 són el primer i tercer quartil, respectivament.
En el cas de l’IMC:
q1 <- quantile(imc, 0.25, na.rm = TRUE)
q3 <- quantile(imc, 0.75, na.rm = TRUE)
iqr <- q3 - q1
lower_bound_imc <- q1 - 1.5 * iqr
upper_bound_imc <- q3 + 1.5 * iqr
# Nombre de valors atípics inferiors de l'IMC
n_lower_bound_imc <- sum(imc < lower_bound_imc, na.rm = TRUE)
# Nombre de valors atípics superiors de l'IMC
n_upper_bound_imc <- sum(imc > upper_bound_imc, na.rm = TRUE)En el cas del pes:
q1_weight <- quantile(fin_df$Weight_kg, 0.25, na.rm = TRUE)
q3_weight <- quantile(fin_df$Weight_kg, 0.75, na.rm = TRUE)
iqr_weight <- q3_weight - q1_weight
lower_bound_weight <- q1_weight - 1.5 * iqr_weight
upper_bound_weight <- q3_weight + 1.5 * iqr_weight
# Nombre de valors atípics inferiors del pes
n_lower_bound_weight <- sum(fin_df$Weight_kg < lower_bound_weight, na.rm = TRUE)
# Nombre de valors atípics superiors del pes
n_upper_bound_weight <- sum(fin_df$Weight_kg > upper_bound_weight, na.rm = TRUE)En el cas de l’altura:
q1_height <- quantile(fin_df$Height_m, 0.25, na.rm = TRUE)
q3_height <- quantile(fin_df$Height_m, 0.75, na.rm = TRUE)
iqr_height <- q3_height - q1_height
lower_bound_height <- q1_height - 1.5 * iqr_height
upper_bound_height <- q3_height + 1.5 * iqr_height
# Nombre de valors atípics inferiors de l'altura
n_lower_bound_height <- sum(fin_df$Height_m < lower_bound_height, na.rm = TRUE)
# Nombre de valors atípics superiors de l'altura
n_upper_bound_height <- sum(fin_df$Height_m > upper_bound_height, na.rm = TRUE)En la taula següent es mostra el nombre de valors atípics de l’IMC, de l’altura i del pes.
outliers_count <- data.frame(
Variable = c("IMC", "Weight_kg", "Height_m"),
lower_outliers = c(
n_lower_bound_imc, n_lower_bound_weight, n_lower_bound_height
),
upper_outliers = c(
n_upper_bound_imc, n_upper_bound_weight, n_upper_bound_height
)
)
kable(
outliers_count,
caption = "Nombre de valors atípics",
col.names = c(
"Variable", "Valors atípics inferiors", "Valors atípics superiors"
)
) |>
kable_styling(
"striped",
latex_options = c("striped", "scale_down", "repeat_header"),
position = "center",
full_width = FALSE
)| Variable | Valors atípics inferiors | Valors atípics superiors |
|---|---|---|
| IMC | 0 | 529 |
| Weight_kg | 0 | 152 |
| Height_m | 0 | 0 |
Podem acceptar que els homes cremen en promig més calories als entrenaments que les dones? Responeu a la pregunta utilitzant un nivell de confiança del 97,5%.
Nota:
S’han de realitzar els càlculs per comparar mitjanes manualment. No
es poden usar funcions de R que calculin directament el contrast com a
t.test o similar. Si que es poden usar funcions com
mean, sd, qnorm,
pnorm, qt i pt.
Seguiu els passos que es detallen a continuació.
Resposta:
Hipòtesis nul·la (H0): \(\mu_1 = \mu_2\): La mitjana de calories cremades pels homes és igual a la mitjana de calories cremades per les dones.
Hipòtesis alternativa (H1): \(\mu_1 > \mu_2\): La mitjana de calories cremades pels homes és major que la mitjana de calories cremades per les dones.
Resposta:
Contrast unilateral per la dreta de dues mostres independents sobre la mitjana, amb variàncies desconegudes. Segons el teorema del límit central, podem assumir normalitat quan fem un test sobre les mitjanes amb un nombre de mostres gran.
Per veure si podem assumir igualtat de variàncies, fem la prova F d’Snedecor (contrast d’homogeneïtat de variàncies):
alpha <- 0.025
# Subconjunts de calories cremades per homes i dones
calories_homes <- fin_df$Calories_Burned[fin_df$Gender == "Male"]
calories_dones <- fin_df$Calories_Burned[fin_df$Gender == "Female"]
# Variances de les dues mostres
mean1 <- mean(calories_homes)
mean2 <- mean(calories_dones)
# Nombre d'elements de les dues mostres
n1 <- length(calories_homes)
n2 <- length(calories_dones)
# Desviacions estàndard de les dues mostres
s1 <- sd(calories_homes)
s2 <- sd(calories_dones)
# F_observed
fobs <- s1^2 / s2^2
fcrit_l <- qf(alpha / 2, n1 - 1, n2 - 1)
fcrit_u <- qf(1 - alpha / 2, n1 - 1, n2 - 1)
# p-valor
pvalue <- min(
pf(fobs, df1 = n1 - 1, df2 = n2 - 2, lower.tail = FALSE),
pf(fobs, df1 = n1 - 1, df2 = n2 - 2)
) * 2
# Resultats de la prova F d'Snedecor
c(fobs, fcrit_l, fcrit_u, pvalue)## [1] 1.05041507 0.95615544 1.04585231 0.01393386
El valor de p és 0.0139, que és menor de 0.025, per tant rebutgem la hipòtesi nul·la i assumim que les variàncies són diferents. Caldrà fer el test amb variàncies diferents (t-test de Welch).
Realitzeu els càlculs de l’estadístic de contrast, valor crític i p valor a un nivell de confiança del 97,5%.
Resposta:
# Estadístic de contrast
tobs <- (mean1 - mean2) / sqrt((s1^2 / n1) + (s2^2 / n2))
# Graus de llibertat aproximats
df <- ((s1^2 / n1) + (s2^2 / n2))^2 /
(((s1^2 / n1)^2) / (n1 - 1) + ((s2^2 / n2)^2) / (n2 - 1))
# Valor crític
tcrit <- qt(1 - alpha, df)
# p-valor del test unilateral per la dreta
calories_burned_pt <- pt(tobs, df = df, lower.tail = FALSE)
# Resultats del test
c(tobs, tcrit, calories_burned_pt)## [1] 0.1481908 1.9600827 0.4410968
Off topic:
Comprovació amb les funcions d’R:
# test d'homoscedasticitat
var_test <- var.test(
calories_homes, calories_dones,
alternative = "two.sided",
conf.level = 0.975
)
c(var_test$statistic, var_test$p.value)## F
## 1.05041507 0.01393143
if (var_test$p.value < 0.025) {
var_equal <- FALSE # Les variàncies no són iguals, es fa el test de Welch
} else {
var_equal <- TRUE # Les variàncies són iguals,
}
# Comprovació amb t.test
t_test_result <- t.test(
calories_homes, calories_dones,
conf.level = 0.975,
alternative = "greater",
var.equal = var_equal
)
c(t_test_result$statistic, t_test_result$p.value)## t
## 0.1481908 0.4410968
Resposta:
El valor de p és 0.4411, que és un valor més gran de 0.025, per tant no rebutgem la hipòtesi nul·la. És a dir, no hi ha prou evidència per acceptar que els homes cremen més calories que les dones als entrenaments.
Volem investigar quines variables expliquen el valor de les calories
cremades (Calories_Burned). Estimeu un model de regressió
lineal múltiple que tingui com a variables explicatives:
Session_Duration_hours, Experience_Level,
Workout_Frequency_days_week, Height_m,
Weight_m, Gender i
Workout_Type.
Interpreteu el model lineal ajustat. Com és la qualitat de l’ajust?
Interpreteu breument la contribució de la variable
Experience_Level sobre la variable depenent.
Resposta:
# Càlcul del model de regressió lineal múltiple
model <- lm(
Calories_Burned ~ Session_Duration_hours + Experience_Level +
Workout_Frequency_days_week + Height_m + Weight_kg + Gender +
Workout_Type,
data = fin_df
)
# Resum del model
summary(model)##
## Call:
## lm(formula = Calories_Burned ~ Session_Duration_hours + Experience_Level +
## Workout_Frequency_days_week + Height_m + Weight_kg + Gender +
## Workout_Type, data = fin_df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -272.46 -49.96 -9.88 43.31 372.91
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -365.97518 9.80638 -37.320 <2e-16 ***
## Session_Duration_hours 1052.84626 2.79080 377.256 <2e-16 ***
## Experience_Level 59.07094 1.46821 40.233 <2e-16 ***
## Workout_Frequency_days_week 37.05314 1.10226 33.616 <2e-16 ***
## Height_m 14.47045 5.70921 2.535 0.0113 *
## Weight_kg 0.03321 0.03427 0.969 0.3325
## GenderMale 0.67295 1.35676 0.496 0.6199
## Workout_TypeHIIT 451.90977 1.92862 234.318 <2e-16 ***
## Workout_TypeStrength 151.98634 1.91970 79.172 <2e-16 ***
## Workout_TypeYoga -297.30110 1.92313 -154.592 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 95.92 on 19990 degrees of freedom
## Multiple R-squared: 0.9635, Adjusted R-squared: 0.9635
## F-statistic: 5.87e+04 on 9 and 19990 DF, p-value: < 2.2e-16
Gairebé tote les variables tenen un efecte significatiu en la
variable dependent (p-value < 0.05), excepte
GenderMale i Weight_kg.
El valor de R2 ajustat és 0.9635, un valor molt elevat, indica que el model explica un percentatge molt alt de la variabilitat de les calories cremades (96.35 %).
La variable predictora Experience_Level té un efecte
significatiu en la variable dependent (p-value < 0.05).
En concret, te un efecte positiu, és a dir, a mesura que augmenta el
nivell d’experiència, augmenten les calories cremades durant
l’entrenament (59.07 calories cremades per cada nivell
d’experiència).
Analitzeu possibles problemes de multicol·linealitat (alta correlació
entre variables explicatives) mitjançant la interpretació de la matriu
de correlacions de les variables explicatives quantitatives i del factor
d’inflació de la variància (vif). Es pot utilitzar la funció
vif de la llibreria car.
Resposta:
# Matriu de correlacions de les variables explicatives quantitatives
cor_matrix <- cor(
fin_df[c(
"Session_Duration_hours", "Workout_Frequency_days_week",
"Height_m", "Weight_kg"
)],
use = "complete.obs"
)
cor_matrix## Session_Duration_hours Workout_Frequency_days_week
## Session_Duration_hours 1.000000000 0.637626139
## Workout_Frequency_days_week 0.637626139 1.000000000
## Height_m 0.006781343 0.007252810
## Weight_kg -0.002275379 -0.003893683
## Height_m Weight_kg
## Session_Duration_hours 0.006781343 -0.002275379
## Workout_Frequency_days_week 0.007252810 -0.003893683
## Height_m 1.000000000 0.353758800
## Weight_kg 0.353758800 1.000000000
# Gràfica de la matriu de correlacions
corrplot(cor_matrix,
method = "circle",
diag = FALSE,
type = "upper",
tl.srt = 45,
tl.col = "black"
)En la gràfica de la matriu de correlacions, podem observar que no hi
ha cap parella de variables explicatives quantitatives amb una
correlació molt alta (per exemple, superior a 0.8 o inferior a -0.8), de
fet només es mostren dues correlacions directes moderades: entre
Session_Duration_hours i
Workout_Frequency_days_week (0.64) i entre
Height_m i Weight_kg (0.35). Per tant, no
sembla haver-hi problemes greus de multicol·linealitat entre les
variables explicatives quantitatives.
## GVIF Df GVIF^(1/(2*Df))
## Session_Duration_hours 1.972570 1 1.404482
## Experience_Level 2.282941 1 1.510941
## Workout_Frequency_days_week 2.192816 1 1.480816
## Height_m 1.143372 1 1.069286
## Weight_kg 1.144245 1 1.069694
## Gender 1.000395 1 1.000197
## Workout_Type 1.001246 3 1.000208
En quant al factor d’inflació de la variància (VIF), si en fixem en l’última columna tots els valors són inferiors a 5 (potencialment problemàtic) i propers a 1 (absència de col·linealitat). Això indica que no hi ha problemes de multicol·linealitat entre les variables.
Analitzeu gràficament els residus del model per comprovar la linealitat, l’homoscedasticitat, la normalitat i la presència d’outliers. A quina conclusió arribeu?
Resposta:
# Gràfics de diagnòstic del model
par(mfrow = c(1, 2))
plot(model, which = 1, main = "Model Fit")
plot(model, which = 2, main = "Q-Q Plot")La gràfica de “Residuals vs Fitted” mostra una clara desviació respecte a la línia horitzontal, especialment als extrems, on la corba vermella indica una tendència no lineal. Aquest comportament suggereix que la relació entre les variables explicatives i la variable resposta no és estrictament lineal. En quant a la homoscedasticitat, la mateixa gràfica mostra un patró en forma d’embut, on la variabilitat augmenta amb els valors ajustats. Aquest patró és típic d’heteroscedasticitat, és a dir, la variància dels errors no és constant.
En la gràfica “Q-Q Residuals”, al centre se segueix la diagonal, mentre que a les cues se separen una mica, sobretot a la dreta. Aquesta desviació indica que els residus no segueixen una distribució normal perfecta, especialment en els extrems.
La presència d’outliers es pot observar en les dues gràfiques. En la primera, hi ha punts que es troben lluny de la línia horitzontal, i en la segona, hi ha punts que es desvien significativament de la línia diagonal.
En conclusió, els gràfics de diagnòstic indiquen que el model de regressió lineal múltiple presenta problemes de no linealitat, heteroscedasticitat, desviació de la normalitat i presència d’outliers.
Ajusteu un model predictiu basat en la regressió logística per predir la probabilitat de ser una persona sense experiència en fitness.
A partir de la variable Experience_Level, classifiqueu
les persones sense experiència (valor 1) en comparació a la resta (valor
2 o 3). Com a regressors considereu les mateixes variables que en
apartat anterior. Nota: Calories_Burned ara és una variable
explicativa del model de regressió.
Mostreu el resultat del model i interpreteu-lo en termes de: quines són les variables significatives i com és la qualitat del model.
Resposta:
Primer creem la variable binària Without_Experience, que
val 1 si la persona no té experiència (Experience_Level = 1) i 0
altrament.
Després separem el conjunt de dades en un conjunt d’entrenament (70%) i un de prova (30%).
# Fixem el valor de la llavor per a la reproductibilitat
set.seed(42)
# Separació del conjunt de dades en train (70%) i test (30%) de forma aleatòria
train_indexes <- sample(1:nrow(fin_df), size = 0.7 * nrow(fin_df))
train_dataset <- fin_df[train_indexes, ]
test_dataset <- fin_df[-train_indexes, ]A continuació, ajustem el model de regressió logística.
# Model de regressió logística
logistic_model <- glm(
Without_Experience ~ Session_Duration_hours + Workout_Frequency_days_week +
Height_m + Weight_kg + Gender + Workout_Type + Calories_Burned,
data = train_dataset,
family = binomial(link = "logit")
)
# Resum del model
summary(logistic_model)##
## Call:
## glm(formula = Without_Experience ~ Session_Duration_hours + Workout_Frequency_days_week +
## Height_m + Weight_kg + Gender + Workout_Type + Calories_Burned,
## family = binomial(link = "logit"), data = train_dataset)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 8.1491979 0.3982147 20.464 < 2e-16 ***
## Session_Duration_hours 11.1644562 0.4630786 24.109 < 2e-16 ***
## Workout_Frequency_days_week -1.3038111 0.0429654 -30.346 < 2e-16 ***
## Height_m 0.0055454 0.2105665 0.026 0.978990
## Weight_kg -0.0040391 0.0011675 -3.459 0.000541 ***
## GenderMale -0.0247900 0.0498334 -0.497 0.618867
## Workout_TypeHIIT 6.3337820 0.2306999 27.455 < 2e-16 ***
## Workout_TypeStrength 2.1101334 0.1020544 20.677 < 2e-16 ***
## Workout_TypeYoga -4.1281999 0.1543039 -26.754 < 2e-16 ***
## Calories_Burned -0.0146002 0.0004959 -29.442 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 19314.9 on 13999 degrees of freedom
## Residual deviance: 9979.7 on 13990 degrees of freedom
## AIC: 9999.7
##
## Number of Fisher Scoring iterations: 6
Les variables significatives en el model de regressió logística són aquelles amb un valor de p menor a 0.05.
# Variables significatives (p < 0.05)
significant_vars <- summary(logistic_model)$coefficients[, "Pr(>|z|)"] < 0.05
# Elimina l'intercept
significant_vars <- significant_vars[names(significant_vars) != "(Intercept)"]
# Noms de les variables significatives
names(significant_vars)## [1] "Session_Duration_hours" "Workout_Frequency_days_week"
## [3] "Height_m" "Weight_kg"
## [5] "GenderMale" "Workout_TypeHIIT"
## [7] "Workout_TypeStrength" "Workout_TypeYoga"
## [9] "Calories_Burned"
Per avaluar la qualitat del model, podem utilitzar la corba ROC i l’àrea sota la corba (AUC). Un AUC proper a 1 indica un bon model, mentre que un AUC proper a 0.5 indica un model poc informatiu (entre 0,7 i 0,8 es considera acceptable, entre 0,8 i 0,9 és excel·lent, i superior a 0,9 és excepcional).
# Prediccions de probabilitats sobre el conjunt de test
prob_predictions <- predict(
logistic_model,
newdata = test_dataset,
type = "response"
)
# Corba ROC
roc_curve <- roc(test_dataset$Without_Experience, prob_predictions)
# Plot de la corba ROC i valor AUC
plot(roc_curve, print.auc = TRUE, main = "ROC Curve", legacy.axes = TRUE)
abline(a = 0, b = 1, lty = 2, col = "grey")És excepcional perquè té un AUC de 0.919. Això indica que el model té una bona capacitat de discriminació: distingeix força bé entre persones amb i sense experiència. En termes pràctics, quan el model compara dues persones a l’atzar (una amb experiència i l’altra sense), hi ha una alta probabilitat que assigni una probabilitat més alta de ser sense experiència a la persona que realment no té experiència.
A continuació analitzeu la precisió del model, comparant la predicció del model sobre les mateixes dades del conjunt de dades. Assumirem que la predicció del model és 1 (Sense experiència) si la probabilitat del model de regressió logística és superior o igual a 0.5 i 0 en cas contrari. Calculeu la matriu de confusió.
Interpreteu els resultats. Indiqueu els valors de sensibilitat i
especificitat i interpreteu-los. Es pot utilitzar funció confusionMatrix
de la llibreria Caret.
Resposta:
# Convertir les probabilitats a classes amb un llindar de 0.5
class_predictions <- ifelse(prob_predictions > 0.5, "Alt", "No Alt")
class_predictions <- factor(class_predictions, levels = c("No Alt", "Alt"))
# Convertir la referència a factor amb els mateixos nivells
reference_factor <- factor(
ifelse(test_dataset$Without_Experience == 1, "Alt", "No Alt"),
levels = c("No Alt", "Alt")
)
# Matriu de confusió
confusion_matrix <- confusionMatrix(
data = class_predictions,
reference = reference_factor,
positive = "Alt"
)
confusion_matrix## Confusion Matrix and Statistics
##
## Reference
## Prediction No Alt Alt
## No Alt 2145 568
## Alt 570 2717
##
## Accuracy : 0.8103
## 95% CI : (0.8002, 0.8202)
## No Information Rate : 0.5475
## P-Value [Acc > NIR] : <2e-16
##
## Kappa : 0.6172
##
## Mcnemar's Test P-Value : 0.9764
##
## Sensitivity : 0.8271
## Specificity : 0.7901
## Pos Pred Value : 0.8266
## Neg Pred Value : 0.7906
## Prevalence : 0.5475
## Detection Rate : 0.4528
## Detection Prevalence : 0.5478
## Balanced Accuracy : 0.8086
##
## 'Positive' Class : Alt
##
La precisió del model és del 0.8103, que indica que el model classifica correctament el 81.03 % de les persones en el conjunt de dades de prova. El valor de la sensibilitat és 0.8271, que indica la capacitat del model per identificar correctament les persones sense experiència. En aquest cas, el model identifica correctament el 82.71 % de les persones sense experiència. El valor de l’especificitat és 0.7901, que indica la capacitat del model per identificar correctament les persones amb experiència. En aquest cas, el model identifica correctament el 79.01 % de les persones amb experiència.
En conjunt, el model presenta un rendiment equilibrat i raonable per a una classificació binària amb un punt de tall del 50 %, ja que tant la sensibilitat com l’especificitat són altes, una mica més alta la sensibilitat, la qual cosa és desitjable en aquest context si es vol identificar correctament les persones sense experiència.
En aquest apartat analitzarem si existeixen diferències
significatives en la variable Calories_Burned en funció del
tipus d’entrenament (Workout_Type).
Visualitzar les dades per tipus d’entrenament amb un boxplot.
Resposta:
# Boxplot de Calories_Burned per Workout_Type
ggplot(fin_df, aes(x = Workout_Type, y = Calories_Burned)) +
geom_boxplot(fill = "lightblue", color = "darkblue") +
labs(
title = "Boxplot de Calories_Burned per Workout_Type",
x = "Tipus d'entrenament",
y = "Calories Cremades"
) +
theme_minimal()En els diagrames de caixa es pot observar que hi ha diferències en la
distribució de les calories cremades segons el tipus d’entrenament. Per
exemple, l’entrenament de tipus HIIT sembla tenir una
mediana més alta de calories cremades en comparació amb altres tipus
d’entrenament com Yoga, Cardio i
Strength. A més, hi ha una variabilitat diferent en les
calories cremades entre els diferents tipus d’entrenament, amb alguns
tipus mostrant una distribució més ampla que altres. HIIT
té una variabilitat més gran en les calories cremades, mentre que
Yoga sembla tenir una variabilitat més petita.
Escriviu la hipòtesi nul·la i l’alternativa.
Resposta:
Calculeu l’anàlisi de variància, fent servir la funció
aov o lm. Interpreteu el resultat de
l’anàlisi, tenint en compte els valors: Sum Sq,
Mean Sq, F i Pr(> F).
Resposta:
# Càlcul de l'ANOVA d'un factor
anova_model <- aov(Calories_Burned ~ Workout_Type, data = fin_df)
# Resum de l'ANOVA
summary(anova_model)## Df Sum Sq Mean Sq F value Pr(>F)
## Workout_Type 3 1.485e+09 494905823 2780 <2e-16 ***
## Residuals 19996 3.560e+09 178021
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
La suma de quadrats entre grups (Sum Sq) indica variabilitat
explicada per les diferències entre grups (Sum Sq del factor), i la
variabilitat no explicada: la variabilitat interna dins dels grups (Sum
Sq residual). Com més gran sigui Sum Sq del factor en
relació amb la total, més probable és que hi hagi diferències reals
entre grups. En aquest cas, la Sum Sq del factor és
1.484.717.469,03, mentre que la Sum Sq residual és
3.559.709.305,57. Això indica que una part significativa de la
variabilitat total en les calories cremades es pot atribuir a les
diferències entre els tipus d’entrenament.
Mean Sq és la mitjana de les sumes de quadrats, que es
calcula \(MS=\frac{Sum Sq}{df}\), on
\(df\) són els graus de llibertat
associats a cada component. En aquest cas, el
Mean Sq del factor \((MS_{Between})\) és 494.905.823,01, mentre
que la Mean Sq residual \((MS_{Within})\) és 178.021,07.
El valor de l’estadístic \(F=\frac{MS_{Between}}{MS_{Within}}\) és 2.780,04., com que aquest valor és gran, indica que hi ha una gran variabilitat entre els grups en comparació amb la variabilitat dins dels grups, altrament seria gairebé igual a 1.
El valor de p associat a l’estadístic F és \(<2e-16\), menor que 0.05, per tant rebutgem la hipòtesi nul·la i acceptem l’alternativa. Això indica que hi ha diferències significatives en les calories cremades segons el tipus d’entrenament.
Proporcioneu l’estimació de l’efecte dels nivells del factor
Workout_Type.
Resposta:
En el model ANOVA el valor p és petit (< 0.05), això indica necessitem el model complet amb els efectes dels nivells del factor per explicar les diferències en les calories cremades segons el tipus d’entrenament.
# Estimació dels efectes dels nivells del factor Workout_Type
effects <- model.tables(anova_model, "means")$tables$Workout_Type
# Mitjana global
mitjana_global <- mean(fin_df$Calories_Burned, na.rm = TRUE)
mitjana_global## [1] 1280.11
# Diferència entre la mitjana de cada grup i la mitjana global
efectes_df <- data.frame(
"Mitjana del grup" = as.numeric(effects),
"Diferència respecte mitjana global" = as.numeric(effects) - mitjana_global,
row.names = names(effects)
)
efectes_df## Mitjana.del.grup Diferència.respecte.mitjana.global
## Cardio 1211.5447 -68.56492
## HIIT 1652.5332 372.42361
## Strength 1361.4305 81.32086
## Yoga 897.1072 -383.00238
Interpreteu els resultats obtinguts en els apartats anteriors.
Resposta:
L’anàlisi de variància (ANOVA) indica que hi ha diferències significatives en les calories cremades segons el tipus d’entrenament realitzat. El valor de p és menor que 0.05, la qual cosa ens porta a rebutjar la hipòtesi nul·la i acceptar que almenys dos tipus d’entrenament són diferents en termes de calories cremades.
Els efectes estimats dels nivells del factor
Workout_Type mostren com cada tipus d’entrenament afecta
les calories cremades en comparació amb la mitjana global. Per exemple,
l’entrenament de tipus HIIT té un efecte positiu més elevat
(1.652,53), indicant que aquest tipus d’entrenament està associat amb un
augment significatiu en les calories cremades en comparació amb altres
tipus d’entrenament.
Mostreu visualment l’adequació del model ANOVA. Verificar els
supòsits de normalitat i homoscedasticitat. Podeu fer servir
plot sobre el model ANOVA calculat.
En cas de trobar diferències a les mitjanes, cal fer anàlisis post hoc on es fan les comparacions múltiples de tots els parells de mitjanes possibles. Si tinguéssim homogeneïtat de variàncies, prova de Scheffé si les variàncies no són iguals optar per la prova T2 de Tamhane.
Resposta:
# Gràfics de diagnòstic del model ANOVA
par(mfrow = c(1, 2))
plot(anova_model, which = 1, main = "Model Fit")
plot(anova_model, which = 2, main = "Q-Q Plot")En l’apartat anterior hem vist que hi ha diferències significatives en les calories cremades segons el tipus d’entrenament. Ara, per determinar quins tipus d’entrenament són diferents entre si, abans de realitzar les proves post hoc, hem de verificar els supòsits de normalitat i homoscedasticitat. Això s’analitzarà en els següents subapartats.
Interpreteu la normalitat dels residus a partir del gràfic Normal Q-Q que heu mostrat a l’apartat anterior i verifica amb una prova de contrast (Anderson-Darling). Tot i això, recordem que l’ANOVA és una prova robusta davant la violació del supòsit de normalitat si tenim n ≥ 30 gràcies al Teorema del Límit Central.
Resposta:
# Residus del model ANOVA
res <- residuals(anova_model)
# Prova d'Anderson-Darling per normalitat dels residus
ad_test <- ad.test(res)
ad_test##
## Anderson-Darling normality test
##
## data: res
## A = 199.49, p-value < 2.2e-16
El valor p de la prova d’Anderson-Darling és \(< 2.2e-16\), que és menor que 0.05, per tant rebutgem la hipòtesi nul·la i assumim que els residus no segueixen una distribució normal. A més, el valor de l’estadístic d’Anderson-Darling és 199,49, el qual és relativament alt, suggerint una desviació significativa de la normalitat. Aquesta conclusió és coherent amb el gràfic Q-Q, on es pot observar una desviació notable dels punts respecte a la línia diagonal, especialment en les cues de la distribució. Tot i això, com que tenim un nombre d’observacions gran (n > 30), l’ANOVA és robusta davant la violació del supòsit de normalitat gràcies al Teorema del Límit Central.
El gràfic “Residuals vs Fitted” proporciona informació sobre la homocedasticitat dels residus.
Mostreu, interpreteu aquest gràfic i verifiqueu amb una prova de contrast. Si no es compleix homocedasticitat, recorda que es recomana una ANOVA de Welch. L’ANOVA de Welch no assumeix la igualtat de variàncies i utilitza una correcció en els graus de llibertat per calcular el valor F i el valor p. Confirma amb la prova de contrast d’igualtat de variàncies de Levene.
Resposta:
El gràfic “Residuals vs Fitted”, el valor ajustat d’una observació és, en l’ANOVA d’un factor, la mitjana del grup al qual correspon l’observació. Per tant, per a totes les observacions que corresponen al nivell i, el valor ajustat és la mitjana del grup i. En aquest tipus de gràfic de residus apareixeran tires verticals de punts, una tira per cada nivell. Si la variància és constant, l’extensió vertical de les tires serà aproximadament la mateixa. Si la variància no és constant, apareixerà un patró en la dispersió vertical dels residus. És a dir, els nivells de tractament mostraran una disposició dels residus amb diferent dispersió vertical.
En aquest cas, el gràfic mostra clarament que les tires de punts tenen diferents extensions verticals, indicant que la variància no és constant entre els diferents tipus d’entrenament. Aquesta observació suggereix que el supòsit d’homoscedasticitat no es compleix, ja que la línia vermella no és horitzontal.
# Prova d'homogeneïtat de variàncies mitjançant el test de Bartlett
bartlett_test <- bartlett.test(
Calories_Burned ~ Workout_Type,
data = fin_df
)
bartlett_test##
## Bartlett test of homogeneity of variances
##
## data: Calories_Burned by Workout_Type
## Bartlett's K-squared = 1857.5, df = 3, p-value < 2.2e-16
El valor de p de la prova de Bartlett és 0, que és menor que 0.05, per tant rebutgem la hipòtesi nul·la i assumim que les variàncies no són iguals. Aquesta conclusió és coherent amb l’observació feta en el gràfic “Residuals vs Fitted”.
Per confirmar aquesta observació, realitzem la prova de Levene.
## Levene's Test for Homogeneity of Variance (center = median)
## Df F value Pr(>F)
## group 3 407.77 < 2.2e-16 ***
## 19996
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Df és el nombre de grups menys 1 per al factor i el
nombre total d’observacions menys el nombre de grups per als residus. El
valor de p és \(< 2.2e-16\), que és
menor que 0.05, per tant rebutgem la hipòtesi nul·la i assumim que les
variàncies no són iguals. Per tant, realitzem la prova T2 de Tamhane per
a les comparacions múltiples.
F és l’estadístic de la prova de Levene, que mesura la
variabilitat entre els grups en comparació amb la variabilitat dins dels
grups. Un valor alt de F indica que hi ha diferències significatives en
les variàncies entre els grups, mentre que un valor proper a 1 indica
que les variàncies són similars. En aquest cas, el valor de F és 407,77,
el qual és relativament alt, suggerint que hi ha diferències
significatives en les variàncies entre els diferents tipus
d’entrenament.
# Prova post hoc T2 de Tamhane
post_hoc_tamhane <- pairwise.t.test(
fin_df$Calories_Burned,
fin_df$Workout_Type,
p.adjust.method = "none",
pool.sd = FALSE # variàncies no iguals
)
post_hoc_tamhane##
## Pairwise comparisons using t tests with non-pooled SD
##
## data: fin_df$Calories_Burned and fin_df$Workout_Type
##
## Cardio HIIT Strength
## HIIT <2e-16 - -
## Strength <2e-16 <2e-16 -
## Yoga <2e-16 <2e-16 <2e-16
##
## P value adjustment method: none
Tots els valors de p són menors que 0.05, per tant hi ha diferències significatives entre tots els parells de tipus d’entrenament en termes de calories cremades.
Ara analitzarem si les calories cremades depenen del tipus
d’entrenament (Workout_Type), de tenir un índex de massa
corporal saludable i si existeix interacció entre aquests dos
factors.
En primer lloc es crearà una variable binària anomenada
BMI_bin que indiqui un índex de massa corporal alt si és
major a 25 i normal/baix si és igual o menor a 25.
També analitzarem si hi ha interacció entre aquests dos factors. Si no hi ha interacció entre els factors, cal crear un model sense interacció.
Seguiu els passos que s’indiquen a continuació.
BIM per a majors de 25 igual a AltResposta:
# Crear la variable binària BMI_bin
fin_df$BMI_bin <- ifelse(fin_df$BMI > 25, "Alt", "Normal/Baix")
fin_df$BMI_bin <- factor(fin_df$BMI_bin, levels = c("Normal/Baix", "Alt"))
# Diagrama de violí del BMI segons BMI_bin
ggplot(fin_df, aes(x = BMI_bin, y = BMI, fill = BMI_bin)) +
geom_violin(trim = FALSE) +
geom_boxplot(width = 0.1, position = position_dodge(0.9)) +
labs(
title = "Diagrama de violí del BMI segons el tipus BMI_bin",
x = "Categoria de BMI",
y = "Índex de Massa Corporal (BMI)"
) +
theme_minimal() +
theme(legend.position = "none")Dibuixeu un gràfic que permeti avaluar si hi ha interacció entre els dos factors. Feu un gràfic que mostri les calories mitjanes cremades segons tipus d’entrenament diferenciant entre aquelles persones amb un índex de massa corporal alt i les que tenen un índex de massa corporal normal o baix.
Resposta:
En l’eix X apareixen els diferents tipus d’entrenament, mentre que l’eix Y mostra les calories cremades mitjanes per cada combinació de tipus d’entrenament i categoria de BMI (Índex de Massa Corporal). No s’aprecia cap intersecció clara entre les línies, i més o menys tenen les mateixes pendents, la qual cosa suggeriria que no hi ha interacció entre els dos factors.
# Gràfic d'interacció entre Workout_Type i BMI_bin
interaction.plot(
x.factor = fin_df$Workout_Type,
trace.factor = fin_df$BMI_bin,
response = fin_df$Calories_Burned,
fun = mean,
type = "b",
grid = TRUE,
col.grid = "lightgray",
col = c("blue", "red"),
bg = "transparent",
pch = c(19, 17),
xlab = "Tipus d'entrenament",
ylab = "Calories cremades mitjanes",
legend = TRUE,
main = "Gràfic d'interacció entre Workout_Type i BMI_bin"
)Si canviem l’ordre dels factors, tampoc es pot observar cap intersecció clara entre les línies, les línies són més o menys paral·leles. Això reforça la idea que no hi ha interacció significativa entre els dos factors.
interaction.plot(
x.factor = fin_df$BMI_bin,
trace.factor = fin_df$Workout_Type,
response = fin_df$Calories_Burned,
fun = mean,
type = "b",
grid = TRUE,
col.grid = "lightgray",
col = c("blue", "red", "darkgreen", "purple"),
bg = "transparent",
pch = c(19, 17, 15, 18),
xlab = "Tipus d'índex de massa corporal",
ylab = "Calories cremades mitjanes",
legend = TRUE,
main = "Gràfic d'interacció entre Workout_Type i BMI_bin"
)Escriviu les hipòtesis.
Resposta:
En no haver-hi interacció entre els factors, s’escriuen les hipòtesis dels efectes principals de cada factor:
Workout_Type):BMI_bin):Resposta:
# Càlcul de l'ANOVA multifactorial sense interacció
anova_multifactorial <- aov(
Calories_Burned ~ Workout_Type + BMI_bin,
data = fin_df
)
# Resum de l'ANOVA multifactorial
summary(anova_multifactorial)## Df Sum Sq Mean Sq F value Pr(>F)
## Workout_Type 3 1.485e+09 494905823 2780.3 <2e-16 ***
## BMI_bin 1 5.695e+05 569544 3.2 0.0737 .
## Residuals 19995 3.559e+09 178001
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Comproveu els supòsits gràficament i amb les proves estadístiques de l’apartat anterior.
Resposta:
# Gràfics de diagnòstic del model ANOVA multifactorial
par(mfrow = c(1, 2))
plot(anova_multifactorial, which = 1, main = "Model Fit")
plot(anova_multifactorial, which = 2, main = "Q-Q Plot")Els gràfics són molt similars als gràfics de l’ANOVA d’un factor, ja que el factor tipus d’index de massa corporal no és significatiu, valor p > 0.05.
El resultat de l’ANOVA amb dos factors i el resultat de l’ANOVA amb un factor, és el mateix quan s’afegeix un factor a l’ANOVA d’un factor, si aquest no és significatiu i no hi ha interacció entre els factors a la fórmula de l’ANOVA.
| Apartat | Estadístic | Resultat |
|---|---|---|
| Contrast d’hipòtesi | p-valor | Amb variances desconegudes i diferents, apliquem el test de Welch: p = 0.441 (p > 0.025), no hi ha prou evidència per acceptar que els homes cremen més calories que les dones als entrenaments amb un nivell de conviança del 97.25%. |
| Regressió lineal múltiple | R² ajustat, valor p dels coeficients, correlació, VIF, normalitat i homoscedasticitat | El valor d’R² és molt alt (96.35%) i explica gairebé tota la variabilitat de Calories_Burned, les variables significatives són (p < 0.05): Session_Duration_hours, Experience_Level, Workout_Frequency_days_week, Workout_Type i Height_m. No hi ha problemes de multicol·linealitat entre les variables explicatives quantitatives (vif < 5, matriu de correlació < 0.8). Els residus no són normals i hi ha heteroscedasticitat, no obstant això, la inferència pot ser robusta gràcies al nombre elevat de mostres, segons el teorema del límit central. |
| Regressió logística | AUC, Estimacions dels coeficients, valor p dels coeficients, matriu de confusió, sensibilitat i especificitat, normalitat i homoscedasticitat | L’AUC és excepcional (AUC > 0.9), indicant una bona capacitat de discriminació del model. Les variables significatives (p < 0.05) són: Session_Duration_hours, Workout_Frequency_days_week, Weight_kg, Workout_Type i Calories_Burned. La matriu de confusió mostra una precisió alta, amb sensibilitat i especificitat també altes. Els residus no són normals i hi ha heteroscedasticitat, no obstant això, la inferència pot ser robusta gràcies al nombre elevat de mostres, segons el teorema del límit central. |
| ANOVA d’un factor | F value, Sum Sq, Mean Sq, valor p, efectes dels nivells del factor, normalitat i homoscedasticitat | Hi ha diferències significatives en les calories cremades segons el tipus d’entrenament (p < 0.05). Els efectes dels nivells del factor mostren com cada tipus d’entrenament afecta les calories cremades. Els residus no són normals i hi ha heteroscedasticitat, no obstant això, la inferència pot ser robusta gràcies al nombre elevat de mostres, segons el teorema del límit central. |
| ANOVA multifactorial | Interacció entre factors, F value, Sum Sq, Mean Sq, valor p, normalitat i homoscedasticitat | No hi ha interacció significativa entre els factors. Hi ha diferències significatives en les calories cremades segons el tipus d’entrenament (p < 0.05), però no segons la categoria de BMI (p > 0.05). Els residus no són normals i hi ha heteroscedasticitat, no obstant això, la inferència pot ser robusta gràcies al nombre elevat de mostres, segons el teorema del límit central. |