%%html
<style>
.container{
width: 80% !important;
margin-left: 10% !important;
margin-right: 10% !important;
}
.MathJax {
font-size: 1.3em;
}
.rendered_html tr, .rendered_html th, .rendered_html td {
text-align: right !important;
}
a[data-snippet-code]::after {
background: #262931 !important;
}
.titre-pers{
font-family: arial;
font-size: 250% !important;
line-height: 200% !important;
text-align: center !important;
color: #4c8be2 !important;
}
.rendered_html h1,
.text_cell_render h1 {
color: #86bed9 !important;
line-height: 150% !important;
}
.rendered_html h2,
.text_cell_render h2 {
color: #b08c20 !important;
padding-left: .5rem !important;
line-height: 150% !important;
}
.rendered_html h3,
.text_cell_render h3 {
color: #3aa237 !important;
padding-left: 1rem !important;
line-height: 150% !important;
font-size: 120% !important;
}
.rendered_html h4,
.text_cell_render h4 {
color: #29858a !important;
padding-left: 2rem !important;
font-size: 110% !important;
}
.rendered_html h5,
.text_cell_render h5 {
color: #21417d !important;
padding-left: 2.5rem !important;
font-size: 110% !important;
}
.rendered_html h6,
.text_cell_render h6 {
color: #d8a802c2 !important;
padding-left: 1rem !important;
font-family: sans-serif !important;
font-size: 120% !important;
font-weight: normal !important;
font-style: normal !important;
}
.renf{
font-size: 18px !important;
font-family: Arial !important;
color: #14db9a !important;
}
.renf2{
font-size: 18px !important;
font-family: Arial !important;
color: orangered !important;
}
.alert{
padding: 5px 0 5px 15px;
border-radius: 5px;
margin-left: 10px;
width: auto;
}
.output_subarea jupyter-widgets-view{
padding: 0 !important;
}
</style>
# importation des différents modules
import statistics
import seaborn as sns
import matplotlib
import matplotlib.patches as mpatches
import matplotlib.ticker as mtick
import matplotlib.pylab as pylab
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from statsmodels.formula.api import ols
import statsmodels.api as sm
from scipy.stats import spearmanr
from scipy.stats import chi2_contingency
from scipy.stats import ttest_ind
import scipy.stats as st
import scipy as sp
import math
from math import sqrt
import numpy as np
import pandas as pd
from PIL import Image
import os, glob
from IPython.core.interactiveshell import InteractiveShell
from IPython.core.display import display, HTML
InteractiveShell.ast_node_interactivity = "all"
plt.style.use('seaborn-white')
%matplotlib inline
pd.options.mode.chained_assignment = None # default='warn'
#import jtplot module in notebook
from jupyterthemes import jtplot
# choose which theme to inherit plotting style from
# onedork | grade3 | oceans16 | chesterish | monokai | solarizedl | solarizedd
jtplot.style(theme='grade3')
# set "context" (paper, notebook, talk, poster)
# scale font-size of ticklabels, legend, etc.
# remove spines from x and y axes and make grid dashed
jtplot.style(context='notebook', fscale=0.8, ticks=True, grid=False)
# turn on X- and Y-axis tick marks (default=False)
# turn off the axis grid lines (default=True)
# and set the default figure size
jtplot.style(ticks=True, fscale=1, grid=False, figsize=(8,5))
# reset default matplotlib rcParams
jtplot.reset()
# importations de modules "local"
import sys
sys.path.append('functions/')
import fonctions_ocr
import acp_perso
import adjust_text
import fonctions_perso
Objet de la mission :
On importe les données suivantes, pour tous les pays et pour la dernière année disponible (2018), depuis le
site de la FAO :
data = pd.read_csv('data/FAOSTAT_data_mini.csv')
pd.set_option('display.max_columns', data.shape[1]+1)
data
Code Domaine | Domaine | Code zone | Zone | Code Élément | Élément | Code Produit | Produit | Code année | Année | Unité | Valeur | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | FBS | Nouveaux Bilans Alimentaire | 2 | Afghanistan | 664 | Disponibilité alimentaire (Kcal/personne/jour) | 2901 | Total General | 2018 | 2018 | Kcal/personne/jour | 2040.00 |
1 | FBS | Nouveaux Bilans Alimentaire | 2 | Afghanistan | 674 | Disponibilité de protéines en quantité (g/pers... | 2901 | Total General | 2018 | 2018 | g/personne/jour | 55.52 |
2 | FBS | Nouveaux Bilans Alimentaire | 2 | Afghanistan | 664 | Disponibilité alimentaire (Kcal/personne/jour) | 2903 | Produits Vegetaux | 2018 | 2018 | Kcal/personne/jour | 1849.00 |
3 | FBS | Nouveaux Bilans Alimentaire | 2 | Afghanistan | 674 | Disponibilité de protéines en quantité (g/pers... | 2903 | Produits Vegetaux | 2018 | 2018 | g/personne/jour | 44.73 |
4 | FBS | Nouveaux Bilans Alimentaire | 2 | Afghanistan | 664 | Disponibilité alimentaire (Kcal/personne/jour) | 2941 | Produits Animaux | 2018 | 2018 | Kcal/personne/jour | 191.00 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
1033 | FBS | Nouveaux Bilans Alimentaire | 181 | Zimbabwe | 674 | Disponibilité de protéines en quantité (g/pers... | 2901 | Total General | 2018 | 2018 | g/personne/jour | 44.15 |
1034 | FBS | Nouveaux Bilans Alimentaire | 181 | Zimbabwe | 664 | Disponibilité alimentaire (Kcal/personne/jour) | 2903 | Produits Vegetaux | 2018 | 2018 | Kcal/personne/jour | 1749.00 |
1035 | FBS | Nouveaux Bilans Alimentaire | 181 | Zimbabwe | 674 | Disponibilité de protéines en quantité (g/pers... | 2903 | Produits Vegetaux | 2018 | 2018 | g/personne/jour | 32.56 |
1036 | FBS | Nouveaux Bilans Alimentaire | 181 | Zimbabwe | 664 | Disponibilité alimentaire (Kcal/personne/jour) | 2941 | Produits Animaux | 2018 | 2018 | Kcal/personne/jour | 159.00 |
1037 | FBS | Nouveaux Bilans Alimentaire | 181 | Zimbabwe | 674 | Disponibilité de protéines en quantité (g/pers... | 2941 | Produits Animaux | 2018 | 2018 | g/personne/jour | 11.59 |
1038 rows × 12 columns
# On supprime les colonnes inutiles
data=data.drop(['Code Domaine', 'Domaine', 'Code année'], axis=1)
# On renomme les colonnes
data.columns = ['code_pays', 'pays', 'code_element', 'element', 'code_prod', 'prod', 'annee', 'unite', 'valeur']
# Table-pivot pour avoir les vatiables en colonnes
data = data.pivot_table(index=['code_pays', 'pays', 'code_prod', 'prod', 'annee', 'unite'], columns=['element'], values=['valeur']).reset_index()
data.sample(2)
code_pays | pays | code_prod | prod | annee | unite | valeur | ||
---|---|---|---|---|---|---|---|---|
element | Disponibilité alimentaire (Kcal/personne/jour) | Disponibilité de protéines en quantité (g/personne/jour) | ||||||
572 | 134 | Malte | 2903 | Produits Vegetaux | 2018 | Kcal/personne/jour | 2462.0 | NaN |
226 | 57 | Bélarus | 2941 | Produits Animaux | 2018 | Kcal/personne/jour | 838.0 | NaN |
data = data[data['pays'] != 'France']
On crée le df minimal, df_mini, c'est-à-dire celui qui comprend les 4 variables exigéees :
evol_pop_pct
part_prot_anim
dispo_prot_g_p_j
dispo_alim_kcal_j_pers
Remarque:
-> On ne s'occupe que des 3 dernières variables pour le moment, c'est-à-dire celles relatives à
l'alimentation
data.columns = ['code_pays', 'pays', 'code_prod', 'prod', 'annee', 'unite', 'dispo_alim_kcal_j_pers', 'dispo_prot_g_p_j']
# on crée le df minimal, c'est à dire celui qui comprend les 4 variables exigéees
df_mini = data.copy()
df_mini = df_mini[df_mini['code_prod'] == 2901]
df_mini_temp=df_mini.copy()
df_mini_temp['val']=df_mini.loc[:,['dispo_alim_kcal_j_pers', 'dispo_prot_g_p_j']].max(axis=1)
df_mini_temp=df_mini_temp.groupby(['code_pays', 'pays', 'code_prod', 'prod', 'annee', 'unite'])[['val']].max()
df_mini_temp=df_mini_temp.unstack().reset_index()
df_mini=df_mini_temp.copy()
df_mini.columns = ['code_pays', 'pays', 'code_prod', 'prod', 'annee', 'dispo_alim_kcal_j_pers', 'dispo_prot_g_p_j']
df_mini = df_mini[['code_pays', 'pays', 'annee', 'dispo_alim_kcal_j_pers', 'dispo_prot_g_p_j']]
df_mini.sample(2)
# on vérifie la présence éventuelle de valeurs nulles
df_mini.isna().sum()
code_pays | pays | annee | dispo_alim_kcal_j_pers | dispo_prot_g_p_j | |
---|---|---|---|---|---|
19 | 32 | Cameroun | 2018 | 2733.0 | 71.89 |
44 | 70 | Polynésie française | 2018 | 2910.0 | 96.28 |
code_pays 0 pays 0 annee 0 dispo_alim_kcal_j_pers 0 dispo_prot_g_p_j 0 dtype: int64
df_part_prot = data[((data['code_prod'] == 2941) | (data['code_prod'] == 2903)) & (data['unite'] == 'g/personne/jour')]
df_part_prot = df_part_prot[['code_pays', 'pays', 'code_prod', 'prod', 'annee', 'dispo_prot_g_p_j']]
df_part_prot_temp = df_part_prot.groupby(['code_pays', 'pays', 'code_prod'])['dispo_prot_g_p_j'].max()
df_part_prot_temp = df_part_prot_temp.unstack().reset_index()
df_part_prot_temp['part_prot_anim'] = (df_part_prot_temp[2941] / (df_part_prot_temp[2941] + df_part_prot_temp[2903]))*100
df_part_prot_temp=df_part_prot_temp[['code_pays', 'part_prot_anim']]
df_mini=pd.merge(df_mini, df_part_prot_temp)
df_mini
code_pays | pays | annee | dispo_alim_kcal_j_pers | dispo_prot_g_p_j | part_prot_anim | |
---|---|---|---|---|---|---|
0 | 1 | Arménie | 2018 | 2997.0 | 94.35 | 48.050021 |
1 | 2 | Afghanistan | 2018 | 2040.0 | 55.52 | 19.434438 |
2 | 3 | Albanie | 2018 | 3360.0 | 115.74 | 53.347732 |
3 | 4 | Algérie | 2018 | 3322.0 | 91.83 | 26.930197 |
4 | 7 | Angola | 2018 | 2385.0 | 53.05 | 30.744581 |
... | ... | ... | ... | ... | ... | ... |
167 | 256 | Luxembourg | 2018 | 3465.0 | 108.65 | 62.098481 |
168 | 272 | Serbie | 2018 | 2828.0 | 84.36 | 49.851843 |
169 | 273 | Monténégro | 2018 | 3500.0 | 114.53 | 61.355103 |
170 | 276 | Soudan | 2018 | 2578.0 | 72.98 | 27.610304 |
171 | 351 | Chine | 2018 | 3191.0 | 100.63 | 39.749578 |
172 rows × 6 columns
df_mini.isna().sum()
df_mini.duplicated().sum()
code_pays 0 pays 0 annee 0 dispo_alim_kcal_j_pers 0 dispo_prot_g_p_j 0 part_prot_anim 0 dtype: int64
0
pop = pd.read_csv('data/FAOSTAT_pop.csv')
pop.sample(5)
Code Domaine | Domaine | Code zone | Zone | Code Élément | Élément | Code Produit | Produit | Code année | Année | Valeur | Note | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
109 | OA | Séries temporelles annuelles | 54 | Danemark | 511 | Population totale | 3010 | Population-Estimations | 2018 | 2018 | 5752.126 | NaN |
405 | OA | Séries temporelles annuelles | 197 | Sierra Leone | 511 | Population totale | 3010 | Population-Estimations | 2018 | 2018 | 7650.150 | NaN |
226 | OA | Séries temporelles annuelles | 112 | Jordanie | 511 | Population totale | 3010 | Population-Estimations | 2008 | 2008 | 6556.478 | NaN |
117 | OA | Séries temporelles annuelles | 60 | El Salvador | 511 | Population totale | 3010 | Population-Estimations | 2018 | 2018 | 6420.746 | NaN |
406 | OA | Séries temporelles annuelles | 200 | Singapour | 511 | Population totale | 3010 | Population-Estimations | 2008 | 2008 | 4775.804 | NaN |
pop = pop[['Code zone', 'Zone', 'Année', 'Valeur']]
pop.columns = ['code_pays', 'pays', 'annee', 'pop']
pop = pop.groupby(['code_pays', 'pays', 'annee'])[['pop']].max()
pop=pop.unstack().reset_index()
pop.columns = ['code_pays', 'pays', 'pop_2008', 'pop_2018']
pop['evol_pop_pct']=((pop['pop_2018'] - pop['pop_2008'])/pop['pop_2008'])*100
pop.head(2)
code_pays | pays | pop_2008 | pop_2018 | evol_pop_pct | |
---|---|---|---|---|---|
0 | 1 | Arménie | 2907.618 | 2951.745 | 1.517634 |
1 | 2 | Afghanistan | 27722.276 | 37171.921 | 34.086830 |
pop[(pop['pop_2008'].isna()) | (pop['pop_2018'].isna()) ][['pays', 'pop_2008', 'pop_2018']].sort_values(by="pays")
pays | pop_2008 | pop_2018 | |
---|---|---|---|
231 | Bonaire, Saint-Eustache et Saba | NaN | 25.711 |
232 | Curaçao | NaN | 162.752 |
235 | Saint-Barthélemy | NaN | 9.816 |
234 | Saint-Martin (partie française) | NaN | 37.264 |
233 | Sint Maarten (partie néerlandaise) | NaN | 41.940 |
229 | Soudan | NaN | 41801.533 |
183 | Soudan (ex) | 41797.776 | NaN |
230 | Soudan du Sud | NaN | 10975.927 |
pop_comp = pd.read_csv("data/Bq_Mondiale_Complement.csv")
pop_comp['pop_2008']=pop_comp['pop_2008']/1000
pop_comp['pop_2018']=pop_comp['pop_2018']/1000
z=0
for i in [232, 234, 233, 229, 230]:
pop.iloc[i,2] = pop_comp.iloc[z,4]
pop.iloc[i,4] = ((pop.iloc[i,3] - pop.iloc[i,2])/pop.iloc[i,2])*100
z+=1
pop.iloc[229,:]
code_pays 276 pays Soudan pop_2008 33060.837 pop_2018 41801.533 evol_pop_pct 26.438218 Name: 229, dtype: object
On vérifie que la somme des populations nationales est cohérent avec les chiffres officiels de la population mondiale en 2018 (≈ 7.6 milliards)
pop_mondiale=pop['pop_2018'].sum()
pop_mondiale
9090746.135
On constate que la somme donne un résultat trop important
-> on vérifie que les données de la Chine ne sont pas en double
pattern = pop.pays.str.contains("chine", case=False)
chaine_chine = pop[pattern]
liste_chine=chaine_chine['pays'].unique()
liste_chine
df_liste = []
for x in liste_chine:
z=pop[pop['pays'] == x]
df_liste.append(z)
chine = pd.concat(df_liste)
chine
array(['Chine, continentale', 'Chine - RAS de Hong-Kong', 'Chine - RAS de Macao', 'Chine, Taiwan Province de', 'Chine'], dtype=object)
code_pays | pays | pop_2008 | pop_2018 | evol_pop_pct | |
---|---|---|---|---|---|
35 | 41 | Chine, continentale | 1353569.484 | 1427647.786 | 5.472811 |
81 | 96 | Chine - RAS de Hong-Kong | 6881.863 | 7371.730 | 7.118232 |
112 | 128 | Chine - RAS de Macao | 515.239 | 631.636 | 22.590875 |
191 | 214 | Chine, Taiwan Province de | 23019.045 | 23726.460 | 3.073173 |
237 | 351 | Chine | 1383985.631 | 1459377.612 | 5.447454 |
Les données de la population de la Chine apparaissent 2 fois
-> On supprime les données correspondant au code pays 351
pop=pop[pop['code_pays'] != 351]
On vérifie à nouveau
pop['pop_2018'].sum()
7631368.523
Le résultat est maintenant cohérent
On peut maintenant merger les données relatives à l'alimentation et à la population
df_mini=pd.merge(df_mini, pop[['code_pays', 'evol_pop_pct']])
df_mini.sample(5)
code_pays | pays | annee | dispo_alim_kcal_j_pers | dispo_prot_g_p_j | part_prot_anim | evol_pop_pct | |
---|---|---|---|---|---|---|---|
42 | 66 | Fidji | 2018 | 2781.0 | 71.14 | 39.957836 | 4.509553 |
49 | 79 | Allemagne | 2018 | 3554.0 | 105.40 | 60.749526 | 2.539502 |
142 | 211 | Suisse | 2018 | 3354.0 | 95.47 | 62.700325 | 11.843253 |
41 | 63 | Estonie | 2018 | 3247.0 | 105.38 | 62.080091 | -1.280005 |
82 | 119 | Lettonie | 2018 | 3229.0 | 102.72 | 61.409657 | -11.182452 |
dispo_kcal
dispo_prot
df_mini.columns
Index(['code_pays', 'pays', 'annee', 'dispo_alim_kcal_j_pers', 'dispo_prot_g_p_j', 'part_prot_anim', 'evol_pop_pct'], dtype='object')
df_mini=df_mini.drop('annee', axis=1)
df_mini.columns=['code_pays', 'pays', 'dispo_kcal', 'dispo_prot', 'part_prot_anim', 'evol_pop_pct']
df_mini.sample(5)
code_pays | pays | dispo_kcal | dispo_prot | part_prot_anim | evol_pop_pct | |
---|---|---|---|---|---|---|
45 | 72 | Djibouti | 2694.0 | 65.75 | 21.323194 | 17.463539 |
143 | 213 | Turkménistan | 2830.0 | 88.62 | 40.385917 | 18.540867 |
170 | 276 | Soudan | 2578.0 | 72.98 | 27.610304 | 26.438218 |
148 | 220 | Trinité-et-Tobago | 2991.0 | 85.58 | 51.781750 | 5.736270 |
54 | 86 | Grenade | 2402.0 | 71.32 | 56.668069 | 5.687680 |
On va ajouter plusieurs variables explicatives qui nous permettront de mieux caractériser nos groupes de
pays:
Ainsi, pour chaque pays, on va indiquer :
df_global = df_mini.copy()
df_global.shape
(171, 6)
## on crée un df de correspondance 'pays_continent' qui associe tous les pays à leur continent
import pathlib2 as pl2
# le dossier 'pop_contient' contient 5 fichiers, 1 par continent
ps = pl2.Path('data/pop_continent')
df_liste = []
for p in ps.glob('*.csv'):
n=str(p).split('_')[-1][:-4].capitalize()
df = pd.read_csv(p)
df['continent'] = n
df_liste.append(df)
pays_continent = pd.concat(df_liste)
pays_continent = pays_continent[['Code zone', 'Zone', 'continent']]
pays_continent.columns = ['code_pays', 'pays', 'continent']
pays_continent.duplicated().sum()
pays_continent = pays_continent.sort_values(by="code_pays")
pays_continent.sample(4)
df_global = pd.merge(df_global, pays_continent, on='code_pays')
# on crée un code_continent pour faciliter l'utilisation de cette variable
liste_continents = df_global['continent'].unique()
# on crée un dictionnaire de la forme cont_dico = {'code_cont': 'nom_cont'}
cont_dico = {}
def code_cont(cont):
for i in range(5):
cont_dico[i] = liste_continents[i]
if cont == liste_continents[i]:
return i
df_global['code_cont'] = df_global['continent'].apply(lambda x: code_cont(x))
df_global['pays']=df_global['pays_x']
df_global.drop(columns=['pays_x', 'pays_y'], inplace=True)
df_global.sample(1)
for k, val in cont_dico.items():
print(k, val, sep=' : ')
0
code_pays | pays | continent | |
---|---|---|---|
45 | 216 | Thaïlande | Asie |
10 | 148 | Nauru | Oceanie |
50 | 191 | Saint-Vincent-et-les Grenadines | Ameriques |
30 | 140 | Monaco | Europe |
code_pays | dispo_kcal | dispo_prot | part_prot_anim | evol_pop_pct | continent | code_cont | pays | |
---|---|---|---|---|---|---|---|---|
106 | 154 | 3072.0 | 84.49 | 43.283229 | 0.756731 | Europe | 1 | Macédoine du Nord |
0 : Asie 1 : Europe 2 : Afrique 3 : Ameriques 4 : Oceanie
df_global.shape
(171, 8)
pop_glo = pop[['code_pays', 'pop_2018']]
pop_glo.columns=['code_pays', 'pop_M']
df_global = pd.merge(df_global, pop_glo, how='left')
df_global
code_pays | dispo_kcal | dispo_prot | part_prot_anim | evol_pop_pct | continent | code_cont | pays | pop_M | |
---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2997.0 | 94.35 | 48.050021 | 1.517634 | Asie | 0 | Arménie | 2951.745 |
1 | 2 | 2040.0 | 55.52 | 19.434438 | 34.086830 | Asie | 0 | Afghanistan | 37171.921 |
2 | 3 | 3360.0 | 115.74 | 53.347732 | -3.994368 | Europe | 1 | Albanie | 2882.740 |
3 | 4 | 3322.0 | 91.83 | 26.930197 | 21.588450 | Afrique | 2 | Algérie | 42228.408 |
4 | 7 | 2385.0 | 53.05 | 30.744581 | 42.009157 | Afrique | 2 | Angola | 30809.787 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
166 | 255 | 3769.0 | 99.87 | 58.656253 | 6.525984 | Europe | 1 | Belgique | 11482.178 |
167 | 256 | 3465.0 | 108.65 | 62.098481 | 24.482649 | Europe | 1 | Luxembourg | 604.245 |
168 | 272 | 2828.0 | 84.36 | 49.851843 | -2.840464 | Europe | 1 | Serbie | 8802.754 |
169 | 273 | 3500.0 | 114.53 | 61.355103 | 1.044389 | Europe | 1 | Monténégro | 627.809 |
170 | 276 | 2578.0 | 72.98 | 27.610304 | 26.438218 | Afrique | 2 | Soudan | 41801.533 |
171 rows × 9 columns
On importe les données du site de la FAO
le PIB/hab est exprimé en $ PPA internationaux constants de 2011
data_secu=pd.read_csv('data/FAOSTAT_secu_alim.csv')
pib=data_secu[data_secu['Code Élément'] == 6126]
pib=pib[['Code zone', 'Valeur']]
pib.columns=['code_pays', 'pib_hab']
pib.sample(2)
code_pays | pib_hab | |
---|---|---|
105 | 225 | 66968.3 |
371 | 234 | 21590.8 |
Methode : left
depuis df_global
-> nous permettra de détecter la présence éventuelle de valeurs nulles
df_global = pd.merge(df_global, pib, how='left')
df_global.isna().sum()
pays_manque_pib=list(df_global[df_global['pib_hab'].isna()]['pays'])
pays_manque_pib
code_pays 0 dispo_kcal 0 dispo_prot 0 part_prot_anim 0 evol_pop_pct 0 continent 0 code_cont 0 pays 0 pop_M 0 pib_hab 8 dtype: int64
['Cuba', 'Polynésie française', "Iran (République islamique d')", 'République populaire démocratique de Corée', 'Nouvelle-Calédonie', 'Chine, Taiwan Province de', 'Venezuela (République bolivarienne du)', 'Yémen']
On traite ces valeurs manquantes en cherchant ces valeurs dans la base de la Banque Mondiale
-> Mais ces éléments sont également absents dans la base de la Banque Mondiale
-> On exclut ces pays de notre analyse (on crée un dataframe à part pour ces pays)
df_pays_sans_pib = df_global.loc[df_global['pays'].isin(pays_manque_pib),:]
df_pays_sans_pib
code_pays | dispo_kcal | dispo_prot | part_prot_anim | evol_pop_pct | continent | code_cont | pays | pop_M | pib_hab | |
---|---|---|---|---|---|---|---|---|---|---|
30 | 49 | 3344.0 | 84.89 | 40.570150 | 0.900269 | Ameriques | 3 | Cuba | 11338.134 | NaN |
44 | 70 | 2910.0 | 96.28 | 66.046946 | 5.157142 | Oceanie | 4 | Polynésie française | 277.679 | NaN |
66 | 102 | 3087.0 | 85.73 | 28.858043 | 13.421385 | Asie | 0 | Iran (République islamique d') | 81800.188 | NaN |
79 | 116 | 2019.0 | 53.34 | 19.122610 | 5.098539 | Asie | 0 | République populaire démocratique de Corée | 25549.604 | NaN |
105 | 153 | 2825.0 | 84.66 | 57.517421 | 13.497639 | Oceanie | 4 | Nouvelle-Calédonie | 279.993 | NaN |
144 | 214 | 2983.0 | 86.20 | 50.556845 | 3.073173 | Asie | 0 | Chine, Taiwan Province de | 23726.460 | NaN |
160 | 236 | 2120.0 | 54.17 | 45.357209 | 4.527767 | Ameriques | 3 | Venezuela (République bolivarienne du) | 28887.118 | NaN |
164 | 249 | 2154.0 | 54.52 | 19.035393 | 30.177658 | Asie | 0 | Yémen | 28498.683 | NaN |
df_global = df_global.dropna()
df_global.shape
df_global.sample(2)
(163, 10)
code_pays | dispo_kcal | dispo_prot | part_prot_anim | evol_pop_pct | continent | code_cont | pays | pop_M | pib_hab | |
---|---|---|---|---|---|---|---|---|---|---|
26 | 41 | 3194.0 | 100.72 | 39.362653 | 5.472811 | Asie | 0 | Chine, continentale | 1427647.786 | 15243.2 |
131 | 194 | 3307.0 | 86.94 | 37.658155 | 30.184069 | Asie | 0 | Arabie saoudite | 33702.756 | 47596.7 |
risque=pd.read_csv('data/risque.csv')
risque.sample(2)
pays | risque | |
---|---|---|
59 | Gabon | 5 |
145 | Sao Tomé-et-Principe | 6 |
df_global = pd.merge(df_global, risque, how='left')
df_global.isna().sum()
code_pays 0 dispo_kcal 0 dispo_prot 0 part_prot_anim 0 evol_pop_pct 0 continent 0 code_cont 0 pays 0 pop_M 0 pib_hab 0 risque 0 dtype: int64
Pas de valeurs nulles pour notre variable risque
df_global.shape
df_global.sample(2)
(163, 11)
code_pays | dispo_kcal | dispo_prot | part_prot_anim | evol_pop_pct | continent | code_cont | pays | pop_M | pib_hab | risque | |
---|---|---|---|---|---|---|---|---|---|---|---|
11 | 16 | 2563.0 | 60.73 | 20.681706 | 11.830941 | Asie | 0 | Bangladesh | 161376.708 | 4441.4 | 5 |
59 | 97 | 3316.0 | 89.64 | 57.150826 | -2.845995 | Europe | 1 | Hongrie | 9707.499 | 31072.9 | 3 |
prods = pd.read_csv('data/FAOSTAT_tous_prod.csv')
prods.head(2)
Code Domaine | Domaine | Code zone | Zone | Code Élément | Élément | Code Produit | Produit | Code année | Année | Unité | Valeur | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | FBS | Nouveaux Bilans Alimentaire | 2 | Afghanistan | 5142 | Nourriture | 2511 | Blé et produits | 2018 | 2018 | Milliers de tonnes | 5898.0 |
1 | FBS | Nouveaux Bilans Alimentaire | 2 | Afghanistan | 5142 | Nourriture | 2805 | Riz et produits | 2018 | 2018 | Milliers de tonnes | 589.0 |
tous_prods = prods.groupby(['Zone'])[['Valeur']].sum()
tous_prods=tous_prods.reset_index()
tous_prods.columns=['Zone','Total_Nourr']
volaille = prods[prods['Code Produit'] == 2734]
volaille=pd.merge(volaille, tous_prods)
volaille['part_vol_nourr'] = round((volaille['Valeur'] / volaille['Total_Nourr']) * 100, 2)
volaille['qte_volaille_kg'] = volaille['Valeur']*1000
volaille=volaille[['Code zone', 'part_vol_nourr', 'qte_volaille_kg']]
volaille.columns=['code_pays', 'part_vol_nourr', 'qte_volaille_kg']
volaille.sample(5)
code_pays | part_vol_nourr | qte_volaille_kg | |
---|---|---|---|
154 | 211 | 1.74 | 132000.0 |
156 | 208 | 0.59 | 30000.0 |
162 | 220 | 12.04 | 88000.0 |
53 | 231 | 5.94 | 18526000.0 |
151 | 276 | 0.32 | 67000.0 |
df_global = pd.merge(df_global, volaille, how='left')
df_global['qte_volaille_kg']=df_global['qte_volaille_kg']/df_global['pop_M']
df_global.isna()['part_vol_nourr'].sum()
df_global.sample(3)
0
code_pays | dispo_kcal | dispo_prot | part_prot_anim | evol_pop_pct | continent | code_cont | pays | pop_M | pib_hab | risque | part_vol_nourr | qte_volaille_kg | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
136 | 210 | 3184.0 | 105.56 | 63.992042 | 7.959895 | Europe | 1 | Suède | 9971.638 | 53146.5 | 1 | 2.16 | 16.847784 |
31 | 52 | 3149.0 | 94.42 | 34.452447 | 12.782592 | Asie | 0 | Azerbaïdjan | 9949.537 | 14209.6 | 5 | 1.67 | 13.869992 |
56 | 93 | 2121.0 | 49.62 | 21.342201 | 15.306959 | Ameriques | 3 | Haïti | 11123.178 | 1767.2 | 6 | 1.96 | 8.630627 |
df_global.sample(5)
code_pays | dispo_kcal | dispo_prot | part_prot_anim | evol_pop_pct | continent | code_cont | pays | pop_M | pib_hab | risque | part_vol_nourr | qte_volaille_kg | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
66 | 105 | 3528.0 | 126.94 | 58.834187 | 19.380598 | Asie | 0 | Israël | 8381.516 | 39543.2 | 2 | 7.83 | 67.529550 |
158 | 255 | 3769.0 | 99.87 | 58.656253 | 6.525984 | Europe | 1 | Belgique | 11482.178 | 51246.0 | 2 | 1.33 | 11.496077 |
62 | 100 | 2533.0 | 63.35 | 23.677979 | 12.657312 | Asie | 0 | Inde | 1352642.280 | 6496.8 | 5 | 0.42 | 2.320643 |
6 | 9 | 3307.0 | 106.77 | 64.671724 | 10.681070 | Ameriques | 3 | Argentine | 44361.150 | 22745.9 | 6 | 5.71 | 44.250431 |
133 | 207 | 2698.0 | 60.73 | 40.688292 | 11.383559 | Ameriques | 3 | Suriname | 575.990 | 16609.4 | 6 | 5.87 | 32.986684 |
part_imp_vol
= (Importations / (Production + Exportations + Variation des Stocks)) * 100
Remarque :
Production + Exportations + Variation des Stocks
= 0, alors
part_imp_vol
= infImportations
!= 0, cela veut dire que ce pays importe tout ce qu'il consomme part_imp_vol
par le max de vol['part_imp_vol']vol = pd.read_csv('data/FAOSTAT_volaille.csv')
vol.head(5)
Code Domaine | Domaine | Code zone | Zone | Code Élément | Élément | Code Produit | Produit | Code année | Année | Unité | Valeur | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | FBS | Nouveaux Bilans Alimentaire | 2 | Afghanistan | 5511 | Production | 2734 | Viande de Volailles | 2018 | 2018 | Milliers de tonnes | 29 |
1 | FBS | Nouveaux Bilans Alimentaire | 2 | Afghanistan | 5611 | Importations - Quantité | 2734 | Viande de Volailles | 2018 | 2018 | Milliers de tonnes | 25 |
2 | FBS | Nouveaux Bilans Alimentaire | 2 | Afghanistan | 5072 | Variation de stock | 2734 | Viande de Volailles | 2018 | 2018 | Milliers de tonnes | 0 |
3 | FBS | Nouveaux Bilans Alimentaire | 2 | Afghanistan | 5911 | Exportations - Quantité | 2734 | Viande de Volailles | 2018 | 2018 | Milliers de tonnes | 0 |
4 | FBS | Nouveaux Bilans Alimentaire | 2 | Afghanistan | 5301 | Disponibilité intérieure | 2734 | Viande de Volailles | 2018 | 2018 | Milliers de tonnes | 53 |
vol=vol[['Code zone', 'Élément', 'Valeur']]
vol.columns = ['code_pays', 'element', 'valeur']
vol = vol.pivot_table(index=['code_pays'], columns=['element'], values=['valeur'])
vol = vol.reset_index()
vol.columns = ['code_pays', 'dispo_int', 'exp', 'imp', 'nourr', 'prod', 'var_stocks']
vol=vol.fillna(0)
vol.sample(10)
code_pays | dispo_int | exp | imp | nourr | prod | var_stocks | |
---|---|---|---|---|---|---|---|
60 | 95 | 223.0 | 1.0 | 18.0 | 222.0 | 206.0 | 0.0 |
144 | 211 | 132.0 | 2.0 | 45.0 | 132.0 | 97.0 | 8.0 |
36 | 56 | 400.0 | 6.0 | 57.0 | 396.0 | 349.0 | 0.0 |
7 | 10 | 1211.0 | 39.0 | 16.0 | 1211.0 | 1235.0 | 0.0 |
1 | 2 | 53.0 | 0.0 | 25.0 | 53.0 | 29.0 | 0.0 |
171 | 273 | 10.0 | 0.0 | 9.0 | 10.0 | 4.0 | 2.0 |
130 | 189 | 11.0 | 0.0 | 10.0 | 11.0 | 2.0 | 0.0 |
147 | 215 | 81.0 | 0.0 | 1.0 | 81.0 | 79.0 | 0.0 |
116 | 167 | 252.0 | 26.0 | 121.0 | 238.0 | 169.0 | 12.0 |
31 | 50 | 34.0 | 1.0 | 14.0 | 30.0 | 26.0 | 5.0 |
vol['part_imp_vol'] = round(vol['imp'] / (vol['exp'] + vol['var_stocks'] + vol['prod']) * 100, 2)
vol[vol['part_imp_vol'] == np.inf]
code_pays | dispo_int | exp | imp | nourr | prod | var_stocks | part_imp_vol | |
---|---|---|---|---|---|---|---|---|
35 | 55 | 5.0 | 0.0 | 4.0 | 3.0 | 0.0 | 0.0 | inf |
46 | 72 | 3.0 | 0.0 | 3.0 | 3.0 | 0.0 | 0.0 | inf |
129 | 188 | 4.0 | 0.0 | 3.0 | 3.0 | 0.0 | 0.0 | inf |
131 | 191 | 9.0 | 0.0 | 8.0 | 8.0 | 0.0 | 0.0 | inf |
vol = vol.replace([np.inf, -np.inf], np.nan)
vol[vol['part_imp_vol'].isna()][['code_pays', 'exp', 'imp', 'prod', 'var_stocks', 'part_imp_vol']].sort_values(by="code_pays")
code_pays | exp | imp | prod | var_stocks | part_imp_vol | |
---|---|---|---|---|---|---|
35 | 55 | 0.0 | 4.0 | 0.0 | 0.0 | NaN |
46 | 72 | 0.0 | 3.0 | 0.0 | 0.0 | NaN |
129 | 188 | 0.0 | 3.0 | 0.0 | 0.0 | NaN |
131 | 191 | 0.0 | 8.0 | 0.0 | 0.0 | NaN |
z = vol['part_imp_vol'].max()
vol = vol.fillna(z)
vol.sample(3)
code_pays | dispo_int | exp | imp | nourr | prod | var_stocks | part_imp_vol | |
---|---|---|---|---|---|---|---|---|
18 | 28 | 1615.0 | 0.0 | 1.0 | 1563.0 | 1896.0 | 282.0 | 0.05 |
22 | 37 | 9.0 | 0.0 | 5.0 | 9.0 | 7.0 | 2.0 | 55.56 |
167 | 251 | 58.0 | 1.0 | 11.0 | 58.0 | 49.0 | 1.0 | 21.57 |
vol=vol[['code_pays', 'part_imp_vol']]
df_global = pd.merge(df_global, vol, how='left')
df_global.shape
(163, 14)
df_global.sample(3)
code_pays | dispo_kcal | dispo_prot | part_prot_anim | evol_pop_pct | continent | ... | pop_M | pib_hab | risque | part_vol_nourr | qte_volaille_kg | part_imp_vol | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2997.0 | 94.35 | 48.050021 | 1.517634 | Asie | ... | 2951.745 | 12715.0 | 6 | 1.71 | 15.245219 | 275.00 |
92 | 137 | 3051.0 | 88.93 | 46.339818 | 2.253196 | Afrique | ... | 1267.185 | 22208.2 | 4 | 7.42 | 39.457538 | 6.12 |
19 | 32 | 2733.0 | 71.89 | 15.662818 | 30.975456 | Afrique | ... | 25216.267 | 3603.5 | 5 | 0.45 | 3.172555 | 2.53 |
3 rows × 14 columns
df_global.loc[df_global['code_pays'] == 229, 'pays'] = 'Royaume-Uni'
df_global.loc[df_global['code_pays'] == 19, 'pays'] = 'Bolivie'
df_global.loc[df_global['code_pays'] == 231, 'pays'] = 'États-Unis'
df_global.loc[df_global['code_pays'] == 117, 'pays'] = 'Corée du Sud'
df_global.loc[df_global['code_pays'] == 215, 'pays'] = 'Tanzanie'
df_global.loc[df_global['code_pays'] == 41, 'pays'] = 'Chine Continentale'
df_global.loc[df_global['code_pays'] == 214, 'pays'] = 'Chine - Taiwan'
imposition = pd.read_csv("data/imposition.csv")
imposition.sample(3)
pays | taux_imposition | |
---|---|---|
31 | Cambodge | 21.7 |
178 | Trinité-et-Tobago | 39.7 |
123 | Nicaragua | 60.6 |
df_global=pd.merge(df_global, imposition, how='left')
liste_pays_manque_imposition=list(df_global[df_global['taux_imposition'].isna()]['pays'].sort_values())
liste_pays_manque_imposition
['Chine - RAS de Macao', 'Guyana', 'Kirghizistan', 'République de Moldova', 'Slovaquie', 'Tchéquie', 'Turkménistan']
Ces 7 pays ne fournissent pas d'information sur leur taux d'imposition
-> on les exclut de l'analyse (on crée un df spécifique pour ces pays)
df_pays_sans_impot = df_global.loc[df_global['pays'].isin(liste_pays_manque_imposition),:]
df_pays_sans_impot
code_pays | dispo_kcal | dispo_prot | part_prot_anim | evol_pop_pct | continent | ... | pib_hab | risque | part_vol_nourr | qte_volaille_kg | part_imp_vol | taux_imposition | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
55 | 91 | 2913.0 | 86.51 | 41.081956 | 4.310159 | Ameriques | ... | 9312.1 | 6 | 3.98 | 38.510615 | 1.96 | NaN |
73 | 113 | 2729.0 | 84.41 | 40.172965 | 19.962991 | Asie | ... | 5133.2 | 6 | 0.42 | 3.331202 | 362.50 | NaN |
84 | 128 | 3327.0 | 109.47 | 64.775738 | 22.590875 | Asie | ... | 131908.0 | 3 | 4.63 | 34.830187 | 216.67 | NaN |
97 | 146 | 2383.0 | 61.64 | 49.107722 | -1.481853 | Europe | ... | 12373.0 | 5 | 3.26 | 18.756429 | 55.88 | NaN |
110 | 167 | 3277.0 | 86.98 | 60.738101 | 2.306042 | Europe | ... | 39452.8 | 3 | 2.72 | 22.314570 | 58.45 | NaN |
130 | 199 | 2871.0 | 70.57 | 53.011195 | 0.993505 | Europe | ... | 32067.4 | 3 | 1.96 | 14.120631 | 54.72 | NaN |
138 | 213 | 2830.0 | 88.62 | 40.385917 | 18.540867 | Asie | ... | 14845.3 | 6 | 0.70 | 4.614674 | 67.39 | NaN |
7 rows × 15 columns
df_global=df_global.dropna()
df_global.shape
df_global.sample(10)
(156, 15)
code_pays | dispo_kcal | dispo_prot | part_prot_anim | evol_pop_pct | continent | ... | pib_hab | risque | part_vol_nourr | qte_volaille_kg | part_imp_vol | taux_imposition | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
114 | 173 | 3537.0 | 105.59 | 54.863150 | -1.134595 | Europe | ... | 31765.7 | 3 | 3.36 | 30.457582 | 2.00 | 40.7 |
42 | 67 | 3343.0 | 117.99 | 63.923729 | 3.818572 | Europe | ... | 48191.2 | 1 | 1.99 | 19.375016 | 9.83 | 37.3 |
8 | 11 | 3695.0 | 109.12 | 60.328079 | 6.591787 | Europe | ... | 55687.2 | 1 | 2.07 | 18.557283 | 46.91 | 51.5 |
50 | 83 | 3078.0 | 73.57 | 48.987359 | 17.300351 | Oceanie | ... | 2258.9 | 5 | 2.83 | 17.264150 | 100.00 | 32.7 |
74 | 114 | 2197.0 | 61.75 | 24.161943 | 29.153070 | Afrique | ... | 4203.8 | 4 | 0.12 | 0.603200 | 3.33 | 37.2 |
136 | 210 | 3184.0 | 105.56 | 63.992042 | 7.959895 | Europe | ... | 53146.5 | 1 | 2.16 | 16.847784 | 37.85 | 49.1 |
149 | 230 | 3102.0 | 86.45 | 44.261916 | -4.143432 | Europe | ... | 12338.0 | 6 | 2.81 | 24.567106 | 8.22 | 41.7 |
71 | 110 | 2705.0 | 87.36 | 55.638237 | -1.039729 | Asie | ... | 41074.1 | 1 | 3.35 | 18.773261 | 34.64 | 46.7 |
61 | 99 | 3654.0 | 146.13 | 71.840142 | 8.317999 | Europe | ... | 56157.5 | 2 | 3.24 | 29.698883 | 10.00 | 30.6 |
104 | 157 | 2582.0 | 63.53 | 30.946010 | 14.081669 | Ameriques | ... | 5694.9 | 6 | 4.27 | 20.416051 | 4.55 | 60.6 |
10 rows × 15 columns
df_global = df_global.reindex(columns=['code_pays', 'pays', 'code_cont', 'continent',
'pop_M', 'evol_pop_pct', 'pib_hab', 'risque', 'taux_imposition',
'dispo_kcal', 'dispo_prot', 'part_prot_anim',
'qte_volaille_kg', 'part_vol_nourr', 'part_imp_vol'])
df_global.columns = ['code_pays', 'pays', 'code_cont', 'continent',
'pop_M', 'evol_pop', 'pib_hab', 'risque', 'taux_impot',
'dispo_kcal', 'dispo_prot', 'part_prot_anim',
'qte_volaille', 'vol_part_nourr', 'vol_part_import']
df_global.sample(1)
df_global.dtypes
code_pays | pays | code_cont | continent | pop_M | evol_pop | ... | dispo_kcal | dispo_prot | part_prot_anim | qte_volaille | vol_part_nourr | vol_part_import | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
98 | 147 | Namibie | 2 | Afrique | 2448.301 | 19.815415 | ... | 2469.0 | 65.78 | 35.025844 | 9.802716 | 1.75 | 118.18 |
1 rows × 15 columns
code_pays int64 pays object code_cont int64 continent object pop_M float64 evol_pop float64 pib_hab float64 risque int64 taux_impot object dispo_kcal float64 dispo_prot float64 part_prot_anim float64 qte_volaille float64 vol_part_nourr float64 vol_part_import float64 dtype: object
df_global['taux_impot'] = df_global['taux_impot'].astype('float64')
df_index_pays=df_global.set_index('pays')
df_index_pays=df_index_pays[['dispo_kcal', 'dispo_prot', 'part_prot_anim', 'evol_pop']]
df_index_pays.to_csv("data/exports/df_index_pays.csv", index=True, header = True)
df_global[df_global['pop_M'] <= 1000].value_counts().sum()
df_global.shape
23
(156, 15)
df_global.to_csv("data/exports/global.csv", index = False, header = True)
evol_pop
part_prot_anim
dispo_kcal
dispo_prot
data = df_global.copy()
data=data[['dispo_kcal', 'dispo_prot', 'part_prot_anim', 'evol_pop']]
data.sample(3)
data.to_csv("data/exports/data_apc.csv", index=False, header = True)
dispo_kcal | dispo_prot | part_prot_anim | evol_pop | |
---|---|---|---|---|
34 | 2952.0 | 79.08 | 55.538695 | 1.126689 |
147 | 1981.0 | 46.94 | 26.097145 | 40.409459 |
100 | 3297.0 | 106.69 | 65.432562 | 2.966278 |
data = df_global.copy()
data.set_index('pays',inplace = True)
data=data[['dispo_kcal', 'dispo_prot', 'part_prot_anim', 'evol_pop']]
data.sample(3)
data.to_csv("data/exports/data_apc.csv", index=False, header = True)
dispo_kcal | dispo_prot | part_prot_anim | evol_pop | |
---|---|---|---|---|
pays | ||||
Népal | 2769.0 | 74.34 | 19.101426 | 5.359286 |
Dominique | 2952.0 | 79.08 | 55.538695 | 1.126689 |
Ouganda | 1981.0 | 46.94 | 26.097145 | 40.409459 |
from scipy import stats
def corrfunc(x, y, **kws):
r, _ = stats.pearsonr(x, y)
ax = plt.gca()
ax.annotate("r = {:.2f}".format(r),
xy=(.1, .9), xycoords=ax.transAxes)
sns.set_context('notebook', font_scale = 0.8)
sns.set_theme(style="white", palette="Set2", font_scale= 0.8)
g=sns.pairplot(data, height=2)
g.map_lower(corrfunc)
plt.savefig('data/exports/img_charts/1.correlation_var_actives_pairplot.png', dpi = 300)
plt.show(g);
L'analyse des variables 2 à 2 nous fournit plusieurs renseignements :
display(data.describe().T)
fig = plt.figure(figsize=(16,2))
sns.set_theme(style="whitegrid", palette="deep")
col=data.columns
color=['r', 'g', 'b', 'm']
plt.suptitle('Distribution', color='darkred')
for i in range(4):
ax2 = fig.add_subplot(1,4,i+1)
ax2=sns.histplot(x=data[col[i]], color=color[i])
plt.ylabel("")
plt.tick_params(axis='y', pad=2)
plt.tick_params(axis='x', pad=2)
plt.xlabel(col[i], labelpad=10)
plt.grid(alpha=0.3)
plt.savefig('data/exports/img_charts/2-1.distribution_var_actives.png', dpi = 300)
fig = plt.figure(figsize=(16.3,2))
meanpointprops = dict(marker='8', markersize=4, markeredgecolor='tomato', markerfacecolor='tomato')
plt.style.use('seaborn-notebook')
sns.set_theme(style="whitegrid", palette="deep")
col=data.columns
color=['r', 'g', 'b', 'm']
plt.suptitle('Répartition', color='darkred')
for i in range(4):
ax1 = fig.add_subplot(1,4,i+1)
ax1=sns.boxplot(x=data[col[i]], color=color[i], showmeans=True, meanprops=meanpointprops, saturation=0.8, width=0.4)
plt.tick_params(axis='x', pad=2)
plt.xlabel(col[i], labelpad=10)
plt.savefig('data/exports/img_charts/2-2.repartition_var_actives.png', dpi = 300)
plt.show();
count | mean | std | min | 25% | 50% | 75% | max | |
---|---|---|---|---|---|---|---|---|
dispo_kcal | 156.0 | 2877.333333 | 456.837846 | 1786.000000 | 2560.000000 | 2852.500000 | 3293.250000 | 3885.000000 |
dispo_prot | 156.0 | 82.065192 | 21.107699 | 42.410000 | 65.695000 | 80.990000 | 98.640000 | 146.130000 |
part_prot_anim | 156.0 | 42.495794 | 16.068676 | 9.873380 | 27.580997 | 43.772572 | 55.825707 | 73.087995 |
evol_pop | 156.0 | 15.889108 | 14.624651 | -12.811027 | 5.492335 | 12.766813 | 26.353623 | 75.555724 |
data['evol_pop'].nlargest().head(4)
pays Oman 75.555724 Koweït 55.771761 Jordanie 51.991938 Maldives 50.150822 Name: evol_pop, dtype: float64
La réprésentation graphique de la distribution et de la répartition de nos variables nous permet de valider nos données en constatant l'absence d'outliers notamment.
On va effectuer un clustering par segmentation hiérarchique ascendante (clustering agglomeratif)
--> on considère chaque pays comme un cluster
---> puis à chaque itération on les regroupe (ici en utilisant la méthode de Ward)
----> on continue les itérations jusqu'à ce que tous les pays soient regroupés en un seul cluster
Notion de distance à prendre en compte :
distance entre 2 individus
-> quelle métrique utilisée distance euclidienne
qui est le plus
souvent utilisée (cf la longueur du segment qui lie 2 points) mais on pourrait aussi utiliser d'autres
distances comme par exemple la distance de Manhattan (somme de la distance parcourue sur chaque
axe)distance entre 2 groupes d'individus
-> quelle méthode de liens (linkage) ? inertie interclasse
soit élevée inertie intraclasse
méthode de Ward
car elle vise
à minimiser l'inertie intraclasse induite par le regroupement de 2 clusters à chaque itérationfrom sklearn.preprocessing import normalize
from scipy.cluster.hierarchy import linkage, fcluster
from sklearn import preprocessing
from scipy.cluster.hierarchy import dendrogram
sns.set_theme(style="white")
plt.style.use('seaborn-white')
# préparation des données pour le clustering
X = data.values
names = data.index
# Centrage et Réduction
std_scale = preprocessing.StandardScaler().fit(X)
X_scaled = std_scale.transform(X)
# Clustering hiérarchique
Z = linkage(X_scaled, 'ward')
# Affichage du dendrogramme
fig = plt.figure(figsize=(15,6))
plt.title('Classification hiérarchique - Dendrogramme')
plt.ylabel('distance')
dendrogram(
Z,
labels = names
)
plt.savefig('data/exports/img_charts/3.dendo_h_brut.png', dpi = 300)
plt.show();
On va maintenant déterminer à quel niveau on va couper le dendrogramme, c'est-à-dire déterminer le nombre
de groupes de notre partitionnement.
Visuellement, il faut le couper dès lors que les distances entre les noeuds du graphique (qui correspondent
aux regroupements) deviennent importantes (si les distances sont courtes, alors les différences entre les
individus ne sont pas très importantes).
Ainsi, on peut estimer à partir du dendrogramme que notre population peut se partitionner en 5 groupes (en
coupant à une distance de 8).
On peut s'appuyer sur d'autres outils pour déterminer le nombre de groupes optimal à constituer :
La part d'inertie en fonction du nombre de groupes
from fonctions_perso import graph_nb_groupes_dendro
graph_nb_groupes_dendro(data, 20)
Grâce à la variance cumulée, on connait le pourcentage de l’inertie totale expliquée selon le nombre de groupes retenu.
On utilise alors la méthode "du coude" (Elbow method) : on choisit le coude de la courbe comme le nombre de groupes à constituer ; ici, c'est bien à partir de 5 groupes que l'augmentation marginale de la variance cumulée diminue.
Score Silhouette
from fonctions_perso import sil_pers
sns.set_theme(style="white")
plt.style.use('seaborn-white')
sil_pers(data, 20)
Pour avoir une donnée chiffrée, on peut utiliser le score de silhouette :
--> c'est une mesure de similarité entre un point et les autres points du cluster par rapport aux autres
clusters
(plus il est élevé, plus la qualité de la partition est importante)
On peut également représenter cette mesure graphiquement, en fonction du nombre de groupes, comme présenté ici.
On constate que la qualité semble optimale pour 5 ou 6 groupes
# Affichage du dendrogramme
fig = plt.figure(figsize=(8,20))
sns.set_theme(style="white")
plt.style.use('seaborn-white')
plt.title('Dendrogramme - Coupe à 5 clusters')
plt.xlabel('distance')
dendrogram(
Z,
labels = names,
orientation = "left",
color_threshold=8
)
plt.axvline(x=8, color='r', linestyle='--')
plt.savefig('data/exports/img_charts/6.dendo_v_cut_5_clusters.png', dpi = 300)
plt.show();
# Affichage du dendrogramme tronqué
fig = plt.figure(figsize=(8,3))
plt.style.use('seaborn-white')
ax = plt.axes()
plt.title('Version tronquée du dendrogramme')
plt.ylabel('distance')
dendrogram(
Z,
truncate_mode='lastp', p= 5,
labels = names
)
plt.savefig('data/exports/img_charts/7.dendo_v_tronquee.png', dpi = 300)
plt.show();
cluster
dans notre dataframe dans laquelle on indique, pour
chaque pays, le numéro du cluster auquel il appartient# Coupage du dendrogramme en 5 clusters
clusters = fcluster(Z, 5, criterion='maxclust')
clusters
groupes_cah = clusters
array([5, 2, 4, 5, 2, 5, 4, 4, 4, 5, 5, 1, 1, 1, 5, 3, 2, 5, 5, 3, 4, 1, 2, 1, 2, 5, 5, 5, 3, 5, 5, 5, 3, 4, 5, 5, 5, 1, 5, 1, 4, 1, 4, 1, 1, 3, 2, 4, 5, 3, 5, 4, 5, 1, 3, 2, 1, 4, 5, 5, 4, 1, 1, 2, 4, 4, 4, 3, 5, 1, 5, 3, 2, 1, 4, 3, 5, 1, 3, 1, 2, 4, 2, 2, 5, 3, 3, 4, 3, 5, 5, 5, 5, 2, 1, 1, 4, 5, 3, 5, 1, 3, 2, 4, 1, 5, 1, 1, 1, 4, 4, 2, 2, 2, 4, 2, 4, 5, 5, 5, 2, 5, 2, 2, 5, 5, 4, 1, 2, 1, 4, 5, 2, 1, 2, 5, 3, 5, 5, 3, 2, 4, 5, 4, 3, 5, 5, 5, 2, 5, 2, 4, 4, 5, 4, 1], dtype=int32)
# On ajoute la variable 'cluster' au df
data['cluster']=clusters
data.sample(1)
dispo_kcal | dispo_prot | part_prot_anim | evol_pop | cluster | |
---|---|---|---|---|---|
pays | |||||
Ghana | 3035.0 | 61.6 | 24.951299 | 26.325425 | 3 |
df_global['cluster']=clusters
df_global.to_csv("data/exports/global_c.csv", index = False, header = True)
# On vérifie que le nombre d'individus par cluster est le même que celui du dendrogramme
data.cluster.value_counts()
5 50 4 31 1 29 2 27 3 19 Name: cluster, dtype: int64
for i in range(5):
print(f"Groupe {i+1}:")
print(", ".join(data[data['cluster'] == i+1].index.sort_values()))
print(" ")
Groupe 1: Bangladesh, Bolivie, Botswana, Cabo Verde, Cambodge, Djibouti, El Salvador, Eswatini, Fidji, Guatemala, Géorgie, Honduras, Inde, Indonésie, Jamaïque, Lesotho, Namibie, Nicaragua, Népal, Pakistan, Paraguay, Philippines, Pérou, République démocratique populaire lao, Soudan, Sri Lanka, Suriname, Thaïlande, Équateur Groupe 2: Afghanistan, Angola, Gambie, Guinée-Bissau, Haïti, Iraq, Kenya, Libéria, Madagascar, Malawi, Mozambique, Nigéria, Ouganda, Rwanda, République centrafricaine, Sao Tomé-et-Principe, Sierra Leone, Sénégal, Tadjikistan, Tanzanie, Tchad, Timor-Leste, Togo, Zambie, Zimbabwe, Éthiopie, Îles Salomon Groupe 3: Belize, Burkina Faso, Bénin, Cameroun, Congo, Côte d'Ivoire, Gabon, Ghana, Guinée, Jordanie, Koweït, Liban, Maldives, Mali, Mauritanie, Niger, Oman, Vanuatu, Émirats arabes unis Groupe 4: Albanie, Allemagne, Argentine, Australie, Autriche, Belgique, Canada, Chine - RAS de Hong-Kong, Corée du Sud, Danemark, Espagne, Estonie, Finlande, Fédération de Russie, Grèce, Irlande, Islande, Israël, Italie, Lituanie, Luxembourg, Malte, Monténégro, Norvège, Pays-Bas, Pologne, Portugal, Roumanie, Royaume-Uni, Suède, États-Unis Groupe 5: Afrique du Sud, Algérie, Antigua-et-Barbuda, Arabie saoudite, Arménie, Azerbaïdjan, Bahamas, Barbade, Bosnie-Herzégovine, Brésil, Bulgarie, Bélarus, Chili, Chine Continentale, Chypre, Colombie, Costa Rica, Croatie, Dominique, Grenade, Hongrie, Japon, Kazakhstan, Kiribati, Lettonie, Macédoine du Nord, Malaisie, Maroc, Maurice, Mexique, Mongolie, Myanmar, Nouvelle-Zélande, Ouzbékistan, Panama, République dominicaine, Saint-Kitts-et-Nevis, Saint-Vincent-et-les Grenadines, Sainte-Lucie, Samoa, Serbie, Slovénie, Suisse, Trinité-et-Tobago, Tunisie, Turquie, Ukraine, Uruguay, Viet Nam, Égypte
import geopandas as gpd
shapefile = 'data/countries_110m/ne_110m_admin_0_countries.shp' #Read shapefile using Geopandas
gdf = gpd.read_file(shapefile)[['ADMIN', 'ADM0_A3', 'geometry']] #Rename columns.
gdf.columns = ['country', 'country_code', 'geometry']
gdf[gdf['country_code'] == 'CHN']
gdf['geometry'].head()
country | country_code | geometry | |
---|---|---|---|
139 | China | CHN | MULTIPOLYGON (((109.47521 18.19770, 108.65521 ... |
0 MULTIPOLYGON (((180.00000 -16.06713, 180.00000... 1 POLYGON ((33.90371 -0.95000, 34.07262 -1.05982... 2 POLYGON ((-8.66559 27.65643, -8.66512 27.58948... 3 MULTIPOLYGON (((-122.84000 49.00000, -122.9742... 4 MULTIPOLYGON (((-122.84000 49.00000, -120.0000... Name: geometry, dtype: geometry
pop_code = pd.read_csv('data/FAOSTAT_pop_carte.csv')
pop_code=pop_code[['Code zone', 'Zone']]
pop_code.columns = ['code_pays', 'pays']
pop_code.loc[pop_code['code_pays'] == 'GBR', 'pays'] = 'Royaume-Uni'
pop_code.loc[pop_code['code_pays'] == 'BOL', 'pays'] = 'Bolivie'
pop_code.loc[pop_code['code_pays'] == 'USA', 'pays'] = 'États-Unis'
pop_code.loc[pop_code['code_pays'] == 'KOR', 'pays'] = 'Corée du Sud'
pop_code.loc[pop_code['code_pays'] == 'TZA', 'pays'] = 'Tanzanie'
pop_code.loc[pop_code['code_pays'] == 'CHN', 'pays'] = 'Chine Continentale'
pop_code.loc[pop_code['code_pays'] == 'TWN', 'pays'] = 'Chine - Taiwan'
pop_code.sample(3)
code_pays | pays | |
---|---|---|
12 | AZE | Azerbaïdjan |
128 | POL | Pologne |
170 | VNM | Viet Nam |
data.columns
Index(['dispo_kcal', 'dispo_prot', 'part_prot_anim', 'evol_pop', 'cluster'], dtype='object')
data_geo=data.reset_index()
data_geo.columns=['pays', 'dispo_kcal', 'dispo_prot', 'part_prot_anim', 'evol_pop', 'cluster']
data_geo = pd.merge(data_geo, pop_code)
data_geo.sample(3)
pays | dispo_kcal | dispo_prot | part_prot_anim | evol_pop | cluster | code_pays | |
---|---|---|---|---|---|---|---|
8 | Autriche | 3695.0 | 109.12 | 60.328079 | 6.591787 | 4 | AUT |
10 | Barbade | 2956.0 | 88.69 | 57.526215 | 2.391533 | 5 | BRB |
80 | Libéria | 2168.0 | 42.78 | 27.302478 | 33.568736 | 2 | LBR |
#Perform left merge to preserve every row in gdf.
merged = gdf.merge(data_geo, left_on = 'country_code', right_on = 'code_pays', how = 'left')
#Replace NaN values to string 'Pas de donnees'.
merged=merged.replace(np.nan, 'No Data')
#Drop row corresponding to 'Antarctica'
merged = merged.drop(merged.index[159])
from matplotlib import patches
merged['cluster']=merged['cluster'].astype('str')
fig, ax = plt.subplots(1, figsize=(18,12), facecolor='white')
merged.plot(column='cluster', ax=ax, cmap='Set2', legend=True, figsize=(18, 12), edgecolors='black', label='cluster',
legend_kwds=dict(loc='lower right', bbox_to_anchor=(0.1, 0.1), title="Cluster\n", fontsize=10, frameon=True,
ncol=1, fancybox=True, framealpha=1, shadow=True, borderpad=0.5))
ax.axis('off')
ax.add_artist(patches.Rectangle((-197,-60), 394, 150,
edgecolor = 'black', facecolor = 'lightblue',
fill = True, linestyle = 'solid',
linewidth =1, zorder = -3))
plt.title('Répartition des clusters au niveau mondial',
y=1.02, fontdict={'size': 20, 'color':'darkred', 'weight': 500})
plt.savefig('data/exports/img_charts/8.carte_monde_repartition_clusters.png', dpi = 300)
plt.show();
with Image.open("data/exports/img_charts/8.carte_monde_repartition_clusters.png") as im:
(width, height) = (im.width, im.height)
im_cent = im.crop((600, 800, width-500, height-900))
im_cent.save("data/exports/img_charts/8.carte_monde_repartition_clusters.png")
pays_continent.loc[pays_continent['code_pays'] == 229, 'pays'] = 'Royaume-Uni'
pays_continent.loc[pays_continent['code_pays'] == 19, 'pays'] = 'Bolivie'
pays_continent.loc[pays_continent['code_pays'] == 231, 'pays'] = 'États-Unis'
pays_continent.loc[pays_continent['code_pays'] == 117, 'pays'] = 'Corée du Sud'
pays_continent.loc[pays_continent['code_pays'] == 215, 'pays'] = 'Tanzanie'
pays_continent.loc[pays_continent['code_pays'] == 41, 'pays'] = 'Chine Continentale'
pays_continent.loc[pays_continent['code_pays'] == 214, 'pays'] = 'Chine - Taiwan'
data_cont=pd.merge(data, pays_continent, left_index=True, right_on='pays')
data_cont.set_index('pays', inplace=True)
data_cont.drop(columns='code_pays', inplace=True)
data_cont.sample(2)
dispo_kcal | dispo_prot | part_prot_anim | evol_pop | cluster | continent | |
---|---|---|---|---|---|---|
pays | ||||||
Saint-Vincent-et-les Grenadines | 2968.0 | 90.18 | 55.976935 | 1.677230 | 5 | Ameriques |
Viet Nam | 3025.0 | 98.58 | 40.235366 | 10.786388 | 5 | Asie |
sns.set_theme(style="white", font_scale= 0.8)
sns.pairplot(data, hue='cluster', height=2, palette="Set2")
plt.savefig('data/exports/img_charts/9.correlation_var_actives_cluster_pairplot.png', dpi = 300)
plt.show();
On constate que :
Le groupe 3 semble avoir une inertie intraclasse relativement importante, en particulier quand on compare la
variable évolution de la population
avec les autres variables
df=df_global.drop(columns=['code_pays', 'code_cont', 'continent', 'pop_M', 'vol_part_nourr'])
df.set_index('pays')
df['cluster']=clusters
df;
df.dtypes
pays object evol_pop float64 pib_hab float64 risque int64 taux_impot float64 dispo_kcal float64 dispo_prot float64 part_prot_anim float64 qte_volaille float64 vol_part_import float64 cluster int32 dtype: object
#jtplot.style(theme='grade3', context='notebook', fscale=0.8, spines=True, gridlines='--', ticks=True, grid=False, figsize=(18, 16))
sns.pairplot(df, hue='cluster', height=1.4, palette="Set2")
plt.savefig('data/exports/img_charts/10.correlation_var_actives_et_illus_cluster_pairplot.png', dpi = 300)
plt.show();
On se rend compte de la difficulté de lire un tel graphique
--> avec un tel nombre de variables, il est difficile d'établir des liens et de trouver une logique globale
(d'où l'intérêt de trouver une autre méthode)
On va comparer les moyennes des variables (actives et illustratives) relativement aux groupes.
--> on évalue la dispersion de la variable attribuable à l’appartenance aux groupes
Dans quelle mesure, la variable prise individuellement, contribue à la constitution des groupes ?
%%html
<img src="https://kopadata.fr/projet5/data/src_img/inertie_synthese.png" width="1200">
df=df.set_index('pays')
df_actives=df[['cluster', 'dispo_kcal', 'dispo_prot', 'part_prot_anim', 'evol_pop']]
df_actives.columns
df_illust=df[['cluster', 'pib_hab', 'risque', 'taux_impot', 'qte_volaille', 'vol_part_import']]
Index(['cluster', 'dispo_kcal', 'dispo_prot', 'part_prot_anim', 'evol_pop'], dtype='object')
pd.options.display.float_format = '{:.2f}'.format
from fonctions_perso import caracteriser_partition, multi_table
disp=caracteriser_partition(df_actives=df_actives, df_illust=df_illust)
multi_table(disp)
|
|
On constate ainsi que la formation des groupes s'est appuyée avant tout sur la disponbilité alimentaire, tant en protéine (81.3% de sa dispersion est attribuable à l'appartenance aux groupes) qu'en kcal (76%).
Elle repose dans une moindre mesure sur la proportion de protéines animales (65.8%) et sur la croissance démographique (67%).
En incluant les variables illustratives dans notre analyse, on constate que :
pd.options.display.precision=2
df_centroid=df.groupby('cluster').mean()
df_centroid
df.describe()
evol_pop | pib_hab | risque | taux_impot | dispo_kcal | dispo_prot | part_prot_anim | qte_volaille | vol_part_import | |
---|---|---|---|---|---|---|---|---|---|
cluster | |||||||||
1 | 13.05 | 9150.84 | 4.93 | 35.88 | 2628.62 | 68.29 | 34.34 | 14.06 | 157.50 |
2 | 29.68 | 2876.51 | 5.59 | 41.73 | 2244.11 | 54.94 | 22.62 | 4.27 | 38.83 |
3 | 38.62 | 13082.18 | 5.05 | 39.04 | 2788.63 | 75.40 | 33.95 | 15.75 | 362.22 |
4 | 4.99 | 46593.94 | 2.55 | 40.86 | 3461.65 | 111.99 | 60.49 | 27.34 | 74.67 |
5 | 8.21 | 20984.85 | 4.24 | 38.45 | 3034.96 | 88.68 | 50.05 | 29.76 | 311.55 |
evol_pop | pib_hab | risque | taux_impot | dispo_kcal | dispo_prot | part_prot_anim | qte_volaille | vol_part_import | cluster | |
---|---|---|---|---|---|---|---|---|---|---|
count | 156.00 | 156.00 | 156.00 | 156.00 | 156.00 | 156.00 | 156.00 | 156.00 | 156.00 | 156.00 |
mean | 15.89 | 19777.28 | 4.37 | 39.09 | 2877.33 | 82.07 | 42.50 | 20.24 | 194.81 | 3.29 |
std | 14.62 | 19577.46 | 1.59 | 14.82 | 456.84 | 21.11 | 16.07 | 16.51 | 594.75 | 1.52 |
min | -12.81 | 933.10 | 1.00 | 8.50 | 1786.00 | 42.41 | 9.87 | 0.45 | -1950.00 | 1.00 |
25% | 5.49 | 4956.62 | 3.00 | 30.67 | 2560.00 | 65.69 | 27.58 | 5.66 | 2.52 | 2.00 |
50% | 12.77 | 12857.25 | 5.00 | 37.10 | 2852.50 | 80.99 | 43.77 | 17.97 | 21.24 | 4.00 |
75% | 26.35 | 28572.58 | 6.00 | 47.25 | 3293.25 | 98.64 | 55.83 | 28.96 | 106.83 | 5.00 |
max | 75.56 | 114110.00 | 7.00 | 106.00 | 3885.00 | 146.13 | 73.09 | 72.59 | 3155.56 | 5.00 |
Annalyse de centroïdes
--> On va caractériser nos 5 groupes afin de savoir s'il correspondent au profil que nous recherchons
Groupes 1 et 2 :
Valeurs inférieures à la moyenne globale concernant nos variables 'alimentaires'
Les groupes 1 et 2 ne correpondent pas au profil recherché
Groupe 3 :
Ce qui le différencie, c'est une forte croissance démographique, et une part de volaille importée
importante;
Ces 2 aspects sont intéressants pour nous par rapport à notre objectif d'un nouveau marché de vente,
mais ils ne suffisent pas
Le groupe 3 ne correpond pas au profil recherché
Groupes 4 et 5 :
On en conclue que ce sont les pays des groupes 4 et 5 qui conviennent le plus à notre objectif d'implantation
On va essayer de caractériser chacun de nos groupes, plus précisément qu'avec les centroïdes.
--> On va comparer, pour chaque variable, la moyenne de chaque groupe avec la moyenne globale
----> On va comparer les valeurs des centroïdes avec les valeurs du barycentre de la population entière
afin de déterminer si les valeurs observées dans un groupe sont dûes seulement au hasard ou non
On se place dans le cadre d'un tirage sans remise de nk éléments parmi n (nk étant la taille de
l'échantillon du groupe "k" et n celle de l'échantillon global)
Si les valeurs observées pour le groupe k ne relèvent que du hasard, alors, selon le théorème central
limite, la loi de $\scriptstyle \bar{X_q}$ suit une loi normale réduite : $\scriptstyle \bar{X_q} \sim
\mathcal{N}(\mu,\,\sigma^{2})$
On peut alors calculer la valeur centrée suivante, à savoir la valeur-test :
avec $ \scriptstyle \sigma^2 $ : la variance empirique de la population
$$ \scriptstyle vt = \displaystyle \frac{\bar x_g - \bar x}{\sqrt[]{\frac{n-n_g}{n-1}\frac{\sigma^2}{n_g}}} $$
On va réaliser le test suivant :
On calcule les valeurs de ce test pour nos 4 variables actives
Le tableau suivant synthétise les résultats obtenus :
les valeurs en rouge sont celles pour lesquelles la p_value est supérieure à 5%
--> la valeur-test est alors comprise entre [-1.96;1.96]
===> On accepte HO, c\'est-à-dire que la variable ne caractérise
pas la classe
Plus la valeur aboslue de la valeur-test est élevée, plus la variable caractérise la
classe
On lance le test
from fonctions_perso import valeur_test
a,b,c,d,e = valeur_test(df_actives, 'cluster')
a
Groupe_1 | Groupe_2 | Groupe_3 | Groupe_4 | Groupe_5 | |
---|---|---|---|---|---|
dispo_kcal_stat | -2.90 | -7.05 | -0.82 | 7.02 | 2.31 |
dispo_prot_stat | -3.49 | -6.58 | -1.34 | 7.72 | 2.15 |
part_prot_anim_stat | -2.66 | -6.31 | -2.22 | 6.15 | 3.12 |
evol_pop_stat | -1.02 | 4.78 | 6.47 | -4.04 | -3.51 |
multi_table(b)
|
|
|
|
|
On représente le profil de chaque groupe grâce à un graphique de type radar, en utilisant les valeurs-test pour caractériser ces groupes
from fonctions_perso import make_spider
df_rad=e.T.reset_index()
df_rad.columns=['groupes', 'dispo_kcal', 'dispo_prot', 'part_prot_anim', 'evol_pop']
# Intitialisation de la figure
my_dpi=96
plt.figure(figsize=(1200/my_dpi, 1200/my_dpi), dpi=my_dpi)
# Creation de la color palette:
my_palette = plt.cm.get_cmap("Set1", len(df_rad.index))
# Ajoutd'un titre général
plt.suptitle(t="Profil des groupes", size=16, color='darkred', x=0.5, y=1)
# On boucle sur chaque groupe
for row in range(0, len(df_rad.index)):
make_spider(df_rad=df_rad, row=row, title=df_rad['groupes'][row], color=my_palette(row), serie=1)
plt.tight_layout(h_pad=2, pad=1)
plt.savefig('data/exports/img_charts/11.profils_clusters_radar_graph.png', dpi = 300)
plt.show();
Caractérisation des groupes
Le groupe 1
Le groupe 2
Le groupe 3
Le groupe 4
Le groupe 5
Notre objectif étant de trouver un nouveau marché international pour vendre nos produits, la taille du marché ne doit pas être trop étroite
Ainsi, on commence par supprimer les pays dont la population est inférieure à 1 million d'habitants
# On prépare notre df
df=df_global.drop(columns=['code_pays', 'code_cont', 'continent', 'vol_part_nourr'])
df['cluster']=clusters
select_c4 = df.loc[df['cluster'] == 4]
select_c4=select_c4.drop(columns='cluster');
# On supprime les pays de moins de 1 million d'habitants
select_c4=select_c4.loc[select_c4['pop_M'] >= 1000]
nb_pays=select_c4.shape[0]
print(f"Il reste encore {nb_pays} pays candidats possibles.")
select_c4.sample(1)
Il reste encore 27 pays candidats possibles.
pop_M | evol_pop | pib_hab | risque | taux_impot | dispo_kcal | dispo_prot | part_prot_anim | qte_volaille | vol_part_import | |
---|---|---|---|---|---|---|---|---|---|---|
pays | ||||||||||
Norvège | 5337.9620 | 11.8831 | 63332.8000 | 1 | 37.0000 | 3371.0000 | 113.4300 | 59.8695 | 20.6071 | 2.3000 |
On va attribuer une note à chaque pays :
--> pour chaque variable, aussi bien actives qu'illustratives, on attribue une note à chaque pays (en
fonction de son classement pour cette variable)
--> puis on agrège l'ensemble des notes obtenues par chaque pays, en les pondérant
Pour rappel, les variables sont les suivantes :
--> 'evol_pop', 'pib_hab', 'taux_impot', 'dispo_kcal', 'dispo_prot', 'part_prot_anim', 'qte_volaille',
'vol_part_import'
On va les différencier en fonction du niveau de valeur souhaité :
Cas particulier de la variabe 'risque pays' :
# on passe la variable 'pays' en index
select_c4=select_c4.set_index('pays')
# on enlève la variable 'pop_M'
sel=select_c4.drop(columns='pop_M')
# nombre d'individus
n=sel.shape[0]
# nombre de variables
p=sel.shape[1]
# liste des notes
note=np.arange(1,n+2,1)
# cas particulier de la variable 'risque'
valeurs_risque=np.arange(6,0,-1)
note_risque=np.rint(np.linspace(1,27,6))
# on initialise un tableau qui contiendra le nom des variables des notes
col_new_name=[]
# on boucle sur chaque variable
for i in range(p):
# on récupère le nom des variables dans une liste
c_name=sel.columns[i]
t=sel[c_name].tolist()
# on différencie les variables
if c_name != 'risque':
if c_name != 'taux_impot':
# si évolution dans le même sens, tri ascendant
t=sorted(t)
else:
# sinon, tri descendant
t=sorted(t, reverse=True)
# on crée un dictionnaire {valeurs: note}
dic_n=dict(zip(t,note))
else:
# cas particulier de la variable 'risque'
dic_n=dict(zip(valeurs_risque, note_risque))
# on applique le dict dont le résultat est conservé dans une nouvelle colonne
sel[c_name+'_note']=sel[c_name].apply(lambda x: int(dic_n[x]))
# on ajoute le nom de la variable note dans notre tab 'col_new_name'
col_new_name.append(c_name+'_note')
# on crée un nouveau df avec seulement les colonnes de notes
sel_note=sel[col_new_name]
# on affiche un random de 1 pays
sel_note.sample(1)
dispo_kcal | dispo_kcal_note | |
---|---|---|
pays | ||
Irlande | 3885.00 | 27 |
États-Unis | 3782.00 | 26 |
Belgique | 3769.00 | 25 |
Autriche | 3695.00 | 24 |
Roumanie | 3581.00 | 23 |
Canada | 3566.00 | 22 |
Allemagne | 3554.00 | 21 |
Pologne | 3537.00 | 20 |
Israël | 3528.00 | 19 |
Italie | 3503.00 | 18 |
Portugal | 3480.00 | 17 |
Corée du Sud | 3420.00 | 16 |
Lituanie | 3411.00 | 15 |
Danemark | 3401.00 | 14 |
Australie | 3391.00 | 13 |
Grèce | 3382.00 | 12 |
Norvège | 3371.00 | 11 |
Albanie | 3360.00 | 10 |
Fédération de Russie | 3345.00 | 9 |
Royaume-Uni | 3344.00 | 8 |
Finlande | 3343.00 | 7 |
Espagne | 3322.00 | 6 |
Argentine | 3307.00 | 5 |
Pays-Bas | 3297.00 | 4 |
Chine - RAS de Hong-Kong | 3267.00 | 3 |
Estonie | 3247.00 | 2 |
Suède | 3184.00 | 1 |
evol_pop_note | pib_hab_note | risque_note | taux_impot_note | dispo_kcal_note | dispo_prot_note | part_prot_anim_note | qte_volaille_note | vol_part_import_note | |
---|---|---|---|---|---|---|---|---|---|
pays | |||||||||
Australie | 26 | 17 | 22 | 9 | 13 | 10 | 25 | 25 | 4 |
sel_note['note_finale']=(sel_note['evol_pop_note']*0.5
+ sel_note['pib_hab_note']*4
+ sel_note['taux_impot_note']*1
+ sel_note['dispo_kcal_note']*2
+ sel_note['dispo_prot_note']*2
+ sel_note['part_prot_anim_note']*1.5
+ sel_note['qte_volaille_note']*3
+ sel_note['vol_part_import_note']*2
+ sel_note['risque_note']*1)
select_finale=sel_note.note_finale.sort_values(ascending=False)
select_top_10=select_finale.iloc[:10]
select_top_10.index
print("Les 10 pays candidats sont donc les suivants (entre parenthèse le score final obtenu) :")
for i in range(10):
print(f" - {select_top_10.index[i]} ({select_top_10[i]})"+("," if i<9 else ""))
Index(['Chine - RAS de Hong-Kong', 'Irlande', 'Danemark', 'États-Unis', 'Autriche', 'Israël', 'Norvège', 'Australie', 'Canada', 'Portugal'], dtype='object', name='pays')
Les 10 pays candidats sont donc les suivants (entre parenthèse le score final obtenu) : - Chine - RAS de Hong-Kong (368.5), - Irlande (366.5), - Danemark (351.5), - États-Unis (348.5), - Autriche (291.0), - Israël (288.0), - Norvège (287.5), - Australie (278.5), - Canada (277.5), - Portugal (263.0)
select_finale
pays Chine - RAS de Hong-Kong 368.50 Irlande 366.50 Danemark 351.50 États-Unis 348.50 Autriche 291.00 Israël 288.00 Norvège 287.50 Australie 278.50 Canada 277.50 Portugal 263.00 Finlande 254.50 Lituanie 251.50 Allemagne 249.00 Pays-Bas 248.00 Royaume-Uni 232.00 Suède 224.50 Espagne 219.00 Belgique 213.00 Estonie 197.50 Pologne 192.00 Roumanie 180.50 Corée du Sud 176.00 Italie 175.50 Argentine 170.00 Grèce 169.50 Albanie 143.50 Fédération de Russie 140.50 Name: note_finale, dtype: float64
df=df.set_index('pays')
df_top_10=df[df.index.isin(select_top_10.index)]
df_top_10['note_finale']=sel_note['note_finale']
df_top_10=df_top_10.sort_values(by="note_finale", ascending=False)
df_top_10=df_top_10.loc[:, 'evol_pop': 'cluster']
df_top_10
evol_pop | pib_hab | risque | taux_impot | dispo_kcal | dispo_prot | part_prot_anim | qte_volaille | vol_part_import | cluster | |
---|---|---|---|---|---|---|---|---|---|---|
pays | ||||||||||
Chine - RAS de Hong-Kong | 7.12 | 61071.90 | 3 | 22.90 | 3267.00 | 128.53 | 73.09 | 39.88 | 143.03 | 4 |
Irlande | 9.12 | 83470.60 | 3 | 26.00 | 3885.00 | 117.58 | 61.24 | 24.49 | 37.50 | 4 |
Danemark | 4.63 | 56102.80 | 1 | 23.80 | 3401.00 | 117.08 | 68.10 | 26.95 | 47.32 | 4 |
États-Unis | 7.78 | 61544.40 | 2 | 43.80 | 3782.00 | 113.74 | 64.60 | 56.64 | 0.49 | 4 |
Autriche | 6.59 | 55687.20 | 1 | 51.50 | 3695.00 | 109.12 | 60.33 | 18.56 | 46.91 | 4 |
Israël | 19.38 | 39543.20 | 2 | 26.20 | 3528.00 | 126.94 | 58.83 | 67.53 | 0.00 | 4 |
Norvège | 11.88 | 63332.80 | 1 | 37.00 | 3371.00 | 113.43 | 59.87 | 20.61 | 2.30 | 4 |
Australie | 16.72 | 49576.00 | 2 | 47.40 | 3391.00 | 105.94 | 66.74 | 48.64 | 1.26 | 4 |
Canada | 11.21 | 48924.40 | 2 | 20.50 | 3566.00 | 104.12 | 54.50 | 40.49 | 10.87 | 4 |
Portugal | -3.20 | 34013.10 | 2 | 39.80 | 3480.00 | 116.95 | 63.59 | 31.59 | 18.68 | 4 |
df_acp=df[['cluster', 'evol_pop', 'dispo_kcal', 'dispo_prot', 'part_prot_anim']]
varSupp = df[['pib_hab', 'risque', 'taux_impot', 'qte_volaille', 'vol_part_import']]
pays_select=['Chine - RAS de Hong-Kong', 'Irlande', 'Danemark', 'États-Unis',
'Autriche', 'Israël', 'Norvège', 'Australie', 'Canada', 'Portugal']
df_spec=df_acp[df_acp.index.isin(pays_select)]
df_spec
cluster | evol_pop | dispo_kcal | dispo_prot | part_prot_anim | |
---|---|---|---|---|---|
pays | |||||
Australie | 4 | 16.72 | 3391.00 | 105.94 | 66.74 |
Autriche | 4 | 6.59 | 3695.00 | 109.12 | 60.33 |
Canada | 4 | 11.21 | 3566.00 | 104.12 | 54.50 |
Danemark | 4 | 4.63 | 3401.00 | 117.08 | 68.10 |
Chine - RAS de Hong-Kong | 4 | 7.12 | 3267.00 | 128.53 | 73.09 |
Irlande | 4 | 9.12 | 3885.00 | 117.58 | 61.24 |
Israël | 4 | 19.38 | 3528.00 | 126.94 | 58.83 |
Norvège | 4 | 11.88 | 3371.00 | 113.43 | 59.87 |
Portugal | 4 | -3.20 | 3480.00 | 116.95 | 63.59 |
États-Unis | 4 | 7.78 | 3782.00 | 113.74 | 64.60 |
from acp_perso import acp_global
resultat_acp=acp_global(df=df_acp,
axis_ranks=[(0,1)],
group='cluster',
group_special=df_spec,
varSupp=varSupp,
varSuppQual=None,
values_detail=True,
labels=None,
labels_ind=True,
legend_label=None,
graph_only=None,
widget=True,
data_only=None,
version_name="v1")
Nombre d'individus : 156 Nombre de variables : 4
evol_pop | dispo_kcal | dispo_prot | part_prot_anim | |
---|---|---|---|---|
count | 156.0000 | 156.0000 | 156.0000 | 156.0000 |
mean | 15.8891 | 2877.3333 | 82.0652 | 42.4958 |
std | 14.6247 | 456.8378 | 21.1077 | 16.0687 |
min | -12.8110 | 1786.0000 | 42.4100 | 9.8734 |
25% | 5.4923 | 2560.0000 | 65.6950 | 27.5810 |
50% | 12.7668 | 2852.5000 | 80.9900 | 43.7726 |
75% | 26.3536 | 3293.2500 | 98.6400 | 55.8257 |
max | 75.5557 | 3885.0000 | 146.1300 | 73.0880 |
nombre de composantes calculées : 4
Variables | Moyennes | Ecarts_types | Var_Exp | Prop_Var_Exp | |
---|---|---|---|---|---|
0 | evol_pop | -0.0000 | 1.0000 | 2.8856 | 0.7214 |
1 | dispo_kcal | -0.0000 | 1.0000 | 0.6410 | 0.1602 |
2 | dispo_prot | 0.0000 | 1.0000 | 0.3777 | 0.0944 |
3 | part_prot_anim | -0.0000 | 1.0000 | 0.0957 | 0.0239 |
---------------------------------- | test des bâtons brisés | ----------------------------------
Val.Propre | Seuils | |
---|---|---|
0 | 2.8856 | 2.0833 |
1 | 0.6410 | 1.0833 |
2 | 0.3777 | 0.5833 |
3 | 0.0957 | 0.2500 |
Selon le "test des bâtons brisés, seul le premier facteur est valide (car pour le facteur 2, la valeur du seuil est inférieure à la valeur propre).
----------------------------------------------------------------------------- | coordonnées factorielles des individus (affichage random de 3 individus) | -----------------------------------------------------------------------------
1 | 2 | 3 | 4 | |
---|---|---|---|---|
pays | ||||
Honduras | -1.0930 | -0.2046 | -0.0060 | -0.3201 |
Myanmar | 0.3214 | -0.5269 | 0.1348 | 0.5771 |
Arabie saoudite | 0.0665 | 1.2937 | -0.3903 | -0.4078 |
################################################################################ #### Qualité de la représentation des individus #### ################################################################################ La qualité de représentation des individus sur les axes du plan factoriel Il s'agit de calculer le COS² de chaque individu pour chaque axe. Intéressant si on a plus de 2 dimensions. En effet, pour chaque individu, la somme des COS² sur l'ensemble des facteurs est égale à 1. Ainsi, si on a par exemple 4 dimensions, on peut, en additionnant les COS² sur les 2 premiers facteurs, savoir s'ils sont bien représentés sur le premier plan factoriel (cad si la somme des COS² sur les 2 premiers facteurs est proche de 1), et donc interprétables On affiche un random sur 5 individus de la mesure des COS²
id | COS²_F1 | COS²_F2 | COS²_F3 | COS²_F4 | |
---|---|---|---|---|---|
0 | Arménie | 0.7199 | 0.2123 | 0.0254 | 0.0424 |
1 | Afghanistan | 0.9764 | 0.0002 | 0.0017 | 0.0217 |
2 | Albanie | 0.9106 | 0.0110 | 0.0421 | 0.0364 |
3 | Algérie | 0.0062 | 0.3799 | 0.6099 | 0.0040 |
4 | Angola | 0.8686 | 0.0712 | 0.0473 | 0.0130 |
################################################################################ #### Contributions des individus aux axes #### ################################################################################ Elles permettent de déterminer les individus qui pèsent le plus dans la définition de chaque facteur. On regarde quels sont les individus qui sont les plus contributifs et ce, pour les différents axes Pour chaque axe, on affiche les 5 individus qui contribuent le plus.
|
|
|
|
Le champ components_ de l'objet ACP correspond aux valeurs propres On peut alors calculer la matrice des corrélations variables * facteurs #################################################################################### #### Les vecteurs propres #### ####################################################################################
id | F1 | F2 | F3 | F4 | |
---|---|---|---|---|---|
0 | evol_pop | -0.4140 | 0.8562 | 0.3052 | -0.0481 |
1 | dispo_kcal | 0.5273 | 0.3736 | -0.4318 | -0.6293 |
2 | dispo_prot | 0.5464 | 0.3504 | -0.1234 | 0.7506 |
3 | part_prot_anim | 0.5020 | -0.0676 | 0.8397 | -0.1958 |
#################################################################################### #### Corrélations par facteur #### ####################################################################################
id | COR_F1 | COR_F2 | COR_F3 | COR_F4 | |
---|---|---|---|---|---|
0 | evol_pop | -0.7033 | 0.6855 | 0.1876 | -0.0149 |
1 | dispo_kcal | 0.8957 | 0.2991 | -0.2654 | -0.1946 |
2 | dispo_prot | 0.9282 | 0.2805 | -0.0759 | 0.2322 |
3 | part_prot_anim | 0.8527 | -0.0541 | 0.5161 | -0.0606 |
#################################################################################### #### COS² des variables #### ####################################################################################
id | COS²_var_F1 | COS²_var_F2 | COS²_var_F3 | COS²_var_F4 | |
---|---|---|---|---|---|
0 | evol_pop | 0.4947 | 0.4699 | 0.0352 | 0.0002 |
1 | dispo_kcal | 0.8022 | 0.0895 | 0.0704 | 0.0379 |
2 | dispo_prot | 0.8616 | 0.0787 | 0.0058 | 0.0539 |
3 | part_prot_anim | 0.7270 | 0.0029 | 0.2664 | 0.0037 |
la somme des COS² d’une variable sur l’ensemble des facteurs est égale à 1
#################################################################################### #### contribution des variables #### #################################################################################### La contribution est également basée sur le carré de la corrélation, mais relativisée par l’importance de l’axe
id | CTR_var_F1 | CTR_var_F2 | CTR_var_F3 | CTR_var_F4 | |
---|---|---|---|---|---|
0 | evol_pop | 0.1714 | 0.7331 | 0.0932 | 0.0023 |
1 | dispo_kcal | 0.2780 | 0.1396 | 0.1865 | 0.3960 |
2 | dispo_prot | 0.2986 | 0.1228 | 0.0152 | 0.5634 |
3 | part_prot_anim | 0.2520 | 0.0046 | 0.7051 | 0.0383 |
Non développé pour le moment
#################################################################################### #### Variables illustratives quantitatives #### #################################################################################### ------------------------------------------------------- | corrélations avec les axes factoriels | -------------------------------------------------------
id | COR_VAR_QVE_SUP_F1 | COR_VAR_QVE_SUP_F2 | COR_VAR_QVE_SUP_F3 | COR_VAR_QVE_SUP_F4 | |
---|---|---|---|---|---|
0 | pib_hab | 0.7442 | 0.2781 | 0.1735 | -0.0553 |
1 | risque | -0.6489 | -0.1011 | -0.0934 | 0.0625 |
2 | taux_impot | -0.0582 | -0.0251 | -0.0631 | -0.0451 |
3 | qte_volaille | 0.5544 | 0.0049 | 0.3708 | -0.1012 |
4 | vol_part_import | 0.0120 | -0.0386 | 0.2247 | -0.0148 |
Les variables actives sont bien représentées
--> La longueur du vecteur représentant la variable est liée à la qualité de la représentation de la
variable dans le plan factoriel :
une variable est d'autant mieux représentée que l'extrémité du vecteur qui la représente est
proche du cercle de corrélations
-->On en conclue que nos 4 variables actives sont bien représentées
On peut calculer la qualité de la représentation des variables grâce au COS² (COS² obtenus à partir de la
matrice de corrélations des variables avec les axes factoriels)
-> on additionne le COS² des facteurs 1 et 2
---> plus la valeur est proche de 1, meilleure est la représentation de la variable dans le 1er plan
factoriel
pd.options.display.float_format = '{:.2f}'.format
print(*resultat_acp)
val_centre_reduit valeurs_propres nb_groupes_opt coord_fact_ind cos2_ind contribution_ind vecteurs_propres matrice_cor cor_par_facteur cos2_var contribution_var cor_par_facteur_varIllus centroides_1_2 graph_proj_ind_1_2 graph_combo_1_2 cercle_cor_1_2 corres_ind_pays
df_cos_var = resultat_acp["cos2_var"]
df_cos_var
id | COS²_var_F1 | COS²_var_F2 | COS²_var_F3 | COS²_var_F4 | |
---|---|---|---|---|---|
0 | evol_pop | 0.49 | 0.47 | 0.04 | 0.00 |
1 | dispo_kcal | 0.80 | 0.09 | 0.07 | 0.04 |
2 | dispo_prot | 0.86 | 0.08 | 0.01 | 0.05 |
3 | part_prot_anim | 0.73 | 0.00 | 0.27 | 0.00 |
df_cos_var = df_cos_var[['id', 'COS²_var_F1', 'COS²_var_F2']]
df_temp=df_cos_var.copy()
df_temp['qualite_1er_plan'] = df_cos_var['COS²_var_F1'] + df_cos_var['COS²_var_F2']
df_cos_var=df_temp.copy()
df_cos_var_q=df_cos_var[['id', 'qualite_1er_plan']]
df_cos_var_q
id | qualite_1er_plan | |
---|---|---|
0 | evol_pop | 0.96 |
1 | dispo_kcal | 0.89 |
2 | dispo_prot | 0.94 |
3 | part_prot_anim | 0.73 |
En effet, la projection sur l'axe factoriel de l'extrémité de la flèche représentant une variable correspond au
coefficient de corrélation entre la variable et l'axe factoriel
De plus, pour les variables bien représentées (comme c'est le cas ici), l'angle entre deux variables est lié au
coefficient de corrélation entre ces 2 variables (plus précisément, le cosinus de cet angle correspond au
coefficient de corrélation)
--> on constate que la disponibilité alimentaire en kcal et la disponibilité alimentaire en protéines ont une
corrélation proche de 1
Remarque : cela confirme ce que nous avons trouvé précédemment lors de l'analyse des corrélations des variables
2 à 2 (les disponibilités alimentaires, tant en calories qu'en protéines, suivent la même évolution)
* la 4e variable active, relative à la croissance démographique, est corrélée
négativement à l'axe 1
On peut ainsi caractériser nos axes de la façon suivante :
On peut confirmer l'analyse graphique par une analyse analytique
x=resultat_acp['cor_par_facteur']
x=x[['id', 'COR_F1', 'COR_F2']]
x
id | COR_F1 | COR_F2 | |
---|---|---|---|
0 | evol_pop | -0.70 | 0.69 |
1 | dispo_kcal | 0.90 | 0.30 |
2 | dispo_prot | 0.93 | 0.28 |
3 | part_prot_anim | 0.85 | -0.05 |
x=resultat_acp['contribution_var']
x=x.round(2)
x=x[['id', 'CTR_var_F1', 'CTR_var_F2']]
x
id | CTR_var_F1 | CTR_var_F2 | |
---|---|---|---|
0 | evol_pop | 0.17 | 0.73 |
1 | dispo_kcal | 0.28 | 0.14 |
2 | dispo_prot | 0.30 | 0.12 |
3 | part_prot_anim | 0.25 | 0.00 |
==> on cherche donc des pays avec des coordonnées F1 et F2 positives et élevées
==> c'est-à-dire des pays sur la partie "en haut à droite" du graphique de projection des individus
remarque : L'Islande n'avait pas été prise en compte précédemment en raison de sa trop faible population (moins de 1 million d'habitants)
Nous allons donc effectuer des tests de comparaison entre différents groupes.
Or ces tests supposent que la variable suive une loi normale :
--> c'est pourquoi nous allons commencer par tester la normalité de la distribution de nos variables
Nous allons nous limiter à nos 4 variables actives
On commence par examiner comment elles sont distribuées grâce à un histogramme.
On affiche également la forme estimée de sa distribution : cf. KDE(estimation de densité par noyau)
fig = plt.figure(figsize=(10,6))
col=df_actives.columns[1:]
for i,j in zip(range(len(col)+1), col):
ax = fig.add_subplot(2,2,i+1)
sns.histplot(data=df_actives, x=j, kde=True)
plt.tight_layout()
plt.savefig('data/exports/img_charts/12.distribution_var_actives.png', dpi = 300)
plt.show();
Les différents tests de normalité sont très sensibles aux valeurs aberrantes.
On va visualiser nos variables avec des box-plot pour les déceler
df_total=df_actives.drop(columns='cluster')
df_total.shape[1]
fig = plt.figure(figsize=(12,4))
color=sns.color_palette()
for i in (range(df_total.shape[1])):
ax=fig.add_subplot(2,2,i+1)
ax = sns.boxplot(data=df_total, x=df_total[df_total.columns[i]], color=color[i], width=0.6)
plt.xlabel('')
plt.title(df_total.columns[i], c='darkred')
plt.tight_layout(h_pad=1.5);
plt.savefig('data/exports/img_charts/13.boxplot_var_actives.png', dpi = 300)
plt.show();
On constate qu'il n'y a qu'un seul outlier
df_total.loc[df_total['evol_pop']==df_total['evol_pop'].max(),:]
dispo_kcal | dispo_prot | part_prot_anim | evol_pop | |
---|---|---|---|---|
pays | ||||
Oman | 2940.00 | 85.36 | 49.29 | 75.56 |
Le Q-Q plot, quantile-quantile plot, est un graphique qui permet de comparer les distributions de deux
ensembles de données.
--> un de ces deux ensembles peut être généré à partir d'une loi de probabilité qui sert de référentiel.
----> on va ici choisir la loi normale comme référence
Ainsi, si les données suivent une loi normale, les points obtenus forment une droite, ils sont alignés sur la diagonale principale
fig = plt.figure(figsize=(10,6))
col=df_actives.columns[1:]
for i,j in zip(range(len(col)+1), col):
ax = fig.add_subplot(2,2,i+1)
res = stats.probplot(df_actives[j], dist='norm', plot=ax)
ax.set_title(j, c='darkred', fontsize=11)
plt.tight_layout()
plt.savefig('data/exports/img_charts/14.qq_plot_var_actives.png', dpi = 300)
plt.show();
Graphiquement, nos 4 variables semblent suivre approximativement une loi normale :
Cette analyse graphique permet de se se faire une première idée sur la distribution de nos variables, mais cela
reste trop approximatif et subjectif
--> intérêt de réaliser des tests statistiques
Différents tests statistiques existent pour tester la normalité d'une distribution, l'objectif étant de
vérifier la compatibilité à la loi normale
Nous allons tester nos variables actives avec 5 tests différents, chacun de ces tests ayant sa spécificité
Chaque test renverra au moins deux valeurs :
Statistique : Valeur calculée par le test qui peut être interprétée dans le contexte du test en la comparant à des valeurs critiques de la distribution de la statistique de test.
p-value : Valeur utilisée pour interpréter le test :
-> nos données suivent-elles ou non une loi normale ?
Les tests supposent que l'échantillon ait été tiré d'une distribution gaussienne.
Test de référence
-> la majorité des autres tests sont des variantes de ce test
Limite principale :
-> test "peu puissant" car il faut connaître à priori les paramètres de la distribution à laquelle on
veut se comparer (cf. μ et σ)
Ce test est une variante du test de Kolmogorov-Smirnov où les paramètres de la loi (μ et σ) sont estimés à partir des données.
Ce test serait davantage sensible à la partie centrale de la distribution qu'aux extrémités
Pas d'unanimité sur la puissance de ce test
Test qui est basé sur la statistique W :
Remarque sur la taille de l'échantillon :
C'est une variante du test de Kolmogorov-Smirnov
Spécificités :
Interprétation des résultats différente des autres tests :
C'est le test $K^2$(K-squared) de D'Agostino-Pearson
-> test basé sur les coefficients d'asymétrie et d'aplatissement
Intérêt de ce test :
Inconvénients :
df_a = df_actives.iloc[:,1:]
X_var_actives = df_a.values
scaler = preprocessing.StandardScaler().fit(X_var_actives)
X_scaled = scaler.transform(X_var_actives)
On vérifie que les moyennes sont bien égales à 0 et les écarts-types à 1
X_scaled.mean(axis=0).round(8)
X_scaled.std(axis=0)
array([-0., 0., -0., -0.])
array([1., 1., 1., 1.])
pd.options.display.float_format = '{:.4f}'.format
df_pvalue = pd.DataFrame(index=['ks','lilliefors','shapiro','anderson','agostino'], columns=[col])
df_stat = df_pvalue.copy()
for i,j in zip(range(len(col)+1), col):
data = df_actives[j]
X=X_scaled[i]
alpha = 0.05
alpha_anderson = 0.768
# Test de Kolmogorov-Smirnov
ks_test = st.kstest(X, 'norm')
df_pvalue.loc['ks',j]=ks_test.pvalue
df_stat.loc['ks',j]=ks_test.statistic
# Test de Lilliefors
lilliefors_test = sm.stats.diagnostic.lilliefors(data)
df_pvalue.loc['lilliefors',j]=lilliefors_test[1]
df_stat.loc['lilliefors',j]=lilliefors_test[0]
# Test de Shapiro-Wilk
shapiro_test = st.shapiro(data)
df_pvalue.loc['shapiro',j]=shapiro_test.pvalue
df_stat.loc['shapiro',j]=shapiro_test.statistic
# Test de Anderson-Darling
anderson_test = st.anderson(data)
df_pvalue.loc['anderson',j]=anderson_test.statistic
df_stat.loc['anderson',j]=anderson_test.statistic
# Test de D'Agostino
agostino_test = st.normaltest(data)
df_pvalue.loc['agostino',j]=agostino_test.pvalue
df_stat.loc['agostino',j]=agostino_test.statistic
titre=["statistique", "P_Value"]
for i,df in zip(range(2),[df_stat, df_pvalue]):
print(titre[i])
df=df.reset_index()
df.columns=['test','dispo_kcal', 'dispo_prot', 'part_prot_anim', 'evol_pop']
df=df.set_index('test')
display(df)
print()
statistique
dispo_kcal | dispo_prot | part_prot_anim | evol_pop | |
---|---|---|---|---|
test | ||||
ks | 0.4861 | 0.5151 | 0.3174 | 0.4601 |
lilliefors | 0.0744 | 0.0842 | 0.0989 | 0.0991 |
shapiro | 0.9862 | 0.9805 | 0.9539 | 0.9613 |
anderson | 0.6549 | 0.9216 | 2.3679 | 1.5633 |
agostino | 7.1617 | 4.5276 | 60.1571 | 18.3183 |
P_Value
dispo_kcal | dispo_prot | part_prot_anim | evol_pop | |
---|---|---|---|---|
test | ||||
ks | 0.0176 | 0.0096 | 0.2635 | 0.0291 |
lilliefors | 0.0518 | 0.0161 | 0.0019 | 0.0019 |
shapiro | 0.1244 | 0.0264 | 0.0 | 0.0002 |
anderson | 0.6549 | 0.9216 | 2.3679 | 1.5633 |
agostino | 0.0279 | 0.104 | 0.0 | 0.0001 |
title_text = 'Résultats tests statistiques - Valeurs pvalue'
annotation_text = "En vert, HO acceptée (la variable suit une loi normale)\nEn rouge, HO rejettée (la variable ne suit pas une loi normale)"
fig_background_color = 'none'
fig_border = 'none'
data = [
[ 'dispo_kcal', 'dispo_prot', 'part_prot_anim', 'evol_pop'],
['ks', 0.5925, 0.037, 0.1859, 0.4308],
['lilliefors', 0.0518, 0.0161, 0.0019, 0.0019],
['shapiro', 0.1244, 0.0264, 0.000, 0.0002],
['anderson', 0.6549, 0.9216, 2.3679, 1.5633],
['agostino', 0.0279, 0.104, 0.000, 0.0001],
]
column_headers = data.pop(0)
row_headers = [x.pop(0) for x in data]
cell_text = []
for row in data:
cell_text.append([f'{x}' for x in row])
rcolors = plt.cm.binary(np.full(len(row_headers), 0.1))
ccolors = plt.cm.binary(np.full(len(column_headers), 0.1))
cl_outcomes = {
'white':'#FFFFFF',
'black':'#313639',
}
fig_background_color = cl_outcomes['white']
label_text_color = cl_outcomes['black']
title_text_color = cl_outcomes['black']
back_red = '#f1d5d5'
font_red = '#df0707'
back_green = '#d0ecd5'
font_green = '#047b1c'
plt.figure(tight_layout={'pad':2},
figsize=(7,5)
)
the_table = plt.table(cellText=cell_text,
rowLabels=row_headers,
rowColours=rcolors,
rowLoc='right',
colColours=ccolors,
colLabels=column_headers,
loc='center')
the_table.scale(1, 1.5)
ax = plt.gca()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
plt.box(on=None)
plt.suptitle(title_text, y=0.8, color='darkred')
plt.figtext(0.5, 0.2,
annotation_text,
horizontalalignment='center',
size=9, weight='light',
color=title_text_color
)
the_cells_not_data= [
the_table[0,0], the_table[0,1], the_table[0,2], the_table[0,3],
the_table[1,-1], the_table[2,-1], the_table[3,-1], the_table[4,-1], the_table[5,-1]
]
the_cells_not_norm = [the_table[1,1],
the_table[2,1], the_table[2,2], the_table[2,3],
the_table[3,1], the_table[3,2], the_table[3,3],
the_table[4,1], the_table[4,2], the_table[4,3],
the_table[5,0], the_table[5,2], the_table[5,3]
]
the_cells_norm = [the_table[1,0], the_table[1,2], the_table[1,3],
the_table[2,0],
the_table[3,0],
the_table[4,0],
the_table[5,1]
]
for the_cell in the_cells_not_norm:
the_cell.set_facecolor(back_red)
the_text = the_cell.get_text()
the_text.set_fontstyle('italic')
the_text.set_color(font_red)
ax.add_patch(the_cell)
for the_cell in the_cells_norm:
the_cell.set_facecolor(back_green)
the_text = the_cell.get_text()
the_text.set_weight('bold')
the_text.set_fontstyle('italic')
the_text.set_color(font_green)
ax.add_patch(the_cell)
for the_cell in the_cells_not_data:
the_text = the_cell.get_text()
the_text.set_weight('bold')
ax.add_patch(the_cell)
plt.draw()
fig = plt.gcf()
plt.savefig('data/exports/img_charts/15.synthese_test_norm_var_actives.png', dpi = 300)
plt.show();
Les tests confirment l'analyse graphique :
--> la variable disponibilité alimentaire en kcal
est la plus compatible avec une distribution
gaussienne
--> c'est donc cette variable que l'on va choisir pour réaliser les tests de comparaison entre les clusters
with Image.open(resultat_acp["graph_proj_ind_1_2"]) as im:
# Provide the target width and height of the image
(width, height) = (im.width // 2, im.height // 2)
im_resized = im.resize((width, height))
im_crop = im_resized.crop((150, 50, width, height-20))
im_crop
Pour rappel, nous avons chosi les pays du cluster 4 comme marchés potentiels d'exportation.
--> On a donc intérêt à savoir si ce cluster 4 est bien spécifique, c'est-à-dire s'il se différencie bien des
autres clusters.
Ainsi, nous allons comparer le groupe 4 à chacun des autres groupes
On vérifie que nos 5 échantillons suivent une loi gaussienne
dico_c={}
for i in range(5):
df_kcal = df_actives.loc[df_actives['cluster'] == i+1, 'dispo_kcal']
data_kcal = df_kcal.tolist()
m = np.mean(df_kcal)
var = np.var(df_kcal)
dico_c['df_'+str(i+1)]=df_kcal
dico_c['liste_'+str(i+1)]=data_kcal
dico_c['moyenne_'+str(i+1)]=m
dico_c['variance_'+str(i+1)]=var
shapiro_test = st.shapiro(data_kcal)
p=shapiro_test.pvalue
rep = "H0 rejetée : l'échantillon suit une loi normale" if p > 0.05 else "H0 ne peut pas être rejetée : l'échantillon ne suit pas une loi normale"
print(f"Groupe {i+1} : p_value = {p:.3f} donc {rep}")
Groupe 1 : p_value = 0.323 donc H0 rejetée : l'échantillon suit une loi normale Groupe 2 : p_value = 0.636 donc H0 rejetée : l'échantillon suit une loi normale Groupe 3 : p_value = 0.122 donc H0 rejetée : l'échantillon suit une loi normale Groupe 4 : p_value = 0.140 donc H0 rejetée : l'échantillon suit une loi normale Groupe 5 : p_value = 0.423 donc H0 rejetée : l'échantillon suit une loi normale
color=sns.color_palette("Set2")
sns.displot(data=df_actives, x="dispo_kcal", palette="Set2", hue='cluster', kind="kde", height=5, aspect=2)
sns.set(rc={'figure.figsize':(18,5)})
plt.title("Distribution de la variable 'dipo_kcal' des 5 clusters", fontsize=12, y=0.9, x=.25, color='darkred')
plt.savefig('data/exports/img_charts/16.distribution_par_cluster_dispo_kcal.png', dpi = 300)
plt.show();
fig = plt.figure(figsize=(8,5))
sns.set_theme(style="white", palette="Set2", font_scale= 1)
sns.color_palette('hls', n_colors = 10)
df_cluster = df_actives.copy()
for i in range(5):
df_cluster['groupe '+str(i+1)]=dico_c['df_'+str(i+1)]
df_cluster=df_cluster.loc[:,'groupe 1':'groupe 5']
sns.boxplot(data=df_cluster,
linewidth=2.5,
orient='h',
width=0.6,
showmeans=True,
meanprops={"marker": "o",
"markeredgecolor": "black",
"markerfacecolor":"darkred",
"markersize": "5",
"alpha":0.8}),
plt.title("Répartition de la variable 'dipo_kcal' par cluster", fontsize=12, color='darkred')
plt.savefig('data/exports/img_charts/17.boxplots_repartition_par_cluster_dispo_kcal.png', dpi = 300)
plt.show();
Le principe : on va comparer le groupe 4 à chacun des 4 autres groupes :
-> on va donc réaliser 4 comparaisons (groupe 4 et groupe i, i prenant pour valeur 1, 2, 3 et 5):
==> On va comparer les paramètres suivants :
On procède par étapes :
Test d'égalité des variances : test de Bartlett
Test d'égalité des moyennes : test T de Student
Remarque :
Si le test d'égalité des variances conduit à rejetter H0, on pourra malgré tout réaliser un test
d'égalité des moyennes.
En effet, le test qui sera utilisé pour vérifier l'égalité des moyennes est le ttest_ind du module
SciPy
(cf. le test T), qui a pour paramètre optionnel 'equal_var' :
# test de Bartlett
for i in [0,1,2,4]:
stat, pvalue = st.bartlett(dico_c['liste_4'], dico_c['liste_'+str(i+1)])
print(f"Groupe 4 et groupe {i+1} : la p_value obtenue avec le test de Bartlett est de {round(pvalue,6)} : ")
if pvalue > 0.05:
print(f" {round(pvalue,6)} > 0.05 => On ne peut donc pas rejeter H0 : on considère que les variances sont égales")
else:
print(f" {round(pvalue,6)} <= 0.05 => On rejette donc H0 au profit de H1 : on considère que les variances sont significativement différentes")
print()
Groupe 4 et groupe 1 : la p_value obtenue avec le test de Bartlett est de 0.659106 : 0.659106 > 0.05 => On ne peut donc pas rejeter H0 : on considère que les variances sont égales Groupe 4 et groupe 2 : la p_value obtenue avec le test de Bartlett est de 0.132292 : 0.132292 > 0.05 => On ne peut donc pas rejeter H0 : on considère que les variances sont égales Groupe 4 et groupe 3 : la p_value obtenue avec le test de Bartlett est de 0.007728 : 0.007728 <= 0.05 => On rejette donc H0 au profit de H1 : on considère que les variances sont significativement différentes Groupe 4 et groupe 5 : la p_value obtenue avec le test de Bartlett est de 0.006489 : 0.006489 <= 0.05 => On rejette donc H0 au profit de H1 : on considère que les variances sont significativement différentes
Pour confirmer ce résultat, on peut également effectuer un test du F (de Fisher-Snedecor)
$\displaystyle \mathit{F_{ctr}} = \frac{\sigma_1^2}{\sigma_2^2}$ (Valeur F critique = rapport des 2 variances - la plus élevée étant au numérateur)
$\mathit{df} = \mathit{n_s} -1$ (le degré de liberté = taille de l'échantillon - 1)
La méthode :
Application :
print("Les valeurs obtenues pour la valeur crtique F entre le groupe 4 et les autres groupes sont :")
for i in range(5):
if i != 3:
f_critique = max(dico_c['variance_'+str(i+1)], dico_c['variance_4']) / min(dico_c['variance_'+str(i+1)], dico_c['variance_4'])
dl_g4=len(dico_c['liste_4'])
dl_g=len(dico_c['liste_'+str(i+1)])
dico_c['f_critique_g4_g'+str(i+1)]=f_critique, max(dl_g4, dl_g), min(dl_g4, dl_g)
print(f" - avec le groupe {i+1}: f={f_critique:.5f}, dl_max={max(dl_g4, dl_g)}, dl_min={min(dl_g4, dl_g)}")
print()
print("Regardons la valeur de F sur la table de distribution de F pour une pvalue de 0.05 avec les degrés de libertés obtenus")
Les valeurs obtenues pour la valeur crtique F entre le groupe 4 et les autres groupes sont : - avec le groupe 1: f=1.18282, dl_max=31, dl_min=29 - avec le groupe 2: f=1.76916, dl_max=31, dl_min=27 - avec le groupe 3: f=2.97567, dl_max=31, dl_min=19 - avec le groupe 5: f=2.61053, dl_max=50, dl_min=31 Regardons la valeur de F sur la table de distribution de F pour une pvalue de 0.05 avec les degrés de libertés obtenus
La règle a appliquer :
si $\mathit{F_{calc}} < \mathit{F_{table}}$, alors on ne peut pas
rejeter HO : on considère donc les variances comme égales
Résultats obtenus :
==> les résultats sont identiques à ceux obtenus avec le test de Bartlett
# test T de Student
for i in range(5):
if i != 3:
# Les variances sont égales (avec celle du groupe 4) pour les groupes 1 et 2 (soit i=0 et i=1)
# et elles sont différentes avec celles des groupes 3 (i=2) et 5 (i=4)
stat, pvalue = st.ttest_ind(dico_c['liste_4'], dico_c['liste_'+str(i+1)], equal_var=True if i in [0,1] else False)
print("Groupe 4 et groupe "+str(i+1)+" : la p_value obtenue avec le test T de "+("Student" if i in [0,1] else "Welchqui")+" est de "+str((pvalue,6))+" (statistique = "+str(round(stat,4))+"): ")
if pvalue > 0.05:
print(" => On ne peut donc pas rejeter H0 : on considère que les moyennes sont égales => nos 2 clusters ne sont donc pas différents")
else:
print(" => On rejette donc H0 au profit de H1 : on considère que les moyennes sont significativement différentes => nos 2 clusters sont donc différents")
print()
Groupe 4 et groupe 1 : la p_value obtenue avec le test T de Student est de (6.836076654521506e-28, 6) (statistique = 20.1694): => On rejette donc H0 au profit de H1 : on considère que les moyennes sont significativement différentes => nos 2 clusters sont donc différents Groupe 4 et groupe 2 : la p_value obtenue avec le test T de Student est de (4.99484959248548e-31, 6) (statistique = 23.8663): => On rejette donc H0 au profit de H1 : on considère que les moyennes sont significativement différentes => nos 2 clusters sont donc différents Groupe 4 et groupe 3 : la p_value obtenue avec le test T de Welchqui est de (1.3418171309195947e-09, 6) (statistique = 9.2409): => On rejette donc H0 au profit de H1 : on considère que les moyennes sont significativement différentes => nos 2 clusters sont donc différents Groupe 4 et groupe 5 : la p_value obtenue avec le test T de Welchqui est de (1.7254958364025639e-13, 6) (statistique = 8.8728): => On rejette donc H0 au profit de H1 : on considère que les moyennes sont significativement différentes => nos 2 clusters sont donc différents
On en conclue que pour la variable "disponibilité alimentaire en kcal", le groupe des pays candidats retenu en tant que marché d'exportation possible est bien différent des autres groupes.
compo_cluster = df_global[['cluster', 'pays']].sort_values(by=["cluster","pays"]).reset_index(drop=True)
compo_cluster.head(3)
cluster | pays | |
---|---|---|
0 | 1 | Bangladesh |
1 | 1 | Bolivie |
2 | 1 | Botswana |
compo_cluster.to_csv("data/exports/livrables_temp/P5_03_composition_clusters.csv", index = False, header = True)
from acp_perso import acp_global
resultat_acp=acp_global(df=df_acp,
axis_ranks=[(0,1), (2,3)],
group='cluster',
group_special=df_spec,
varSupp=varSupp,
data_only=True)
centroid_plan_1 = resultat_acp["centroides_1_2"]
centroid_plan_2 = resultat_acp["centroides_3_4"]
centroids_complet = centroid_plan_1 + centroid_plan_2
for i in range(5):
data = {
'groupe': [1,2,3,4,5],
'd1': centroids_complet[0],
'd2': centroids_complet[1],
'd3': centroids_complet[2],
'd4': centroids_complet[3]
}
df_centroids = pd.DataFrame(data, columns=['groupe', 'd1', 'd2', 'd3', 'd4'])
df_centroids
groupe | d1 | d2 | d3 | d4 | |
---|---|---|---|---|---|
0 | 1 | -0.8207 | -0.5658 | -0.1701 | -0.0389 |
1 | 2 | -2.4522 | -0.0774 | 0.0060 | 0.1050 |
2 | 3 | -1.1892 | 1.1873 | 0.1510 | -0.0856 |
3 | 4 | 2.3274 | 0.2616 | -0.0145 | 0.0762 |
4 | 5 | 0.8091 | -0.2434 | 0.0470 | -0.0489 |
df_centroids.to_csv("data/exports/livrables_temp/P5_04_coordonnees_centroides.csv", index = False, header = True)
Lors de l'analyse des variables qui caractérisent nos groupes, nous avions noté que la quantité de volaille consommée dans chacun des pays n'intervenait quasiment pas.
Nous allons donc, au vu des résultats que nous avons obtenus, refaire une ACP avec les variables actives
suivantes :
'dispo_kcal', 'part_prot_anim', 'pib_hab', 'qte_volaille', 'vol_part_import', 'evol_pop'
df_alter = df_global[['pays', 'dispo_kcal', 'part_prot_anim', 'pib_hab', 'qte_volaille', 'vol_part_import', 'evol_pop']]
df_alter=df_alter.set_index('pays')
df_alter.sample(3)
dispo_kcal | part_prot_anim | pib_hab | qte_volaille | vol_part_import | evol_pop | |
---|---|---|---|---|---|---|
pays | ||||||
Norvège | 3371.0000 | 59.8695 | 63332.8000 | 20.6071 | 2.3000 | 11.8831 |
Eswatini | 2426.0000 | 28.1295 | 8606.1000 | 7.0405 | 28.5700 | 8.2229 |
Angola | 2385.0000 | 30.7446 | 6933.5000 | 11.7495 | 1240.7400 | 42.0092 |
# préparation des données pour le clustering
X = df_alter.values
names = df_alter.index
# Centrage et Réduction
std_scale = preprocessing.StandardScaler().fit(X)
X_scaled = std_scale.transform(X)
# Clustering hiérarchique
Z = linkage(X_scaled, 'ward')
# Affichage du dendrogramme
fig = plt.figure(figsize=(15,6))
plt.style.use('seaborn-white')
plt.title('Classification hiérarchique - Dendrogramme')
plt.ylabel('distance')
dendrogram(
Z,
labels = names
)
plt.show();
plt.style.use('seaborn-white')
graph_nb_groupes_dendro(df_alter, 20)
# Affichage du dendrogramme
fig = plt.figure(figsize=(8,20))
plt.style.use('seaborn-white')
plt.title('Dendrogramme - Coupe à 5 clusters')
plt.ylabel('distance')
dendrogram(
Z,
labels = names,
orientation = "left",
color_threshold=10
)
plt.axvline(x=10, color='r', linestyle='--')
plt.show();
# Affichage du dendrogramme
fig = plt.figure(figsize=(8,3))
ax = plt.axes()
plt.title('Version simplifiée du dendrogramme')
plt.ylabel('distance')
dendrogram(
Z,
truncate_mode='lastp', p= 5,
labels = names
)
plt.show();
# Coupage du dendrogramme en 5 clusters
clusters = fcluster(Z, 5, criterion='maxclust')
clusters
array([5, 1, 5, 2, 2, 5, 5, 4, 4, 5, 5, 1, 2, 1, 5, 2, 1, 5, 2, 1, 4, 2, 1, 1, 1, 5, 2, 5, 2, 5, 5, 2, 1, 4, 3, 5, 5, 2, 2, 2, 5, 2, 4, 3, 5, 2, 1, 4, 5, 1, 2, 5, 5, 2, 1, 1, 2, 4, 5, 5, 4, 1, 2, 1, 4, 4, 5, 1, 5, 5, 5, 2, 1, 1, 5, 4, 5, 1, 2, 1, 1, 5, 1, 1, 5, 2, 1, 4, 1, 5, 5, 2, 2, 1, 1, 1, 4, 3, 2, 5, 2, 1, 1, 4, 1, 5, 2, 2, 2, 5, 5, 1, 1, 1, 5, 1, 5, 3, 5, 3, 1, 4, 1, 1, 5, 2, 5, 2, 1, 1, 4, 4, 1, 5, 1, 5, 2, 2, 2, 4, 1, 4, 5, 4, 1, 5, 2, 2, 1, 5, 1, 4, 4, 5, 5, 1], dtype=int32)
df_alter['cluster']=clusters
for i in range(5):
print(f"Groupe {i+1}:")
print(", ".join(df_alter[df_alter['cluster'] == i+1].index.sort_values()))
print(" ")
Groupe 1: Afghanistan, Bangladesh, Botswana, Burkina Faso, Bénin, Cambodge, Cameroun, Côte d'Ivoire, Eswatini, Gambie, Ghana, Guinée, Guinée-Bissau, Haïti, Inde, Iraq, Kenya, Lesotho, Libéria, Madagascar, Malawi, Mali, Mauritanie, Mozambique, Namibie, Niger, Nigéria, Népal, Ouganda, Pakistan, Rwanda, République centrafricaine, République démocratique populaire lao, Sao Tomé-et-Principe, Sierra Leone, Soudan, Sri Lanka, Sénégal, Tadjikistan, Tanzanie, Tchad, Timor-Leste, Togo, Zambie, Zimbabwe, Éthiopie, Îles Salomon Groupe 2: Afrique du Sud, Algérie, Angola, Azerbaïdjan, Belize, Bolivie, Cabo Verde, Chine Continentale, Congo, El Salvador, Fidji, Gabon, Guatemala, Honduras, Indonésie, Jordanie, Kiribati, Liban, Maldives, Maroc, Mongolie, Myanmar, Nicaragua, Oman, Ouzbékistan, Paraguay, Philippines, Pérou, Suriname, Tunisie, Turquie, Vanuatu, Viet Nam, Égypte, Équateur Groupe 3: Djibouti, Dominique, Macédoine du Nord, Saint-Kitts-et-Nevis, Saint-Vincent-et-les Grenadines Groupe 4: Allemagne, Arabie saoudite, Australie, Autriche, Belgique, Canada, Chine - RAS de Hong-Kong, Danemark, Finlande, Irlande, Islande, Israël, Koweït, Luxembourg, Malte, Norvège, Pays-Bas, Royaume-Uni, Suisse, Suède, Émirats arabes unis, États-Unis Groupe 5: Albanie, Antigua-et-Barbuda, Argentine, Arménie, Bahamas, Barbade, Bosnie-Herzégovine, Brésil, Bulgarie, Bélarus, Chili, Chypre, Colombie, Corée du Sud, Costa Rica, Croatie, Espagne, Estonie, Fédération de Russie, Grenade, Grèce, Géorgie, Hongrie, Italie, Jamaïque, Japon, Kazakhstan, Lettonie, Lituanie, Malaisie, Maurice, Mexique, Monténégro, Nouvelle-Zélande, Panama, Pologne, Portugal, Roumanie, République dominicaine, Sainte-Lucie, Samoa, Serbie, Slovénie, Thaïlande, Trinité-et-Tobago, Ukraine, Uruguay
jtplot.reset()
acp_global(
df=df_alter,
axis_ranks=[(0, 1)],
group='cluster',
group_special=df_spec,
varSupp=None,
varSuppQual=None,
labels=None,
labels_ind=True,
legend_label=True,
values_detail=None,
widget=None,
version_name='v_aletr',
graph_only=None,
)
Nombre d'individus : 156 Nombre de variables : 6
dispo_kcal | part_prot_anim | pib_hab | qte_volaille | vol_part_import | evol_pop | |
---|---|---|---|---|---|---|
count | 156.0000 | 156.0000 | 156.0000 | 156.0000 | 156.0000 | 156.0000 |
mean | 2877.3333 | 42.4958 | 19777.2840 | 20.2431 | 194.8081 | 15.8891 |
std | 456.8378 | 16.0687 | 19577.4556 | 16.5123 | 594.7526 | 14.6247 |
min | 1786.0000 | 9.8734 | 933.1000 | 0.4523 | -1950.0000 | -12.8110 |
25% | 2560.0000 | 27.5810 | 4956.6250 | 5.6618 | 2.5250 | 5.4923 |
50% | 2852.5000 | 43.7726 | 12857.2500 | 17.9697 | 21.2400 | 12.7668 |
75% | 3293.2500 | 55.8257 | 28572.5750 | 28.9620 | 106.8300 | 26.3536 |
max | 3885.0000 | 73.0880 | 114110.0000 | 72.5880 | 3155.5600 | 75.5557 |
nombre de composantes calculées : 6
Variables | Moyennes | Ecarts_types | Var_Exp | Prop_Var_Exp | |
---|---|---|---|---|---|
0 | dispo_kcal | -0.0000 | 1.0000 | 3.1156 | 0.5193 |
1 | part_prot_anim | -0.0000 | 1.0000 | 1.1279 | 0.1880 |
2 | pib_hab | -0.0000 | 1.0000 | 0.7310 | 0.1218 |
3 | qte_volaille | -0.0000 | 1.0000 | 0.5602 | 0.0934 |
4 | vol_part_import | -0.0000 | 1.0000 | 0.3156 | 0.0526 |
5 | evol_pop | -0.0000 | 1.0000 | 0.1496 | 0.0249 |
---------------------------------- | test des bâtons brisés | ----------------------------------
Val.Propre | Seuils | |
---|---|---|
0 | 3.1156 | 2.4500 |
1 | 1.1279 | 1.4500 |
2 | 0.7310 | 0.9500 |
3 | 0.5602 | 0.6167 |
4 | 0.3156 | 0.3667 |
5 | 0.1496 | 0.1667 |
Selon le "test des bâtons brisés, seul le premier facteur est valide (car pour le facteur 2, la valeur du seuil est inférieure à la valeur propre).
----------------------------------------------------------------------------- | coordonnées factorielles des individus (affichage random de 3 individus) | -----------------------------------------------------------------------------
1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|
pays | ||||||
Côte d'Ivoire | -1.7978 | -0.4769 | 0.2816 | 0.3836 | -0.4058 | 0.1726 |
Ouganda | -2.9581 | 0.1089 | 0.6566 | -0.3579 | 0.7954 | 0.0737 |
Afrique du Sud | 0.2891 | 0.2047 | -0.1420 | -1.0454 | -0.4526 | -0.1087 |
################################################################################ #### Qualité de la représentation des individus #### ################################################################################ La qualité de représentation des individus sur les axes du plan factoriel Il s'agit de calculer le COS² de chaque individu pour chaque axe. Intéressant si on a plus de 2 dimensions. En effet, pour chaque individu, la somme des COS² sur l'ensemble des facteurs est égale à 1. Ainsi, si on a par exemple 4 dimensions, on peut, en additionnant les COS² sur les 2 premiers facteurs, savoir s'ils sont bien représentés sur le premier plan factoriel (cad si la somme des COS² sur les 2 premiers facteurs est proche de 1), et donc interprétables On affiche un random sur 5 individus de la mesure des COS²
id | COS²_F1 | COS²_F2 | COS²_F3 | COS²_F4 | COS²_F5 | COS²_F6 | |
---|---|---|---|---|---|---|---|
0 | Arménie | 0.1017 | 0.0100 | 0.6888 | 0.1216 | 0.0000 | 0.0779 |
1 | Afghanistan | 0.9492 | 0.0012 | 0.0102 | 0.0010 | 0.0294 | 0.0090 |
2 | Albanie | 0.2070 | 0.1508 | 0.3308 | 0.1555 | 0.0165 | 0.1394 |
3 | Algérie | 0.1798 | 0.1986 | 0.0256 | 0.1484 | 0.4352 | 0.0123 |
4 | Angola | 0.4275 | 0.4256 | 0.1146 | 0.0259 | 0.0008 | 0.0057 |
################################################################################ #### Contributions des individus aux axes #### ################################################################################ Elles permettent de déterminer les individus qui pèsent le plus dans la définition de chaque facteur. On regarde quels sont les individus qui sont les plus contributifs et ce, pour les différents axes Pour chaque axe, on affiche les 5 individus qui contribuent le plus.
|
|
|
|
|
|
Le champ components_ de l'objet ACP correspond aux valeurs propres On peut alors calculer la matrice des corrélations variables * facteurs #################################################################################### #### Les vecteurs propres #### ####################################################################################
id | F1 | F2 | F3 | F4 | F5 | F6 | |
---|---|---|---|---|---|---|---|
0 | dispo_kcal | 0.4676 | -0.2514 | 0.1638 | 0.2989 | -0.7271 | 0.2707 |
1 | part_prot_anim | 0.5178 | 0.0730 | 0.0013 | -0.1424 | 0.5008 | 0.6749 |
2 | pib_hab | 0.4614 | -0.1547 | 0.4926 | 0.2783 | 0.3695 | -0.5536 |
3 | qte_volaille | 0.4063 | 0.3602 | 0.0135 | -0.7338 | -0.2744 | -0.3020 |
4 | vol_part_import | 0.0654 | 0.8700 | -0.0123 | 0.4846 | -0.0611 | 0.0033 |
5 | evol_pop | -0.3619 | 0.1441 | 0.8545 | -0.1990 | -0.0709 | 0.2711 |
#################################################################################### #### Corrélations par facteur #### ####################################################################################
id | COR_F1 | COR_F2 | COR_F3 | COR_F4 | COR_F5 | COR_F6 | |
---|---|---|---|---|---|---|---|
0 | dispo_kcal | 0.8254 | -0.2670 | 0.1401 | 0.2238 | -0.4085 | 0.1047 |
1 | part_prot_anim | 0.9140 | 0.0775 | 0.0011 | -0.1066 | 0.2814 | 0.2610 |
2 | pib_hab | 0.8144 | -0.1643 | 0.4212 | 0.2083 | 0.2076 | -0.2141 |
3 | qte_volaille | 0.7172 | 0.3826 | 0.0115 | -0.5492 | -0.1541 | -0.1168 |
4 | vol_part_import | 0.1155 | 0.9240 | -0.0105 | 0.3627 | -0.0343 | 0.0013 |
5 | evol_pop | -0.6388 | 0.1531 | 0.7306 | -0.1490 | -0.0398 | 0.1048 |
#################################################################################### #### COS² des variables #### ####################################################################################
id | COS²_var_F1 | COS²_var_F2 | COS²_var_F3 | COS²_var_F4 | COS²_var_F5 | COS²_var_F6 | |
---|---|---|---|---|---|---|---|
0 | dispo_kcal | 0.6812 | 0.0713 | 0.0196 | 0.0501 | 0.1669 | 0.0110 |
1 | part_prot_anim | 0.8353 | 0.0060 | 0.0000 | 0.0114 | 0.0792 | 0.0681 |
2 | pib_hab | 0.6633 | 0.0270 | 0.1774 | 0.0434 | 0.0431 | 0.0459 |
3 | qte_volaille | 0.5144 | 0.1464 | 0.0001 | 0.3016 | 0.0238 | 0.0136 |
4 | vol_part_import | 0.0133 | 0.8538 | 0.0001 | 0.1316 | 0.0012 | 0.0000 |
5 | evol_pop | 0.4081 | 0.0234 | 0.5337 | 0.0222 | 0.0016 | 0.0110 |
la somme des COS² d’une variable sur l’ensemble des facteurs est égale à 1
#################################################################################### #### contribution des variables #### #################################################################################### La contribution est également basée sur le carré de la corrélation, mais relativisée par l’importance de l’axe
id | CTR_var_F1 | CTR_var_F2 | CTR_var_F3 | CTR_var_F4 | CTR_var_F5 | CTR_var_F6 | |
---|---|---|---|---|---|---|---|
0 | dispo_kcal | 0.2186 | 0.0632 | 0.0268 | 0.0894 | 0.5287 | 0.0733 |
1 | part_prot_anim | 0.2681 | 0.0053 | 0.0000 | 0.0203 | 0.2508 | 0.4555 |
2 | pib_hab | 0.2129 | 0.0239 | 0.2427 | 0.0775 | 0.1365 | 0.3065 |
3 | qte_volaille | 0.1651 | 0.1298 | 0.0002 | 0.5384 | 0.0753 | 0.0912 |
4 | vol_part_import | 0.0043 | 0.7570 | 0.0002 | 0.2349 | 0.0037 | 0.0000 |
5 | evol_pop | 0.1310 | 0.0208 | 0.7301 | 0.0396 | 0.0050 | 0.0735 |
Non développé pour le moment
On constate que les résultats ne sont pas très différents du partitionnement précédent
df_centroid
X_cent = df_centroid.values
# Centrage et Réduction
std_scale = preprocessing.StandardScaler().fit(X_cent)
X_scaled = std_scale.transform(X_cent)
X_scaled
evol_pop | pib_hab | risque | taux_impot | dispo_kcal | dispo_prot | part_prot_anim | qte_volaille | vol_part_import | |
---|---|---|---|---|---|---|---|---|---|
cluster | |||||||||
1 | 13.0510 | 9150.8448 | 4.9310 | 35.8828 | 2628.6207 | 68.2862 | 34.3442 | 14.0604 | 157.4969 |
2 | 29.6767 | 2876.5074 | 5.5926 | 41.7259 | 2244.1111 | 54.9441 | 22.6156 | 4.2744 | 38.8300 |
3 | 38.6184 | 13082.1789 | 5.0526 | 39.0421 | 2788.6316 | 75.4021 | 33.9479 | 15.7454 | 362.2189 |
4 | 4.9882 | 46593.9419 | 2.5484 | 40.8581 | 3461.6452 | 111.9932 | 60.4910 | 27.3421 | 74.6658 |
5 | 8.2114 | 20984.8500 | 4.2400 | 38.4540 | 3034.9600 | 88.6790 | 50.0502 | 29.7598 | 311.5488 |
array([[-0.45010865, -0.6172628 , 0.43451382, -1.62610509, -0.4992307 , -0.59613643, -0.44531355, -0.44808633, -0.24616659], [ 0.82731809, -1.02985356, 1.062003 , 1.244633 , -1.44496718, -1.28330074, -1.32376895, -1.49811679, -1.17484709], [ 1.51435523, -0.35874433, 0.5498489 , -0.07392349, -0.10566929, -0.22964394, -0.47499633, -0.26728788, 1.35597674], [-1.06961042, 1.84493754, -1.82543196, 0.81825418, 1.54966917, 1.65492072, 1.51304147, 0.97703289, -0.89439807], [-0.82195424, 0.16092314, -0.22093376, -0.3628586 , 0.50019799, 0.45416039, 0.73103735, 1.2364581 , 0.959435 ]])
profil_cent = pd.DataFrame(X_scaled,
columns = df_centroid.columns,
index=df_centroid.index
)
profil_cent=profil_cent.reset_index()
profil_cent=profil_cent.rename(columns = {'cluster': 'groupes'})
for i in range(5):
profil_cent['groupes'][i] = "Groupe " + str(i+1)
profil_cent=test_cent[['groupes', 'dispo_kcal', 'dispo_prot', 'part_prot_anim', 'evol_pop']]
profil_cent
groupes | dispo_kcal | dispo_prot | part_prot_anim | evol_pop | |
---|---|---|---|---|---|
0 | Groupe 1 | -0.4992 | -0.5961 | -0.4453 | -0.4501 |
1 | Groupe 2 | -1.4450 | -1.2833 | -1.3238 | 0.8273 |
2 | Groupe 3 | -0.1057 | -0.2296 | -0.4750 | 1.5144 |
3 | Groupe 4 | 1.5497 | 1.6549 | 1.5130 | -1.0696 |
4 | Groupe 5 | 0.5002 | 0.4542 | 0.7310 | -0.8220 |
from fonctions_perso import make_spider
df_rad=profil_cent
# Intitialisation de la figure
my_dpi=96
plt.figure(figsize=(1200/my_dpi, 1200/my_dpi), dpi=my_dpi)
# Creation de la color palette:
my_palette = plt.cm.get_cmap("Set1", len(df_rad.index))
# Ajoutd'un titre général
plt.suptitle(t="Profil des groupes", size=16, color='darkred', x=0.5, y=1)
# On boucle sur chaque groupe
for row in range(0, len(df_rad.index)):
make_spider(df_rad=df_rad, row=row, title=df_rad['groupes'][row], color=my_palette(row), serie=2, repere=True)
plt.tight_layout(h_pad=2, pad=1)
plt.savefig('data/exports/img_charts/18.supplement_profils_clusters_radar_graph.png', dpi = 300)
plt.show();