Dans parcourSup, pour un étudiant et pour une matière, on dispose de sa moyenne $x$, de la moyenne $m$ de la classe, de la note la plus haute et la plus basse de la classe, respectivement $x_{max}$ et $x_{min}$. Une normalisation classique correspond à calculer $$x_{norm}=\dfrac{x-m}{x_{max}-x_{min}}$$
On considère le nombre $POS$ tel que $x\in [m+POS\sigma ; m+(POS+1)\sigma]$ où $\sigma$ est l'écart-type des notes de la classe qui est inconnue. Cette position permet de situer le niveau de l'étudiant dans sa classe.
On cherche à observer un lien entre $x_{norm}$ (connue) et $POS$ (inconnue).
La simulation suivante permet d'observer qu'il existe une modélisation linéaire fiable (de corrélation très forte, ie supérieur à $95\%$) :
$$POS = a x_{norm}+b$$
A cette fin :
taille_classe=randint(20, 30)
.m=Normale(10, 5)
(un nombre réel au hasard autour de $10$ à plus ou moins $5$ points près).s=Normale(7, 5)
(un nombre réel au hasard autour de $7$ à plus ou moins $5$ points près).taille_classe
notes suivants ces paramètres. On calcule moyenne, note la plus haute et la plus plus basse, la position et la note normalisée
nb_classe
(grand, ie $10000$).
nb_classe=10000
NORM=[]
POS=[]
for k in range(nb_classe) :
#Pour patienter
print("\rCalcul en cours : "+str(round((k+1)/nb_classe*100, 3))+"%", end=' ')
#La taille de cette classe
taille_classe=randint(20,30)
#Les notes de ce groupe
m=Normale(10, 5)#Moyenne de cette classe
while(m<0) : m=Normale(10, 5)
s=Normale(7, 5)#Ecartype de cette classe
while(s<0) : s=Normale(7, 5)
N=[min(20, max(0, Normale(m, s))) for k in range(taille_classe)]#Simulation
#Donnée de ce groupe
Nmax=max(N)
Nmin=min(N)
Nmoy=moyenne(N)#Moyenne réelle
Nect=ecartype(N)#Ecartype réelle
#Tirage d'un étudiant
etu=int(taille_classe*random())
#Note de l'étudiant
note=N[etu]
#Position de l'étudiant
pos=-499
while(Nmoy+pos/100*Nect<note and pos<500) : pos+=1
POS.append((pos+1)/100)
#Notes normalisée
if(Nmax!=Nmin) : NORM.append((note-Nmoy)/(Nmax-Nmin))
else : NORM.append(0)
a=achapeau(NORM, POS)
b=bchapeau(NORM, POS)
r2=RCarre(NORM, POS)
plot(NORM, POS, 'b.')
plot([-5, 5], [-5*a+b, 5*a+b], 'r')
xlim(min(NORM), max(NORM))
xlabel("Notes normalisées")
ylim(min(POS), max(POS))
ylabel("Positions")
show()
print("Corrélation du modèle =", round(sqrt(r2)*100, 3), "%")
print("Estimation de l'erreur (écart moyen entre rouge et bleue) =", round(etErreur(NORM, POS), 5))
print("a =", a)
print("b =", b)
Pous estimer la position réel de l'élève dans sa classe, connaissant $m$, $x_{min}$ et $x_{max}$, on fait
$$\hat{POS}=3.11\dfrac{x-m}{x_{max}-x_{min}}+0.01$$
De plus, si on ne dispose pas d'une valeur, il parait raisonnable de prendre $\hat{POS}=0$.
Dans notre modèle la position est un nombre entre $-5$ et $5$. Pour revenir à des notes (entre $0$ et $20$), on fait
$$\hat{N}=10+2\hat{POS}$$
Pour s'en convaincre, refaisons encore la même simlation
nb_classe=10000
NORM=[]
POS=[]
for k in range(nb_classe) :
#Pour patienter
print("\rCalcul en cours : "+str(round((k+1)/nb_classe*100, 3))+"%", end=' ')
#La taille de cette classe
taille_classe=randint(20,30)
#Les notes de ce groupe
m=Normale(10, 5)#Moyenne de cette classe
while(m<0) : m=Normale(10, 5)
s=Normale(7, 5)#Ecartype de cette classe
while(s<0) : s=Normale(7, 5)
N=[min(20, max(0, Normale(m, s))) for k in range(taille_classe)]#Simulation
#Donnée de ce groupe
Nmax=max(N)
Nmin=min(N)
Nmoy=moyenne(N)#Moyenne réelle
Nect=ecartype(N)#Ecartype réelle
#Tirage d'un étudiant
etu=int(taille_classe*random())
#Note de l'étudiant
note=N[etu]
#Position de l'étudiant
pos=-499
while(Nmoy+pos/100*Nect<note and pos<500) : pos+=1
POS.append(10+2*(pos+1)/100) #<-------------------------------------------- ICI on modifie
#Notes normalisée
if(Nmax!=Nmin) : NORM.append((note-Nmoy)/(Nmax-Nmin))
else : NORM.append(0)
a=achapeau(NORM, POS)
b=bchapeau(NORM, POS)
r2=RCarre(NORM, POS)
plot(NORM, POS, 'b.')
plot([-5, 5], [-5*a+b, 5*a+b], 'r')
xlim(min(NORM), max(NORM))
xlabel("Positions réelles")
ylim(min(POS), max(POS))
ylabel("Notes ajustées")
show()
print("Corrélation du modèle =", round(sqrt(r2)*100, 3), "%")
print("Estimation de l'erreur (écart moyen entre rouge et bleue) =", round(etErreur(NORM, POS), 5))
print("a =", a)
print("b =", b)
from random import random, randint
from math import *
from matplotlib.pyplot import *
###########################################
# MES OUTILS DE PROBA-STATS #
###########################################
def Normale(m, s) :
"""
Tire un nombre réel au hasard suivant une loi normale de paramètre m (moyenne) et s (ecartype)
"""
if(s<=0) : s=1
if(m==0 and s==1) : return sqrt(-2*log(random()))*cos(2*pi*random())
return s*Normale(0, 1)+m
def moyenne(x) :
"""
Moyenne des éléments de la liste passée en paramètre
Cas d'erreur : None
"""
try :
n=len(x)
test=True
for i in range(n) :
test=(test and isinstance(x[i], (int, float)))
except : return None
if(n==0 or not(test)) : return None
res=0
for i in range(n) : res+=x[i]
return res/n
def covariance(x, y) :
"""
Covaraiance des éléments des listes passées en paramètre
Cas d'erreur : None
"""
try :
n=len(x)
m=len(y)
test=True
for i in range(min(n, m)) :
test=(test and isinstance(x[i], (int, float)) and isinstance(y[i], (int, float)))
except : return None
if(n==0 or not(test) or n!=m) : return None
xy=[]
for i in range(n) : xy.append(x[i]*y[i])
mx=moyenne(x)
my=moyenne(y)
mxy=moyenne(xy)
if(mx==None or my==None or mxy==None) : return None
return mxy-mx*my
def variance(x) :
"""
Variance des éléments de la liste passée en paramètre
Cas d'erreur : None
"""
return covariance(x, x)
def ecartype(x) :
"""
Ecartype des éléments de la liste passée en paramètre
Cas d'erreur : None
"""
v=variance(x)
if(v==None or v<0) : return None
return sqrt(v)
def achapeau(x, y) :
"""
Renvoie l'estimation de a dans le modèle linéaire y=ax+b
Cas d'erreur : None
"""
c=covariance(x, y)
v=variance(x)
if(c==None or v==None or v==0) : return None
return c/v
def bchapeau(x, y) :
"""
Renvoie l'estimation de b dans le modèle linéaire y=ax+b
Cas d'erreur : None
"""
a=achapeau(x, y)
mx=moyenne(x)
my=moyenne(y)
if(a==None or mx==None or my==None) : return None
return my-a*mx
def ychapeau(x, y) :
"""
Liste des estimés par le modèle linéaire y=ax+b
Cas d'erreur : None
"""
try :
n=len(x)
m=len(y)
test=True
for i in range(n) :
test=(test and isinstance(x[i], (int, float)))
except : return None
if(not(test)) : return None
a=achapeau(x, y)
b=bchapeau(x, y)
if(m!=n or n==0 or a==None or b==None) : return None
res=[]
for i in range(n) : res.append(a*x[i]+b)
return res
def residu(x, y) :
"""
Liste des résidus (y-ychapeau) par le modèle linéaire y=ax+b
Cas d'erreur : None
"""
try :
n=len(x)
m=len(y)
test=True
for i in range(n) :
test=(test and isinstance(x[i], (int, float)))
except : return None
if(not(test)) : return None
yc=ychapeau(x, y)
if(m!=n or n==0 or yc==None) : return None
res=[]
for i in range(n) : res.append(y[i]-yc[i])
return res
def RCarre(x, y) :
"""
R² (carré de la corrélation linéaire multiple) du modèle y=ax+b
Cas d'erreur None
"""
try :
n=len(x)
m=len(y)
test=True
for i in range(n) :
test=(test and isinstance(x[i], (int, float)))
except : return None
if(not(test)) : return None
yc=ychapeau(x, y)
my=moyenne(y)
if(m!=n or n==0 or yc==None or my==None) : return None
num=0
for i in range(n) : num+=((yc[i]-my)**2)
den=0
for i in range(n) : den+=((y[i]-my)**2)
if(den==0) : return None
return num/den
def etErreur(x, y) :
"""
Estimation de l'erreur y=ax+b
Précisément pour le modèle y=ax+b+e ou e~N(0, s), la fonction renvoie une estimation de s
Cas d'erreur : None
"""
try :
n=len(x)
m=len(y)
except : return None
r=residu(x, y)
if(m!=n or n<2 or r==None) : return None
res=0
for i in range(n) : res+=(r[i]**2)
return sqrt(res/(n-2))