Solución Paso a Paso:

Paso 1: Limpieza y preparación:

En este paso, cargamos los datos, revisamos si existen datos faltantes, eliminamos la variable Hogar, convertimos en factor la variable objetivo y estandarizamos las varibles predictoras.

1. Cargar datos

# Cargar datos:

propietarios_data <- read.csv("Propietarios.csv")

2. Explorar datos

# Visualizar datos
head(propietarios_data)
##   Hogar Ingreso Tamaño       Clase
## 1     1    60.0   18.4 Propietario
## 2     2    85.5   16.8 Propietario
## 3     3    64.8   21.6 Propietario
## 4     4    61.5   20.8 Propietario
## 5     5    87.0   23.6 Propietario
## 6     6   110.1   19.2 Propietario
# Observar características de las variables
str(propietarios_data)
## 'data.frame':    24 obs. of  4 variables:
##  $ Hogar  : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Ingreso: num  60 85.5 64.8 61.5 87 ...
##  $ Tamaño : num  18.4 16.8 21.6 20.8 23.6 19.2 17.6 22.4 20 20.8 ...
##  $ Clase  : chr  "Propietario" "Propietario" "Propietario" "Propietario" ...
# Revisar los datos detalladamente
library(skimr)
skimr::skim(propietarios_data)
Data summary
Name propietarios_data
Number of rows 24
Number of columns 4
_______________________
Column type frequency:
character 1
numeric 3
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
Clase 0 1 11 14 0 2 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
Hogar 0 1 12.50 7.07 1 6.75 12.5 18.25 24.0 ▇▇▆▇▇
Ingreso 0 1 68.44 19.79 33 52.35 64.8 83.10 110.1 ▃▇▅▆▂
Tamaño 0 1 18.95 2.43 14 17.50 19.0 20.80 23.6 ▂▇▆▇▃

3. Eliminar variable Hogar

Hogar parece ser un indentificador y actúa como un número de casa nada más. Para reducir el ruido, la eliminamos del modelo.

library(dplyr)
propietarios_data <- propietarios_data %>%
  select(-Hogar)

4. Convertir en factor la variable objetivo

propietarios_data$Clase <- as.factor(propietarios_data$Clase)

5. Estandarizar variables predictoras

Este modelo es sensible a escalas, así que es beneficioso que las variables predictoras contribuyan de manera equilibrada al modelo.

# Estandarizar 
propietarios_data <- propietarios_data %>%
  mutate(
    Ingreso = scale(Ingreso),
    Tamaño = scale(Tamaño)
  )

Paso 2: Dividir los datos en entrenamiento y prueba

Dividimos los datos en conjuntos aleatorios de de entrenamiento y prueba. Como tenemos pocos datos, entendemos que k = 3 es suficiente. Separaremos los datos en 3 grupos, 2 de entrenamiento y 1 de prueba. De esta forma, nos aseguramos que el conjunto de entrenamiento tenga alrededor de un 75% de los datos y el conjunto de prueba un 25%.

# Crear conjuntos
set.seed(2025)
library(caret)

k <- 3 # Guardar el valor de k

folds <- createFolds(propietarios_data$Clase, k = k)
entrenamiento <- propietarios_data[-folds[[k]],]
prueba <- propietarios_data[folds[[k]],]

# Crear etiquetas 
entrenamiento_etiquetas <- propietarios_data$Clase[folds[[k]]]
prueba_etiquetas <- propietarios_data$Clase[folds[[k]]]

Ver cantidad de conjuntos

dim(entrenamiento)[1]
## [1] 16
dim(prueba)[1]
## [1] 8

Paso 3: Aplicar método de vector soporte

A continuación, aplicamos el modelo SVM con kernel radial y realizamos un ajuste de parámetros usando validación cruzada para encontrar los mejores valores de cost y gamma.

library(e1071)
set.seed(2025)
svm_vc <- tune("svm", Clase ~ ., data = entrenamiento, kernel = "radial", scale = FALSE,
               ranges = list(cost = c(0.001, 0.01, 0.1, 1, 5, 10, 20),
                             gamma = c(0.5, 1, 2, 3, 4, 5, 10)))

# Mejores parámetros
svm_vc$best.parameters
##   cost gamma
## 6   10   0.5

Podemos observar que de todos los rangos estudiados, el modelo con el menor error es con los hiperparámetros de cost = 10 y gamma = 0.5. A continuación, guardaremos estos resultados del mejor modelo.

mejor_modelo <- svm_vc$best.model
mejor_modelo
## 
## Call:
## best.tune(METHOD = "svm", train.x = Clase ~ ., data = entrenamiento, 
##     ranges = list(cost = c(0.001, 0.01, 0.1, 1, 5, 10, 20), gamma = c(0.5, 
##         1, 2, 3, 4, 5, 10)), kernel = "radial", scale = FALSE)
## 
## 
## Parameters:
##    SVM-Type:  C-classification 
##  SVM-Kernel:  radial 
##        cost:  10 
## 
## Number of Support Vectors:  13

Paso 4: Validación cruzada

Adicionalmente en el siguiente paso, aplicamos la validación cruzada manual para SVM y validar la estabilidad del modelo. En cada iteración, se entrenó el modelo con dos folds y se probó con el otro fold. Se calculó la exactitud de clasificación para cada fold y luego se obtuvo el promedio de exactitud.

# Guardar la excatitud de cada fold
exactitud <- numeric(length = k)

# Validación cruzada manual
for(i in 1:k){
  
  # Dividir los datos en conjunto y prueba
  entrenamiento <- propietarios_data[-folds[[i]],]
  prueba <- propietarios_data[folds[[i]],]

  # Crear etiquetas 
  entrenamiento_etiquetas <- propietarios_data$Clase[folds[[i]]]
  prueba_etiquetas <- propietarios_data$Clase[folds[[i]]]

# Entrenar modelo SVM con parámetros óptimos
modelo_svm <- svm(Clase ~ ., data = entrenamiento, kernel = "radial",
                    cost = 10, gamma = 0.5)

# Predecir
pred_svm <- predict(modelo_svm, newdata = prueba)
  
# Evaluar la exactitud del modelo en cada fold
cm <- confusionMatrix(pred_svm, prueba$Clase)
exactitud[i] <- cm$overall["Accuracy"]
  
# Mostrar resultado del fold
cat("Fold", i, "- Exactitud:", exactitud[i], "\n")

}
## Fold 1 - Exactitud: 0.625 
## Fold 2 - Exactitud: 0.75 
## Fold 3 - Exactitud: 0.875

Esto nos permite ver lo consistente que es nuestro modelo en distintas particiones. Cabe destacar que el proceso se intentó con k = 4, y la exactitud del modelo empeoró, lo cual pudimos concluir que el modelo pueda ser muy sensible a particiones.

# Exactitud promedio de validación cruzada
Exactitud_promedio <- round(mean(exactitud),4)*100
 paste("Exactitud_promedio: ",Exactitud_promedio,"%",sep="")
## [1] "Exactitud_promedio: 75%"

En promedio, el modelo acierta el 75% de las veces en los conjuntos de prueba de cada fold, lo cual mostró un rendimiento aceptable.

Paso 5: Predicción y resultados finales

En el siguiente paso, vemos las predicciones obtenidas de este modelo. Utlizamos el mejor modelo para predecir las clases de todo el conjunto de datos. Después generamos una matriz de confusión para comparar las predicciones con las etiquetas reales.

# Hacer prediccion con el mejor modelo
pred_svm1 <- predict(mejor_modelo, newdata = propietarios_data)

# Matriz de confusión
MC1 <- table(pred_svm1, propietarios_data$Clase)

Matriz de confusión

  • 9 casos fueron correctamente clasificados como “No propietario”, mientras 1 fue mal clasificado.
  • 11 casos fueron correctamente clasificados como “Propietario”, mientras 3 fueron mal clasificados.
# Tasa de aciertos
total  <- sum(MC1) 
TA1    <- sum(MC1[1,1]+MC1[2,2])/total
TA1
## [1] 0.875

Tasa de aciertos

La tasa de aciertos fue de 83% en el conjunto de prueba.

Interpretación de Resultados

Notamos que la validación cruzada en el paso 4 nos dio una exactitud más baja (75%) en comparación con la tasa de aciertos final del modelo (83%). Esto se debe a que la validación cruzada se realiza sobre distintos subconjuntos de los datos y simula cómo se comportaría el modelo con diferentes particiones de datos.