import sklearn
import numpy as np
import scipy as sp
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib notebook
%matplotlib inline
import seaborn as sns
TP1_data_file = "./TP1_data.csv"
TP1_data = pd.read_csv(TP1_data_file, sep=',', index_col=0)
TP1_data.shape
(59, 5)
#Statistiques descriptives sur les données
TP1_data.describe()
attribut1 | attribut2 | attribut3 | attribut4 | classe | |
---|---|---|---|---|---|
count | 59.000000 | 59.000000 | 59.000000 | 59.000000 | 59.000000 |
mean | 49.793220 | 23.486441 | 9.594915 | 14.557627 | 0.915254 |
std | 10.490782 | 3.531995 | 8.174992 | 4.057994 | 0.815542 |
min | 28.700000 | 16.500000 | 0.000000 | 3.700000 | 0.000000 |
25% | 40.850000 | 21.050000 | 2.400000 | 12.000000 | 0.000000 |
50% | 49.000000 | 23.300000 | 9.000000 | 15.100000 | 1.000000 |
75% | 59.200000 | 25.100000 | 13.000000 | 16.950000 | 2.000000 |
max | 68.500000 | 33.000000 | 29.100000 | 23.000000 | 2.000000 |
Remarque ad-hoc #1: Les attributs ont des variances différentes.
## Evaluation visuelle de la corrélation globale et par classe
sns.pairplot(data=TP1_data, vars=["attribut1","attribut2","attribut3","attribut4"], hue='classe', kind='reg', diag_kind='kde', palette = sns.color_palette("husl",3))
C:\Users\ouew2201\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\seaborn\axisgrid.py:123: UserWarning: The figure layout has changed to tight self._figure.tight_layout(*args, **kwargs)
<seaborn.axisgrid.PairGrid at 0x183b810bc50>
## Evaluation quantitative de la corrélation globale
X = TP1_data[["attribut1","attribut2","attribut3","attribut4"]]
corr_matrix = X.corr()
print(corr_matrix)
attribut1 attribut2 attribut3 attribut4 attribut1 1.000000 0.017056 -0.890974 -0.622651 attribut2 0.017056 1.000000 -0.258163 -0.242347 attribut3 -0.890974 -0.258163 1.000000 0.372298 attribut4 -0.622651 -0.242347 0.372298 1.000000
## Evaluation quantitative de la corrélation par classe
X0=TP1_data.loc[TP1_data['classe'] == 0][["attribut1","attribut2","attribut3","attribut4"]]
X1=TP1_data.loc[TP1_data['classe'] == 1][["attribut1","attribut2","attribut3","attribut4"]]
X2=TP1_data.loc[TP1_data['classe'] == 2][["attribut1","attribut2","attribut3","attribut4"]]
print("Correlations pour classe 0:",X0.corr(), "\n")
print("Correlations pour classe 1:",X1.corr(), "\n")
print("Correlations pour classe 2:",X2.corr(), "\n")
Correlations pour classe 0: attribut1 attribut2 attribut3 attribut4 attribut1 1.000000 0.302290 -0.784024 0.205335 attribut2 0.302290 1.000000 -0.671647 0.097789 attribut3 -0.784024 -0.671647 1.000000 -0.496368 attribut4 0.205335 0.097789 -0.496368 1.000000 Correlations pour classe 1: attribut1 attribut2 attribut3 attribut4 attribut1 1.000000 0.137804 -0.590792 -0.726196 attribut2 0.137804 1.000000 -0.543282 -0.303635 attribut3 -0.590792 -0.543282 1.000000 0.142723 attribut4 -0.726196 -0.303635 0.142723 1.000000 Correlations pour classe 2: attribut1 attribut2 attribut3 attribut4 attribut1 1.000000 -0.213961 -0.620437 -0.427720 attribut2 -0.213961 1.000000 -0.035696 -0.621881 attribut3 -0.620437 -0.035696 1.000000 0.136993 attribut4 -0.427720 -0.621881 0.136993 1.000000
On observe des corrélations entre les attributs, notamment 1 et 3, et le degré de corrélation varie d'une classe à l'autre.
Certains attributs étant corrélés, une analyse des composantes principales serait utile pour obtenir une représentation utilisant des composantes non corrélées.
# Dataset
y = TP1_data["classe"] #data
X = TP1_data.drop("classe", axis=1) #class
# Définition des distances et des classes
import math
def mahalanobis(p, q, cov):
dif = p - q
return math.sqrt(np.dot(np.dot(dif.T, np.linalg.inv(cov)), dif))
def euclidian(p, q):
dif = p - q
return math.sqrt(np.dot(dif.T, dif))
def manhattan(p, q):
dif = p - q
return sum(abs(dif))
def chebyshev(p, q):
dif = p - q
return max(abs(dif))
index_0 = np.where(y == 0)
index_1 = np.where(y == 1)
index_2 = np.where(y == 2)
y_ = y.values
Dans le cas de la distance euclidienne, nous commençons par normaliser les données. La distance de Mahalanobis ne requiert ni normalisation si ACP au préalable.
# Cas avec distance euclidienne
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(X)
X_norm = scaler.transform(X)
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pca.fit(X_norm)
X_pca2 = pca.transform(X_norm)
pca = PCA(n_components=3)
pca.fit(X_norm)
X_pca3 = pca.transform(X_norm)
from sklearn.manifold import TSNE
tsnee = TSNE()
X_pca3_tsne = tsnee.fit_transform(X_pca3)
fig, axes = plt.subplots(2, 2,figsize=(8,8))
sns.scatterplot(x=X_pca2[:,0], y=X_pca2[:,1],hue=y, ax=axes[0,0], palette = sns.color_palette("husl",3))
sns.scatterplot(x=X_pca3[:,0], y=X_pca3[:,1],hue=y.values, ax=axes[0,1], palette = sns.color_palette("husl",3), size = X_pca3[:,2])
sns.scatterplot(x=X_pca3_tsne[:,0], y=X_pca3_tsne[:,1],hue=y.values, ax=axes[1,1], palette = sns.color_palette("husl",3))
axes[0,0].set_title('2 Composantes principales')
axes[0,1].set_title('3 Composantes principales')
axes[1,1].set_title('3 Composantes principales + TSNE')
Text(0.5, 1.0, '3 Composantes principales + TSNE')
D'après la visualisation les deux représentations, à 2 ou 3 composantes connexes, permettent de séparer les classes assez bien. Nous allons évaluer quantitativement si l'une des deux représentations est plus adéquate que l'autre.
#Calcul de la distance euclidienne aux centroïdes
mean_0 = np.mean(X_pca2[index_0],axis=0)
mean_1 = np.mean(X_pca2[index_1],axis=0)
mean_2 = np.mean(X_pca2[index_2],axis=0)
y_pred = []
for i in range(X_pca2.shape[0]):
dist_0 = euclidian(X_pca2[i, :], mean_0)
dist_1 = euclidian(X_pca2[i, :], mean_1)
dist_2 = euclidian(X_pca2[i, :], mean_2)
y_pred.append(np.argmin([dist_0,dist_1,dist_2]))
print("d_euclidienne 2CP:", np.mean(y_==y_pred))
mean_0 = np.mean(X_pca3[index_0],axis=0)
mean_1 = np.mean(X_pca3[index_1],axis=0)
mean_2 = np.mean(X_pca3[index_2],axis=0)
y_pred = []
for i in range(X_pca3.shape[0]):
dist_0 = euclidian(X_pca3[i, :], mean_0)
dist_1 = euclidian(X_pca3[i, :], mean_1)
dist_2 = euclidian(X_pca3[i, :], mean_2)
y_pred.append(np.argmin([dist_0,dist_1,dist_2]))
print("d_euclidienne 3CP:", np.mean(y_==y_pred))
d_euclidienne 2CP: 0.8983050847457628 d_euclidienne 3CP: 0.8813559322033898
Avec la distane euclidienne, la représentation renvoyant le meilleur score est celle à 2 composantes.
Dans le cas où les données ne sont pas normalisées, on obtient:
d_euclidienne 2CP: 0.8305084745762712
d_euclidienne 3CP: 0.847457627118644
Dans ce cas, la représentation à 3CP retourne le meilleur score.
sns.pairplot(data=TP1_data, vars=["attribut1","attribut2","attribut3","attribut4"], hue='classe', diag_kind='kde',palette = sns.color_palette("husl",3))
C:\Users\ouew2201\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\seaborn\axisgrid.py:123: UserWarning: The figure layout has changed to tight self._figure.tight_layout(*args, **kwargs)
<seaborn.axisgrid.PairGrid at 0x183baaac210>
La forme élliptique des clusters dans la figure ci-dessus suggère que la distance adéquate pour ce jeu de données est la distance de Malanobis.
Nous allons le vérifier en calculant les scores de la classification par centroïde le plus proche, avec les 3 distances.
Remarque ad-hoc #2 : Nous avons déjà les résultats pour la distance euclidienne. Il nous reste les caluls pour les distances de Manhattan, Mahalanobis et Chebyshev.
#Calcul de la distance de Manhattan aux centroïdes
mean_0 = np.mean(X_pca2[index_0],axis=0)
mean_1 = np.mean(X_pca2[index_1],axis=0)
mean_2 = np.mean(X_pca2[index_2],axis=0)
y_pred = []
for i in range(X_pca2.shape[0]):
dist_0 = manhattan(X_pca2[i, :], mean_0)
dist_1 = manhattan(X_pca2[i, :], mean_1)
dist_2 = manhattan(X_pca2[i, :], mean_2)
y_pred.append(np.argmin([dist_0,dist_1,dist_2]))
print("d_manhattan 2CP:", np.mean(y_==y_pred))
mean_0 = np.mean(X_pca3[index_0],axis=0)
mean_1 = np.mean(X_pca3[index_1],axis=0)
mean_2 = np.mean(X_pca3[index_2],axis=0)
y_pred = []
for i in range(X_pca3.shape[0]):
dist_0 = manhattan(X_pca3[i, :], mean_0)
dist_1 = manhattan(X_pca3[i, :], mean_1)
dist_2 = manhattan(X_pca3[i, :], mean_2)
y_pred.append(np.argmin([dist_0,dist_1,dist_2]))
print("d_manhattan 3CP:", np.mean(y_==y_pred))
#Calcul de la distance de Chebyshev aux centroïdes
mean_0 = np.mean(X_pca2[index_0],axis=0)
mean_1 = np.mean(X_pca2[index_1],axis=0)
mean_2 = np.mean(X_pca2[index_2],axis=0)
y_pred = []
for i in range(X_pca2.shape[0]):
dist_0 = chebyshev(X_pca2[i, :], mean_0)
dist_1 = chebyshev(X_pca2[i, :], mean_1)
dist_2 = chebyshev(X_pca2[i, :], mean_2)
y_pred.append(np.argmin([dist_0,dist_1,dist_2]))
print("d_chebysev 2CP:", np.mean(y_==y_pred))
mean_0 = np.mean(X_pca3[index_0],axis=0)
mean_1 = np.mean(X_pca3[index_1],axis=0)
mean_2 = np.mean(X_pca3[index_2],axis=0)
y_pred = []
for i in range(X_pca3.shape[0]):
dist_0 = chebyshev(X_pca3[i, :], mean_0)
dist_1 = chebyshev(X_pca3[i, :], mean_1)
dist_2 = chebyshev(X_pca3[i, :], mean_2)
y_pred.append(np.argmin([dist_0,dist_1,dist_2]))
print("d_chebyshev 3CP:", np.mean(y_==y_pred))
d_manhattan 2CP: 0.8813559322033898 d_manhattan 3CP: 0.8305084745762712 d_chebysev 2CP: 0.8983050847457628 d_chebyshev 3CP: 0.864406779661017
Pour l'application de la distance de Mahalanobis, les données ne doivent pas être normalisées. Nous repartons donc des données non-normalisées.
#ACP pour les données non-normalisées
pca = PCA(n_components=2)
pca.fit(X)
X_pca2 = pca.transform(X)
pca = PCA(n_components=3)
pca.fit(X)
X_pca3 = pca.transform(X)
#Calcul de la distance de Mahalanobis aux centroïdes
mean_0 = np.mean(X_pca2[index_0],axis=0)
mean_1 = np.mean(X_pca2[index_1],axis=0)
mean_2 = np.mean(X_pca2[index_2],axis=0)
cov = np.cov(X_pca2.T)
y_pred = []
for i in range(X_pca2.shape[0]):
dist_0 = mahalanobis(X_pca2[i, :], mean_0,cov)
dist_1 = mahalanobis(X_pca2[i, :], mean_1,cov)
dist_2 = mahalanobis(X_pca2[i, :], mean_2,cov)
y_pred.append(np.argmin([dist_0,dist_1,dist_2]))
print("d_mahalanobis globale 2CP:", np.mean(y_==y_pred))
mean_0 = np.mean(X_pca3[index_0],axis=0)
mean_1 = np.mean(X_pca3[index_1],axis=0)
mean_2 = np.mean(X_pca3[index_2],axis=0)
cov = np.cov(X_pca3.T)
y_pred = []
for i in range(X_pca3.shape[0]):
dist_0 = mahalanobis(X_pca3[i, :], mean_0,cov)
dist_1 = mahalanobis(X_pca3[i, :], mean_1,cov)
dist_2 = mahalanobis(X_pca3[i, :], mean_2,cov)
y_pred.append(np.argmin([dist_0,dist_1,dist_2]))
print("d_mahalanobis globale 3CP:", np.mean(y_==y_pred))
d_mahalanobis globale 2CP: 0.8305084745762712 d_mahalanobis globale 3CP: 0.7966101694915254
PS : À titre indicatif, dans le cas où les données auraient été normalisées, on aurait obtenu:
d_mahalanobis globale 2CP: 0.8983050847457628
d_mahalanobis globale 3CP: 0.7966101694915254
Mais dans ce cas, les résultats seraient difficilement interprétables.
mean_0 = np.mean(X_pca2[index_0],axis=0)
mean_1 = np.mean(X_pca2[index_1],axis=0)
mean_2 = np.mean(X_pca2[index_2],axis=0)
cov0 = np.cov(X_pca2[index_0].T)
cov1 = np.cov(X_pca2[index_1].T)
cov2 = np.cov(X_pca2[index_2].T)
y_pred = []
for i in range(X_pca2.shape[0]):
dist_0 = mahalanobis(X_pca2[i, :], mean_0,cov0)
dist_1 = mahalanobis(X_pca2[i, :], mean_1,cov1)
dist_2 = mahalanobis(X_pca2[i, :], mean_2,cov2)
y_pred.append(np.argmin([dist_0,dist_1,dist_2]))
print("d_mahalanobis locale 2CP:", np.mean(y_==y_pred))
mean_0 = np.mean(X_pca3[index_0],axis=0)
mean_1 = np.mean(X_pca3[index_1],axis=0)
mean_2 = np.mean(X_pca3[index_2],axis=0)
cov0 = np.cov(X_pca3[index_0].T)
cov1 = np.cov(X_pca3[index_1].T)
cov2 = np.cov(X_pca3[index_2].T)
y_pred = []
for i in range(X_pca3.shape[0]):
dist_0 = mahalanobis(X_pca3[i, :], mean_0,cov0)
dist_1 = mahalanobis(X_pca3[i, :], mean_1,cov1)
dist_2 = mahalanobis(X_pca3[i, :], mean_2,cov2)
y_pred.append(np.argmin([dist_0,dist_1,dist_2]))
print("d_mahalanobis locale 3CP:", np.mean(y_==y_pred))
d_mahalanobis locale 2CP: 0.8305084745762712 d_mahalanobis locale 3CP: 0.8813559322033898
PS : À titre indicatif, dans le cas où les données auraient été normalisées, on aurait obtenu:
d_mahalanobis locale 2CP: 0.8813559322033898
d_mahalanobis locale 3CP: 0.8813559322033898
Mais dans ce cas également, les résultats seraient difficilement interprétables.
En résumé les résultats sont les suivants:
d_euclidienne 2CP: 0.8983050847457628
d_euclidienne 3CP: 0.8813559322033898
d_mahalanobis globale 2CP: 0.8305084745762712
d_mahalanobis globale 3CP: 0.7966101694915254
d_mahalanobis locale 2CP: 0.8305084745762712
d_mahalanobis locale 3CP: 0.8813559322033898
Il reste deux cas à tester: appliquer 'Mahalanobis globale et locale' sur les données sans ACP. En effet, la distance de Mahalanobis, ne requiert ni normalisation, ni décorrélation des attributs au préalable. Elle se charge elle-même de réaliser les transformations (ACP + normalisation) en utilisant l'inverse de la matrice de covariance. Les résultats sont ci-dessous.
X_ = X.values
mean_0 = np.mean(X_[index_0],axis=0)
mean_1 = np.mean(X_[index_1],axis=0)
mean_2 = np.mean(X_[index_2],axis=0)
cov = np.cov(X_.T)
y_pred = []
for i in range(X_.shape[0]):
dist_0 = mahalanobis(X_[i, :], mean_0,cov)
dist_1 = mahalanobis(X_[i, :], mean_1,cov)
dist_2 = mahalanobis(X_[i, :], mean_2,cov)
y_pred.append(np.argmin([dist_0,dist_1,dist_2]))
print("d_mahalanobis globale :", np.mean(y_==y_pred))
cov0 = np.cov(X_[index_0].T)
cov1 = np.cov(X_[index_1].T)
cov2 = np.cov(X_[index_2].T)
y_pred = []
for i in range(X_.shape[0]):
dist_0 = mahalanobis(X_[i, :], mean_0,cov0)
dist_1 = mahalanobis(X_[i, :], mean_1,cov1)
dist_2 = mahalanobis(X_[i, :], mean_2,cov2)
y_pred.append(np.argmin([dist_0,dist_1,dist_2]))
print("d_mahalanobis locale :", np.mean(y_==y_pred))
d_mahalanobis globale : 0.847457627118644 d_mahalanobis locale : 0.9322033898305084
La distance de Mahalanobis locale sans transformation des données initiales retourne donc le meilleur score: 0.93
La distance de Mahalanobis locale (une matrice de covariance par classe) retourne un meilleur score que la distance de Mahalanobis globale. On pouvait s'en douter en observant les orientations différentes des ellipses définies pour chacune des classes dans les 'scatter plots'.
Nous avons déjà calculer plus haut le score pour la méthode du plus proche centroïde avec la distance de Mahalanobis locale : 0.93. Nous calculons maintenant le score pour la méthode des k=5 plus proches voisins.
y_pred = []
for i in range(X_.shape[0]):
dist_array = []
for j in range(X_.shape[0]):
if j in index_0[0]:
d = mahalanobis(X_[i, :], X_[j, :],cov0)
elif j in index_1[0]:
d = mahalanobis(X_[i, :], X_[j, :],cov1)
else:
d = mahalanobis(X_[i, :], X_[j, :],cov2)
dist_array.append(d)
five_neighbors = np.argsort(dist_array)[1:6]
y_neighbors = y_[five_neighbors]
y_pred.append(np.bincount(y_neighbors).argmax())
print("d_mahalanobis locale (5nn):", np.mean(y_==y_pred))
d_mahalanobis locale (5nn): 0.864406779661017
d_mahalanobis locale avec centroïde : 0.9322033898305084
d_mahalanobis locale avec 5nn: 0.864406779661017
La méthode retournant le meilleur score est la méthode du plus proche centroïde.
# Author: Ron Weiss <ronweiss@gmail.com>, Gael Varoquaux
# Modified by Thierry Guillemot <thierry.guillemot.work@gmail.com>
# Modified by Davy Ouedraogo <wend.yam.donald.davy.ouedraogo@usherbrooke.ca>
# Initial code at https://scikit-learn.org/stable/auto_examples/mixture/plot_gmm_covariances.html
import matplotlib as mpl
from sklearn.mixture import GaussianMixture
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X_,y_,test_size=0.25,random_state=0)
colors = sns.color_palette("husl",3)
def make_ellipses(gmm, ax):
for n, color in enumerate(colors):
if gmm.covariance_type == 'full':
covariances = gmm.covariances_[n][:2, :2]
elif gmm.covariance_type == 'tied':
covariances = gmm.covariances_[:2, :2]
elif gmm.covariance_type == 'diag':
covariances = np.diag(gmm.covariances_[n][:2])
elif gmm.covariance_type == 'spherical':
covariances = np.eye(gmm.means_.shape[1]) * gmm.covariances_[n]
v, w = np.linalg.eigh(covariances)
u = w[0] / np.linalg.norm(w[0])
angle = np.arctan2(u[1], u[0])
angle = 180 * angle / np.pi # convert to degrees
v = 2. * np.sqrt(2.) * np.sqrt(v)
ell = mpl.patches.Ellipse(gmm.means_[n, :2], v[0], v[1],
180 + angle, color=color)
ell.set_clip_box(ax.bbox)
ell.set_alpha(0.5)
ax.add_artist(ell)
ax.set_aspect('equal', 'datalim')
n_classes = 3
# Try GMMs using different types of covariances.
estimators = {cov_type: GaussianMixture(n_components=n_classes,
covariance_type=cov_type, max_iter=100, random_state=0)
for cov_type in ['spherical', 'diag', 'tied', 'full']}
n_estimators = len(estimators)
plt.figure(figsize=(3 * n_estimators // 2, 6))
plt.subplots_adjust(bottom=.01, top=0.95, hspace=.15, wspace=.05,
left=.01, right=.99)
for index, (name, estimator) in enumerate(estimators.items()):
# Since we have class labels for the training data, we can
# initialize the GMM parameters in a supervised manner.
estimator.means_init = np.array([X_train[y_train == i].mean(axis=0)
for i in range(n_classes)])
# Train the other parameters using the EM algorithm.
estimator.fit(X_train)
h = plt.subplot(2, n_estimators // 2, index + 1)
make_ellipses(estimator, h)
for n, color in enumerate(colors):
data = X_[y_ == n]
plt.scatter(data[:, 0], data[:, 1], s = 0.8, color=color,
label=n)
# Plot the test data with crosses
for n, color in enumerate(colors):
data = X_test[y_test == n]
plt.scatter(data[:, 0], data[:, 1], marker='x', color=color)
y_train_pred = estimator.predict(X_train)
train_accuracy = np.mean(y_train_pred==y_train)#
plt.text(0.05, 0.9, 'Train accuracy: %.1f' % train_accuracy,
transform=h.transAxes)
y_test_pred = estimator.predict(X_test)
test_accuracy = np.mean(y_test_pred==y_test)
#np.mean(y_test_pred.ravel() == y_test.ravel()) * 100
plt.text(0.05, 0.8, 'Test accuracy: %.1f' % test_accuracy,
transform=h.transAxes)
plt.xticks(())
plt.yticks(())
plt.title(name)
plt.legend(scatterpoints=1, loc='lower right', prop=dict(size=12))
plt.show()
C:\Users\ouew2201\AppData\Local\Temp\ipykernel_23424\2618673051.py:28: MatplotlibDeprecationWarning: Passing the angle parameter of __init__() positionally is deprecated since Matplotlib 3.6; the parameter will become keyword-only two minor releases later. ell = mpl.patches.Ellipse(gmm.means_[n, :2], v[0], v[1], C:\Users\ouew2201\AppData\Local\Temp\ipykernel_23424\2618673051.py:28: MatplotlibDeprecationWarning: Passing the angle parameter of __init__() positionally is deprecated since Matplotlib 3.6; the parameter will become keyword-only two minor releases later. ell = mpl.patches.Ellipse(gmm.means_[n, :2], v[0], v[1], C:\Users\ouew2201\AppData\Local\Temp\ipykernel_23424\2618673051.py:28: MatplotlibDeprecationWarning: Passing the angle parameter of __init__() positionally is deprecated since Matplotlib 3.6; the parameter will become keyword-only two minor releases later. ell = mpl.patches.Ellipse(gmm.means_[n, :2], v[0], v[1], C:\Users\ouew2201\AppData\Local\Temp\ipykernel_23424\2618673051.py:28: MatplotlibDeprecationWarning: Passing the angle parameter of __init__() positionally is deprecated since Matplotlib 3.6; the parameter will become keyword-only two minor releases later. ell = mpl.patches.Ellipse(gmm.means_[n, :2], v[0], v[1],
Les 4 options pour les matrices de covariance retournent de bons résultats (J'ai considéré le jeu de données initial sur 4 dimensions. C'est normal d'avoir des scores faibles après normalisation car la variance des attributs est réduite).
full : chaque classe a sa matrice de covariance locale
tied : toutes les classes partagent la même matrice de covariance globale
diag: chaque classe a sa matrice de covariance locale diagonale, i.e on ne tient compte que des différences de variances entre les attributs.
spherical: chaque attribut dans chaque classe a sa propre variance
On se serait attendu à ce que l'option 'full' qui applique la distance de Mahalanobis locale retourne le meilleur score, mais cette option a tendance à retourner un modèle sur-entraîné sur les données d'entraînement et performe moins bien sur les données de test lorsque le jeu de données est petit.
cov0 = np.cov(X_[index_0].T)
cov1 = np.cov(X_[index_1].T)
cov2 = np.cov(X_[index_2].T)
## méthode du plus proche centroïde (nc)
d = np.array([52.1, 23.0, 6.1, 16.5])
# Si votre choix nécessite les données 2D ou 3D
# X.loc['id100'] = [52.1, 23.0, 6.1, 16.5]
# d = pca.transform(X.tail(1))[0]
dist_0 = mahalanobis(d, mean_0,cov0)
dist_1 = mahalanobis(d, mean_1,cov1)
dist_2 = mahalanobis(d, mean_2,cov2)
y_pred = np.argmin([dist_0,dist_1,dist_2])
print("classe prédite avec nc :", y_pred)
## méthode des 5 plus proches voisins (5nn)
dist_array = []
for i in range(X_pca2.shape[0]):
if i in index_0[0]:
dist = mahalanobis(d, X_[i, :],cov0)
elif i in index_1[0]:
dist = mahalanobis(d, X_[i, :],cov1)
else:
dist = mahalanobis(d, X_[i, :],cov2)
dist_array.append(dist)
five_neighbors = np.argsort(dist_array)[1:6]
y_neighbors = y_[five_neighbors]
y_pred = np.bincount(y_neighbors).argmax()
print("classe prédite avec 5nn :", y_pred)
## Modèle de mélange gaussien (gmm)
gmm = GaussianMixture(n_components=3, covariance_type='diag', max_iter=100, random_state=0)
gmm.means_init = np.array([X_train[y_train == i].mean(axis=0) for i in range(3)])
gmm.fit(X_train)
y_pred = gmm.predict([d])
print("classe prédite avec gmm (option diag) :", y_pred[0])
classe prédite avec nc : 1 classe prédite avec 5nn : 1 classe prédite avec gmm (option diag) : 1