Warning: package 'tidyverse' was built under R version 4.3.3
Warning: package 'lubridate' was built under R version 4.3.3
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.3 ✔ readr 2.1.4
✔ forcats 1.0.0 ✔ stringr 1.5.0
✔ ggplot2 3.4.4 ✔ tibble 3.2.1
✔ lubridate 1.9.3 ✔ tidyr 1.3.0
✔ purrr 1.0.2
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
✖ dplyr::select() masks MASS::select()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
# Cargar los datosdata(seeds)# Escalado de variables numéricassemillas_escaladas <- seeds %>%mutate(across(where(is.numeric), scale))# Vista previahead(semillas_escaladas)
area perimeter compactness length of kernel width of kernel
1 0.14175904 0.214948819 6.045733e-05 0.30349301 0.1413640
2 0.01116136 0.008204153 4.274938e-01 -0.16822270 0.1969616
3 -0.19160873 -0.359341919 1.438945e+00 -0.76181710 0.2075516
4 -0.34626388 -0.474200066 1.036904e+00 -0.68733567 0.3187467
5 0.44419577 0.329806966 1.371233e+00 0.06650665 0.8032397
6 -0.16067770 -0.267455401 1.019976e+00 -0.54740087 0.1413640
asymmetry coefficient length of kernel groove variety
1 -0.9838010 -0.3826631 Kama
2 -1.7839036 -0.9198156 Kama
3 -0.6658882 -1.1863572 Kama
4 -0.9585276 -1.2270506 Kama
5 -1.5597684 -0.4742231 Kama
6 -0.8235144 -0.9198156 Kama
Se cargan los paquetes necesarios y se utiliza el dataset seeds que incluye mediciones morfológicas de tres variedades de semillas de trigo: Kama, Rosa y Canadiense.
Todas las variables numéricas se estandarizan con la función scale para que tengan media cero y desviación estándar uno. Esta normalización es fundamental para evitar que variables con escalas diferentes dominen el análisis, lo cual podría sesgar los resultados del modelo LDA.
# Visualizar posibles outliers por clasesemillas_escaladas %>%pivot_longer(cols =-variety) %>%ggplot(aes(x = variety, y = value)) +geom_boxplot() +facet_wrap(~ name, scales ="free") +theme_minimal() +labs(title ="Boxplots por variable - Datos escalados")
# Resumen estadísticosummary(semillas_escaladas)
area.V1 perimeter.V1 compactness.V1
Min. :-1.4632177 Min. :-1.6457532 Min. :-2.6618758
1st Qu.:-0.8858385 1st Qu.:-0.8494033 1st Qu.:-0.5966534
Median :-0.1692697 Median :-0.1832261 Median : 0.1037448
Mean : 0.0000000 Mean : 0.0000000 Mean : 0.0000000
3rd Qu.: 0.8445808 3rd Qu.: 0.8849547 3rd Qu.: 0.7099807
Max. : 2.1763334 Max. : 2.0603364 Max. : 2.0018026
length of kernel.V1 width of kernel.V1 asymmetry coefficient.V1
Min. :-1.6465662 Min. :-1.6642328 Min. :-1.952105
1st Qu.:-0.8267062 1st Qu.:-0.8329169 1st Qu.:-0.757338
Median :-0.2370616 Median :-0.0571987 Median :-0.067308
Mean : 0.0000000 Mean : 0.0000000 Mean : 0.000000
3rd Qu.: 0.7927006 3rd Qu.: 0.8025778 3rd Qu.: 0.710681
Max. : 2.3618888 Max. : 2.0502135 Max. : 3.163032
length of kernel groove.V1 variety
Min. :-1.8089658 Kama :70
1st Qu.:-0.7387301 Rosa :70
Median :-0.3765590 Canadian:70
Mean : 0.0000000
3rd Qu.: 0.9541143
Max. : 2.3234463
# Pairs plot coloreado por clasepairs(semillas_escaladas[1:7], col = semillas_escaladas$variety, pch =19)
El conjunto de datos no presenta valores faltantes, lo cual permite trabajar directamente sin necesidad de imputación.
A través de los diagramas de caja por clase, se visualiza la distribución de cada variable. Algunas variables, como area y surco, muestran valores que podrían considerarse atípicos, aunque no de forma extrema. Esta exploración ayuda a identificar posibles distorsiones antes del análisis discriminante.
Ajuste del modelo LDA
# Ajustar modelo LDAset.seed(123)modelo_lda <-lda(variety ~ ., data = semillas_escaladas)modelo_lda
Call:
lda(variety ~ ., data = semillas_escaladas)
Prior probabilities of groups:
Kama Rosa Canadian
0.3333333 0.3333333 0.3333333
Group means:
area perimeter compactness `length of kernel` `width of kernel`
Kama -0.1763396 -0.2029161 0.3839040 -0.2719163 -0.037002
Rosa 1.1983237 1.2071044 0.5297876 1.1725075 1.108799
Canadian -1.0219841 -1.0041884 -0.9136916 -0.9005912 -1.071797
`asymmetry coefficient` `length of kernel groove`
Kama -0.68690313 -0.6528380
Rosa -0.03684659 1.2462927
Canadian 0.72374972 -0.5934547
Coefficients of linear discriminants:
LD1 LD2
area 1.23306839 -12.2071106
perimeter -4.96159833 11.1082183
compactness -0.14006876 2.0553411
`length of kernel` 2.65315093 3.4694860
`width of kernel` -0.01399365 -0.2697275
`asymmetry coefficient` 0.06773106 -0.4830235
`length of kernel groove` -1.53247351 -3.3980221
Proportion of trace:
LD1 LD2
0.6814 0.3186
El modelo de Análisis Discriminante Lineal (LDA) se ajusta utilizando todas las variables como predictoras y la variedad de semilla como variable de clasificación.
El resumen del modelo muestra que se generan dos funciones discriminantes (LD1 y LD2), que explican la mayor parte de la variabilidad entre clases. LD1 concentra la mayor proporción de información discriminante, mientras que LD2 añade una contribución menor pero útil.
Proyección en funciones discriminantes
proyeccion_lda <-predict(modelo_lda)$x %>%as.data.frame() %>%mutate(clase = semillas_escaladas$variety)ggplot(proyeccion_lda, aes(LD1, LD2, color = clase)) +geom_point(size =2) +labs(title ="Proyección LDA", x ="LD1", y ="LD2") +theme_minimal()
La representación en el plano definido por LD1 y LD2 permite observar una clara separación entre las tres variedades de semillas.
LD1 diferencia principalmente la clase Kama de las demás, mientras que LD2 contribuye a separar las clases Rosa y Canadiense. Esta proyección evidencia que las combinaciones lineales obtenidas por el modelo son efectivas para distinguir entre grupos.
Evaluación del modelo
# Predicciones del modelo (sin validación cruzada)predicciones <-predict(modelo_lda)$class# Matriz de confusiónmatriz_confusion <-table(Real = semillas_escaladas$variety, Predicho = predicciones)matriz_confusion
Predicho
Real Kama Rosa Canadian
Kama 66 1 3
Rosa 0 70 0
Canadian 3 0 67
# Exactitud del modeloexactitud <-sum(diag(matriz_confusion)) /sum(matriz_confusion)exactitud
[1] 0.9666667
La matriz de confusión muestra una clasificación altamente precisa, con muy pocos errores.
La exactitud del modelo supera el 95 %, lo cual refleja un excelente desempeño al clasificar correctamente las semillas según su variedad. Cabe destacar que esta evaluación se realiza sobre los mismos datos usados para entrenar el modelo, por lo que los resultados pueden estar optimizados para esta muestra específica.
Análisis Discriminante en Python
Carga y limpieza (Python)
# %pip install pandas numpy scikit-learn matplotlib seabornimport pandas as pdimport numpy as npfrom sklearn.preprocessing import StandardScaler# Cargar el dataset Seeds desde UCIurl ="https://archive.ics.uci.edu/ml/machine-learning-databases/00236/seeds_dataset.txt"columnas = ["area", "perimetro", "compacidad", "longitud","ancho", "asimetria", "surco", "clase"]semillas = pd.read_csv(url, sep="\s+", names=columnas)# Mapear las clases a nombres descriptivos en españolmapa_clases = {1: "Kama", 2: "Rosa", 3: "Canadiense"}semillas["variedad"] = semillas["clase"].map(mapa_clases)# Escalar las variables numéricasX = semillas.iloc[:, 0:7]escalador = StandardScaler()X_esc = escalador.fit_transform(X)# Variable respuestay = semillas["variedad"]
Se cargan las librerías necesarias y se importa el dataset directamente desde el repositorio UCI.
Se renombran las columnas con nombres más descriptivos y se convierte la variable de clase de numérica a categórica (Kama, Rosa,Canadiense). Esto facilita la interpretación posterior. El resultado es un dataset limpio y preparado para el análisis discriminante.
Se entrena el modelo LDA usando las variables morfológicas de las semillas como predictoras, y la variedad como objetivo.
La transformación del dataset original a través de fit_transform() genera nuevas variables (LD1 y LD2) que maximizan la separación entre las clases. Esta reducción permite visualizar mejor las diferencias entre grupos.
Proyección en LD1 y LD2
import matplotlib.pyplot as plt# Construir DataFrame con las componentesdf_proj = pd.DataFrame({"LD1": X_lda[:, 0],"LD2": X_lda[:, 1],"variedad": y})# Graficar la proyecciónplt.figure()for clase, grupo in df_proj.groupby("variedad"): plt.scatter(grupo["LD1"], grupo["LD2"], label=clase, s=30)plt.title("Proyección LDA (Python)")plt.xlabel("LD1")plt.ylabel("LD2")plt.legend()plt.grid(True)plt.show()
Se crea una gráfica de dispersión utilizando las dos funciones discriminantes obtenidas.
El resultado muestra una clara separación entre las variedades de semillas, donde LD1 distingue principalmente la clase Kama, mientras LD2 ayuda a separar Rosa de Canadiense. La visualización confirma que el modelo fue capaz de encontrar ejes óptimos de discriminación.
Evaluación del modelo
from sklearn.metrics import confusion_matrix, accuracy_score# Predicción dentro de muestray_pred = modelo_lda.predict(X_esc)# Matriz de confusión y exactitudprint("Matriz de confusión:\n", confusion_matrix(y, y_pred))