Analyse de marché La poule qui chante¶

Table des matières :¶

  1. Mise en place

    • 1a. Import librairies

    • 1b. Fonctions

    • 1c. Import des données

    • 1d. Archivage des concurrents

    • 1e. Archivage des outliers

    • 1f. Essai avec score

  2. Analyses descriptives univariées

  3. Analyses descriptives bivariées

  4. Analyse des Composantes Principales (ACP)

    • 4a. Préparation des données

      • i. Séparation des données

      • ii. Standardisation des données

      • iii. Réduction des dimensions

    • 4b. Analyse de l'ACP

      • i. Choix des composantes principales

      • ii. Qualité de représentation des variables (cos2)

      • iii. Visualisation & Interprétation

  5. Classification Ascendante Hiérarchique (CAH)

    • 5a. Préparation des données

      • i. Séparation des données

      • ii. Standardisation des données

    • 5b. Clustering

      • i. Linkage

      • ii. Dendrogram

      • iii. Clusters

  6. Kmeans

  7. Kmeans sur F1 & F2

  8. Comparaison des listes de pays intéressant pour l'exportation de volaille

    • 8a. Détermination du cluster à garder selon la méthode de sélection

  9. Profils du cluster gardé

    • 9a. PCA sur données des pays concernés du cluster sélectionné

    • 9b. Kmeans sur les pays sélectionnés

Mise en place¶

Import librairies¶

In [ ]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from math import *
import plotly.graph_objects as go

from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import AgglomerativeClustering
from sklearn import preprocessing
from sklearn.cluster import KMeans
from scipy.cluster.hierarchy import dendrogram, linkage


import warnings
warnings.filterwarnings("ignore")
warnings.warn("DelftStack")
warnings.warn("Do not show this message")

Fonctions¶

In [ ]:
def show_outliers_str(df, colonne):
    """
    Crée une boucle qui vient afficher sur un graphique les pays avec des valeurs atypiques

    Prérequis : 
        - 1 dataframe avec la variable Zone + 1 variables quantitative minimum
    """


    #Cibler les outliers
    Q1 = df[colonne].quantile(0.25)
    Q3 = df[colonne].quantile(0.75)
    IQR = Q3-Q1

    out_inf = Q1 - (IQR * 1.5)
    out_sup = Q3 + (IQR * 1.5)

    zone_outlier = df.loc[(df[colonne] < out_inf) | (df[colonne] > out_sup)]["Zone"].tolist()
    y_outlier = df.loc[(df[colonne] < out_inf) | (df[colonne] > out_sup)][colonne].tolist()

    #Affichage zones outliers
    for zone in range(len(zone_outlier)):
        plt.text(x=0.1, s=zone_outlier[zone], y=y_outlier[zone])
In [ ]:
def correlation_graph(pca,
                      x_y,
                      features) :
    """Affiche le graphe des correlations

    Positional arguments :
    -----------------------------------
    pca : sklearn.decomposition.PCA : notre objet PCA qui a été fit
    x_y : list ou tuple : le couple x,y des plans à afficher, exemple [0,1] pour F1, F2
    features : list ou tuple : la liste des features (ie des dimensions) à représenter
    """

    # Extrait x et y
    x,y=x_y

    # Taille de l'image (en inches)
    fig, ax = plt.subplots(figsize=(10, 9))

    # Pour chaque composante :
    for i in range(0, pca.components_.shape[1]):

        # Les flèches
        ax.arrow(0,0,
                pca.components_[x, i],
                pca.components_[y, i],
                head_width=0.07,
                head_length=0.07,
                width=0.02, )

        # Les labels
        plt.text(pca.components_[x, i] + 0.05,
                pca.components_[y, i] + 0.05,
                features[i])

    # Affichage des lignes horizontales et verticales
    plt.plot([-1, 1], [0, 0], color='grey', ls='--')
    plt.plot([0, 0], [-1, 1], color='grey', ls='--')

    # Nom des axes, avec le pourcentage d'inertie expliqué
    plt.xlabel('F{} ({}%)'.format(x+1, round(100*pca.explained_variance_ratio_[x],1)))
    plt.ylabel('F{} ({}%)'.format(y+1, round(100*pca.explained_variance_ratio_[y],1)))

    plt.title("Cercle des corrélations (F{} et F{})".format(x+1, y+1))

    # Le cercle
    an = np.linspace(0, 2 * np.pi, 100)
    plt.plot(np.cos(an), np.sin(an))  # Add a unit circle for scale

    # Axes et display
    plt.axis('equal')
    #plt.show(block=False)
In [ ]:
def display_factorial_planes(   X_projected,
                                x_y,
                                pca=None,
                                labels = None,
                                clusters=None,
                                alpha=1,
                                figsize=[10,8],
                                marker="." ):
    """
    Affiche la projection des individus

    Positional arguments :
    -------------------------------------
    X_projected : np.array, pd.DataFrame, list of list : la matrice des points projetés
    x_y : list ou tuple : le couple x,y des plans à afficher, exemple [0,1] pour F1, F2

    Optional arguments :
    -------------------------------------
    pca : sklearn.decomposition.PCA : un objet PCA qui a été fit, cela nous permettra d'afficher la variance de chaque composante, default = None
    labels : list ou tuple : les labels des individus à projeter, default = None
    clusters : list ou tuple : la liste des clusters auquel appartient chaque individu, default = None
    alpha : float in [0,1] : paramètre de transparence, 0=100% transparent, 1=0% transparent, default = 1
    figsize : list ou tuple : couple width, height qui définit la taille de la figure en inches, default = [10,8]
    marker : str : le type de marker utilisé pour représenter les individus, points croix etc etc, default = "."
    """

    # Transforme X_projected en np.array
    X_ = np.array(X_projected)

    # On définit la forme de la figure si elle n'a pas été donnée
    if not figsize:
        figsize = (7,6)

    # On gère les labels
    if  labels is None :
        labels = []
    try :
        len(labels)
    except Exception as e :
        raise e

    # On vérifie la variable axis
    if not len(x_y) ==2 :
        raise AttributeError("2 axes sont demandées")
    if max(x_y )>= X_.shape[1] :
        raise AttributeError("la variable axis n'est pas bonne")

    # on définit x et y
    x, y = x_y

    # Initialisation de la figure
    fig, ax = plt.subplots(1, 1, figsize=figsize)

    # On vérifie s'il y a des clusters ou non
    c = None if clusters is None else clusters

    # Les points
    # plt.scatter(   X_[:, x], X_[:, y], alpha=alpha,
    #                     c=c, cmap="Set1", marker=marker)
    sns.scatterplot(data=None, x=X_[:, x], y=X_[:, y], hue=c)

    # Si la variable pca a été fournie, on peut calculer le % de variance de chaque axe
    if pca :
        v1 = str(round(100*pca.explained_variance_ratio_[x]))  + " %"
        v2 = str(round(100*pca.explained_variance_ratio_[y]))  + " %"
    else :
        v1=v2= ''

    # Nom des axes, avec le pourcentage d'inertie expliqué
    ax.set_xlabel(f'F{x+1} {v1}')
    ax.set_ylabel(f'F{y+1} {v2}')

    # Valeur x max et y max
    x_max = np.abs(X_[:, x]).max() *1.1
    y_max = np.abs(X_[:, y]).max() *1.1

    # On borne x et y
    ax.set_xlim(left=-x_max, right=x_max)
    ax.set_ylim(bottom= -y_max, top=y_max)

    # Affichage des lignes horizontales et verticales
    plt.plot([-x_max, x_max], [0, 0], color='grey', alpha=0.8)
    plt.plot([0,0], [-y_max, y_max], color='grey', alpha=0.8)

    # Affichage des labels des points
    if len(labels) :
        for i,(_x,_y) in enumerate(X_[:,[x,y]]):
            plt.text(_x, _y+0.05, labels[i], fontsize='14', ha='center',va='center')

    # Titre et display
    plt.title(f"Projection des individus (sur F{x+1} et F{y+1})")
    #plt.show()
In [ ]:
def make_spider( df, row, title, color):

    """
    À utiliser avec cette boucle pour afficher un graphique pour chaque ligne (cluster):
    for row in range(0, len(df.index)):
        make_spider( row=row, title='Cluster '+df['Cluster'][row], color=my_palette(row))
        
    """
    my_dpi=100
    plt.figure(figsize=(1700/my_dpi, 1700/my_dpi), dpi=my_dpi)
    
    # number of variable
    categories=list(df)[1:]
    N = len(categories)

    # What will be the angle of each axis in the plot? (we divide the plot / number of variable)
    angles = [n / float(N) * 2 * pi for n in range(N)]
    angles += angles[:1]

    # Initialise the spider plot
    ax = plt.subplot(5,2,row+1, polar=True, )
    plt.subplots_adjust(hspace=0.8)
    
     
    #Go through labels and adjust alignment based on where it is in the circle.
    for label, angle in zip(ax.get_xticklabels(), angles):
        if angle in (0, np.pi) :
            label.set_horizontalalignment('center')
        elif 0 < angle < np.pi :
            label.set_horizontalalignment('left')       
        else :
            label.set_horizontalalignment('right')

    # If you want the first axis to be on top:
    ax.set_theta_offset(pi / 2)
    ax.set_theta_direction(-1)

    # Draw one axe per variable + add labels labels yet
    plt.xticks(angles[:-1], categories, color='grey', size=8)
    

    # Draw ylabels
    ax.set_rlabel_position(0)
    plt.yticks([-2,-1,0,1,2], color="grey", size=12)
    plt.ylim(-3, 3)

    # Ind1
    values=df.loc[row].drop('Cluster').values.flatten().tolist()
    values += values[:1]
    ax.plot(angles, values, color=color, linewidth=2, linestyle='solid')
    ax.fill(angles, values, color=color, alpha=0.4)

    # Add a title
    plt.title(title, size=18, color=color, y=1.2)

Import des données¶

In [ ]:
df_original = pd.read_csv("database/BDD.csv")
In [ ]:
df_original
Out[ ]:
Zone Ratio volaille/carne en % Variation cumulée - Proportion de volaille vs total viande Disponibilité volaille (kg/personne/an) exportation / production en % Stabilite politique Variation cumulée - PIB Variation cumulée - Taux de change en % PIB Tarif EAV en % MNT Distance Cas maladie recense Statut
0 Afghanistan 22.222222 0.000000 2.0 0.000000 -2.80 -27.660894 21.313925 2096.1 5.0 0.0 5490.0 1.0 Pays moins avancé (PMA)
1 Afrique du Sud 58.730159 5.519604 37.0 3.783784 -0.28 -4.577614 10.836361 13950.5 0.0 39.0 8751.0 378.0 Pays moins avancé (PMA)
2 Albanie 27.906977 51.602564 12.0 0.000000 0.38 13.666119 -13.081201 12771.0 10.0 0.0 1461.0 9.0 En developpement
3 Algérie 35.000000 -4.761905 7.0 0.000000 -0.92 -6.517634 21.708918 11809.5 30.0 0.0 1158.0 7.0 En developpement
4 Allemagne 22.222222 5.194805 18.0 42.668428 0.59 0.203876 -4.486119 53071.5 0.0 93.0 682.0 2031.0 Développé
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
171 Uruguay 15.000000 143.386243 9.0 9.090909 1.05 -1.325572 51.882994 23107.0 10.0 12.0 10772.0 17.0 En developpement
172 Vanuatu 40.000000 22.093023 16.0 0.000000 0.70 -8.188176 1.513313 3031.2 20.0 0.0 16440.0 0.0 En developpement
173 Viet Nam 23.809524 61.538462 10.0 0.915332 0.23 17.429591 3.530142 9050.7 25.4 0.0 9869.0 551.0 En developpement
174 Zambie 15.000000 11.111111 3.0 2.040816 0.15 -4.673833 110.289234 3395.5 40.0 0.0 7221.0 0.0 Pays moins avancé (PMA)
175 Zimbabwe 9.615385 30.000000 5.0 0.000000 -0.71 -9.293250 0.000000 2331.8 102.6 11.0 7760.0 0.0 Pays moins avancé (PMA)

176 rows × 14 columns

Archivage des concurrents¶

In [ ]:
#Archivage
df_archive = df_original.loc[df_original["Zone"] == "Chine - RAS de Hong-Kong"]

#Suppression d'abord de la Chine (HK) du dataframe original pour ne pas impacter la moyenne et l'ecart-type
to_drop = df_original.loc[df_original["Zone"] == "Chine - RAS de Hong-Kong"].index.tolist()
df_original.drop(index=to_drop, inplace=True)
In [ ]:
#Essai sans concurrent
ratio_export_moy = df_original["exportation / production en %"].mean()
ratio_export_moy_marge = df_original["exportation / production en %"].std()
ratio_export_marge = ratio_export_moy + ratio_export_moy_marge

#Archivage
df_archive = pd.concat([df_archive, df_original.loc[df_original["exportation / production en %"] > ratio_export_marge]])

#Suppression ou non des concurrents
to_drop = df_original.loc[df_original["exportation / production en %"] > ratio_export_marge].index.tolist()
df_original.drop(index=to_drop, inplace=True)
In [ ]:
ratio_export_marge
Out[ ]:
42.42152276285621

Archivage des outliers¶

In [ ]:
#Variation cumulée - Taux de change > 3000 %
df_archive = pd.concat([df_archive, df_original.loc[df_original["Zone"] == "Soudan"]])
In [ ]:
#Variation cumulée - Taux de change > 3000 %
drop_soudan = df_original.loc[df_original["Zone"] == "Soudan"].index.tolist()
df_original.drop(index=drop_soudan, inplace=True)
In [ ]:
df_archive 
Out[ ]:
Zone Ratio volaille/carne en % Variation cumulée - Proportion de volaille vs total viande Disponibilité volaille (kg/personne/an) exportation / production en % Stabilite politique Variation cumulée - PIB Variation cumulée - Taux de change en % PIB Tarif EAV en % MNT Distance Cas maladie recense Statut
35 Chine - RAS de Hong-Kong 32.394366 22.632519 46.0 2766.666667 0.83 0.227097 -0.256632 59842.2 0.0 20.0 9707.0 7.0 Développé
4 Allemagne 22.222222 5.194805 18.0 42.668428 0.59 0.203876 -4.486119 53071.5 0.0 93.0 682.0 2031.0 Développé
11 Autriche 21.176471 5.628655 18.0 52.702703 1.05 -0.095804 -4.486119 54173.0 0.0 93.0 921.0 76.0 Développé
18 Belgique 20.000000 13.636364 11.0 141.684665 0.43 2.571651 -4.486119 50442.3 0.0 93.0 379.0 374.0 Développé
45 Danemark 32.467532 -17.577465 25.0 91.447368 0.87 4.707651 -4.782455 55356.7 0.0 93.0 1151.0 374.0 Développé
50 Émirats arabes unis 61.363636 -4.938272 27.0 56.140351 0.62 -2.035054 0.000000 71182.4 5.0 114.0 5171.0 0.0 Développé
53 Estonie 33.870968 -12.676056 21.0 55.000000 0.65 14.475236 -4.486119 33821.9 0.0 93.0 2029.0 63.0 Développé
72 Hongrie 31.250000 1.463415 25.0 42.596349 0.81 13.871008 10.460646 29501.1 0.0 93.0 1234.0 746.0 En developpement
78 Irlande 31.578947 5.555556 24.0 63.698630 1.00 31.829009 -4.486119 77749.2 0.0 93.0 949.0 146.0 Développé
91 Lettonie 30.434783 12.524462 21.0 60.606061 0.46 11.885149 -4.486119 28673.6 0.0 93.0 1832.0 26.0 En developpement
95 Lituanie 35.000000 -13.941480 28.0 50.746269 0.78 16.419988 -4.486119 33761.9 0.0 93.0 1734.0 106.0 Développé
96 Luxembourg 22.352941 -1.162791 19.0 50.746269 1.33 0.607030 -4.486119 114986.0 0.0 93.0 384.0 9.0 Développé
97 Macédoine du Nord 50.000000 -17.073171 20.0 50.000000 -0.25 4.825391 -4.689067 15706.5 0.0 0.0 1558.0 2.0 En developpement
113 Namibie 36.363636 1.315789 12.0 120.000000 0.63 -11.586505 11.010186 10335.3 0.0 5.0 7755.0 7.0 Pays moins avancé (PMA)
121 Oman 47.826087 -11.025145 22.0 283.333333 0.75 0.223272 0.000000 34218.4 5.0 64.0 5485.0 1.0 En developpement
128 Pays-Bas (Royaume des) 25.000000 -25.000000 16.0 129.026388 0.92 2.775166 -4.486119 55088.6 0.0 93.0 556.0 825.0 Développé
131 Pologne 37.931034 -8.171604 33.0 44.086022 0.52 16.547778 2.185130 29958.1 0.0 93.0 1318.0 464.0 Développé
153 Slovaquie 23.728814 -14.397321 14.0 47.887324 0.91 6.002342 -4.486119 30061.6 0.0 93.0 1213.0 24.0 Développé
154 Slovénie 33.333333 22.950820 24.0 42.647059 0.87 9.666206 -4.486119 36507.6 0.0 93.0 909.0 68.0 Développé
159 Suriname 72.727273 8.196721 32.0 50.000000 0.13 -16.821851 143.582961 17753.1 40.0 3.0 7160.0 0.0 En developpement
163 Thaïlande 46.428571 -5.230769 13.0 46.741045 -0.75 0.405691 -5.782936 17008.0 30.0 0.0 9470.0 0.0 En developpement
155 Soudan 4.545455 120.000000 1.0 0.000000 -1.98 -19.785436 3022.660865 4614.0 40.0 0.0 4457.0 0.0 Pays moins avancé (PMA)
In [ ]:
df_original.columns
Out[ ]:
Index(['Zone', 'Ratio volaille/carne en %',
       'Variation cumulée - Proportion de volaille vs total viande',
       'Disponibilité volaille (kg/personne/an)',
       'exportation / production en %', 'Stabilite politique',
       'Variation cumulée - PIB', 'Variation cumulée - Taux de change en %',
       'PIB', 'Tarif EAV en %', 'MNT', 'Distance', 'Cas maladie recense',
       'Statut'],
      dtype='object')
In [ ]:
df_original = df_original[['Zone', 'Stabilite politique', 'Ratio volaille/carne en %',
       'Variation cumulée - Proportion de volaille vs total viande',
       'Disponibilité volaille (kg/personne/an)', 
       'Variation cumulée - PIB', 'PIB', 
       'Variation cumulée - Taux de change en %', 'Statut']]
#Non présent : 'exportation / production en %' , 'Tarif EAV en %', 'MNT', 'Distance', 'Cas maladie recense'

Essai avec score¶

In [ ]:
"""rank_descendant = ['exportation / production en %', 'Variation cumulée - PIB', 'Variation cumulée - Taux de change en %',
                  'Tarif EAV en %', 'MNT', 'Distance', 'Cas maladie recense']
rank_ascendant = ['Ratio volaille/carne en %', 'Variation cumulée - Proportion de volaille vs total viande',
                   'Disponibilité volaille (kg/personne/an)', 'Stabilite politique',
                   'PIB']
col_str = ['Zone', 'Statut']


for col in df_original:
    if col in col_str:
        next
    elif col in rank_ascendant:
        df_original[col].rank()
        df_original["rank "+col] = df_original[col].rank(ascending=True)
        df_original.drop(columns=col,inplace=True)
    else : 
        df_original[col].rank()
        df_original["rank "+col] = df_original[col].rank(ascending=False)
        df_original.drop(columns=col,inplace=True)"""
Out[ ]:
'rank_descendant = [\'exportation / production en %\', \'Variation cumulée - PIB\', \'Variation cumulée - Taux de change en %\',\n                  \'Tarif EAV en %\', \'MNT\', \'Distance\', \'Cas maladie recense\']\nrank_ascendant = [\'Ratio volaille/carne en %\', \'Variation cumulée - Proportion de volaille vs total viande\',\n                   \'Disponibilité volaille (kg/personne/an)\', \'Stabilite politique\',\n                   \'PIB\']\ncol_str = [\'Zone\', \'Statut\']\n\n\nfor col in df_original:\n    if col in col_str:\n        next\n    elif col in rank_ascendant:\n        df_original[col].rank()\n        df_original["rank "+col] = df_original[col].rank(ascending=True)\n        df_original.drop(columns=col,inplace=True)\n    else : \n        df_original[col].rank()\n        df_original["rank "+col] = df_original[col].rank(ascending=False)\n        df_original.drop(columns=col,inplace=True)'
In [ ]:
df_original
Out[ ]:
Zone Stabilite politique Ratio volaille/carne en % Variation cumulée - Proportion de volaille vs total viande Disponibilité volaille (kg/personne/an) Variation cumulée - PIB PIB Variation cumulée - Taux de change en % Statut
0 Afghanistan -2.80 22.222222 0.000000 2.0 -27.660894 2096.1 21.313925 Pays moins avancé (PMA)
1 Afrique du Sud -0.28 58.730159 5.519604 37.0 -4.577614 13950.5 10.836361 Pays moins avancé (PMA)
2 Albanie 0.38 27.906977 51.602564 12.0 13.666119 12771.0 -13.081201 En developpement
3 Algérie -0.92 35.000000 -4.761905 7.0 -6.517634 11809.5 21.708918 En developpement
5 Angola -0.38 44.000000 -9.090909 11.0 -18.119206 7216.1 280.579416 Pays moins avancé (PMA)
... ... ... ... ... ... ... ... ... ...
171 Uruguay 1.05 15.000000 143.386243 9.0 -1.325572 23107.0 51.882994 En developpement
172 Vanuatu 0.70 40.000000 22.093023 16.0 -8.188176 3031.2 1.513313 En developpement
173 Viet Nam 0.23 23.809524 61.538462 10.0 17.429591 9050.7 3.530142 En developpement
174 Zambie 0.15 15.000000 11.111111 3.0 -4.673833 3395.5 110.289234 Pays moins avancé (PMA)
175 Zimbabwe -0.71 9.615385 30.000000 5.0 -9.293250 2331.8 0.000000 Pays moins avancé (PMA)

154 rows × 9 columns

Analyses descriptives univariées¶

In [ ]:
df_original.describe()
Out[ ]:
Stabilite politique Ratio volaille/carne en % Variation cumulée - Proportion de volaille vs total viande Disponibilité volaille (kg/personne/an) Variation cumulée - PIB PIB Variation cumulée - Taux de change en %
count 154.000000 154.000000 154.000000 154.000000 154.000000 154.000000 154.000000
mean -0.150130 41.634219 14.739036 21.084416 1.970229 17092.278571 18.674009
std 0.906732 21.545317 35.430104 17.844261 12.850815 18510.330410 58.481823
min -2.800000 0.000000 -100.000000 0.000000 -47.310088 750.800000 -13.081201
25% -0.670000 23.599440 -1.050166 6.000000 -4.653154 3998.000000 -3.104735
50% -0.055000 41.666667 5.902845 18.000000 1.432695 11713.000000 0.859902
75% 0.415000 59.743590 18.825356 33.000000 7.839840 23474.575000 17.021548
max 1.600000 90.000000 229.411765 80.000000 79.809080 122978.000000 473.521841
In [ ]:
df_original.info()
<class 'pandas.core.frame.DataFrame'>
Index: 154 entries, 0 to 175
Data columns (total 9 columns):
 #   Column                                                      Non-Null Count  Dtype  
---  ------                                                      --------------  -----  
 0   Zone                                                        154 non-null    object 
 1   Stabilite politique                                         154 non-null    float64
 2   Ratio volaille/carne en %                                   154 non-null    float64
 3   Variation cumulée - Proportion de volaille vs total viande  154 non-null    float64
 4   Disponibilité volaille (kg/personne/an)                     154 non-null    float64
 5   Variation cumulée - PIB                                     154 non-null    float64
 6   PIB                                                         154 non-null    float64
 7   Variation cumulée - Taux de change en %                     154 non-null    float64
 8   Statut                                                      154 non-null    object 
dtypes: float64(7), object(2)
memory usage: 12.0+ KB

Visualisation¶

In [ ]:
#Sélection des variables numériques
df_valeurs = df_original.select_dtypes(include='number')

for col in df_valeurs:
    sns.boxplot(data=df_original, y=col, palette="Set2")
    plt.title(col)
    show_outliers_str(df_original, col)
    plt.show()

Remarques :

  • Exportation / production en % : La Chine - RAS de Hong Kong vient écraser le boxplot -> outlier trop lourd
  • Cas maladie recensée : L'Allemagne représente le double du 2 outlier -> outlier trop lourd

Analyses bivariées¶

Visualisation¶

In [ ]:
sns.pairplot(df_original, hue="Statut")
Out[ ]:
<seaborn.axisgrid.PairGrid at 0x17924b81b50>

Remarques :

  • Très peu de variables semblent suivrent une loi normale (excepté, eventuellement les 2 variations cumulées)
  • Le PIB est (par définition) calqué au Statut (basé sur l'IDH - Indice du Développement Humain)
  • Dispersion interessante entre le PIB & la Quantité de volaille / hab

Matrice de corrélation
Selon la méthode de Spearman due aux nombreuses variables qui ne suivent pas de loi normale

In [ ]:
#Création de la matrice
matrice_corr = df_valeurs.corr(method="spearman")
In [ ]:
#Affichage de la heatmap
sns.heatmap(matrice_corr, annot=True, cmap="YlGnBu")
Out[ ]:
<Axes: >

Analyse des Composantes Principales (ACP)¶

Préparation des données¶

Séparation des données¶

In [ ]:
#Récupération des statuts pour différentes aides à la visualisations
statuts = df_original["Statut"]

statuts.shape
Out[ ]:
(154,)
In [ ]:
#Valeurs
X = df_valeurs.values

X.shape
Out[ ]:
(154, 7)
In [ ]:
columns_names = df_valeurs.columns

columns_names.shape
Out[ ]:
(7,)
In [ ]:
zones = df_original["Zone"]

zones.shape
Out[ ]:
(154,)

Standardisation des données (mise à l'échelle en soustrayant les valeurs par leur moyenne)¶

In [ ]:
#Définition de la technique utilisée
scaler = StandardScaler()
In [ ]:
#Ajustement et application aux données
X_scaled = scaler.fit_transform(X)
In [ ]:
#Vérification du travail - Attendu : mean == 0 & std == 1
pd.DataFrame(X_scaled).describe().round(2).iloc[1:3,:]
Out[ ]:
0 1 2 3 4 5 6
mean 0.0 0.0 -0.0 0.0 0.0 -0.0 -0.0
std 1.0 1.0 1.0 1.0 1.0 1.0 1.0

Réduction des dimensions¶

Par le calcul des valeurs en fonction des coefficients attribués au composantes

In [ ]:
#Définition de la l'objet utilisé
pca = PCA()
In [ ]:
#Ajustement et application aux données déjà standardisées
X_pca = pca.fit_transform(X_scaled)
In [ ]:
pd.DataFrame(X_pca)
Out[ ]:
0 1 2 3 4 5 6
0 -1.880259 2.062289 -1.330000 -2.405415 -0.499373 1.023903 -0.334697
1 0.941734 0.618582 0.173688 -0.381914 0.544270 0.154930 -0.256669
2 -0.952632 -1.359731 0.311112 0.452148 0.417802 -0.233761 0.041447
3 -0.742771 0.762991 -0.263905 -0.836004 -0.584996 0.056411 0.170349
4 -0.142043 3.222142 -2.876614 1.527697 -0.709295 -1.475165 0.311198
... ... ... ... ... ... ... ...
149 -1.334152 -2.391790 -2.359739 1.094720 1.752884 -0.300198 0.316941
150 -0.040006 -0.328033 -0.295920 -0.687386 0.534045 -1.164239 0.046609
151 -1.404294 -1.303168 0.257723 0.818632 0.520736 -0.209544 -0.050239
152 -1.215138 0.523591 -1.319086 0.407111 -0.680352 -1.352806 -0.183636
153 -1.701333 -0.161361 -0.801318 -1.112686 -0.133228 -0.217026 -0.511426

154 rows × 7 columns

Analyse de l'ACP¶

Choix des composantes principales¶

In [ ]:
#Récupération des variances expliquées
variances_explained = (pca.explained_variance_ratio_*100).round(2)

variances_explained
Out[ ]:
array([33.02, 19.71, 15.95, 10.74, 10.33,  6.85,  3.39])
In [ ]:
#Variances cumulées
var_exp_cum = variances_explained.cumsum().round(1)

var_exp_cum
Out[ ]:
array([ 33. ,  52.7,  68.7,  79.4,  89.8,  96.6, 100. ])
In [ ]:
#Création de la liste des components pour notre graphique
x_list = range (1, len(var_exp_cum)+1)

list(x_list)
Out[ ]:
[1, 2, 3, 4, 5, 6, 7]
In [ ]:
#Visualisation du scree plot
plt.bar(x_list, variances_explained)
#plt.plot(x_list, var_exp_cum, c="red", marker='o')
plt.xlabel("Dimension")
plt.ylabel("Variance expliquée")
plt.title("Variance expliquée par dimension")
plt.show(block=False)

Qualité de représentation des variables (cos2)¶

In [ ]:
# Calcul du cosinus carré des variables
cos_squared = np.square(pca.components_)

# Création d'un dataframe avec le cosinus carré des variables
df_cos_squared = pd.DataFrame(cos_squared, columns=['F{}'.format(i+1) for i in range(X.shape[1])])
df_cos_squared.index = columns_names

#Vérification total des cos2 à 1
df_cos_squared["Total cos2"] = df_cos_squared.sum(axis=1)

df_cos_squared
Out[ ]:
F1 F2 F3 F4 F5 F6 F7 Total cos2
Stabilite politique 0.149707 0.201190 0.087617 0.339007 0.047047 0.175124 0.000308 1.0
Ratio volaille/carne en % 0.266741 0.145017 0.153559 0.002388 0.040628 0.128724 0.262943 1.0
Variation cumulée - Proportion de volaille vs total viande 0.001975 0.092984 0.134769 0.002258 0.422871 0.087472 0.257670 1.0
Disponibilité volaille (kg/personne/an) 0.025998 0.001425 0.045754 0.019916 0.460570 0.020886 0.425452 1.0
Variation cumulée - PIB 0.001699 0.197579 0.513225 0.069987 0.012188 0.191827 0.013496 1.0
PIB 0.542287 0.004994 0.057952 0.043583 0.016425 0.294831 0.039927 1.0
Variation cumulée - Taux de change en % 0.011593 0.356811 0.007124 0.522862 0.000272 0.101135 0.000204 1.0
In [ ]:
#Affichage visuel
i=131

for component in df_cos_squared[["F1", "F2", "F3"]]:
    df_temp = df_cos_squared[component]
    plt.subplot(i)
    plt.subplots_adjust(left=0.2, right=2, wspace=0.2)
    sns.lineplot(df_temp)
    plt.ylabel("cos2 de variable")
    plt.xticks(rotation=90)
    plt.ylim(0, 0.55)
    plt.grid(which="both")
    plt.title(component)
    i += 1

plt.show()

Visualisation & Interprétation¶

Remarques :

  • F1 == Stabilité et richesse du pays : 0 -> +1 = ( - )
  • F2 == Préfèrence pour la volaille : 0 -> +1 = (+)
In [ ]:
#F1 et F2
X_proj = pca.transform(X_scaled)
x_y = (0,1)

correlation_graph(pca, x_y, columns_names)
plt.show()
In [ ]:
display_factorial_planes(X_proj, x_y, clusters=statuts) #, labels=zones.tolist()
In [ ]:
#F2 et F3
x_y = (1,2)

correlation_graph(pca, x_y, columns_names)
In [ ]:
#Définition des 3 axes pour la représentation 3D
x = X_proj[:,0] #F1
y = X_proj[:,1] #F2
z = X_proj[:,2] #F3
In [ ]:
#Spatialisation 3D avec F3 ajouté
fig = go.Figure(data=[go.Scatter3d(x=x, y=y, z=z, 
                                   mode='markers',
                                   hovertext=zones.tolist(),
                                   marker=dict(
                                       size=10, color=z, colorscale='plotly3',
                                       opacity=0.8, reversescale=True))])

fig.update_layout(margin=dict(l=0, r=0, b=0, t=0), 
                  scene=dict(
                      xaxis=dict(range=[-4, 4]),  
                      yaxis=dict(range=[-4, 4]),
                      zaxis=dict(range=[-4, 4])))

fig.show()
In [ ]:
#Zoom sur la zone des pays jugé interessants (quart en bas-droite)
df_projete_F1_F2 = pd.DataFrame(X_proj)
df_projete_F1_F2.index = zones

df_temp = df_projete_F1_F2.loc[(df_projete_F1_F2[0] > 0) & (df_projete_F1_F2[1] < 0)]
In [ ]:
#Affichage des pays interessant sur F1 & F2
sns.scatterplot(df_temp, x=0, y=1)

for zone in  df_temp.index:
    X = df_temp.loc[df_temp.index==zone,[0]].values[0]
    Y = df_temp.loc[df_temp.index==zone,[1]].values[0]
    plt.text(x=X, y=Y, s=zone)
In [ ]:
list_pays_interessant_ACP = df_temp.index.tolist()
In [ ]:
print("Nombre de pays interessant :",len(list_pays_interessant_ACP))
list_pays_interessant_ACP
Nombre de pays interessant : 35
Out[ ]:
['Australie',
 'Bahamas',
 'Bélarus',
 'Bulgarie',
 'Canada',
 'Chili',
 'Chine - RAS de Macao',
 'Chypre',
 'Costa Rica',
 'Dominique',
 'Espagne',
 "États-Unis d'Amérique",
 'Finlande',
 'Grenade',
 'Guyana',
 'Islande',
 'Italie',
 'Japon',
 'Kiribati',
 'Maldives',
 'Malte',
 'Nauru',
 'Norvège',
 'Nouvelle-Zélande',
 'Panama',
 'Portugal',
 'Qatar',
 'République de Corée',
 'Roumanie',
 "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord",
 'Saint-Kitts-et-Nevis',
 'Seychelles',
 'Suède',
 'Suisse',
 'Tchéquie']

Classification Ascendante Hiérarchique (CAH)¶

Préparation des données¶

Séparation des données¶

In [ ]:
#Récupération des statuts pour différentes aides à la visualisations
statuts = df_original["Statut"]

statuts.shape
Out[ ]:
(154,)
In [ ]:
#Récupération uniquement des valeurs numériques
df_valeurs = df_original.select_dtypes("number")
In [ ]:
#Valeurs
X = df_valeurs.values

X.shape
Out[ ]:
(154, 7)
In [ ]:
#En-têtes colonnes
columns_names = df_valeurs.columns

columns_names.shape
Out[ ]:
(7,)
In [ ]:
#Pays
zones = df_original["Zone"]

zones.shape
Out[ ]:
(154,)

Standardisation des données (mise à l'échelle en soustrayant les valeurs par leur moyenne et divisant par leur écart-type)¶

In [ ]:
#Définition de la technique utilisée
scaler = preprocessing.StandardScaler()
In [ ]:
#Ajustement et application aux données
X_scaled = scaler.fit_transform(X)
In [ ]:
#Vérification du travail - Attendu : mean == 0 & std == 1
pd.DataFrame(X_scaled).describe().round(2).iloc[1:3,:]
Out[ ]:
0 1 2 3 4 5 6
mean 0.0 0.0 -0.0 0.0 0.0 -0.0 -0.0
std 1.0 1.0 1.0 1.0 1.0 1.0 1.0

Clustering¶

Linkage¶

In [ ]:
Z = linkage(X_scaled, method="ward")
Z[:5]
Out[ ]:
array([[ 31.        ,  33.        ,   0.18191767,   2.        ],
       [ 64.        ,  91.        ,   0.38249551,   2.        ],
       [  1.        ,  53.        ,   0.38756286,   2.        ],
       [ 42.        ,  58.        ,   0.41442566,   2.        ],
       [111.        , 153.        ,   0.41458065,   2.        ]])

Dendrogram¶

In [ ]:
fig, ax = plt.subplots(1, 1, figsize=(40,15))

_ = dendrogram(Z, ax=ax, labels=zones.tolist())

plt.title("Hierarchical Clustering Dendrogram")
ax.set_xlabel("Zone")
ax.set_ylabel("Distance")
ax.tick_params(axis='x', which='major', labelsize=15)
ax.tick_params(axis='y', which='major', labelsize=15)

Clusters¶

In [ ]:
#Définition du nombre de cluster voulu (coupe dans l'arbre CAH)
k=3
In [ ]:
model = AgglomerativeClustering(n_clusters=k, linkage="ward")
model.fit(X_scaled)
Out[ ]:
AgglomerativeClustering(n_clusters=3)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
AgglomerativeClustering(n_clusters=3)
In [ ]:
model.labels_
Out[ ]:
array([0, 0, 0, 0, 2, 1, 1, 2, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0,
       0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 2, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 2, 0, 1, 0,
       0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
       1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0,
       0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0],
      dtype=int64)
In [ ]:
df_temp = pd.DataFrame(X_scaled)

df_temp.columns = columns_names
In [ ]:
df_temp["Cluster"] = model.labels_
In [ ]:
df_dendo = df_temp.groupby("Cluster").mean().reset_index()
In [ ]:
my_palette = plt.cm.get_cmap("Set2", len(df_dendo.index))
In [ ]:
for row in range(0, len(df_dendo.index)):
        make_spider(df_dendo, row=row, title='Cluster '+str(row+1), color=my_palette(row))
In [ ]:
#Récupération des pays intéressant
df_temp.index = df_original["Zone"]
list_pays_interessant_dendro = df_temp.loc[df_temp["Cluster"] == 1].index.tolist()

print("Il y a {} pays concernés".format(len(list_pays_interessant_dendro)))
list_pays_interessant_dendro
Il y a 43 pays concernés
Out[ ]:
['Antigua-et-Barbuda',
 'Arabie saoudite',
 'Australie',
 'Bahamas',
 'Bahreïn',
 'Barbade',
 'Canada',
 'Chili',
 'Chine - RAS de Macao',
 'Chypre',
 'Dominique',
 'Espagne',
 "États-Unis d'Amérique",
 'Fidji',
 'Finlande',
 'Grenade',
 'Islande',
 'Israël',
 'Italie',
 'Jamaïque',
 'Japon',
 'Koweït',
 'Malaisie',
 'Malte',
 'Maurice',
 'Micronésie (États fédérés de)',
 'Nauru',
 'Norvège',
 'Nouvelle-Zélande',
 'Panama',
 'Portugal',
 'Qatar',
 'République de Corée',
 "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord",
 'Sainte-Lucie',
 'Saint-Kitts-et-Nevis',
 'Saint-Vincent-et-les Grenadines',
 'Samoa',
 'Seychelles',
 'Suède',
 'Suisse',
 'Tchéquie',
 'Trinité-et-Tobago']
In [ ]:
sns.pairplot(df_temp, hue="Cluster")
Out[ ]:
<seaborn.axisgrid.PairGrid at 0x1792bf49b50>

Kmeans¶

In [ ]:
#Sélection des caractéristiques pertinentes
features = df_valeurs
In [ ]:
#Normalisation des données
scaler = StandardScaler()
scaled_features = scaler.fit_transform(features)

Calcul du coût (détermine le nombre de clusters le plus efficient)

In [ ]:
#Liste des coûts pour x clusters 
list_inertia = []

#Nombre de cluster à tester
k_range = range(1,20)

for k in k_range:
    model =  KMeans(n_clusters=k).fit(scaled_features)
    list_inertia.append(model.inertia_)
In [ ]:
#Affichage des coûts - Méthode du coude
sns.lineplot(x=k_range, y=list_inertia)
plt.xlabel("Nombre de cluster")
plt.ylabel("Coût du modèle (inertia)")
Out[ ]:
Text(0, 0.5, 'Coût du modèle (inertia)')
In [ ]:
list_ecarts = []
mean = np.mean(list_inertia)
i=0

for k in list_inertia:
    ecart = abs(k - mean)
    i += 1
    list_ecarts.append(ecart)

ecart_min = min(list_ecarts)
n_cluster = list_ecarts.index(ecart_min)+1
In [ ]:
n_cluster
Out[ ]:
7
In [ ]:
# Appliquer l'algorithme de clustering (k-means)
model = KMeans(n_clusters=n_cluster)

df_original['Cluster'] = model.fit_predict(scaled_features)

#Centroïdes
centroides = model.cluster_centers_

#Inertia - Distance cumulée entre un centroïde et ses points
inertia = model.inertia_
In [ ]:
inertia
Out[ ]:
403.78981739169905
In [ ]:
sns.pairplot(data=df_original, hue="Cluster");

plt.show()

Kmeans sur F1 & F2¶

In [ ]:
df_projete_F1_F2 = df_projete_F1_F2[[0,1]]
In [ ]:
df_projete_F1_F2.head()
Out[ ]:
0 1
Zone
Afghanistan -1.880259 2.062289
Afrique du Sud 0.941734 0.618582
Albanie -0.952632 -1.359731
Algérie -0.742771 0.762991
Angola -0.142043 3.222142
In [ ]:
#Sélection des 2 premières composantes principales
features = df_projete_F1_F2
In [ ]:
#Normalisation des données
scaler = StandardScaler()
scaled_features = scaler.fit_transform(features)

Calcul du coût (détermine le nombre de clusters le plus efficient)

In [ ]:
#Liste des coûts pour x clusters 
list_inertia = []

#Nombre de cluster à tester
k_range = range(1,20)

for k in k_range:
    model =  KMeans(n_clusters=k).fit(scaled_features)
    list_inertia.append(model.inertia_)
In [ ]:
from sklearn.metrics import silhouette_score
silhouettes = []
K = range(2, 10)

for k in K:
    kmeanModel = KMeans(n_clusters=k)
    kmeanModel.fit(scaled_features)
    silhouettes.append(silhouette_score(scaled_features, kmeanModel.labels_))
In [ ]:
#Affichage des coûts - Méthode du coude
sns.lineplot(x=k_range, y=list_inertia)
plt.xlabel("Nombre de cluster")
plt.ylabel("Coût du modèle (inertia)")
Out[ ]:
Text(0, 0.5, 'Coût du modèle (inertia)')
In [ ]:
plt.figure(figsize=(10,4))
plt.plot(K, silhouettes, 'bx-')
plt.xlabel('k')
plt.ylabel('Score de silhouette')
plt.title('Le score de silhouette montrant le k optimal')
plt.show()
In [ ]:
"""
#Sélection auto du nombre de clusters selon le coude
list_ecarts = []
mean = np.mean(list_inertia)
i=0

for k in list_inertia:
    ecart = abs(k - mean)
    i += 1
    list_ecarts.append(ecart)

ecart_min = min(list_ecarts)
n_cluster = list_ecarts.index(ecart_min)+1
"""
Out[ ]:
'\n#Sélection auto du nombre de clusters selon le coude\nlist_ecarts = []\nmean = np.mean(list_inertia)\ni=0\n\nfor k in list_inertia:\n    ecart = abs(k - mean)\n    i += 1\n    list_ecarts.append(ecart)\n\necart_min = min(list_ecarts)\nn_cluster = list_ecarts.index(ecart_min)+1\n'
In [ ]:
#Choix du nombre de clusters
#n_clusters = n_cluster
n_clusters = 4
In [ ]:
# Appliquer l'algorithme de clustering (k-means)
model = KMeans(n_clusters=n_clusters)
model.fit(scaled_features)

df_original['Cluster'] = model.fit_predict(scaled_features)
In [ ]:
#Récupération des centroïdes
centroides = model.cluster_centers_

centroides
Out[ ]:
array([[-1.11271601, -0.66735214],
       [ 1.17867231, -0.53882106],
       [-0.33337686,  2.35529953],
       [-0.07997519,  0.39710942]])
In [ ]:
#Visualisation des individus, clusters et centroïdes sur PC1 et PC2
sns.scatterplot(data=centroides, x=centroides[:,0], y=centroides[:,1], c="red", marker="x")
sns.scatterplot(data=df_projete_F1_F2, x=0, y=1, hue=model.fit_predict(scaled_features))

plt.xlabel("PC1")
plt.ylabel("PC2")
plt.show() 
In [ ]:
sns.pairplot(data=df_original, hue="Cluster");

Récupération du cluster correspondant à la plus haute Stabilité politique car il correpond au cluster où la plus part des variables sont hautes

In [ ]:
#Récupération du cluster correspondant à la plus haute Stabilité politique
dict_PIB_max = {}

for cluster in df_original["Cluster"].unique().tolist():
    PIB_max = df_original.loc[df_original["Cluster"] == cluster, "PIB"].max()
    dict_PIB_max[cluster] = PIB_max

cluster_interessant = max(dict_PIB_max, key=dict_PIB_max.get)

dict_PIB_max
Out[ ]:
{2: 27582.8, 3: 26943.3, 0: 28604.9, 1: 122978.0}
In [ ]:
cluster_interessant
Out[ ]:
1
In [ ]:
list_pays_interessant_kmeans_PCA = df_original.loc[df_original["Cluster"] == cluster_interessant, "Zone"].tolist()

Comparaison des listes de pays intéressant pour l'exportation de volaille¶

In [ ]:
#Rappel des listes
"""
list_pays_interessant_ACP
list_pays_interessant_dendro
list_pays_interessant_kmeans_PCA
"""
Out[ ]:
'\nlist_pays_interessant_ACP\nlist_pays_interessant_dendro\nlist_pays_interessant_kmeans_PCA\n'
In [ ]:
common = list(set(list_pays_interessant_ACP) & set(list_pays_interessant_dendro) & set(list_pays_interessant_kmeans_PCA))

print("Il y a {} pays en communs qui sont les suivants :".format(len(common)))
common
Il y a 28 pays en communs qui sont les suivants :
Out[ ]:
['Islande',
 'Dominique',
 'Norvège',
 'Suède',
 'Finlande',
 'Grenade',
 'République de Corée',
 'Suisse',
 'Nauru',
 'Canada',
 'Portugal',
 'Chine - RAS de Macao',
 'Nouvelle-Zélande',
 'Malte',
 "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord",
 'Japon',
 'Tchéquie',
 "États-Unis d'Amérique",
 'Chypre',
 'Chili',
 'Australie',
 'Panama',
 'Saint-Kitts-et-Nevis',
 'Italie',
 'Qatar',
 'Bahamas',
 'Espagne',
 'Seychelles']

Idée :
Faire une heatmap avec un tableau de contingence : en ordonnée tous les pays et en abscisse toutes les listes

In [ ]:
df_heatmap = df_original[["Zone"]]
df_heatmap["ACP"] = 0
df_heatmap["Dendrogram"] = 0
df_heatmap["Kmeans_pca"] = 0
In [ ]:
for pays in df_heatmap["Zone"]:
    if pays in list_pays_interessant_ACP :
        df_heatmap.loc[df_heatmap["Zone"] == pays, "ACP"] = 1
    if pays in list_pays_interessant_dendro :
        df_heatmap.loc[df_heatmap["Zone"] == pays, "Dendrogram"] = 1
    if pays in list_pays_interessant_kmeans_PCA :
        df_heatmap.loc[df_heatmap["Zone"] == pays, "Kmeans_pca"] = 1
In [ ]:
df_heatmap.set_index("Zone", inplace=True)
In [ ]:
df_heatmap["Total"] = df_heatmap["ACP"] + df_heatmap["Dendrogram"] + df_heatmap["Kmeans_pca"]
In [ ]:
pays_todrop = df_heatmap.loc[df_heatmap["Total"] == 0].index.tolist()
df_heatmap.drop(index=pays_todrop, inplace=True)
In [ ]:
plt.figure(figsize=(5,10))
sns.heatmap(df_heatmap.sort_values("Total", ascending=False))

print("Nombre totaux de pays concernés : {}".format(len(df_heatmap)))
Nombre totaux de pays concernés : 50
In [ ]:
df_acp = df_projete_F1_F2.loc[df_projete_F1_F2.index.isin(list_pays_interessant_ACP)]
In [ ]:
df_dendrogram = df_projete_F1_F2.loc[df_projete_F1_F2.index.isin(list_pays_interessant_dendro)]
In [ ]:
df_kmeans_pca  = df_projete_F1_F2.loc[df_projete_F1_F2.index.isin(list_pays_interessant_kmeans_PCA)]
In [ ]:
list_df = [df_dendrogram, df_acp, df_kmeans_pca]
methodes = ["Dendogram", "ACP", "Kmeans sur ACP"]
In [ ]:
from scipy.stats import kstest

for i, df in enumerate(list_df):
    #Récupération uniquement des valeurs
    df_temp_values = df.select_dtypes("number")
    print("\n=====================================\n",methodes[i],
          "\n=====================================\n",
          str(df.index.values),"\n-------")

    for col in df_temp_values.columns:

        print("-------","PC"+str(col+1),"-------")

        #Test de Kolmogorov-Smirnov en comparant à une distribution normale
        statistic, p_value = kstest(df_temp_values[col], 'norm')

        #Affiche les résultats du test
        print("Statistique du test : {}".format(statistic))
        print("Valeur P : {}".format(p_value))

        #Interprète les résultats
        alpha = 0.05  #Niveau de signification

        if p_value > alpha:
            print("Les données semblent suivre une distribution normale.")
        else:
            print("Les données ne suivent pas une distribution normale.")

        
            sns.histplot(df_temp_values[col])
            
            plt.show()
=====================================
 Dendogram 
=====================================
 ['Antigua-et-Barbuda' 'Arabie saoudite' 'Australie' 'Bahamas' 'Bahreïn'
 'Barbade' 'Canada' 'Chili' 'Chine - RAS de Macao' 'Chypre' 'Dominique'
 'Espagne' "États-Unis d'Amérique" 'Fidji' 'Finlande' 'Grenade' 'Islande'
 'Israël' 'Italie' 'Jamaïque' 'Japon' 'Koweït' 'Malaisie' 'Malte'
 'Maurice' 'Micronésie (États fédérés de)' 'Nauru' 'Norvège'
 'Nouvelle-Zélande' 'Panama' 'Portugal' 'Qatar' 'République de Corée'
 "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord" 'Sainte-Lucie'
 'Saint-Kitts-et-Nevis' 'Saint-Vincent-et-les Grenadines' 'Samoa'
 'Seychelles' 'Suède' 'Suisse' 'Tchéquie' 'Trinité-et-Tobago'] 
-------
------- PC1 -------
Statistique du test : 0.704285659374785
Valeur P : 5.902137585801043e-22
Les données ne suivent pas une distribution normale.
------- PC2 -------
Statistique du test : 0.2759942510946001
Valeur P : 0.0021854915426771093
Les données ne suivent pas une distribution normale.
=====================================
 ACP 
=====================================
 ['Australie' 'Bahamas' 'Bélarus' 'Bulgarie' 'Canada' 'Chili'
 'Chine - RAS de Macao' 'Chypre' 'Costa Rica' 'Dominique' 'Espagne'
 "États-Unis d'Amérique" 'Finlande' 'Grenade' 'Guyana' 'Islande' 'Italie'
 'Japon' 'Kiribati' 'Maldives' 'Malte' 'Nauru' 'Norvège'
 'Nouvelle-Zélande' 'Panama' 'Portugal' 'Qatar' 'République de Corée'
 'Roumanie' "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord"
 'Saint-Kitts-et-Nevis' 'Seychelles' 'Suède' 'Suisse' 'Tchéquie'] 
-------
------- PC1 -------
Statistique du test : 0.5603567726327162
Valeur P : 6.927449904112383e-11
Les données ne suivent pas une distribution normale.
------- PC2 -------
Statistique du test : 0.5002126215973748
Valeur P : 1.267879155799694e-08
Les données ne suivent pas une distribution normale.
=====================================
 Kmeans sur ACP 
=====================================
 ['Antigua-et-Barbuda' 'Arabie saoudite' 'Australie' 'Bahamas' 'Bahreïn'
 'Barbade' 'Canada' 'Chili' 'Chine - RAS de Macao' 'Chypre' 'Costa Rica'
 'Dominique' 'Espagne' "États-Unis d'Amérique" 'Fidji' 'Finlande'
 'Grenade' 'Islande' 'Israël' 'Italie' 'Jamaïque' 'Japon' 'Koweït'
 'Malaisie' 'Malte' 'Maurice' 'Micronésie (États fédérés de)' 'Nauru'
 'Norvège' 'Nouvelle-Zélande' 'Panama' 'Portugal' 'Qatar'
 'République de Corée'
 "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord" 'Sainte-Lucie'
 'Saint-Kitts-et-Nevis' 'Saint-Vincent-et-les Grenadines' 'Samoa'
 'Seychelles' 'Suède' 'Suisse' 'Tchéquie' 'Trinité-et-Tobago'] 
-------
------- PC1 -------
Statistique du test : 0.7053427418272163
Valeur P : 1.561336787838266e-22
Les données ne suivent pas une distribution normale.
------- PC2 -------
Statistique du test : 0.2770513335470314
Valeur P : 0.0017736078081265205
Les données ne suivent pas une distribution normale.

Détermination du cluster à garder selon la méthode de sélection¶

In [ ]:
#Récupération des pays concernés par cette méthode
df_temp_dendro = df_original.loc[df_original["Zone"].isin(list_pays_interessant_dendro)][['Zone', 'Stabilite politique', 'Ratio volaille/carne en %',
       'Variation cumulée - Proportion de volaille vs total viande',
       'Disponibilité volaille (kg/personne/an)', 'Variation cumulée - PIB',
       'PIB', 'Variation cumulée - Taux de change en %']]
df_temp_dendro["Cluster"] = "0" #pour groupby dessus

df_temp_dendro.set_index("Zone", inplace=True)
In [ ]:
df_temp_dendro = df_temp_dendro.groupby("Cluster").mean().reset_index()

df_temp_dendro
Out[ ]:
Cluster Stabilite politique Ratio volaille/carne en % Variation cumulée - Proportion de volaille vs total viande Disponibilité volaille (kg/personne/an) Variation cumulée - PIB PIB Variation cumulée - Taux de change en %
0 0 0.695349 50.381174 5.66744 40.581395 -3.343409 37385.57907 0.93754
In [ ]:
#Récupération des pays concernés par cette méthode
df_temp_acp = df_original.loc[df_original["Zone"].isin(list_pays_interessant_ACP)][['Zone', 'Stabilite politique', 'Ratio volaille/carne en %',
       'Variation cumulée - Proportion de volaille vs total viande',
       'Disponibilité volaille (kg/personne/an)', 'Variation cumulée - PIB',
       'PIB', 'Variation cumulée - Taux de change en %']]
df_temp_acp["Cluster"] = "1" #pour groupby dessus

df_temp_acp.set_index("Zone", inplace=True)
In [ ]:
df_temp_acp = df_temp_acp.groupby("Cluster").mean().reset_index()

df_temp_acp
Out[ ]:
Cluster Stabilite politique Ratio volaille/carne en % Variation cumulée - Proportion de volaille vs total viande Disponibilité volaille (kg/personne/an) Variation cumulée - PIB PIB Variation cumulée - Taux de change en %
0 1 0.748571 41.96452 6.886203 31.857143 2.123106 39173.451429 1.649405
In [ ]:
#Récupération des pays concernés par cette méthode
df_temp_Kmeans_acp = df_original.loc[df_original["Zone"].isin(list_pays_interessant_kmeans_PCA)][['Zone', 'Stabilite politique', 'Ratio volaille/carne en %',
       'Variation cumulée - Proportion de volaille vs total viande',
       'Disponibilité volaille (kg/personne/an)', 'Variation cumulée - PIB',
       'PIB', 'Variation cumulée - Taux de change en %']]
df_temp_Kmeans_acp["Cluster"] = "2" #pour groupby dessus

df_temp_Kmeans_acp.set_index("Zone", inplace=True)
In [ ]:
df_temp_Kmeans_acp = df_temp_Kmeans_acp.groupby("Cluster").mean().reset_index()

df_temp_Kmeans_acp
Out[ ]:
Cluster Stabilite politique Ratio volaille/carne en % Variation cumulée - Proportion de volaille vs total viande Disponibilité volaille (kg/personne/an) Variation cumulée - PIB PIB Variation cumulée - Taux de change en %
0 2 0.693409 50.417965 5.59221 40.25 -3.15123 36994.275 1.129569
In [ ]:
#Concaténation des 3 dataframe temporaire
df_methode_temp = pd.concat([df_temp_Kmeans_acp, df_temp_acp, df_temp_dendro])
In [ ]:
methodes = ["Dendrogram", "ACP", "Kmeans sur PC1 & PC2"]
In [ ]:
df_methode_temp.reset_index(inplace=True)
df_methode_temp.drop(columns={"index"}, inplace=True)
In [ ]:
df_methode_temp
Out[ ]:
Cluster Stabilite politique Ratio volaille/carne en % Variation cumulée - Proportion de volaille vs total viande Disponibilité volaille (kg/personne/an) Variation cumulée - PIB PIB Variation cumulée - Taux de change en %
0 2 0.693409 50.417965 5.592210 40.250000 -3.151230 36994.275000 1.129569
1 1 0.748571 41.964520 6.886203 31.857143 2.123106 39173.451429 1.649405
2 0 0.695349 50.381174 5.667440 40.581395 -3.343409 37385.579070 0.937540
In [ ]:
#Récupération des données seulement
values_spider = df_methode_temp.values

#Définition de la technique utilisée
scaler = StandardScaler()

#Ajustement et application aux données
X_scaled = scaler.fit_transform(values_spider)


df_spider_methode = pd.DataFrame(X_scaled)
df_spider_methode.columns = df_methode_temp.columns
In [ ]:
#Vérification du travail - Attendu : mean == 0 & std == 1
df_spider_methode.describe().round(0).iloc[1:3,:]
Out[ ]:
Cluster Stabilite politique Ratio volaille/carne en % Variation cumulée - Proportion de volaille vs total viande Disponibilité volaille (kg/personne/an) Variation cumulée - PIB PIB Variation cumulée - Taux de change en %
mean 0.0 0.0 -0.0 0.0 -0.0 -0.0 -0.0 -0.0
std 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
In [ ]:
df_spider_methode
Out[ ]:
Cluster Stabilite politique Ratio volaille/carne en % Variation cumulée - Proportion de volaille vs total viande Disponibilité volaille (kg/personne/an) Variation cumulée - PIB PIB Variation cumulée - Taux de change en %
0 1.224745 -0.744714 0.711728 -0.769583 0.665663 -0.668830 -0.903263 -0.363364
1 0.000000 1.413535 -1.414203 1.412316 -1.413419 1.413535 1.394015 1.365310
2 -1.224745 -0.668821 0.702475 -0.642732 0.747756 -0.744705 -0.490752 -1.001946
In [ ]:
my_palette = plt.cm.get_cmap("Set2", len(df_spider_methode.index))

for row in range(0, len(df_spider_methode.index)):
        make_spider(df_spider_methode, row=row, title='Methode '+ methodes[row], color=my_palette(row))

Remarques sur les le choix des pays selon la méthode :

  • Dendrogram (CAH) -> Pays riches (économie), stables (politique), préférents de plus en plus la volaille (socioculturel) => choisi <=
  • ACP -> Pays moins riches (économie), moins stables (politique), pas plus de préférences pour la volaille (socioculturel)
  • Kmeans sur PC1 & PC2 -> Pays moins riches (économie), moins stables (politique), grande préfèrences pour la volaille mais qui n'évolue pas (socioculturel)

Profils du cluster gardé¶

In [ ]:
#Récupération des variables PC1 & PC2 pour les pays sélectionnés
df_kmeans_pca = df_projete_F1_F2.loc[df_projete_F1_F2.index.isin(list_pays_interessant_kmeans_PCA)]

df_kmeans_pca.shape
Out[ ]:
(44, 2)
In [ ]:
#Récupération des variables originales pour les pays sélectionnés
df_temp = df_original.loc[df_original["Zone"].isin(list_pays_interessant_kmeans_PCA)]

df_temp = df_temp.set_index("Zone")
In [ ]:
df_kmeans_pca = df_kmeans_pca.merge(df_original, on="Zone")
In [ ]:
df_kmeans_pca.shape
Out[ ]:
(44, 12)

PCA sur données des pays concernés du cluster sélectionné¶

In [ ]:
#Récupération des pays concernés
list_zones = df_kmeans_pca.Zone.to_list()
In [ ]:
#Sélection des caractéristiques pertinentes
features = df_original.loc[df_original["Zone"].isin(list_zones)]

features = features[["Zone",'Stabilite politique', 'Ratio volaille/carne en %', 
                          'Variation cumulée - Proportion de volaille vs total viande',
                          'Disponibilité volaille (kg/personne/an)',
                          'PIB','Variation cumulée - Taux de change en %']] #, 'Variation cumulée - PIB'
features.set_index("Zone", inplace=True)

columns_names = features.columns

#Normalisation des données
scaler = StandardScaler()
scaled_features = scaler.fit_transform(features)
In [ ]:
#Appel du model
model = PCA()

#Fit du model sur les valeurs
model.fit(scaled_features)

#Création de la matrice des composantes principales
X_pca = model.transform(scaled_features)
In [ ]:
#Variance expliquée
model.explained_variance_ratio_ * 100
Out[ ]:
array([41.32961659, 19.44882793, 15.26607706, 11.35935361,  9.59863363,
        2.99749118])
In [ ]:
#F1 et F2
x_y = (0,1)

correlation_graph(model, x_y, columns_names)
plt.show()
In [ ]:
display_factorial_planes(X_pca, x_y, labels=features.index)

Kmeans sur les pays séléctionnés¶

In [ ]:
X_pca.shape
Out[ ]:
(44, 6)
In [ ]:
#Récupération de PC1 et PC2 -> 60% de variance expliquée -> assez représentatifs des variables
scaled_features = X_pca[:,[0,1]]

scaled_features.shape
Out[ ]:
(44, 2)

Calcul du coût (détermine le nombre de clusters le plus efficient)

In [ ]:
#Liste des coûts pour x clusters 
list_inertia = []

#Nombre de cluster à tester
k_range = range(1,20)

for k in k_range:
    model =  KMeans(n_clusters=k).fit(scaled_features)
    list_inertia.append(model.inertia_)
In [ ]:
#Affichage des coûts - Méthode du coude
sns.lineplot(x=k_range, y=list_inertia)
plt.xlabel("Nombre de cluster")
plt.ylabel("Coût du modèle (inertia)")
Out[ ]:
Text(0, 0.5, 'Coût du modèle (inertia)')
In [ ]:
silhouettes = []
K = range(2, 10)

for k in K:
    kmeanModel = KMeans(n_clusters=k)
    kmeanModel.fit(scaled_features)
    silhouettes.append(silhouette_score(scaled_features, kmeanModel.labels_))
In [ ]:
plt.figure(figsize=(10,4))
plt.plot(K, silhouettes, 'bx-')
plt.xlabel('k')
plt.ylabel('Score de silhouette')
plt.title('Le score de silhouette montrant le k optimal')
plt.show()
In [ ]:
n_cluster = 3
In [ ]:
# Appliquer l'algorithme de clustering (k-means)
model = KMeans(n_clusters=n_cluster)

model.fit(scaled_features)
#df_kmeans_pca['Cluster'] = model.fit_predict(scaled_features)

#Centroïdes
centroides = model.cluster_centers_

#Inertia - Distance cumulée entre un centroïde et ses points
inertia = model.inertia_
In [ ]:
inertia
Out[ ]:
52.09410753006094
In [ ]:
#Passage en dataframe pour faciliter l'utilisation et la visualisation
df_pca = pd.DataFrame(X_pca)

df_pca.index = features.index
In [ ]:
features["Cluster"] = model.fit_predict(scaled_features)
In [ ]:
plt.figure(figsize=(12,10))
sns.scatterplot(df_pca, x=0, y=1, hue=features["Cluster"])

for ind, pays in enumerate(df_pca.index):
    x = df_pca.iloc[ind, 0] + 0.01
    y = df_pca.iloc[ind, 1] + 0.03
    plt.text(x=x, y=y, s=pays)
plt.xlabel("Préférence pour la volaille")
plt.ylabel("Stabilité et richesse")
plt.ylim(top=2.5)
plt.legend(bbox_to_anchor=(1.1, 1))

plt.show()
In [ ]:
#Nombre de colonnes dans le dataframe
nombre_de_colonnes = len(features.columns)

# Calcule le nombre de lignes nécessaire en fonction du nombre total de colonnes
nombre_de_lignes = -(-nombre_de_colonnes // 2) #Plafond de la division par 2

#Crée une figure et ses sous-graphiques
fig, axs = plt.subplots(nombre_de_lignes, 2, figsize=(15, 5 * nombre_de_lignes))

#Ajuste l'espacement entre les sous-graphiques
plt.tight_layout(h_pad=5)

#Boucle pour créer des boxplots pour chaque colonne
for i, (col, ax) in enumerate(zip(features.columns, axs.flatten())):
    #Trace le boxplot dans le subplot actuel
    features.boxplot(column=[col], by='Cluster', grid=False, vert=True, ax=ax)
    
    ax.set_title(col)

plt.show()
In [ ]:
features.index = list_zones
In [ ]:
cluster_0  = features.loc[features["Cluster"] == 0].index.tolist()
cluster_1  = features.loc[features["Cluster"] == 1].index.tolist()
cluster_2  = features.loc[features["Cluster"] == 2].index.tolist()
In [ ]:
cluster_0
Out[ ]:
['Canada',
 'Chine - RAS de Macao',
 'Chypre',
 'Espagne',
 'Finlande',
 'Italie',
 'Japon',
 'Malte',
 'Norvège',
 'Nouvelle-Zélande',
 'Panama',
 'Portugal',
 'République de Corée',
 "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord",
 'Suède',
 'Suisse',
 'Tchéquie']
In [ ]:
cluster_1
Out[ ]:
['Antigua-et-Barbuda',
 'Arabie saoudite',
 'Bahreïn',
 "États-Unis d'Amérique",
 'Israël',
 'Koweït',
 'Malaisie',
 'Qatar',
 'Saint-Vincent-et-les Grenadines',
 'Trinité-et-Tobago']
In [ ]:
cluster_2
Out[ ]:
['Australie',
 'Bahamas',
 'Barbade',
 'Chili',
 'Costa Rica',
 'Dominique',
 'Fidji',
 'Grenade',
 'Islande',
 'Jamaïque',
 'Maurice',
 'Micronésie (États fédérés de)',
 'Nauru',
 'Sainte-Lucie',
 'Saint-Kitts-et-Nevis',
 'Samoa',
 'Seychelles']