Imagina que quieres predecir si un paciente tiene una enfermedad rara
(clase minoritaria).
Si preguntas a un solo médico (árbol de decisión),
podría equivocarse.
Pero si consultas a 100 médicos
(bosque), cada uno especializado en aspectos distintos,
y votan, la predicción será más precisa.
¿Por qué funciona mejor?
Los errores individuales se compensan (como un promedio
inteligente).
Dos fuentes de aleatoriedad:
Resultado
Árboles diversos y menos correlacionados → Mayor
generalización.
Objetivo:
Encontrar la pregunta (umbral de una característica) que mejor
separa las clases.
Métrica: Impureza de Gini (más intuitiva que la entropía)
\[ [ Gini = 1 - \sum_{i=1}^{C} (p_i)^2 ] \]
\[ Gini = 1 - (p_{\text{clase A}})^2 - (p_{\text{clase B}})^2 \]
Ejemplos:
Si un nodo tiene 50% de cada clase → Gini = 0.5 (máxima impureza).
Si un nodo tiene 100% de una clase → Gini = 0 (pureza perfecta).
Ejemplo práctico:
\[ Gini = 1 - (0.7)^2 - (0.3)^2 = \] \[ 1 - 0.49 - 0.09 = 0.42 ] \]
Fórmula:
\[
\text{Ganancia} = Gini_{\text{padre}} - Gini_{\text{hijo}}
\]
Interpretación:
Si la división reduce la impureza (Gini) → Ganancia alta.
El árbol elige la división con máxima ganancia.
Ejemplo:
990 pacientes sanos vs. 10 enfermos.
Un modelo que siempre predice “sano” tendría 99% de precisión, pero sería inútil para detectar la enfermedad.
Asignar mayor penalización a errores en la clase minoritaria para mejorar el balanceo.
Entrenar cada árbol con un subconjunto balanceado de datos para evitar el sesgo hacia la mayoría.
##
## Call:
## randomForest(formula = class ~ ., data = data, classwt = c(sano = 1, enfermo = 99))
## Type of random forest: classification
## Number of trees: 500
## No. of variables tried at each split: 1
##
## OOB estimate of error rate: 2.2%
## Confusion matrix:
## enfermo sano class.error
## enfermo 0 10 1.00000000
## sano 12 978 0.01212121
##
## Call:
## randomForest(formula = class ~ ., data = data, sampsize = c(enfermo = 10, sano = 10), replace = TRUE)
## Type of random forest: classification
## Number of trees: 500
## No. of variables tried at each split: 1
##
## OOB estimate of error rate: 32.1%
## Confusion matrix:
## enfermo sano class.error
## enfermo 6 4 0.400000
## sano 317 673 0.320202
## Confusion Matrix and Statistics
##
## Reference
## Prediction enfermo sano
## enfermo 10 0
## sano 0 990
##
## Accuracy : 1
## 95% CI : (0.9963, 1)
## No Information Rate : 0.99
## P-Value [Acc > NIR] : 4.317e-05
##
## Kappa : 1
##
## Mcnemar's Test P-Value : NA
##
## Sensitivity : 1.00
## Specificity : 1.00
## Pos Pred Value : 1.00
## Neg Pred Value : 1.00
## Prevalence : 0.01
## Detection Rate : 0.01
## Detection Prevalence : 0.01
## Balanced Accuracy : 1.00
##
## 'Positive' Class : enfermo
##
## Confusion Matrix and Statistics
##
## Reference
## Prediction enfermo sano
## enfermo 10 314
## sano 0 676
##
## Accuracy : 0.686
## 95% CI : (0.6562, 0.7147)
## No Information Rate : 0.99
## P-Value [Acc > NIR] : 1
##
## Kappa : 0.0413
##
## Mcnemar's Test P-Value : <2e-16
##
## Sensitivity : 1.00000
## Specificity : 0.68283
## Pos Pred Value : 0.03086
## Neg Pred Value : 1.00000
## Prevalence : 0.01000
## Detection Rate : 0.01000
## Detection Prevalence : 0.32400
## Balanced Accuracy : 0.84141
##
## 'Positive' Class : enfermo
##
Usaremos los datos del Ames Housing son sacados de kaggle, en estos encontramos los precios de la vivienda en Estados Unidos
## Order PID MS.SubClass MS.Zoning
## Min. : 1.0 Min. :5.263e+08 Min. : 20.00 Length:2930
## 1st Qu.: 733.2 1st Qu.:5.285e+08 1st Qu.: 20.00 Class :character
## Median :1465.5 Median :5.355e+08 Median : 50.00 Mode :character
## Mean :1465.5 Mean :7.145e+08 Mean : 57.39
## 3rd Qu.:2197.8 3rd Qu.:9.072e+08 3rd Qu.: 70.00
## Max. :2930.0 Max. :1.007e+09 Max. :190.00
##
## Lot.Frontage Lot.Area Street Alley
## Min. : 21.00 Min. : 1300 Length:2930 Length:2930
## 1st Qu.: 58.00 1st Qu.: 7440 Class :character Class :character
## Median : 68.00 Median : 9436 Mode :character Mode :character
## Mean : 69.22 Mean : 10148
## 3rd Qu.: 80.00 3rd Qu.: 11555
## Max. :313.00 Max. :215245
## NA's :490
## Lot.Shape Land.Contour Utilities Lot.Config
## Length:2930 Length:2930 Length:2930 Length:2930
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## Land.Slope Neighborhood Condition.1 Condition.2
## Length:2930 Length:2930 Length:2930 Length:2930
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## Bldg.Type House.Style Overall.Qual Overall.Cond
## Length:2930 Length:2930 Min. : 1.000 Min. :1.000
## Class :character Class :character 1st Qu.: 5.000 1st Qu.:5.000
## Mode :character Mode :character Median : 6.000 Median :5.000
## Mean : 6.095 Mean :5.563
## 3rd Qu.: 7.000 3rd Qu.:6.000
## Max. :10.000 Max. :9.000
##
## Year.Built Year.Remod.Add Roof.Style Roof.Matl
## Min. :1872 Min. :1950 Length:2930 Length:2930
## 1st Qu.:1954 1st Qu.:1965 Class :character Class :character
## Median :1973 Median :1993 Mode :character Mode :character
## Mean :1971 Mean :1984
## 3rd Qu.:2001 3rd Qu.:2004
## Max. :2010 Max. :2010
##
## Exterior.1st Exterior.2nd Mas.Vnr.Type Mas.Vnr.Area
## Length:2930 Length:2930 Length:2930 Min. : 0.0
## Class :character Class :character Class :character 1st Qu.: 0.0
## Mode :character Mode :character Mode :character Median : 0.0
## Mean : 101.9
## 3rd Qu.: 164.0
## Max. :1600.0
## NA's :23
## Exter.Qual Exter.Cond Foundation Bsmt.Qual
## Length:2930 Length:2930 Length:2930 Length:2930
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## Bsmt.Cond Bsmt.Exposure BsmtFin.Type.1 BsmtFin.SF.1
## Length:2930 Length:2930 Length:2930 Min. : 0.0
## Class :character Class :character Class :character 1st Qu.: 0.0
## Mode :character Mode :character Mode :character Median : 370.0
## Mean : 442.6
## 3rd Qu.: 734.0
## Max. :5644.0
## NA's :1
## BsmtFin.Type.2 BsmtFin.SF.2 Bsmt.Unf.SF Total.Bsmt.SF
## Length:2930 Min. : 0.00 Min. : 0.0 Min. : 0
## Class :character 1st Qu.: 0.00 1st Qu.: 219.0 1st Qu.: 793
## Mode :character Median : 0.00 Median : 466.0 Median : 990
## Mean : 49.72 Mean : 559.3 Mean :1052
## 3rd Qu.: 0.00 3rd Qu.: 802.0 3rd Qu.:1302
## Max. :1526.00 Max. :2336.0 Max. :6110
## NA's :1 NA's :1 NA's :1
## Heating Heating.QC Central.Air Electrical
## Length:2930 Length:2930 Length:2930 Length:2930
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## X1st.Flr.SF X2nd.Flr.SF Low.Qual.Fin.SF Gr.Liv.Area
## Min. : 334.0 Min. : 0.0 Min. : 0.000 Min. : 334
## 1st Qu.: 876.2 1st Qu.: 0.0 1st Qu.: 0.000 1st Qu.:1126
## Median :1084.0 Median : 0.0 Median : 0.000 Median :1442
## Mean :1159.6 Mean : 335.5 Mean : 4.677 Mean :1500
## 3rd Qu.:1384.0 3rd Qu.: 703.8 3rd Qu.: 0.000 3rd Qu.:1743
## Max. :5095.0 Max. :2065.0 Max. :1064.000 Max. :5642
##
## Bsmt.Full.Bath Bsmt.Half.Bath Full.Bath Half.Bath
## Min. :0.0000 Min. :0.00000 Min. :0.000 Min. :0.0000
## 1st Qu.:0.0000 1st Qu.:0.00000 1st Qu.:1.000 1st Qu.:0.0000
## Median :0.0000 Median :0.00000 Median :2.000 Median :0.0000
## Mean :0.4314 Mean :0.06113 Mean :1.567 Mean :0.3795
## 3rd Qu.:1.0000 3rd Qu.:0.00000 3rd Qu.:2.000 3rd Qu.:1.0000
## Max. :3.0000 Max. :2.00000 Max. :4.000 Max. :2.0000
## NA's :2 NA's :2
## Bedroom.AbvGr Kitchen.AbvGr Kitchen.Qual TotRms.AbvGrd
## Min. :0.000 Min. :0.000 Length:2930 Min. : 2.000
## 1st Qu.:2.000 1st Qu.:1.000 Class :character 1st Qu.: 5.000
## Median :3.000 Median :1.000 Mode :character Median : 6.000
## Mean :2.854 Mean :1.044 Mean : 6.443
## 3rd Qu.:3.000 3rd Qu.:1.000 3rd Qu.: 7.000
## Max. :8.000 Max. :3.000 Max. :15.000
##
## Functional Fireplaces Fireplace.Qu Garage.Type
## Length:2930 Min. :0.0000 Length:2930 Length:2930
## Class :character 1st Qu.:0.0000 Class :character Class :character
## Mode :character Median :1.0000 Mode :character Mode :character
## Mean :0.5993
## 3rd Qu.:1.0000
## Max. :4.0000
##
## Garage.Yr.Blt Garage.Finish Garage.Cars Garage.Area
## Min. :1895 Length:2930 Min. :0.000 Min. : 0.0
## 1st Qu.:1960 Class :character 1st Qu.:1.000 1st Qu.: 320.0
## Median :1979 Mode :character Median :2.000 Median : 480.0
## Mean :1978 Mean :1.767 Mean : 472.8
## 3rd Qu.:2002 3rd Qu.:2.000 3rd Qu.: 576.0
## Max. :2207 Max. :5.000 Max. :1488.0
## NA's :159 NA's :1 NA's :1
## Garage.Qual Garage.Cond Paved.Drive Wood.Deck.SF
## Length:2930 Length:2930 Length:2930 Min. : 0.00
## Class :character Class :character Class :character 1st Qu.: 0.00
## Mode :character Mode :character Mode :character Median : 0.00
## Mean : 93.75
## 3rd Qu.: 168.00
## Max. :1424.00
##
## Open.Porch.SF Enclosed.Porch X3Ssn.Porch Screen.Porch
## Min. : 0.00 Min. : 0.00 Min. : 0.000 Min. : 0
## 1st Qu.: 0.00 1st Qu.: 0.00 1st Qu.: 0.000 1st Qu.: 0
## Median : 27.00 Median : 0.00 Median : 0.000 Median : 0
## Mean : 47.53 Mean : 23.01 Mean : 2.592 Mean : 16
## 3rd Qu.: 70.00 3rd Qu.: 0.00 3rd Qu.: 0.000 3rd Qu.: 0
## Max. :742.00 Max. :1012.00 Max. :508.000 Max. :576
##
## Pool.Area Pool.QC Fence Misc.Feature
## Min. : 0.000 Length:2930 Length:2930 Length:2930
## 1st Qu.: 0.000 Class :character Class :character Class :character
## Median : 0.000 Mode :character Mode :character Mode :character
## Mean : 2.243
## 3rd Qu.: 0.000
## Max. :800.000
##
## Misc.Val Mo.Sold Yr.Sold Sale.Type
## Min. : 0.00 Min. : 1.000 Min. :2006 Length:2930
## 1st Qu.: 0.00 1st Qu.: 4.000 1st Qu.:2007 Class :character
## Median : 0.00 Median : 6.000 Median :2008 Mode :character
## Mean : 50.63 Mean : 6.216 Mean :2008
## 3rd Qu.: 0.00 3rd Qu.: 8.000 3rd Qu.:2009
## Max. :17000.00 Max. :12.000 Max. :2010
##
## Sale.Condition SalePrice
## Length:2930 Min. : 12789
## Class :character 1st Qu.:129500
## Mode :character Median :160000
## Mean :180796
## 3rd Qu.:213500
## Max. :755000
##
Identificamos columnas que no aportan información predictiva. Eliminamos identificadores como “Order” y “PID”
Variables numéricas: reemplazamos NA con la mediana Variables categóricas: reemplazamos NA con la moda
Eliminamos columnas categóricas con un solo nivel
# Limpieza y preprocesamiento de datos.
# La siguiente función calcula la moda de una variable categórica.
# Esto es útil para imputar valores faltantes en columnas categóricas.
Mode <- function(x) {
ux <- unique(x) # Obtiene valores únicos.
ux[which.max(tabulate(match(x, ux)))] # Retorna el valor más frecuente.
}
# Eliminar columnas irrelevantes.
irrelevant_cols <- c("Order", "PID") # Identificamos columnas no útiles para el modelo.
ames_data <- ames_data %>%
select(-any_of(irrelevant_cols)) # Excluimos estas columnas.
# Imputar valores faltantes para columnas numéricas y categóricas.
ames_data <- ames_data %>%
mutate(across(where(is.numeric), ~ ifelse(is.na(.), median(., na.rm = TRUE), .))) %>%
mutate(across(where(is.character), ~ ifelse(is.na(.), Mode(.), .)))
# Ahora, los datos están listos para el modelado.
Mode <- function(x) {
ux <- unique(x)
ux[which.max(tabulate(match(x, ux)))]
}
# Eliminar columnas irrelevantes solo si existen
irrelevant_cols <- c("Order", "PID")
ames_data <- ames_data %>%
select(-any_of(irrelevant_cols))
# Imputar valores faltantes
ames_data <- ames_data %>%
mutate(across(where(is.numeric), ~ ifelse(is.na(.), median(., na.rm = TRUE), .))) %>%
mutate(across(where(is.character), ~ ifelse(is.na(.), Mode(.), .)))
# Eliminar columnas categóricas con un solo nivel
single_level_cols <- names(Filter(function(x) length(unique(x)) == 1, ames_data))
ames_data <- ames_data %>% select(-all_of(single_level_cols))Las variables categóricas se convierten explícitamente a factores. Necesario para que R las trate correctamente
Separamos características (X) y precio de venta (y) 80% para entrenamiento, 20% para prueba. Usamos createDataPartition para una división estratificada
Eliminamos columnas con demasiados valores NA. Incluye “Alley”, “Fireplace.Qu”, “Pool.QC”, “Fence”, “Misc.Feature”
# Convertir variables categóricas a factores explícitamente
ames_data <- ames_data %>%
mutate(across(where(is.character), as.factor))
# Separar características (X) y etiqueta objetivo (y)
X <- ames_data %>% select(-SalePrice)
y <- ames_data$SalePrice
# Dividir los datos en conjuntos de entrenamiento y prueba
set.seed(120224)
train_index <- createDataPartition(y, p = 0.8, list = FALSE)
train_data <- X[train_index, ]
test_data <- X[-train_index, ]
train_labels <- y[train_index]
test_labels <- y[-train_index]
# Eliminar columnas con demasiados valores NA en train_data y test_data
columns_to_remove <- c("Alley", "Fireplace.Qu", "Pool.QC", "Fence", "Misc.Feature")
train_data <- train_data %>% select(-all_of(columns_to_remove))
test_data <- test_data %>% select(-all_of(columns_to_remove))Recomendación general: √p para regresión (donde p = número de predictores) Probamos valores: 2, 4, 6, 8
Más árboles = mejor estabilidad, pero mayor costo computacional Probamos valores: 50, 100, 200
Evaluamos todas las combinaciones posibles Seleccionamos la que maximiza la correlación entre predicciones y valores reales
library(randomForest)
# Manejo de valores faltantes
ames_data <- ames_data %>%
mutate(across(where(is.numeric), ~ ifelse(is.na(.), median(., na.rm = TRUE), .))) %>%
mutate(across(where(is.character), ~ ifelse(is.na(.), as.character(getmode(.)), .)))
ames_data <- ames_data %>% select(-c(Alley, Pool.QC, Fence, Misc.Feature))
ames_data <- ames_data[complete.cases(ames_data), ]
# Función auxiliar para calcular la moda
getmode <- function(v) {
uniqv <- unique(v)
uniqv[which.max(tabulate(match(v, uniqv)))]
}
# Dividir los datos en entrenamiento y prueba
set.seed(20241124)
train_indices <- sample(1:nrow(ames_data), 0.8 * nrow(ames_data))
train_data <- ames_data[train_indices, ]
test_data <- ames_data[-train_indices, ]
# Definir grid de hiperparámetros
rf_grid <- expand.grid(
mtry = c(2, 4, 6, 8),
ntree = c(50, 100, 200)
)
# Iterar sobre combinaciones
rf_results <- data.frame()
for (i in 1:nrow(rf_grid)) {
set.seed(20241124)
rf_model <- randomForest(
SalePrice ~ .,
data = train_data,
mtry = rf_grid$mtry[i],
ntree = rf_grid$ntree[i]
)
preds <- predict(rf_model, test_data)
acc <- cor(preds, test_data$SalePrice, use = "complete.obs") # Calcular correlación
rf_results <- rbind(rf_results, cbind(rf_grid[i, ], correlation = acc))
}# Mejor combinación
best_rf_combination <- rf_results[which.max(rf_results$correlation), ]
print(best_rf_combination)## mtry ntree correlation
## 8 8 100 0.9256744
# Entrenar modelo con los mejores hiperparámetros
best_rf_model <- randomForest(
SalePrice ~ .,
data = train_data,
mtry = best_rf_combination$mtry,
ntree = best_rf_combination$ntree
)
# Predecir en conjunto de prueba
predictions <- predict(best_rf_model, test_data)
# Crear dataframe para visualización
results_df <- data.frame(
Actual = test_data$SalePrice,
Predicted = predictions
)