Les nouvelles technologies pour l’enseignement des mathématiques
Intégration des TICE dans l’enseignement des mathématiques

MathémaTICE, première revue en ligne destinée à promouvoir les TICE à travers l’enseignement des mathématiques.

Les algorithmes du programme de Mathématiques de Première technologique (2019).
Article mis en ligne le 5 mai 2019
dernière modification le 28 avril 2021

par Benjamin Clerc

Cet article peut être librement diffusé et son contenu réutilisé pour une utilisation non commerciale (contacter l’auteur pour une utilisation commerciale) suivant la licence CC-by-nc-sa

Dans le programme de mathématiques de première technologique, on trouve parmi la liste des compétences mathématiques travaillées :
- Calculer, appliquer des techniques et mettre en œuvre des algorithmes ;
Il y a dans ce programme des indications au sujet de ces algorithmes que l’on peut mettre en œuvre, nous allons ici les illustrer.
Nous faisons le choix de vous proposer une approche 100% fonctionnelle, comme préconisé dans les programmes.
Ceci concerne toutes les séries technologiques sauf la série STD2A.

Algorithmique et programmation
La pratique de l’algorithmique et de la programmation se poursuit au cycle terminal. En continuité avec la classe de seconde, le langage utilisé est Python. Le programme vise la consolidation des notions de variable, d’instruction conditionnelle et de boucle ainsi que l’utilisation des fonctions. La seule notion nouvelle est celle de liste qui trouve naturellement sa place dans de nombreuses parties du programme et aide à la compréhension de notions mathématiques telles que les suites numériques, les tableaux de valeurs, les séries statistiques...

Capacités attendues

Variables

 utiliser un générateur de nombres aléatoires entre 0 et 1 pour simuler une loi de Bernoulli de paramètre p ;
La loi de Bernoulli de paramètre p peut être simulée en coupant l’intervalle [0,1] en deux sous-intervalles, l’un de taille p, l’autre de taille 1−p :

>>> from random import random
>>> p=0.7
>>> if random()<p:
	      print(1)
        else:
	      print(0)
1


 utiliser la notion de compteur ;
Lorsque l’on souhaite répéter un nombre donné de fois la même instruction ou le même bloc d’instructions, la commande for est la plus appropriée. Elle intègre un compteur, souvent appelé i (on peut lui donner le nom que l’on veut)
Admettons que l’on veuille répéter 7 fois la loi de Bernoulli de paramètre p ci-dessus. Voici ce que l’on peut faire.

>>> from random import random
>>> p=0.7
>>> for i in range(7):
	if random()<p:
		print(1)
	else:
		print(0)
0
0
0
1
1
1
0

Lorsque l’on souhaite répéter un certain nombre de fois, inconnu, la même instruction ou le même bloc d’instructions, la commande while est la plus appropriée et le plus souvent elle nécessite un compteur. On va répéter la loi de Bernoulli de paramètre p jusqu’à ce qu’elle ait rencontré 10 succès.

>>> from random import random
>>> compteur=0
>>> p=0.7
>>> while compteur<10:
	if random()<p:
		print(1)
		compteur+=1
	else:
		print(0)
1
1
0
1
0
1
0
1
1
0
0
1
0
1
0
1
1


 utiliser le principe d’accumulateur pour calculer une somme, un produit.
On s’est servi de ce principe pour incrémenter le compteur ci-dessus, mais illustrons cela avec un calcul de somme, par exemple les carrés des nombres inférieurs à 10 :

>>> somme=0
>>> for i in range(10):
	somme+=i**2
>>> print(somme)
285

Commentaires
 Les notions relatives aux types de variables et à l’affectation sont consolidées. Comme en classe de seconde, on utilise le symbole « ← » pour désigner l’affectation dans un algorithme écrit en langage naturel.
Python est un langage à typage dynamique, ce qui signifie que la valeur que l’on affecte à une variable définit son type. Le type du contenu d’une variable peut donc changer si on change sa valeur.
La fonction type()
Pour connaître le type d’une donnée ou d’une variable, il suffit d’utiliser la fonction type().

>>> type(6)
<class 'int'>
>>> a = 6
>>> type(a)
<class 'int'>
>>> a = "math"
>>> type(a)
<class 'str'>
>>> a = 6.1
>>> type(a)
<class 'float'>

Le type int (entier)
Ce type est utilisé pour stocker un entier ( integer en anglais).

>>> type(1234)
<class 'int'>

Le type float (flottant)
Ce type est utilisé pour stocker des nombres à virgule flottante ( floating point numbers en anglais). En français, on parle de flottant.

>>> a=12.3
>>> type(a)
<class 'float'>
>>> a=6.
>>> type(a)
<class 'float'>
>>> a
6.0
>>> a = 2.99792458e8
>>> a
299792458.0
>>> type(a)
<class 'float'>

Le type str (chaîne de caractères)
Dans Python, une donnée de type str est une suite quelconque de caractères délimitée soit par des apostrophes, soit par des guillemets. str est l’abréviation de string, qui veut dire chaîne en français.

>>> a = "hello world !"
>>> type(a)
<class 'str'>
>>> b="1+2"
>>> type(b)
<class 'str'>
>>> b
'1+2'

Le type list (liste)
Dans Python, on peut définir une liste comme une collection d’éléments séparés par des virgules, l’ensemble étant écrit entre crochets.

>>> jour = ["lundi", "mardi", "mercredi", "jeudi", "vendredi","samedi","dimanche"]
>>> type(jour)
<class 'list'>
>>> L=[x**2 for x in range(10)]
>>> type(L)
<class 'list'>
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Fonctions

 identifier les entrées et les sorties d’une fonction ;
Prenons l’exemple de la loi de Bernoulli, on peut définir une fonction loi de Bernoulli de paramètre 0.7 :

from random import random
def bernoulli():
    if random()<0.7:
        return(1)
    else:
        return(0)

>>> bernoulli()
1
>>> bernoulli()
0

Il n’y a pas d’entrée, et en sortie on obtient 1 si l’expérience est un succès, 0 sinon.
Si l’on veut définir une loi de Bernoulli de paramètre p (au lieu de 0.7), on prendra en entrée le paramètre p, la sortie étant la même que précédemment :

from random import random
def bernoulli(p):
    if random()<p:
        return(1)
    else:
        return(0)

>>> bernoulli(0.4)
1
>>> bernoulli(0.8)
0

Si l’on veut répéter n fois l’expérience :

from random import random
def bernoulli(n,p):
    succes=0
    for i in range(n):
        if random()<p:
            succes+=1
    return succes

>>> bernoulli(100,0.8)
82

En entrée, on renseigne le nombre d’expériences n et la probabilité du succès p. En sortie, on récupère le nombre de succès.
 structurer un programme en ayant recours aux fonctions. (Ceci est illustré partout dans l’article, nous n’en dirons pas plus ...)
Commentaires
 L’accent est mis sur la programmation modulaire qui permet de découper une tâche complexe en tâches plus simples.
Prenons l’exemple du calcul de l’écart-type pour une liste de données, on peut programmer la fonction :

from math import sqrt
def ecarttype(Liste):
    N=len(Liste)
    E_X=sum(Liste)/N
    varX = 0
    for i in range(N):
        varX += (Liste[i]-E_X)**2
    varX=varX/N
    return sqrt(varX)

>>> ecarttype([x**2 for x in range(10)])
26.852374196707448

On peut aussi programmer les fonctions esperance et variance afin de les utiliser dans la fonction ecarttype et éventuellement de s’en servir par ailleurs :

from math import sqrt
def esperance(Liste):
    return sum(Liste)/len(Liste)
def variance(Liste):
    E_X=esperance(Liste)
    N=len(Liste)
    varX = 0
    for i in range(N):
        varX += (Liste[i]-E_X)**2
    varX=varX/N
    return varX
def ecarttype(Liste):
    return sqrt(variance(Liste))

ecarttype([x**2 for x in range(10)])

Listes

 générer une liste (en extension, par ajouts successifs, en compréhension) ;
Une liste est une séquence modifiable. Un élément d’une liste peut être de n’importe quel type.

>>> L=[6,1,69]
>>> type(L)
<class 'list'>
>>> L[2]=1969
>>> L
[6, 1, 1969]
>>> liste=[L,7,11,67,"Marie",18.2]
>>> liste
[[6, 1, 1969], 7, 11, 67, 'Marie', 18.2]
>>> [a,b]=[0,1]
>>> a
0
>>> [a,b]
[0, 1]

La liste vide :

>>> L = []
>>> L
[]

Longueur d’une liste : fonction len

>>> a = [0,1,2,3]
>>> len(a)
4


 manipuler des éléments d’une liste (ajouter, supprimer...) et leurs indices ;
Concaténation et multiplication
On concatène avec + :

>>> a=[1,2,3]
>>> b=a+[4,5]
>>> b
[1, 2, 3, 4, 5]
>>> [0,1,2]+["Marie","Mylène"]+[67,72]
[0, 1, 2, 'Marie', 'Mylène', 67, 72]

On peut utiliser la concaténation pour insérer un terme à la fin de la liste, mais on préférera utiliser la méthode append :

>>> L = ["Benjamin","Alain"]
>>> L.append("Yves")
>>> L
['Benjamin', 'Alain', 'Yves']

On multiplie avec * :

>>> [1,2,3]*3
[1, 2, 3, 1, 2, 3, 1, 2, 3]
>>> [1,2]*3
[1, 2, 1, 2, 1, 2]
>>> 2*["Pile","Face"]
['Pile', 'Face', 'Pile', 'Face']

Convertir avec la fonction list
On peut convertir n’importe quel itérable en liste :

>>> list("abcdef")
['a', 'b', 'c', 'd', 'e', 'f']
>>> list(b"abcdef")
[97, 98, 99, 100, 101, 102]
>>> list((0,1,2,3,4,5))
[0, 1, 2, 3, 4, 5]
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(range(5,9))
[5, 6, 7, 8]

Indexing et slicing
Les listes sont des séquences, elles sont donc indexables (les items sont repérés par un indice) et sliceables (on peut en extraire des tranches grâce à des plages d’indices). Comme d’habitude, l’indice du premier item est zéro :

>>> a=[42, 43, 45, 47]
>>> a[2]
45
>>> a[2]=12
>>> a
[42, 43, 12, 47]

Indices négatifs : Python numérote le dernier item avec -1, l’avant-dernier avec -2, et ainsi de suite :

>>> a=[42, 43, 45, 47]
>>> a[-1]
47
>>> a[-2]
45

Si on est obligé d’initialiser les termes dans le désordre, on commencera par créer une liste triviale.
Supposons par exemple qu’on veuille L de longueur 10, on écrira :

>>> L=[None]*10
>>> L[5]=12
>>> L
[None, None, None, None, None, 12, None, None, None, None]

sans déclencher d’erreur.
Un peu de slicing :

>>> a=[0,1,2,3,4,5,6,7,8,9]
>>> a[3:7]
[3, 4, 5, 6]
>>> a[3:3]
[]
>>> a[5:]
[5, 6, 7, 8, 9]
>>> a[:5]
[0, 1, 2, 3, 4]
>>> a[-3:-1]
[7, 8]
>>> a[-5:]
[5, 6, 7, 8, 9]
>>> a[:]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

On peut faire du slicing avec un pas (positif ou négatif) :

>>> a[2:20:3]
[2, 5, 8]
>>> a[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[20:2:-2]
[9, 7, 5, 3]

Remplacer une tranche par une autre
Dans une liste, on peut remplacer une tranche par une autre (il s’agit d’une mutation) :

>>> L=list(range(15))
>>> L[2:5]=5*[0]
>>> L
[0, 1, 0, 0, 0, 0, 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

On peut utiliser ce procédé pour changer un terme :

>>> L=list(range(15))
>>> L[3:4]=[7]
>>> L
[0, 1, 2, 7, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

On peut utiliser cette méthode pour supprimer une tranche :

>>> L[2:5]=[]
>>> L
[0, 1, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

On peut utiliser ce procédé pour supprimer un terme d’indice connu :

>>> L[3:4]=[]
>>> L
[0, 1, 5, 7, 8, 9, 10, 11, 12, 13, 14]

On peut utiliser ce procédé pour insérer un élément où on veut :

>>> L[5:5]=[25]
>>> L
[0, 1, 5, 7, 8, 25, 9, 10, 11, 12, 13, 14]

On peut insérer une tranche :

>>> L[5:5]=[1,1,1,1,1]
>>> L
[0, 1, 5, 7, 8, 1, 1, 1, 1, 1, 25, 9, 10, 11, 12, 13, 14]

Compréhension
En mathématiques (théorie des ensembles), l’axiome de compréhension est fondamental :
Axiome : Si E est un ensemble et P une propriété exprimée dans le langage de la théorie des ensembles, alors $\left\{x \in E | P\right\}$ est un ensemble.
Il est très courant en mathématiques de définir des ensembles en compréhension. L’ensemble des nombres pairs, par exemple est l’ensemble $\left\{n \in \mathbb{Z} | n\equiv 0 [2] \right\}$.
On peut avec Python définir des listes en compréhension (on dit aussi en intension avec un s) :

>>> X=[1,5,7,12]
>>> Y=[x**2 for x in X]
>>> Y
[1, 25, 49, 144]

Y est la liste des carrés des nombres appartenant à X. On peut même ajouter une condition :

>>> Y=[x**2 for x in X if x**2<30]
>>> Y
[1, 25]
>>> Y=[x**2 for x in X if x<10]
>>> Y
[1, 25, 49]

On peut redéfinir X à partir de X directement :

>>> X=[1,5,7,12]
>>> X=[x**2 for x in X]
>>> X
[1, 25, 49, 144]

On peut imbriquer des listes en compréhension :

>>> L = [[k for k in range(m)] for m in range(6)]
>>> L
[[], [0], [0, 1], [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4]]

Fusionner
Soit L une liste de listes. On peut récupérer les items des listes de L en une seule instruction :

>>> L = [[k for k in range(m)] for m in range(1,6)]
>>> L
[[0], [0, 1], [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4]]
>>> fusion=[x for SL in L for x in SL]
>>> fusion
[0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4]

On peut utiliser cette technique pour effectuer des produits de listes :

>>> valeur = [1,7,8,9,10,"Valet","Dame","Roi"]
>>> couleur = ["Coeur","Carreau","Pique","Trèfle"]
>>> jeu = [(n,c) for n in valeur for c in couleur]
>>> jeu
[(1, 'Coeur'), (1, 'Carreau'), (1, 'Pique'), (1, 'Trèfle'), (7, 'Coeur'), (7, 'Carreau'), (7, 'Pique'), 
(7, 'Trèfle'), (8, 'Coeur'), (8, 'Carreau'), (8, 'Pique'), (8, 'Trèfle'), (9, 'Coeur'), (9, 'Carreau'), 
(9, 'Pique'), (9, 'Trèfle'), (10, 'Coeur'), (10, 'Carreau'), (10, 'Pique'), (10, 'Trèfle'), ('Valet', 
'Coeur'), ('Valet', 'Carreau'), ('Valet', 'Pique'), ('Valet', 'Trèfle'), ('Dame', 'Coeur'), 
('Dame', 'Carreau'), ('Dame', 'Pique'), ('Dame', 'Trèfle'), ('Roi', 'Coeur'), ('Roi', 'Carreau'), 
('Roi', 'Pique'), ('Roi', 'Trèfle')]
>>> (10,"Trèfle") in jeu
True

Trier avec sort ou sorted (arguments reverse, key)
La fonction sorted et la méthode sort font la même chose : elles trient les items dans l’ordre croissant (par défaut). La fonction sorted prend n’importe quel itérable et retourne les items dans l’ordre, sous forme de liste :

>>> L=[7,12,3,2]
>>> sorted(L)
[2, 3, 7, 12]
>>> L
[7, 12, 3, 2]

La méthode sort s’applique à une liste et modifie cette liste :

>>> L.sort()
>>> L
[2, 3, 7, 12]

Les deux, sort et sorted, acceptent les arguments reverse et key. L’argument reverse permet de trier dans l’ordre décroissant :

>>> sorted([7,12,3,2],reverse=True)
[12, 7, 3, 2]

L’argument key permet de choisir la fonction avec laquelle on fera le tri. Si on veut trier selon la valeur absolue, on fera :

>>> sorted([-13,15,-2,6,-6],key=abs)
[-2, 6, -6, -13, 15]

Très pratique avec des données complexes :

>>> def f(t):
     return t[1]
>>> L=[["Marie",21],["Mylène",17],["Fred",24],["Denis",23]]
>>> sorted(L,key=f)
[['Mylène', 17], ['Marie', 21], ['Denis', 23], ['Fred', 24]]

Instruction del (mot réservé)
Pour effacer un item selon son rang, ou une plage d’items :

>>> L=[7,12,3,2]
>>> del L[2]
>>> L
[7, 12, 2]

Une autre syntaxe :

>>> del(L[2])

On peut utiliser del sur une plage d’indices :

>>> L=list(range(11))
>>> del L[2:5]
>>> L
[0, 1, 5, 6, 7, 8, 9, 10]
>>> L=list(range(21))
>>> L
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
>>> del L[::3]
>>> L
[1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20]

Méthode index
Pour trouver l’indice d’un terme :

>>> L=[0, 1, 6, 7, 8, 9, 10, 11, 12, 5, 6, 7, 8, 9, 10]
>>> L.index(6)
2

C’est l’index de la première occurrence du terme qui est renvoyé, si l’on veut l’index de la seconde occurrence, on peut démarrer la recherche plus loin :

>>> L.index(6,3)
10

Méthode count
Pour compter les occurrences d’un item :

>>> L = [1,2,1,1,1,1,2,1,2]
>>> L.count(2)
3
>>> L.count(3)
0

Méthode remove
Pour supprimer une occurrence (ne supprime que la première occurrence) :

>>> L = [1,10,56,23,897,56,1000]
>>> L.remove(56)
>>> L
[1, 10, 23, 897, 56, 1000]
>>> L.remove(100000)
Traceback (most recent call last):
  File "<pyshell#165>", line 1, in <module>
    L.remove(100000)
ValueError: list.remove(x): x not in list

Méthode append
Pour insérer un terme à la fin :

>>> L = [1,10,56,23,897,56,1000]
>>> L.append(1945)
>>> L
[1, 10, 56, 23, 897, 56, 1000, 1945]

Méthode insert
Pour insérer un terme où on veut :

>>> L.insert(3,666)
>>> L
[1, 10, 56, 666, 23, 897, 56, 1000, 1945]

Méthode extend
Pour étendre une liste par concaténation :

>>> LL=[456,567]
>>> L.extend(LL)
>>> L
[1, 10, 56, 666, 23, 897, 56, 1000, 1945, 456, 567]

Méthode pop
Supprime le dernier terme et le retourne :

>>> L.pop()
567
>>> L
[1, 10, 56, 666, 23, 897, 56, 1000, 1945, 456]

Fonction max
Retourne le plus grand élément :

>>> max(L)
1945

Fonction min
Retourne le plus petit élément :

>>> min(L)
1

Méthode copy
Pour copier une liste dans une autre :

>>> LL=L.copy()
>>> LL
[1, 10, 56, 666, 23, 897, 56, 1000, 1945, 456]
>>> LL==L
True
>>> LL is L
False

Méthode clear
Pour effacer le contenu d’une liste :

>>> L.clear()
>>> L
[]


 itérer sur les éléments d’une liste.
Le type list est itérable
On peut itérer une liste :

>>> for k in [1,2,3]:
	print(k,end=" ")
1 2 3

Les listes supportent les tests x in list et x not in list.

>>> solide_platon=["Tétraèdre","Hexaèdre","Octaèdre","Dodécaèdre","Icosaèdre"]
>>> "polyèdre" in solide_platon
False

Commentaires
 La génération des listes en compréhension et en extension est mise en lien avec la notion d’ensemble. Les conditions apparaissant dans les listes définies en compréhension permettent de travailler la logique.
 Afin d’éviter des confusions, il est recommandé de se limiter aux listes sans présenter d’autres types de collections.

Sélection de données

 traiter un fichier contenant des données réelles pour en extraire de l’information et l’analyser ;
Nous utiliserons un gros fichier de données issu du site de l’INSEE, sur les communes de France métropolitaine et de Corse, débarrassé des données incomplètes (pour les DOM-TOM) :

Données sur les villes de France (Métropolitaine et Corse)

Nous traiterons ce fichier avec le module pandas qui est une librairie Python spécialisée dans l’analyse des données. Pour ma part, je l’utilise dans Spyder, mais cela est un détail à régler de votre côté ... Un objet de type « data frame » permet de réaliser de nombreuses opérations de filtrage, prétraitements, etc., préalables à la modélisation statistique.
Dans Spyder, on crée un fichier Python :

import matplotlib as plt
import pandas
data=pandas.read_csv("villes_virgule.csv",sep=",")
pandas.options.display.max_rows = 10 #On fait le choix de n'afficher que 10 lignes dans la console 

On obtient dans la console un extrait des données qui sont enregistrées dans 36568 lignes et 12 colonnes, des « ... » indiquent les « trous » présents dans cet affichage :

In[63]:data
Out[63]: 
      dep                   nom     cp   ...         lat  alt_min  alt_max
0       1     Ambérieu-en-Bugey   1500   ...     45.9500      237      753
1       1   Ambérieux-en-Dombes   1330   ...     46.0000      265      302
2       1               Ambléon   1300   ...     45.7500      330      940
3       1              Ambronay   1500   ...     46.0000      225      765
4       1              Ambutrix   1500   ...     45.9333      237      370
   ..                   ...    ...   ...         ...      ...      ...
36563  2B               Vivario  20219   ...     42.1731      400     2390
36564  2B             Volpajola  20290   ...     42.5262       45     1231
36565  2B                Zalana  20272   ...     42.2606      193      847
36566  2B                 Zilia  20214   ...     42.5303      172     1935
36567  2B                 Zuani  20272   ...     42.2714      411     1013

[36568 rows x 12 columns]

Par rapport au fichier original, la colonne des index des lignes a été rajoutée à gauche.
Il est possible d’afficher toutes les données grâce à l’explorateur de variables :

Structure DataFrame
Une matrice DataFrame correspond à une matrice individus-variables où les lignes correspondent à des observations, les colonnes à des attributs décrivant les individus. Concernant notre fichier « villes_virgule.csv » : la première ligne correspond aux noms des champs (des variables) ; à partir de la seconde ligne, nous disposons des valeurs pour chaque enregistrement (individu, ici une commune). Dans ce qui suit, nous chargeons le fichier de données et nous procédons à quelques vérifications.
Si l’on demande le type de data :

print(type(data))
<class 'pandas.core.frame.DataFrame'>

Voyons maintenant l’architecture de la structure DataFrame :

print(data.shape)
(36568, 12)

La ligne d’en-tête et la colonne d’index ne sont pas comptées.
Pour afficher l’en-tête de toutes les colonnes :

print(data.columns)
Index(['dep', 'nom', 'cp', 'nb_hab_2010', 'nb_hab_1999', 'nb_hab_2012', 'dens',
       'surf', 'long', 'lat', 'alt_min', 'alt_max'],
      dtype='object')

On a donc le numéro du département, le nom de la commune, le code postal, le nombre d’habitants en 2010, le nombre d’habitants en 1999, le nombre d’habitants en 2012, la densité, la surface, la longitude, la latitude, l’altitude minimum et l’altitude maximum.
On peut récupérer les données statistiques de base pour chacune des colonnes :

print(data.describe())
        nb_hab_2010   nb_hab_1999      ...            alt_min       alt_max
count  3.656800e+04  3.656800e+04      ...       36568.000000  36568.000000
mean   1.716628e+03  1.598173e+03      ...         193.157569    391.109166
std    1.470343e+04  1.393150e+04      ...         194.694120    449.305955
min    0.000000e+00  0.000000e+00      ...          -5.000000      2.000000
25%    1.940000e+02  1.750000e+02      ...          62.000000    140.000000
50%    4.270000e+02  3.795000e+02      ...         138.000000    236.000000
75%    1.050000e+03  9.252500e+02      ...         253.000000    435.000000
max    2.243833e+06  2.125851e+06      ...        1785.000000   4807.000000
[8 rows x 9 columns]

Certaines des données ont plus ou moins d’intérêt selon le contexte, ici par exemple count correspond au nombre de données pour chaque colonne c’est-à-dire 36568 ...
On peut alors demander plus précisément les données pour une colonne précise :

print(data["dens"].describe())
count    36568.000000
mean       153.984112
std        703.669064
min          0.000000
25%         18.000000
50%         39.000000
75%         90.000000
max      26660.000000
Name: dens, dtype: float64

Si l’on demande le calcul explicite de la moyenne :

print(data["nb_hab_2010"].mean())
1716.6284456355284

Ou la population totale en 2010 :

sum(data["nb_hab_2010"])
Out[75]: 62773669

Le nombre de communes par département :

print(data['dep'].value_counts())
62    895
2     816
80    782
76    745
57    730
...
90    102
94     47
93     40
92     36
75      1
Name: dep, Length: 96, dtype: int64

data[« dens »] est une liste, on peut donc lui appliquer toutes les méthodes des listes :

data["dens"][0]
Out[80]: 562

data["dens"][0:3]
Out[81]: 
0    562
1    101
2     19
Name: dens, dtype: int64

data.dens[0:3]
Out[82]: 
0    562
1    101
2     19
Name: dens, dtype: int64
data["dens"].sort_values()
Out[84]: 
11593        0
22300        0
21144        0
3800         0
1577         0
 
35923    23476
36009    24343
36021    25377
35952    25778
35919    26660
Name: dens, Length: 36568, dtype: int64

On peut accéder aux valeurs du DataFrame via des indices ou plages d’indice. La structure se comporte alors comme une matrice. La cellule en haut et à gauche est de coordonnées (0,0). Il y a différentes manières de le faire, l’utilisation de .iloc[,] constitue une des solutions les plus simples. N’oublions pas que Shape permet d’obtenir les dimensions (lignes et colonnes) du DataFrame :

print(data.iloc[0,0])
1
print(data.iloc[5,8])
5.65
print(data.iloc[-1,0])
2B
print(data.iloc[data.shape[0]-1,0])
2B
print(data.iloc[0:5,:])
  dep                  nom    cp   ...         lat  alt_min  alt_max
0   1    Ambérieu-en-Bugey  1500   ...     45.9500      237      753
1   1  Ambérieux-en-Dombes  1330   ...     46.0000      265      302
2   1              Ambléon  1300   ...     45.7500      330      940
3   1             Ambronay  1500   ...     46.0000      225      765
4   1             Ambutrix  1500   ...     45.9333      237      370
[5 rows x 12 columns]
print(data.iloc[0:5,0:2])
  dep                  nom
0   1    Ambérieu-en-Bugey
1   1  Ambérieux-en-Dombes
2   1              Ambléon
3   1             Ambronay
4   1             Ambutrix
print(data.iloc[0:5,[0,2,4]])
  dep    cp  nb_hab_1999
0   1  1500        11432
1   1  1330         1407
2   1  1300           86
3   1  1500         2144
4   1  1500          586

Nous pouvons isoler les sous-ensembles d’observations répondant à des critères définis sur les champs. Nous utiliserons préférentiellement la méthode .loc[,] dans ce cadre. Par exemple si l’on veut toutes les données correspondant au code postal 34400 :

print(data.loc[data['cp']=="34400",:])
      dep                     nom     cp   ...         lat  alt_min  alt_max
13262  34                   Lunel  34400   ...     43.6833        2       53
13263  34              Lunel-Viel  34400   ...     43.6833        6       50
13359  34          Saint-Christol  34400   ...     43.7333       24       92
13384  34              Saint-Just  34400   ...     43.6500        2        9
13391  34  Saint-Nazaire-de-Pézan  34400   ...     43.6333        0        5
13399  34            Saint-Sériès  34400   ...     43.7333       14       68
13405  34             Saturargues  34400   ...     43.7167       16       68
13437  34               Vérargues  34400   ...     43.7167       15       65
13447  34              Villetelle  34400   ...     43.7333       10       67
[9 rows x 12 columns]

Si l’on veut les communes du Nord de l’Hérault :

print(data.loc[(data['dep']=="34") & (data['lat'] >43.9),:])
      dep                nom     cp   ...         lat  alt_min  alt_max
13112  34             Agonès  34190   ...     43.9167      119      323
13169  34           Cazilhac  34190   ...     43.9250      132      523
13211  34             Ganges  34190   ...     43.9333      138      540
13235  34            Laroque  34190   ...     43.9167      124      490
13288  34         Montoulieu  34190   ...     43.9333      159      522
13291  34  Moulès-et-Baucels  34190   ...     43.9500      156      727
[6 rows x 12 columns]

pour visualiser l’évolution démographique des communes Héraultaises de plus de 10000 habitants en 1999 :

colonnes = ['nom','nb_hab_1999','nb_hab_2010','nb_hab_2012']
print(data.loc[(data['dep'] =="34") & (data['nb_hab_1999'] >10000),colonnes])
                    nom  nb_hab_1999  nb_hab_2010  nb_hab_2012
13110              Agde        20066        24567        22500
13139           Béziers        69359        70955        71700
13161  Castelnau-le-Lez        14208        14948        15000
13208        Frontignan        19130        22526        23200
13236            Lattes        13760        15927        16300
13262             Lunel        22346        25277        24400
13271           Mauguio        14846        16307        15800
13289       Montpellier       225511       257351       253000
13412              Sète        39579        42774        42800


 réaliser un tableau croisé de données sur deux critères à partir de données brutes.

On utilisera un second fichier, plus approprié, qui répertorie, selon leur ville de résidence, la LV2 étudiée par des lycéens :

LV2 étudiée


import matplotlib as plt
import pandas
data=pandas.read_csv("LV2.csv",sep=",")
pandas.options.display.max_rows = 10

data
Out[6]: 
     LV2       ville
0    ANG  Saint-Just
1    ITA  Lansargues
2    ITA  Saint-Just
3    ITA  Lunel-Viel
4    ANG  Lansargues
..   ...         ...
104  ITA       Lunel
105  ALL       Lunel
106  ANG       Lunel
107  ITA  Lansargues
108  ITA  Lansargues
[109 rows x 2 columns]

On peut alors calculer les effectifs et les fréquences :

data["LV2"].value_counts()
Out[2]: 
ANG    33
ITA    33
ALL    23
ESP    20
Name: LV2, dtype: int64

data["LV2"].value_counts(normalize=True)
Out[3]: 
ANG    0.302752
ITA    0.302752
ALL    0.211009
ESP    0.183486
Name: LV2, dtype: float64

Et réaliser le tri croisé :

pandas.crosstab(data["LV2"],data["ville"])
Out[5]: 
ville  Lansargues  Lunel  Lunel-Viel  Saint-Just
LV2                                             
ALL             5     10           3           5
ANG             7     12           8           6
ESP             4      6           6           4
ITA             9     11           7           6

Ou avec la métode pivot_table(), qui donne exactement la même chose :

data.pivot_table(index="LV2",columns="ville",aggfunc=len)

Suites numériques

Calculer un terme de rang donné d’une suite, une somme finie de termes.

Le calcul d’un terme de rang donné d’une suite définie de manière explicite, c’est-à-dire à l’aide d’une fonction de n est quelque chose de très simple :

def u(n):
    return 3*n+1

Si l’on veut simplement un terme de rang donné, il suffit d’utiliser la fonction u :

>>> u(5)
16

Regardons maintenant du côté des suites définies par récurrence. Prenons le cas d’une suite définie par $u_{n+1} = f(u_n)$ et de premier terme $u_{p}$ :

def f(x):
    return 3*x+1
def suite_recurrente(f,debut,fin,u_p):
    u=u_p
    for i in range(debut,fin):
        u=f(u)
    return u

Après avoir défini la fonction $f$, qui calcule $u_{n+1}$ en fonction de $u_{n}$, on calcule à l’aide d’une boucle les termes d’indice compris entre début+1 et fin. Si l’on veut $u_{12}$ :

>>> suite_recurrente(f,0,12,2)
1328602

Étant donné que les suites arithmétiques et géométriques apparaissent dans le programme, il semble légitime de s’y intéresser particulièrement. On peut bien sûr utiliser la fonction suite_recurrente() (l’expression explicite de ces suites est au programme de Terminale) précédentes, il suffit d’écrire la relation de récurrence adéquate. Sinon, il est possible aussi de créer un outil spécifique qui prendrait en paramètres les rangs de début et de fin ainsi que le terme initial et la raison.

def suite_geometrique_recurrence(debut,fin,u_p,raison):
    u=u_p
    for i in range(debut,fin):
        u*=raison
    return u
def suite_arithmetique_recurrence(debut,fin,u_p,raison):
    u=u_p
    for i in range(debut,fin):
        u+=raison
    return u

Utilisation :

>>> suite_arithmetique_recurrence(0,10,2,0.9)
11.000000000000002
>>> suite_geometrique_recurrence(0,10,2,0.9)
0.6973568802000003

A noter les petites différences avec les résultats attendus de l’ordre de $10^{-15}$ dues à la gestion des nombres décimaux (flottants) dans Python. Cela peut se régler en utilisant la fonction round().
Si la suite est définie de manière explicite et que l’on veut la somme des termes de rang compris entre debut et fin :

def u(n):
    return 3*n+1
def somme_explicite(debut,fin):
    s=0
    for i in range(debut,fin+1):
        s=s+u(i)
    return s

Utilisation :

>>> somme_explicite(5,9)
110

Si la suite est définie de manière récurrente et que l’on veut la somme des termes de rang compris entre debut et fin avec $u_{debut} = u_p$ :

def f(x):
    return 3*x+1
def somme_recurrente(debut,fin,u_p):
    s=u_p
    u=u_p
    for i in range(debut,fin):
        u=f(u)
        s=s+u
    return s

Utilisation :

>>> somme_recurrente(3,7,5)
663

Déterminer une liste de termes d’une suite et les représenter.

Pour une suite déterminée de manière explicite :

import matplotlib.pyplot as plt
def u(n):
	return 0.2*n**2-1.2*n+1.6
def graphe(u,debut,fin):
        for i in range(debut,fin):
                plt.scatter(i,u(i),c="red",marker='x')
        plt.grid()
        plt.show()

Taper dans la console :

>>> graphe(u,0,12)
[1.6, 0.6, 0, -0.2, 0, 0.6, 1.6, 3.0, 4.8, 7.0, 9.6, 12.6]


Pour une suite déterminée de manière récurrente :

import matplotlib.pyplot as plt
def u(n):
	return 0.2*n**2-1.2*n+1.6
def suite_recurrente(u,debut,fin,u_p):
    liste_x=[debut]
    liste_y=[u_p]
    for i in range(debut,fin):
        liste_x.append(i+1)
        liste_y.append(u(liste_y[i]))
    plt.scatter(liste_x,liste_y)
    plt.grid()
    plt.show()
    return liste_y

Taper dans la console :

>>> suite_recurrente(u,0,25,3)
[3, -0.200, 1.848, 0.065, 1.522, 0.237, 1.327, 0.360, 1.194, 0.452, 1.098, 0.523, 1.027, 0.579, 
0.973, 0.622, 0.931, 0.656, 0.899, 0.683, 0.874, 0.704, 0.854, 0.721, 0.839, 0.734]

Déterminer le rang à partir duquel les termes d’une suite sont supérieurs ou inférieurs à un seuil donné, ou aux termes de même rang d’une autre suite

Pour une suite, définie de manière explicite, pour laquelle on cherche à partir de quel rang son terme devient supérieur à un seuil donné :

def u(n):
	return 0.2*n**2-1.2*n+1.6
def seuil(u,debut,s):
        u_n=u(debut)
        compteur=debut
        while u_n<s:
                compteur+=1
                u_n=u(compteur)
        return u_n,compteur

utilisation :

>>> seuil(u,0,1000)
(1008.0, 74)

Le seuil de 1000 est dépassé au rang 74 avec $u_{74}=1008$.

Pour une suite, définie par récurrence, pour laquelle on cherche à partir de quel rang son terme devient supérieur à un seuil donné :

def u(n):
	return 3*n+1
def seuil(u,debut,u_p,s):
        compteur=debut
        while u_p<s:
                compteur+=1
                u_p=u(u_p)
        return u_p,compteur

utilisation :

>>> seuil(u,1,5,1000)
(1336, 6)

Le seuil de 1000 est dépassé au rang 6 avec $u_{6}=1336$.

Pour une suite, définie de manière explicite, pour laquelle on cherche à partir de quel rang son terme devient inférieur à un seuil donné :

def u(n):
	return -0.2*n**2+1.2*n+1000
def seuil(u,debut,s):
        u_n=u(debut)
        compteur=debut
        while u_n>s:
                compteur+=1
                u_n=u(compteur)
        return u_n,compteur

utilisation :

>>> seuil(u,0,500)
(481.5999999999999, 54)

La suite passe sous le seuil de 500 au rang 54 avec $u_{54}=481.6$.

Pour une suite, définie par récurrence, pour laquelle on cherche à partir de quel rang son terme devient inférieur à un seuil donné :

def u(n):
	return 1.03*n-250
def seuil(u,debut,u_p,s):
        compteur=debut
        while u_p>s:
                compteur+=1
                u_p=u(u_p)
        return u_p,compteur

utilisation :

>>> seuil(u,1,2000,250)
(69.7698357481176, 10)

La suite passe sous le seuil de 250 au rang 10 avec $u_{11}\approx 69.77$.

Déterminer le rang à partir duquel les termes d’une suite sont supérieurs aux termes de même rang d’une autre suite avec deux suites définies par récurrence :

def u(n):
	return n+400
def v(n):
	return 1.025*n
def seuil(u,v,debut,u_p,v_p):
        compteur=debut
        while u_p>=v_p:
                compteur+=1
                u_p=u(u_p)
                v_p=v(v_p)
        return u_p,v_p,compteur

utilisation :

>>> seuil(u,v,0,10000,10000)
(24800, 24933.486986108404, 37)

Déterminer le rang à partir duquel les termes d’une suite sont supérieurs aux termes de même rang d’une autre suite avec deux suites définies de manière implicite :

def u(n):
	return 10000+400*n
def v(n):
	return 10000*1.025**n
def seuil(u,v,debut):
        compteur=debut
        u_p,v_p=u(debut),v(debut)
        while u_p>=v_p:
                compteur+=1
                u_p=u(compteur)
                v_p=v(compteur)
        return u_p,v_p,compteur

utilisation :

>>> seuil(u,v,0)
(24800, 24933.4869861084, 37)

Fonctions de la variable réelle

Calculer une valeur approchée d’une solution d’une équation par balayage.

Cette méthode, classique, consiste, pour une fonction continue et monotone sur un intervalle [a ; b] dans lequel on sait trouver la solution de $f(x) = k$, à calculer $f(x)$, pour $x$ allant de a à b avec un pas de h, après avoir vérifié la croissance ou la décroissance de la fonction. Si f est croissante, « Tant Que » f(x) < k est vérifiée (On utilisera pour cela un While.), on calcule f(x). Dès que f(x) > k on arrête l’algorithme. Si f est décroissante, « Tant Que » f(x) > k est vérifiée, on calcule f(x). Dès que f(x) < k on arrête l’algorithme. On obtient un encadrement de longueur h de la solution
Il faut déterminer a et b, les bornes de l’intervalle dans lequel on va appliquer le balayage.
Fonction qui calcule cet encadrement, renvoyé dans un tuple, en fonction du nombre n de décimales souhaitées :
Cet algorithme détermine par balayage un encadrement de racine de 2 d’amplitude 10^(-n).

def balayage(f,k,a,b,n):
    fa,fb=f(a),f(b)
    h=10**(-n)
    x=a
    if fa<fb:
        while f(x)<k:
            x=x+h
    else:
        while f(x)>k:
            x=x+h
    return (round(x-h,n),round(x,n))

Utilisation : Compiler le fichier puis dans la console :

>> balayage(f,0,0,5,5)
(1.41421, 1.41422)

Le round permet ici de corriger quelques erreurs d’affichage des flottants [1].
Il est à noter que cet algorithme est très long à donner une réponse si n>=9.
On peut donc chercher à l’optimiser, par exemple en utilisant une boucle for qui va faire la même chose que le programme précédent mais pour h allant de 10-1 à 10-n en réduisant l’intervalle [a ; b] à chaque boucle.
C’est beaucoup plus rapide, jusqu’à n=16 où ça bogue, parce que les flottants (décimaux) ne sont affichés qu’avec 16 chiffres après la virgule.

def balayage2(f,k,a,b,n):
    fa,fb=f(a),f(b)
    for i in range(1,n+1):
        h=10**(-i)
        x=a
        if fa<fb:
            while f(x)<k:
                x=x+h
        else:
            while f(x)>k:
                x=x+h
        a=x-h
    return (round(x-h,n),round(x,n))

Croisement de deux variables catégorielles

À partir de deux listes représentant deux caractères d’individus, déterminer un sous-ensemble d’individus répondant à un critère (filtre, utilisation des ET, OU, NON).

Soit l’on se donne une telle liste soit on la génère aléatoirement, par exemple :

from random import randint
taille=[randint(160,190) for t in range(35)]
distance_lycee=[randint(0,25) for d in range(35)]
echantillon=[]
for i in range(35):
    echantillon.append([taille[i],distance_lycee[i]])

On obtient par exemple :

>>> echantillon
[[165, 18], [180, 19], [174, 22], [181, 17], [184, 12], [180, 16], [172, 10], [169, 3],
[168, 25], [183, 5], [181, 4], [188, 12], [188, 23], [179, 2], [160, 23], [181, 14], 
[183, 1], [170, 7], [172, 3], [182, 15], [161, 9], [167, 1], [164, 22], [189, 17], 
[173, 11], [181, 11], [164, 11], [168, 25], [163, 14], [165, 12], [188, 8], [183, 2], 
[174, 19], [188, 8], [179, 0]]

On peut alors déterminer différents sous-ensembles :

>>> grands=[x for x in echantillon if x[0]>185]
>>> grands
[[188, 12], [188, 23], [189, 17], [188, 8], [188, 8]]
>>> petits=[x for x in echantillon if x[0]<165]
>>> petits
[[160, 23], [161, 9], [164, 22], [164, 11], [163, 14]]
>>> proches=[x for x in echantillon if x[1]<5]
>>> proches
[[169, 3], [181, 4], [179, 2], [183, 1], [172, 3], [167, 1], [183, 2], [179, 0]]
>>> pas_trop_loin=[x for x in echantillon if x[1]>5 and x[1]<15]
>>> pas_trop_loin
[[184, 12], [172, 10], [188, 12], [181, 14], [170, 7], [161, 9], [173, 11], [181, 11], 
[164, 11], [163, 14], [165, 12], [188, 8], [188, 8]]
>>> petits_ou_proches=[x for x in echantillon if x[0]<165 or x[1]<5]
>>> petits_ou_proches
[[169, 3], [181, 4], [179, 2], [160, 23], [183, 1], [172, 3], [161, 9], [167, 1], 
[164, 22], [164, 11], [163, 14], [183, 2], [179, 0]]
>>> petits_ou_grands=[x for x in echantillon if not(165<x[0]<185)]
>>> petits_ou_grands
[[165, 18], [188, 12], [188, 23], [160, 23], [161, 9], [164, 22], [189, 17], [164, 11], 
[163, 14], [165, 12], [188, 8], [188, 8]]

Dresser le tableau croisé de deux variables catégorielles à partir du fichier des individus et calculer des fréquences conditionnelles ou marginales.

Sans le module Pandas
On peut partir de deux listes, ou d’une liste regroupant les deux listes, nous choisissons de générer cette liste aléatoirement dans le programme Python, en utilisant la méthode choice() pour tirer aléatoirement un élément d’une liste (ici dans les deux listes intitules), puis de traiter les données afin d’établir le tableau croisé :

from random import choice
def tableau_croise(intitules1,intitules2,n):
    echantillon=[]
    L1=len(intitules1)
    L2=len(intitules2)
    for i in range(n):
        echantillon.append([choice(intitules1),choice(intitules2)])
    a=[[0]*len(intitules2)]
    for i in range(L1-1):
        a+=[[0]*len(intitules2)]
    for k in range(n):
        i = intitules1.index(echantillon[k][0])
        j = intitules2.index(echantillon[k][1])
        a[i][j]+=1
    etiquettes=["Effectifs",intitules2[0],intitules2[1],intitules2[2],"Total"]
    ligne1=[intitules1[0],a[0][0],a[0][1],a[0][2],a[0][0]+a[0][1]+a[0][2]]
    ligne2=[intitules1[1],a[1][0],a[1][1],a[1][2],a[1][0]+a[1][1]+a[1][2]]
    ligne3=["Total",a[0][0]+a[1][0],a[0][1]+a[1][1],a[0][2]+a[1][2],n]
    tab=[etiquettes,ligne1,ligne2,ligne3]
    form="{0:15}{1:^10}{2:^10}{3:^10}{4:^10}" # ^ sert à centrer le texte. 10 est la largeur en nombre de caractères
    for val in tab:
        print(form.format(*val))

La méthode index() cherche un élément dans la liste et renvoie son index. En termes simples, la méthode index() trouve un élément donné dans une liste et renvoie sa position, ce qui nous permet de les compter et d’établir le tableau des effectifs.
A noter que l’on crée la matrice a=[0,0,...,0]...[0,0,...,0] avec :

a=[[0]*len(intitules2)]
    for i in range(L1-1):
        a+=[[0]*len(intitules2)]

et non pas avec :

a=[[0]*len(intitules2)]*len(intitules1)

Plutôt qu’un long discours l’explication est dans le code ci-dessous :

>>> b=[[0,0,0],[0,0,0]]
>>> b
[[0, 0, 0], [0, 0, 0]]
>>> b[1][2]=1
>>> b
[[0, 0, 0], [0, 0, 1]]
>>> a=[[0]*3]*2
>>> a
[[0, 0, 0], [0, 0, 0]]
>>> a[1][2]=1
>>> a
[[0, 0, 1], [0, 0, 1]]

Une petite mise en forme s’impose si l’on veut obtenir quelque chose de lisible (ceci n’a pas besoin d’être expliqué aux élèves, il s’agit de cosmétique ...).
Utilisation :

>>> tableau_croise(["Célibataire","Marié"],["Collège","Lycée","Supérieur"],35)
Effectifs       Collège    Lycée   Supérieur   Total   
Marié              4         9         9         22    
Célibataire        4         9         10        23    
Total              8         18        19        45   

Pour obtenir les fréquences, on complète le code précédent avec :

    print("\n")
    etiquettes_f=["Fréquences",intitules2[0],intitules2[1],intitules2[2],"Total"]
    ligne1_f=[intitules1[0],round(a[0][0]/n,4),round(a[0][1]/n,4),round(a[0][2]/n,4),round((a[0][0]+a[0][1]+a[0][2])/n,4)]
    ligne2_f=[intitules1[1],round(a[1][0]/n,4),round(a[1][1]/n,4),round(a[1][2]/n,4),round((a[1][0]+a[1][1]+a[1][2])/n,4)]
    ligne3_f=["Total",round((a[0][0]+a[1][0])/n,4),round((a[0][1]+a[1][1])/n,4),round((a[0][2]+a[1][2])/n,4),1]
    tab_freq=[etiquettes_f,ligne1_f,ligne2_f,ligne3_f]
    for val in tab_freq:
        print(form.format(*val))

Pour obtenir :

Fréquences      Collège    Lycée   Supérieur   Total   
Marié            0.0889     0.2       0.2      0.4889  
Célibataire      0.0889     0.2      0.2222    0.5111  
Total            0.1778     0.4      0.4222      1 

On a alors dans les colonnes « Total » les fréquences marginales :
 17,78% des individus ont un niveau Collège.
 48,89% des individus sont Mariés.
Avec le module Pandas
Sinon, on peut traiter un fichier csv qui contient ces données avec le module pandas qui est une librairie Python spécialisée dans l’analyse des données. Pour ma part, je l’utilise dans Spyder, mais cela est un détail à régler de votre côté ... Un objet de type « data frame » permet de réaliser de nombreuses opérations de filtrage, prétraitements, etc., préalables à la modélisation statistique.
Dans Spyder, on crée un fichier Python :

import pandas
data=pandas.read_csv("statut_niveau.csv",sep="\t|,")
pandas.options.display.max_rows = 10

qui ouvre un fichier statut_niveau.csv qui contient les données, le voici :

Statut et niveau

On vérifie dans la console :

data
Out[33]: 
         Statut     Niveau
0   Célibataire    Collège
1   Célibataire      Lycée
2   Célibataire    Collège
3   Célibataire  Supérieur
4         Marié  Supérieur
..          ...        ...
52        Marié      Lycée
53        Marié  Supérieur
54        Marié      Lycée
55  Célibataire  Supérieur
56  Célibataire    Collège
[57 rows x 2 columns]

Et on demande le tableau croisé :

pandas.crosstab(data["Statut"],data["Niveau"])
Out[34]: 
Niveau       Collège  Lycée  Supérieur
Statut                                
Célibataire       13      6          7
Marié             10     11         10

Avec les totaux par ligne et par colonne :

pandas.crosstab(data["Statut"],data["Niveau"], margins=True)
Out[75]: 
Niveau       Collège  Lycée  Supérieur  All
Statut                                     
Célibataire       13      6          7   26
Marié             10     11         10   31
All               23     17         17   57

On peut obtenir le tableau des fréquences conditionnelles selon le niveau :

pandas.crosstab(data["Statut"],data["Niveau"],normalize="index", margins=True)
Out[41]: 
Niveau        Collège     Lycée  Supérieur
Statut                                    
Célibataire  0.500000  0.230769   0.269231
Marié        0.322581  0.354839   0.322581
All          0.403509  0.298246   0.298246

On lit ainsi que :
 « 50% des individus Célibataires ont un niveau Collège. » (fréquence conditionnelle)
 « 35,48% des individus Mariés ont un niveau Lycée . » (fréquence conditionnelle)
 « 40,35% desindividus ont un niveau Collège » (fréquence marginale)
On peut obtenir le tableau des fréquences conditionnelles selon le Statut :

pandas.crosstab(data["Statut"],data["Niveau"], Normalize="columns", margins=True)
Out[79]: 
Niveau        Collège     Lycée  Supérieur      All
Statut                                             
Célibataire  0.565217  0.352941   0.411765  0.45614
Marié        0.434783  0.647059   0.588235  0.54386

On lit ainsi que :
 « 35,29% des individus ayant un niveau Lycée sont célibataires. » (fréquence conditionnelle)
 « 58,82% des individus ayant un niveau Supérieur sont mariés. » (fréquence conditionnelle)
 « 45,61% des individus sont célibataires. » (fréquence marginale)
Et enfin le tableau des fréquences :

pandas.crosstab(data["Statut"],data["Niveau"],normalize="all")
Out[43]: 
Niveau        Collège     Lycée  Supérieur
Statut                                    
Célibataire  0.228070  0.105263   0.122807
Marié        0.175439  0.192982   0.175439

Avec les totaux :

pandas.crosstab(data["Statut"],data["Niveau"],normalize="all",margins=True)
Out[80]: 
Niveau        Collège     Lycée  Supérieur      All
Statut                                             
Célibataire  0.228070  0.105263   0.122807  0.45614
Marié        0.175439  0.192982   0.175439  0.54386
All          0.403509  0.298246   0.298246  1.00000

Où l’on retrouve les fréquences marginales dans la colonne et la ligne « All ».

Si l’on s’intéresse à des données numériques assez nombreuses, il peut être intéressant de croiser ces données par classes d’intervalles, avec le fichier ci-dessous :

taille et distance


import pandas
data=pandas.read_csv("taille_distance.csv",sep="\t|,")
pandas.options.display.max_rows = 10

On demande alors dans la console :

pandas.crosstab(pandas.cut(data["taille"], bins = [160, 170,180,190,200], include_lowest=True), 
... pandas.cut(data["dist lycée"],bins=[0,10,20,30], include_lowest=True))
Out[53]: 
dist lycée        (-0.001, 10.0]  (10.0, 20.0]  (20.0, 30.0]
taille                                                      
(159.999, 170.0]              38            32            17
(170.0, 180.0]                27            29            12
(180.0, 190.0]                30            22            17
(190.0, 200.0]                23            18             9

Variables aléatoires

Simuler des échantillons de taille n d’une loi de Bernoulli à partir d’un générateur de nombres aléatoires entre 0 et 1.

Le module random est un module qui regroupe des fonctions permettant de simuler le hasard. Parmi ces fonctions, la fonction random() donne un flottant au hasard dans l’intervalle [0 ; 1[. Soit p la probabilité du succès d’une expérience de Bernoulli.
On peut ainsi simuler une épreuve de Bernoulli :

def bernoulli(p):
    if random()<p:
        return(1)
    else:
        return(0)

Utilisation :

>>> bernoulli(0.8)
1

On peut alors répéter cette expérience n fois :

def echantillon(n,p):
    L=[bernoulli(p) for i in range(n)]
    return L

Utilisation :

>>> echantillon(25,0.6)
[1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1]

On peut alors calculer la fréquence du succès lors de N expériences d’un échantillon de taille n :

def frequences(N,n,p):
    F=[sum(echantillon(n,p))/n for k in range(N)]
    return F

Utilisation :

>>> frequences(500,100,0.7)
[0.66, 0.65, 0.68, 0.8, 0.67, 0.71, 0.68, 0.6, 0.78, 0.72, 0.7, 0.68, 0.63, ...

Représenter par un histogramme ou par un nuage de points les fréquences observées des 1 dans N échantillons de taille n d’une loi de Bernoulli.

On peut illustrer graphiquement le résultat obtenu après avoir calculé la fréquence du succès lors de N expériences d’un échantillon de taille n (voir paragraphe précédent la fonction frequences(N,n,p) réutilisée ici) :
 par un histogramme :

import matplotlib.pyplot as plt
def histogramme(N,n,p):
    plt.grid()
    plt.hist(frequences(N,n,p),range=(p-0.25,p+0.25),edgecolor = 'grey')
    plt.xlabel("fréquences")
    plt.ylabel("effectifs")
    plt.title("histogramme")
    return plt.show()

Utilisation :

>>> histogramme(1000,100,0.8)


 par un nuage de points :

import matplotlib.pyplot as plt
def nuage(N,n,p):
    plt.grid()
    plt.scatter(range(N),frequences(N,n,p),c="red",marker=".")
    plt.xlim([0,N])
    plt.ylim([0,1])
    return plt.show()

Utilisation :

>>> nuage(2000,500,0.7)

Compter le nombre de valeurs situées dans un intervalle de la forme [p-ks ; p+ks] pour k∈1 ; 2 ; 3

Pour calculer l’écart-type :

from math import sqrt
def esperance(Liste):
    return sum(Liste)/len(Liste)
def variance(Liste):
    E_X=esperance(Liste)
    N=len(Liste)
    varX = 0
    for i in range(N):
        varX += (Liste[i]-E_X)**2
    varX=varX/N
    return varX
def ecarttype(Liste):
    return sqrt(variance(Liste))

Pour compter le nombre de valeurs situées dans un intervalle de la forme $[p-ks ; p+ks]$ pour $k\in\left\{1 ; 2 ; 3\right\}$ :

def effectifs(F,p,k):
    s=ecarttype(F)
    a=p-k*s
    b=p+k*s
    i=0
    for x in F:
        if x>=a and x<=b:
            i=i+1
    return i

Utilisation :

>>> effectifs(frequences(1000,100,0.7),0.7,1)
658
>>> effectifs(frequences(1000,100,0.7),0.7,2)
961
>>> effectifs(frequences(1000,100,0.7),0.7,3)
998

Et si l’on veut plutôt récupérer la fréquence de 1 dans ces intervalles :

def pourcentages(F,N,p,k):
    s=ecarttype(F)
    a=p-k*s
    b=p+k*s
    i=0
    for x in F:
        if x>=a and x<=b:
            i=i+1
    return(100*i/N)

Utilisation (en utilisant la fonction fréquence vue deux paragraphes au-dessus) :

>>> pourcentages(frequences(1000,100,0.7),1000,0.7,3)
99.7
>>> pourcentages(frequences(1000,100,0.7),1000,0.7,2)
96.2
>>> pourcentages(frequences(1000,100,0.7),1000,0.7,1)
68.5

On peut utiliser la fonction nuage(N,n,p) vue précédemment pour y rajouter les droites d’équations y =μ-kσ et y = μ +kσ pour illustrer cela :

def nuage_ks(N,n,p,k):
    L=frequences(N,n,p)
    E_X=esperance(L)
    s=ecarttype(L)
    a=E_X-k*s
    b=E_X+k*s
    plt.plot([0,N],[a,a],"b-")
    plt.plot([0,N],[b,b],"b-")
    titre=("Nuage de points et droites d'équations y =μ-{0}σ et y = μ +{0}σ.".format(k,k))
    plt.suptitle(titre)
    nuage(N,n,p)
    return plt.show()

Utilisation :

>>> nuage_ks(1000,100,0.7,2)


Bernoulli

Bibliographie