WeightedDataset have been created
This commit is contained in:
parent
1c52a26328
commit
4153f97679
@ -2,7 +2,7 @@
|
||||
# encoding: utf-8
|
||||
|
||||
from .calculus import Expression, Polynom, Fraction, random_str
|
||||
from .stat import Dataset
|
||||
from .stat import Dataset, WeightedDataset
|
||||
|
||||
# -----------------------------
|
||||
# Reglages pour 'vim'
|
||||
|
@ -2,6 +2,7 @@
|
||||
# encoding: utf-8
|
||||
|
||||
from .dataset import Dataset
|
||||
from .weightedDataset import WeightedDataset
|
||||
|
||||
# -----------------------------
|
||||
# Reglages pour 'vim'
|
||||
|
@ -7,95 +7,79 @@
|
||||
#
|
||||
#
|
||||
|
||||
from math import sqrt, cos, ceil
|
||||
from math import sqrt, ceil
|
||||
from collections import Counter
|
||||
from .dataset import Dataset
|
||||
from ..calculus.generic import flatten_list
|
||||
|
||||
class Serie():
|
||||
""" Classe réprésentant un série statistique avec rendu latex """
|
||||
|
||||
def __init__(self, valeurs = None, val_name = "Valeurs", effectifs = None, eff_name = "Effectifs", random = 0):
|
||||
class WeightedDataset(dict):
|
||||
""" A weighted dataset with statistics and latex rendering methods
|
||||
|
||||
>>> w = WeightedDataset([1, 2, 3, 4], "Enfants", [10, 11, 12, 13])
|
||||
>>> print(w)
|
||||
{1: 10, 2: 11, 3: 12, 4: 13}
|
||||
>>> w.effectif_total()
|
||||
46
|
||||
>>> w.sum()
|
||||
120
|
||||
>>> w.mean()
|
||||
2.608695652173913
|
||||
>>> w.deviation()
|
||||
56.95652173913044
|
||||
>>> w.variance()
|
||||
1.2381852551984878
|
||||
>>> w.sd()
|
||||
1.1127377297451937
|
||||
|
||||
"""
|
||||
Initialisation de la série statistique
|
||||
Les paramètres sont optionnels ils pourront être ajouté après la création de la série
|
||||
|
||||
:param valeurs: valeurs de la série statistique
|
||||
:param val_name: nom pour les "valeurs"
|
||||
:param effectifs: effectifs de chaque
|
||||
:param eff_name: nom pour les "effectifs"
|
||||
:param random: taille de série si elle doit être initialisé aléatoirement (uniformement sur [0,1]
|
||||
def __init__(self, datas = [], data_name = "Valeurs", weights = [], weight_name = "Effectifs"):
|
||||
"""
|
||||
self.set_serie = False
|
||||
if valeurs:
|
||||
self.set_values(valeurs, effectifs)
|
||||
|
||||
self.val_name = val_name
|
||||
self.eff_name = eff_name
|
||||
|
||||
def set_values(self, valeurs, effectifs = None):
|
||||
Initiate the WeightedDataset
|
||||
"""
|
||||
Effecte les valeurs de la série statistique
|
||||
/!\ mettre les valeurs dans l'ordre croissant si elles sont pondérées!!
|
||||
|
||||
:param valeurs: valeurs de la série statistique
|
||||
:param effectifs: effectifs de chaque
|
||||
"""
|
||||
if not effectifs:
|
||||
effectifs = [1]*len(valeurs)
|
||||
valeurs.sort()
|
||||
|
||||
if len(valeurs) != len(effectifs):
|
||||
raise ValueError("Valeurs et effectifs ne sont pas de la même longueur")
|
||||
|
||||
dict_tmp = [(v,effectifs[i]) for (i,v) in enumerate(valeurs)]
|
||||
|
||||
self.serie = dict(dict_tmp)
|
||||
self.valeurs = valeurs
|
||||
self.effectifs = effectifs
|
||||
self.effectif_total = sum(self.effectifs)
|
||||
|
||||
# On classe les données dans un dictionnaire
|
||||
# Il faut plutot utiliser un dictionnaire car il trie automatiquement dans l'ordre croissant les clés.
|
||||
self.serie = {}
|
||||
for (i,v) in enumerate(self.valeurs):
|
||||
if v in self.serie.keys():
|
||||
self.serie[v] += self.effectifs[i]
|
||||
if datas and not weights:
|
||||
weightedDatas = Counter(datas)
|
||||
elif datas and weights:
|
||||
if len(datas) != len(weights):
|
||||
raise ValueError("Datas and weights should have same length")
|
||||
else:
|
||||
self.serie[v] = self.effectifs[i]
|
||||
weightedDatas = {i[0]:i[1] for i in zip(datas, weights)}
|
||||
|
||||
# On garde une forme (valeur, effectif) pour pouvoir trier les éléments car les dictionnaires ne le permettent pas)
|
||||
self.serieCouple = [(v,e) for (v,e) in self.serie.items()]
|
||||
self.serieCouple.sort(key = lambda s:s[0])
|
||||
dict.__init__(self, weightedDatas)
|
||||
|
||||
self.set_serie = True
|
||||
self.data_name = data_name
|
||||
self.weight_name = weight_name
|
||||
|
||||
def moyenne(self):
|
||||
"""
|
||||
Calcul la moyenne des valeurs pondérées par les effectifs (s'ils ont été passé en arguments)
|
||||
def add_data(self, data, weight = 1):
|
||||
try:
|
||||
self[data] += weight
|
||||
except KeyError:
|
||||
self[data] = weight
|
||||
|
||||
:return: renvoie la moyenne.
|
||||
"""
|
||||
if self.set_serie:
|
||||
val_eff = [i*self.serie[i] for i in self.serie]
|
||||
return sum(val_eff) / self.effectif_total
|
||||
else:
|
||||
raise ValueError("La série statistique n'a pas été rentrée")
|
||||
def total_weight(self):
|
||||
return sum(self.values())
|
||||
|
||||
def effectif_total(self):
|
||||
return self.total_weight()
|
||||
|
||||
def sum(self):
|
||||
""" Not really a sum but the sum of the product of key and values """
|
||||
return sum([k*v for (k,v) in self.items()])
|
||||
|
||||
def mean(self):
|
||||
return self.sum()/self.effectif_total()
|
||||
|
||||
def deviation(self):
|
||||
""" Compute the deviation (not normalized) """
|
||||
mean = self.mean()
|
||||
return sum([v*(k - mean)**2 for (k,v) in self.items()])
|
||||
|
||||
def variance(self):
|
||||
"""
|
||||
Calcul la variance des valeurs pondérées par les effectifs (s'ils ont été passé en arguments)
|
||||
return self.deviation()/self.effectif_total()
|
||||
|
||||
:return: renvoie la variance
|
||||
"""
|
||||
moy = self.moyenne()
|
||||
ecart = [self.serie[v] * (v - moy)**2 for v in self.serie.keys()]
|
||||
|
||||
return sum(ecart) / self.effectif_total
|
||||
|
||||
def ecart_type(self):
|
||||
"""
|
||||
Calcul l'écart type des valeurs pondérées par les effectifs (s'ils ont été passé en arguments)
|
||||
|
||||
:return: renvoie l'écart-type
|
||||
"""
|
||||
def sd(self):
|
||||
""" Compute the standard deviation """
|
||||
return sqrt(self.variance())
|
||||
|
||||
def quartiles(self):
|
||||
@ -103,8 +87,18 @@ class Serie():
|
||||
Calcul les quartiles de la série.
|
||||
|
||||
:return: un tuple avec (min, Q1, Me, Q3, Max)
|
||||
|
||||
: Exemple:
|
||||
|
||||
>>> w = WeightedDataset(flatten_list([i*[i] for i in range(5)]))
|
||||
>>> w.quartiles()
|
||||
(1, 2, 3.0, 4, 4)
|
||||
>>> w = WeightedDataset(flatten_list([i*[i] for i in range(6)]))
|
||||
>>> w.quartiles()
|
||||
(1, 3, 4, 5, 5)
|
||||
|
||||
"""
|
||||
return (min(self.serie) , self.quartile(1) , self.quartile(2) , self.quartile(3), max(self.serie))
|
||||
return (min(self.keys()) , self.quartile(1) , self.quartile(2) , self.quartile(3), max(self.keys()))
|
||||
|
||||
def quartile(self, quartile = 1):
|
||||
"""
|
||||
@ -116,21 +110,29 @@ class Serie():
|
||||
|
||||
: Example:
|
||||
|
||||
>>> s = Serie()
|
||||
>>>
|
||||
>>> w = WeightedDataset(flatten_list([i*[i] for i in range(5)]))
|
||||
>>> w.quartile(1)
|
||||
2
|
||||
>>> w.quartile(2)
|
||||
3.0
|
||||
>>> w.quartile(3)
|
||||
4
|
||||
>>> w = WeightedDataset(flatten_list([i*[i] for i in range(6)]))
|
||||
>>> w.quartile(1)
|
||||
3
|
||||
>>> w.quartile(2)
|
||||
4
|
||||
>>> w.quartile(3)
|
||||
5
|
||||
|
||||
"""
|
||||
position = self.posi_quartile(quartile)[0]
|
||||
|
||||
conteur = 0
|
||||
# on utilise la forme avec les couples pour avoir des valeurs classées.
|
||||
for (val , eff) in self.serieCouple:
|
||||
conteur += eff
|
||||
if conteur >= position:
|
||||
val_quartile = val
|
||||
break
|
||||
|
||||
return val_quartile
|
||||
# -1 to match with list indexing
|
||||
position = self.posi_quartile(quartile) - 1
|
||||
expanded_values = flatten_list([v*[k] for (k,v) in self.items()])
|
||||
if position.is_integer():
|
||||
return (expanded_values[int(position)] + expanded_values[int(position)+1])/2
|
||||
else:
|
||||
return expanded_values[ceil(position)]
|
||||
|
||||
def posi_quartile(self, quartile = 1):
|
||||
"""
|
||||
@ -140,121 +142,12 @@ class Serie():
|
||||
|
||||
:return : la position du quartile (arondis à l'entier suppérieur, non arrondis)
|
||||
"""
|
||||
return (ceil(quartile * self.effectif_total / 4), (quartile * self.effectif_total / 4))
|
||||
return quartile * self.effectif_total() / 4
|
||||
|
||||
|
||||
# --------------------------
|
||||
# Rendu latex
|
||||
|
||||
def moyenne_latex(self, val_cara = "x", eff_cara = "n", end_cara = "p", moy_cara= "\\bar{x}"):
|
||||
"""
|
||||
Renvoie le code latex pour afficher le calcul de la moyenne
|
||||
|
||||
:param val_cara: caractère représentant les valeurs (x par défaut)
|
||||
:param eff_cara: caractère représentant les effectifs (n par défaut)
|
||||
:param end_cara: caractère représentant le nombre de valeurs (p par défaut)
|
||||
:param moy_cara: nom de la moyenne (\\bar{x} par défaut)
|
||||
|
||||
:return: le code latex pour afficher le calcul de la moyenne
|
||||
"""
|
||||
moy = self.moyenne()
|
||||
latex = moy_cara + " &=& "
|
||||
latex += "\\frac{{{x}_1 \\times {n}_1 + {x}_2 \\times {n}_2 + ... + {x}_{p} \\times {n}_{p}}}{{{n}_1 + {n}_2 + ... + {n}_p}} \\\\ \n".format(x = val_cara, n=eff_cara, p = end_cara)
|
||||
latex += "\t &=& \\frac{"
|
||||
for (i,v) in enumerate(self.valeurs):
|
||||
latex += "{x:.2f} \\times {p:.2f} + ".format(x = v, p = self.effectifs[i])
|
||||
latex = latex[:-2] # on enlève le + et l'espace de fin de calcul
|
||||
latex += "}}{{{eff_tot}}}\\\\ \n".format(eff_tot = self.effectif_total)
|
||||
latex += "\t &=& {moy:.2f}".format(moy=moy)
|
||||
|
||||
return latex
|
||||
|
||||
def variance_latex(self, val_cara = "x", eff_cara = "n", end_cara = "p", moy_cara= "\\bar{x}", var_cara = "V"):
|
||||
"""
|
||||
Renvoie le code latex pour afficher le calcul de la moyenne
|
||||
|
||||
:param val_cara: caractère représentant les valeurs (x par défaut)
|
||||
:param eff_cara: caractère représentant les effectifs (n par défaut)
|
||||
:param end_cara: caractère représentant le nombre de valeurs (p par défaut)
|
||||
:param moy_cara: nom de la moyenne (\\bar{x} par défaut)
|
||||
:param var_var: nom de la variance (V par défaut)
|
||||
|
||||
:return: le code latex pour afficher le calcul de la moyenne
|
||||
"""
|
||||
moy = self.moyenne()
|
||||
var = self.variance()
|
||||
|
||||
latex = var_cara + " &=& "
|
||||
latex += "\\frac{{ {n}_1 ({x}_1 - {moy})^2 + {n}_2 ({x}_2 - {moy})^2 + ... + {n}_{p} ({x}_{p} - {moy})^2}}{{{n}_1 + {n}_2 + ... + {n}_p}}\\\\ \n".format(x = val_cara, n=eff_cara, p = end_cara, moy = moy_cara)
|
||||
latex += "\t &=& \\frac{ "
|
||||
for (i,v) in enumerate(self.valeurs):
|
||||
latex += "{p:.2f} ({x:.2f} - {moy:.2f})**2 + ".format(p = self.effectifs[i], x = v, moy = moy)
|
||||
latex = latex[:-2] # on enlève le + et l'espace de fin de calcul
|
||||
latex += "}}{{{eff_tot}}}\\\\ \n".format(eff_tot = self.effectif_total)
|
||||
latex += "\t &=& {var:.2f}".format(var = var)
|
||||
|
||||
return latex
|
||||
|
||||
def ecart_type_latex(self, val_cara = "x", eff_cara = "n", end_cara = "p", moy_cara= "\\bar{x}", var_cara = "V", ecar_cara = "\\sigma"):
|
||||
"""
|
||||
Renvoie le code latex pour afficher le calcul de la moyenne
|
||||
|
||||
:param val_cara: caractère représentant les valeurs (x par défaut)
|
||||
:param eff_cara: caractère représentant les effectifs (n par défaut)
|
||||
:param end_cara: caractère représentant le nombre de valeurs (p par défaut)
|
||||
:param moy_cara: nom de la moyenne (\\bar{x} par défaut)
|
||||
:param var_var: nom de la variance (V par défaut)
|
||||
:param ecar_var: nom de la variance (V par défaut)
|
||||
|
||||
:return: le code latex pour afficher le calcul de la moyenne
|
||||
"""
|
||||
ecart = self.ecart_type()
|
||||
|
||||
# On récupère le calcul de la variance
|
||||
latex = self.variance_latex(val_cara, eff_cara, end_cara, moy_cara, var_cara)
|
||||
latex += "\n\n"
|
||||
|
||||
# On y ajoute celui de l'écart-type (ya un soucis ici à cause du conflit entre format et {
|
||||
latex += "{ecar_cara} &=& \\sqrt{{{var_cara}}} \\\\ \n".format(ecar_cara = ecar_cara, var_cara = var_cara)
|
||||
latex += "\t &=& {ecart:.2f}".format(ecart = ecart)
|
||||
|
||||
return latex
|
||||
|
||||
def quartiles_latex(self):
|
||||
""" Renvoie le code latex pour afficher le calcul des quartiles
|
||||
|
||||
:return : le code latex pour afficher le calcul des quartiles
|
||||
"""
|
||||
latex = self.quartile_latex()
|
||||
latex += self.quartile_latex(1)
|
||||
latex += self.quartile_latex(3)
|
||||
|
||||
return latex
|
||||
|
||||
def quartile_latex(self, quartile = 2):
|
||||
""" Renvoie le code latex pour afficher le calcul du quartile
|
||||
|
||||
:param quartile: numéro du quartile
|
||||
|
||||
:return : le code latex pour afficher le calcul du quartile
|
||||
"""
|
||||
if quartile == 2 : # médiane
|
||||
quartile_tpl = """ Position de la médiane: $\\dfrac{{\\mbox{{effectif total}}}}{{2}} = \\dfrac{{{eff_tot}}}{{2}} = {posi_q}$. Donc la médiane se trouve à la position: {posi_q_ceil}.
|
||||
|
||||
On a ainsi $Me = {val_q}$
|
||||
"""
|
||||
else:
|
||||
quartile_tpl = """ Position de $Q_{q}$: $\\dfrac{{{q} \\times \\mbox{{effectif total}}}}{{4}} = \\dfrac{{{q} \\times {eff_tot}}}{{4}} = {posi_q}$. Donc $Q_{q}$ se trouve à la position: {posi_q_ceil}.
|
||||
|
||||
On a ainsi $Q_{q} = {val_q}$
|
||||
"""
|
||||
posi_q_ceil , posi_q = self.posi_quartile(quartile)
|
||||
val_q = self.quartile(quartile)
|
||||
|
||||
latex = quartile_tpl.format(eff_tot = self.effectif_total, q = quartile, posi_q = posi_q, posi_q_ceil = posi_q_ceil, val_q = val_q)
|
||||
|
||||
return latex
|
||||
|
||||
def tabular_latex(self):
|
||||
""" Renvoie le code latex pour afficher le tableau
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user