)

Accueil

Traduction

Tutoriel Python - sommaire

Tutoriel Python - recherche

L'auteur : Patrick Darcheville

Vous pouvez me contacter via Facebook pour questions & suggestions : Page Facebook relative à mon site

Statistiques avec Python

Dans ce chapitre je vais évoquer le module statistics et la bibliothèque matplotlib. Je vais revenir aussi sur la bibliothèque numpy car cette librairie est aussi fort utile en statistiques.
Le module random de Numpy est beaucoup plus performant que celui de base (le module qui n'exige aucune installation).
La bibliothèque Pandas n'est pas évoquée ici ; compte tenu de son importance elle fait l'objet de tout un chapitre. Bibliothèque Pandas

Module statistics

Créer une commande qui calcule la moyenne simple d'une série de notes, c'est facile. Par contre la programmation de la variance, de l'écart type c'est beaucoup plus compliqué ...
Donc ce module statistics tombe à pic car il propose des fonctions intéressantes.

Liste des fonctions du module

>>> import statistics as stat
>>> dir(stat)
 'bisect_left', 'bisect_right', 'erf', 'exp', 'fabs', 'fmean',
 'fsum', 'geometric_mean', 'groupby', 'harmonic_mean', 'hypot', 
 'itemgetter', 'log', 'math', 'mean', 'median', 'median_grouped', 
 'median_high', 'median_low', 'mode', 'multimode', 'numbers', 
 'pstdev', 'pvariance', 'quantiles', 'random', 'sqrt', 'stdev',
 'tau', 'variance']

Utilisation des fonctions du module

>>> import statistics as stat
>>> notes1 = [15, 12,12, 8,14]
>>> stat.mean(notes1)
12.2
>>> stat.median(notes1)
12
>>> stat.mode(notes1)
12
>>> stat.stdev(notes1)
2.6832815729997477
>>> notes2 = [12,13,11,14,10]
>>> stat.mean(notes2)
12
>>> stat.stdev(notes2)
1.5811388300841898

Syntaxes

mean(série) : fonction qui calcule la moyenne simple d'une série de notes.

La médiane est une valeur qui permet de partager une série numérique ordonnée en deux parties de même nombre ; syntaxe : median(série)

le mode désigne la valeur la plus représentée dans une série ; syntaxe : mode(série)

L’écart type est un indicateur de dispersion dans une série ; syntaxe : stdev(série)
Vous constatez ici, que pour les deux séries la moyenne est pratiquement la même (12,2 et 12,0).
Mais le premier élève est beaucoup plus irrégulier dans ses résultats que le second. Donc l'écart type est plus grand pour le premier que pour le second.
Je rappelle que l'écart type est la racine carrée de la variance ; il existe bien sûr la fonction variance() dans le module statistics.

Une série avec plusieurs valeurs modales

>>> notes = ['A','B','C','B','E','C','D','D','B','E','A','B','C',
'D','C','E','D','C','B','A','D']
>>> len(notes)
21
>>> notes.sort(reverse =True)
>>> notes
['E', 'E', 'E', 'D', 'D', 'D', 'D', 'D', 'C', 'C', 'C', 'C', 'C',
 'B', 'B', 'B', 'B', 'B', 'A', 'A', 'A']
>>> print(notes[11])
C
>>> import statistics as stat
>>> moyenne = stat.mean(notes)
...
TypeError: can't convert type 'str' to numerator/denominator
>>> mediane = stat.median(notes)
...
TypeError: unsupported operand type(s) for /: 'str' and 'int'

Dans beaucoup de pays les notes ne sont pas des nombres mais des lettres.
Il est impossible de calculer la moyenne de lettres et c'est logique.
La fonction stat.median() plante !
Par contre on peut utiliser les fonctions stat.mode() & stat.multimode(). En effet une série statistique peut avoir plusieurs valeurs modales. C'est le cas dans l'exemple : 5 copies ont la note C et 5 ont la notes B ; pour les autres notes le nombre de copies est plus faible.

La bibliothèque matplotlib

Matplotlib est une bibliothèque pour visualiser des données sous forme de graphiques.
Elle peut donc être utilisée en mathématiques pour représenter des fonctions mais aussi en statistiques pour afficher les histogrammes, camemberts correspondant à des séries statistiques.
Il faut éventuellement installer cette extension de Python via l'utilitaire PIP. Dès que l'extension est installée il suffit d'importer le module pyplot de cette extension.

Tracer des courbes

Matplotlib permet entre autres de représenter des fonctions.

Premier tracé

Commandes dans la console :

>>> from matplotlib import pyplot
>>> pyplot.plot([-3,0,3], [-6,0,6])
>>> pyplot.show()

Nous devons importer le module pyplot de la librairie matplotlib.
La fonction plot() permet de définir les couples de points et demande en arguments deux séries de valeurs : la série des abscisses et la série des ordonnées. Ces deux arguments peuvent être des variables qui contiennent des séries de type "range" ou "list".
La fonction show() affiche le graphique.
Après la dernière instruction pyplot.show() une fenêtre légendée "figure1" et contenant le graphique s'affiche. Cette fenêtre peut être enregistrée comme fichier.png.

Le graphique : module matplolib de python

Il s'agit d'une droite puisque les couples (x,y) correspondent à la fonction y = 2x (une fonction linéaire)
La fonction show() est utilisée sans arguments et est bloquante ; tant que la fenêtre n'a pas été fermée, la suite du code ne s'éxécute pas.

Programmes construisant des graphiques

Pour sauvegarder nos travaux nous allons les stocker dans des programmes.

Parabole

Pour le précédent tracé nous n'avions que trois couples (x,y) mais c'était suffisant, puisqu'il s'agissait d'une fonction linéaire.
Pour tracer une parabole il faut beaucoup plus de points. Nous allons donc utiliser toute la puissance de la fonction range(start,stop,step) pour produire un grand nombre de couples x,y.

Le script
# nom programme : graphique3.py
# objet : f(x) = x2
import matplotlib.pyplot as plt

plt.grid()
liste1 = range(-50,52,2)
liste2 = [x**2 for x  in range(-50,52,2)]
plt.plot(liste1, liste2)
plt.title("X au carré")
plt.xlabel("Valeurs de X")
plt.ylabel("Valeurs de y = f(x)")
plt.show()
plt.close()

J'ai utilisé une autre syntaxe pour importer le module matplotlib.pyplot et donner à ce module un alias : import matplotlib.pyplot as plt.
plt.grid() : on rajoute une grille dans le repère orthogonal.
Les séries de x et y sont produites grâce à la fonction range().

Le graphique
module matplolib de python

Diagramme en bâtons (ou histogramme)

Le module pyplot est aussi incontournable pour représenter graphiquement des séries statistiques.

Thème : distribution des notes à un examen pour un groupe d'étudiants.

Le script
# nom programme : graphique1.py
# objet : histogramme sur distribution notes dans une classe
import matplotlib.pyplot as plt

plt.grid()
x = [6,8,10,12,14,16]
y = [3,5,6,8,7,2]
largeur = 0.5
plt.bar(x, y, largeur)
plt.title("Diagramme en bâtons ou histogramme")
plt.xlabel("Notes")
plt.ylabel("Nombre de copies correspondantes")
plt.show()

Le programme demande l'affichage d'une grille dans le repère orthogonal.
x contient la liste des abscisses (notes possibles)
y contient la liste des ordonnées (effectif de chaque note)
Pour produire un histogramme, il faut utiliser la fonction bar(série des x, série des y, largeur de chaque barre)

Le graphique

Un diagramme circulaire ou "camembert"

Il faut utiliser la fonction pie(série de valeurs, légendes, couleurs)

Le script
# graphique4.py
import matplotlib.pyplot as plt

legendes = 'Allemagne', 'France', 'Belgique', 'Espagne'
serie = [15, 80, 45, 40]
couleurs = ['green', 'yellow', 'orange', 'purple']

plt.pie(serie, labels=legendes, colors=couleurs)
plt.title("Diagramme circulaire ou 'camembert'")
plt.show()
Le graphique
module matplolib de python

Un nuage de points

Le script
# nom programme : graphique2.py
# objet : nuage de points

import matplotlib.pyplot as plt
import numpy as np

plt.grid()
x = [1, 2, 3, 4, 5, 6]
y = [2, 3.5 ,5.5 ,8.5 ,9.5, 11.5]
plt.scatter(x, y)
corr_coef = np.corrcoef(x,y)
print("coefficient de corrélation linéaire :", corr_coef)
plt.title("Nuage de points")
plt.xlabel("valeurs de x")
plt.ylabel("valeurs de y")
plt.show()

Pour produire un nuage de points il faut utiliser la fonction scatter(abscisses, ordonnées)

Le graphique

Notez que j'ai rajouté une grille : plt.grid()

On note que les points sont pratiquement alignés sur une droite. Il y a donc une corrélation linéaire pratiquement parfaite entre les deux séries.
Je calcule le coefficient de correlation linéaire avec la méthode corrcoef() de numpy. Le résultat est 0.99377171 donc une corrélation positive quasi parfaite. -->

Retour sur Numpy

Dans le chapitre précédent je vous ai montré qu'on pouvait faire du calcul matriciel via Numpy. On peut faire aussi employer cette librairie à des fins statistiques mais aussi pour générer de grosses masses de données aléatoires.

Un programme statistique

Je vous montre comment calculer les centiles (centiles, déciles, quartiles selon que les tranches sont 1% ou 10% ou 25% ) avec Numpy.
Les centiles indiquent les fréquences cumulées par chaque tranche.
Les centiles (souvent les déciles ou quartiles) sont fort utilisées en économie. "En France 10 % des ménages détiennent près de la moitié du patrimoine brut" : il s'agit d'un décile.

Le script

Ne pouvant attendre les données réelles pour développer mon programme, j'ai généré de façon aléatoire des données réalistes via le module numpy.random.

# numpy_stat.py
# calcul des déciles (tranches de 10%)

import numpy as np
import matplotlib.pyplot as plt

# génération âge d'une population étudiante d'effectif 100
ages = np.random.randint(17,30,size = 100)
print("Ages :")
print(ages)
print("----------------")
      
bornes = [10,20,30,40,50,60,70,80,90,100]
deciles =[]
print ("les déciles")
for i in bornes:
    decile = np.percentile(ages,i)
    decile =round(decile,2)
    deciles.append(decile)
    print('Décile : ', decile)
      
plt.grid()
plt.plot(bornes,deciles)
plt.title("Déciles des âges")
plt.xlabel("Effectif")
plt.ylabel("Ages")
plt.show()

ages = np.random.randint(17,30,size = 100) : lisez le paragraphe qui suit pour comprendre cette instruction. Je fais appel au module numpy.random traité ci-dessous.
Une boucle permet de calculer les 10 déciles. Notez l'emploi de la méthode percentile() de numpy pour calculer ces déciles.
Sauriez-vous calculez les quartiles (25%,50%,75%,100%) ?

Le rendu

Ages :
[23, 22, 20, 23, 27, 17, 18, 24, 27, 25, 26, 21, 27, 19, 22, 19, 29, 22, 
26, 20, 21, 28, 27, 21, 26, 17, 24, 19, 26, 
20, 25, 27, 20, 28, 25, 21, 20, 23, 24, 28, 30, 29, 23, 22, 18, 23, 25, 
24, 21, 18, 18, 26, 25, 27, 18, 27, 17, 27, 
26, 22, 19, 27, 23, 24, 23, 26, 29, 19, 17, 29, 29, 30, 21, 26, 22, 28, 
30, 17, 17, 22, 27, 28, 21, 23, 24, 19, 24, 
21, 23, 20, 30, 20, 24, 28, 30, 26, 25, 30, 22, 23]
----------------
les déciles
Décile :  18.0
Décile :  20.0
Décile :  21.0
Décile :  22.6
Décile :  23.5
Décile :  25.0
Décile :  26.0
Décile :  27.0
Décile :  29.0
Décile :  30.0

Le graphique

Le module random de numpy

Les limites du module standard random

Donc pour résumer, dans ce module standard chaque fonction ne génère qu'une valeur. Il faut donc une boucle pour en générer N !

Intérêt du module numpy.random

Le module random de numpy est beaucoup plus performant ; chaque fonction peut générer un "ndarray" de données aléatoires.

Dans le cadre du "machine learning" (apprentissage automatique) il faut travailler sur de gros volumes de données. Mais il peut être difficile de collecter des données réelles, surtout en début de projet.
En attendant d'avoir des données réelles on peut faire appel à numpy.random pour générer de gros volumes de données de façon aléatoire.
Un chapitre sur le "machine learning" en Python

Commandes dans la console

>>> import numpy.random as rd
>>> notes1 = rd.randint(1,19,size = 100)
>>> notes1
array([ 3, 17, 12, 10,  6,  6, 10, 12,  7,  4, 15,  1, 16, 16,  1,  3,  7,
        8,  4, 14, 16,  9,  5,  6,  6,  6,  7, 18,  4,  7, 16,  8,  3,  8,
       10,  9,  8,  1, 16,  4,  6,  9, 16,  1,  1,  3, 11,  5,  4,  4, 15,
       10, 10, 13,  5,  5, 11,  9, 15, 14,  1, 16,  4, 16, 16,  6,  6, 11,
       16, 16, 18, 10, 17,  9,  7, 14,  7,  6, 10,  4, 16, 13, 13, 16,  5,
        3,  5,  3, 15, 17, 11,  4, 14,  6, 18, 17,  2, 13,  8,  4],
      dtype=int32)
>>> type(notes1)
class 'numpy.ndarray'
>>> notes2 = rd.normal(10,2,100)
>>> notes2
array([ 9.42084688, 11.08475873, 11.61502587, 13.02482095,  7.04863442,
        9.17255292,  9.58270892, 10.91470868,  9.01321415, 10.59227396,
       10.33568607,  7.81311842,  8.61028418,  9.8901284 , 10.95542152,
       10.15132111,  7.31427246,  6.96508046, 10.49249624, 11.46874963,
       11.64044746, 11.75622714,  7.73333667, 12.668295  , 11.96851323,
        9.14717175, 12.42025448,  8.13963466,  9.00353857, 12.23310896,
       10.62219603,  8.65559122, 13.19522794, 10.6129247 ,  8.5782003 ,
       10.80128303, 11.70176016, 13.37591238, 10.19659239,  7.24242952,
        9.78070767,  8.50755413, 11.76693361,  7.23803953, 10.23190109,
        8.58129748,  7.95387798, 13.5016386 , 12.02672822,  9.43095968,
        9.08934778,  9.15384013, 11.55753535,  9.51553414, 11.94140356,
       10.85120366,  6.16876229,  8.59423958, 12.06436741,  5.99964162,
       15.84784693, 13.020842  , 10.66742559, 11.54619821, 10.32056536,
       12.41947941, 11.98918886, 12.04222062,  7.18621196, 12.50069814,
        8.69571884, 10.48646794, 10.93135243, 11.77211378, 12.32980908,
        8.60189913,  9.03501045,  9.0658879 ,  7.43441883,  7.97364561,
       10.08799758, 12.75491389, 13.50861813,  9.47343381,  7.77787099,
       10.21008362, 11.28364672, 11.30114782, 10.86044199,  8.66795738,
        9.96467535,  6.2549757 ,  8.65739699, 12.36593145, 10.34699184,
        7.72966331,  9.30156268,  7.788179  ,  7.29200727,  9.21757223])
>>> type(notes2)
class 'numpy.ndarray'
>>> tableau = rd.rand(3,2)
>>> tableau
array([[0.64150792, 0.99222752],
       [0.11673579, 0.61548791],
       [0.91202205, 0.04444887]])
>>> type(tableau)
class 'numpy.ndarray'
>>> tab3 = rd.choice([1,2,3,4,5,6],size =3)
>>> tab3
array([2, 3, 2])

Commmentaires

J'ai importé le module numpy.random sous l'alias rd.
Je lui ai demandé de générer 100 notes entières comprises entre 1 et 19 avec la fonction randint().
Puis je lui ai demmandé de générer 100 valeurs selon une distribution normale (moyenne de 10 avec un écart type de 2) grâce à la fonction normale(moyenne, écart-type, taille)
Ensuite je lui ai demandé de générer une "ndarray" à deux dimensions : 3 lignes et 2 colonnes avec la fonction rand()
Et enfin je lui demande de générer trois nombres parmi une liste avec la fonction choice()
Dans chaque cas il y a génération non pas d'un terme mais d'une séquence !