.. _winesknnacprst: ================================= Plus proches voisins - projection ================================= .. only:: html **Links:** :download:`notebook `, :downloadlink:`html `, :download:`PDF `, :download:`python `, :downloadlink:`slides `, :githublink:`GitHub|_doc/notebooks/lectures/wines_knn_acp.ipynb|*` On projette le jeu de données initiale selon les premiers axes d’une `analyse en composantes principales (ACP) `__. .. code:: ipython3 %matplotlib inline .. code:: ipython3 from papierstat.datasets import load_wines_dataset df = load_wines_dataset() X = df.drop(['quality', 'color'], axis=1) y = df['quality'] On utilise la classe `PCA `__. .. code:: ipython3 from sklearn.decomposition import PCA from sklearn.preprocessing import normalize pca = PCA(n_components=5) pca.fit(X) .. parsed-literal:: PCA(copy=True, iterated_power='auto', n_components=5, random_state=None, svd_solver='auto', tol=0.0, whiten=False) On regarde la variance expliquée par chaque axe : la valeur propre. .. code:: ipython3 import pandas eig = pandas.DataFrame(dict(valeur=pca.explained_variance_ratio_)) ax = eig.plot(kind='bar', figsize=(3,3)) ax.set_title("Valeurs propres ACP"); .. image:: wines_knn_acp_6_0.png Le premier explique une grande part de la variance, trop grande. C’est louche. Regardons les coordonnées du premier axe. .. code:: ipython3 v1 = pandas.DataFrame(dict(valeur=pca.components_[0,:])) v1.index = df.columns[:-2] ax = v1.plot(kind='bar') ax.set_title("Coordonnées du premier axe de l'ACP"); .. image:: wines_knn_acp_8_0.png Il est quasiment égal à une seule coordonnées. Cela suppose que les variables ont des échelles différentes. .. code:: ipython3 df.iloc[:, 3:8].describe() .. raw:: html
residual_sugar chlorides free_sulfur_dioxide total_sulfur_dioxide density
count 6497.000000 6497.000000 6497.000000 6497.000000 6497.000000
mean 5.443235 0.056034 30.525319 115.744574 0.994697
std 4.757804 0.035034 17.749400 56.521855 0.002999
min 0.600000 0.009000 1.000000 6.000000 0.987110
25% 1.800000 0.038000 17.000000 77.000000 0.992340
50% 3.000000 0.047000 29.000000 118.000000 0.994890
75% 8.100000 0.065000 41.000000 156.000000 0.996990
max 65.800000 0.611000 289.000000 440.000000 1.038980
.. code:: ipython3 mean_val = df.describe().T['mean'] mean_val.index = df.columns[:-1] ax = mean_val.plot(kind='bar', figsize=(6,3)) ax.set_title("Moyenne de chaque variable"); .. image:: wines_knn_acp_11_0.png La valeur moyenne de chaque variable ressemble étrangement aux coordonnées du premier axe de l’ACP. Il faut normaliser les données avec `normalize `__. .. code:: ipython3 pca = PCA(n_components=5) Xn = normalize(X) pca.fit(Xn) .. parsed-literal:: PCA(copy=True, iterated_power='auto', n_components=5, random_state=None, svd_solver='auto', tol=0.0, whiten=False) C’est beaucoup mieux. .. code:: ipython3 import pandas eig = pandas.DataFrame(dict(valeur=pca.explained_variance_ratio_)) ax = eig.plot(kind='bar', figsize=(3,3)) ax.set_title("Valeur propres de l'ACP\naprès normalisation"); .. image:: wines_knn_acp_15_0.png .. code:: ipython3 v2 = pandas.DataFrame(pca.components_[0:2,:]).T v2.index = df.columns[:-2] v2.columns = ['v1', 'v2'] ax = v2.plot(y=['v1', 'v2'], kind='bar', figsize=(6,3)) ax.legend(loc='upper left') ax.set_title("Comparaison des coordonnées\ndes deux premiers axes de l'ACP"); .. parsed-literal:: c:\Python364_x64\lib\site-packages\pandas\plotting\_core.py:1716: UserWarning: Pandas doesn't allow columns to be created via a new attribute name - see https://pandas.pydata.org/pandas-docs/stable/indexing.html#attribute-access series.name = label .. image:: wines_knn_acp_16_1.png L’alcool, l’acidité, le dioxyde, le pH semble jouer un rôle plus grand que les autres variables. On projette maintenant les observations. .. code:: ipython3 proj = pca.transform(Xn) .. code:: ipython3 pl = pandas.DataFrame(proj[:, :3]) pl.columns = ['v1', 'v2', 'v3'] pl['quality'] = df['quality'] pl['color'] = df['color'] Premier graphe selon les couleurs. .. code:: ipython3 import seaborn ax = seaborn.lmplot(x="v1", y="v2", hue="color", truncate=True, data=pl, scatter_kws={"s": 1}, fit_reg=False, size=3) ax.ax.set_title("Projection des vins sur\nles deux premiers axes de l'ACP"); .. image:: wines_knn_acp_21_0.png Autre façon de tracer le même graphe. .. code:: ipython3 import matplotlib.pyplot as plt fig, ax = plt.subplots(1, 2, figsize=(8, 4)) red = pl[pl.color == 'red'] white = pl[pl.color == 'white'] # second graphd avec pandas red.plot(x='v1', y='v2', label='red', kind='scatter', ax=ax[0], color="red", s=1) white.plot(x='v1', y='v2', label='white', kind='scatter', ax=ax[0], color="blue", s=1) # troisième graphe pour la densité seaborn.kdeplot(red.v1, red.v2, cmap="Reds", shade=True, shade_lowest=False, ax=ax[1]) seaborn.kdeplot(white.v1, white.v2, cmap="Blues", shade=True, shade_lowest=False, ax=ax[1]) ax[0].set_title("Projection des vins sur\nles deux premiers axes de l'ACP") ax[1].set_title("Estimation de la densité"); .. image:: wines_knn_acp_23_0.png Avec le troisième axe. .. code:: ipython3 import matplotlib.pyplot as plt fig, ax = plt.subplots(1, 2, figsize=(8, 4)) red = pl[pl.color == 'red'] white = pl[pl.color == 'white'] red.plot(x='v1', y='v2', label='red', kind='scatter', ax=ax[0], color="red", s=1) white.plot(x='v1', y='v2', label='white', kind='scatter', ax=ax[0], color="blue", s=1) ax[0].set_title("Axes 1, 2") red.plot(x='v1', y='v3', label='red', kind='scatter', ax=ax[1], color="red", s=1) white.plot(x='v1', y='v3', label='white', kind='scatter', ax=ax[1], color="blue", s=1) ax[1].set_title("Axes 1, 3"); .. image:: wines_knn_acp_25_0.png On représente maintenant les notes des vins. .. code:: ipython3 import matplotlib.pyplot as plt import matplotlib.colors as colors import matplotlib.cm as cmx fig, axs = plt.subplots(1, 3, figsize=(12, 4)) # Choisir un dégragé ici cmap = plt.get_cmap('plasma') cnorm = colors.Normalize(vmin=pl['quality'].min(), vmax=pl['quality'].max()) scalar = cmx.ScalarMappable(norm=cnorm, cmap=cmap) for i, data, title in [(0, pl, 'tous'), (1, red, 'red'), (2, white, 'white')]: ax = axs[i] # On trace les points pour que le texte n'apparaissent pas en dehors des zones pl.plot(x='v1', y='v2', kind='scatter', color="white", ax=ax) for note in sorted(set(data['quality'])): sub = data[data.quality == note] if sub.shape[0] > 100: sub = sub.sample(n=30) color = scalar.to_rgba(note) for i, row in enumerate(sub.itertuples()): ax.text(row[1], row[2], str(row[4]), color=color) ax.set_title(title); .. image:: wines_knn_acp_27_0.png Les vins rouges et blancs apparaissent comme très différents, cela vaudra sans doute le coup de faire deux modèles si la performance n’est pas assez bonne. Les bonnes notes ne se détache pas particulièremnt sur ces graphes. Le problème est peut-être simple mais ce ne sont pas ces graphes qui vont nous le dire.