# TP 24 (fichiers à compléter) : Statistiques
# BCPST1B 2025-2026
# Lycée Hoche, Versailles
# L.-C. LEFÈVRE

#%% exécuter une fois pour toutes

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

tips = pd.read_csv("tips.csv")
print(tips.info())

#### Partie I

#%% affichage
print(tips)

#%% une colonne seule
print(tips["total_bill"])

#%% une autre
print(tips["smoker"])

#%% toutes les informations
print(tips.info())

#%% seulement l'entrée 0
print(tips.loc[0])

#%% … et seulement sa facture totale
print(tips["total_bill"].loc[0])

#%% filtrer
print(tips[tips["day"] == "Sat"])

#%% conversion en liste habituelle
print(tips["total_bill"].to_list())

#### Partie II
# fonctions à écrire

#%% exercice 2.1

def moyenne(L):
    n = len(L)
    S = 0
    for i in range(n):
        S = S + L[i]
    return S / n

# test
print(moyenne(tips["total_bill"].to_list()))

#%% exercice 2.2

def maximum(L):
    M = L[0]
    for i in range(1, len(L)):
        if L[i] > M:
            M = L[i]
    return M

# test
print(maximum(tips["total_bill"].to_list()))

#%% exercice 2.3

def minimum(L):
    m = L[0]
    for i in range(1, len(L)):
        if L[i] < m:
            m = L[i]
    return m

# test
print(minimum(tips["total_bill"].to_list()))

#%% exercice 4

def variance(L):
    n = len(L)
    S = 0
    SS = 0
    for i in range(n):
        S = S + L[i]
        SS = SS + L[i]**2
    # appliquer K-H
    return (SS/n) - (S/n)**2

def ecarttype(L):
    return np.sqrt(variance(L))

# test
print(variance(tips["total_bill"].to_list()))
print(ecarttype(tips["total_bill"].to_list()))

#### Partie III

#%% exercice 5.1

def regroupe(L):
    M = int(maximum(L)) + 1
    C = [0] * M
    for i in range(len(L)):
        C[int(L[i])] += 1
    return C

# test
L = tips["total_bill"].to_list()
C = regroupe(L)
print(C)

#%% exercice 5.2

def regroupe_dixième(L):
    M = (int(maximum(L)) + 1) * 10
    C = [0] * M
    for i in range(len(L)):
        C[int(L[i]*10)] += 1
    return C

# test
L = tips["total_bill"].to_list()
C = regroupe_dixième(L)
print(C)

#%% exercice 6.1

def cumuls(C):
    n = len(C)
    D = [0] * n
    D[0] = C[0]
    for i in range(1, n):
        D[i] = D[i-1] + C[i]
    return D

# test
L = tips["total_bill"].to_list()
C = regroupe(L)
print(cumuls(C))

#%% exercice 6.2

def mediane(C):
    D = cumuls(C)
    N = D[-1]
    i = 0
    while D[i] < N/2:
        i = i + 1
    return i

# test
L = tips["total_bill"].to_list()
C = regroupe(L)
print(mediane(C))

#%% médiane pour les données regroupées au dixième
L = tips["total_bill"].to_list()
C = regroupe_dixième(L)
m = mediane(C) / 10
print(m)

#%% exercice 7.1

# adaptation immédiate de la médiane
def quantile(C, p):
    D = cumuls(C)
    N = D[-1]
    i = 0
    while D[i] < N*p:
        i = i + 1
    return i

def quartile1(C):
    return quantile(C, 1/4)

def quartile3(C):
    return quantile(C, 3/4)

def interquartile(C):
    return quartile3(C) - quartile1(C)

# test
L = tips["total_bill"].to_list()
C = regroupe(L)
print(quartile1(C))
print(quartile3(C))
print(interquartile(C))

#%% regroupés au dixième

L = tips["total_bill"].to_list()
C = regroupe_dixième(L)
print(quartile1(C)/10)
print(quartile3(C)/10)
print(interquartile(C)/10)

#%% variante : sans la fonction cumul

def quantile(C, p):
    # compter l'effectif total
    N = 0
    for i in range(len(C)):
        N = N + C[i]
    # re-compter du début, s'arrêter à la fraction N*p
    i = 0
    S = 0
    while S < N*p:
        S = S + C[i]
        i = i + 1
    # ici : on vient de dépasser N*p, mais i est un cran trop grand
    return i - 1

L = tips["total_bill"].to_list()
C = regroupe(L)
print(quantile(C, 1/4))
print(quantile(C, 1/2))
print(quantile(C, 3/4))

#### Partie IV
# Représentations graphiques : exécuter simplement les cellules et observer.
# On fabrique d'abord le bon tableau, puis on le trace.

#%% diagramme en barres : effectif
t = tips.groupby("day").size()
print(t)
t.plot.bar()
plt.show()

#%%
# groupés, barres côtes à côte
t = pd.crosstab(tips["day"], tips["sex"])
print(t)
t.plot.bar()
plt.show()

#%%
# groupés, barres empilées
t = pd.crosstab(tips["day"], tips["sex"])
print(t)
t.plot.bar(stacked=True)
plt.show()

#%% diagramme en camembert
t = tips.groupby("day").size()
print(t)
t.plot.pie()
plt.show()

#%%
# groupés
t = pd.crosstab(tips["sex"], tips["day"])
print(t)
t.plot.pie(subplots=True)
plt.show()

#%% boite à moustaches
tips.boxplot(column="total_bill", grid=False, vert=False)
plt.show()

#%%
# groupés
tips.boxplot(column="total_bill", by="day", grid=False, vert=False)
plt.show()

#%% histogramme
tips.plot.hist(column="total_bill")
plt.show()

#%%
# varier la finesse
tips.plot.hist(column="total_bill", bins=5)
plt.show()

#%%
# au contraire
tips.plot.hist(column="total_bill", bins=20)
plt.show()

#%%
# version cumulée
tips.plot.hist(column="total_bill", cumulative=True)
plt.show()

#%%
# groupés
tips.plot.hist(column="total_bill", by="sex", subplots=True)
plt.show()

#%% nuage de points
tips.plot.scatter(x="total_bill", y="tip")
plt.show()

#%%
# filtrer d'abord
tips[tips["sex"] == "Male"].plot.scatter(x="total_bill", y="tip")
plt.show()

#### Partie V

#%% exercice 10

# même méthode que pour variance avec K-H
def covariance(X, Y):
    n = len(X)
    assert len(Y) == n, "Les deux listes doivent être de même longueur."
    SX = 0
    SY = 0
    SXY = 0
    for i in range(n):
        SX = SX + X[i]
        SY = SY + Y[i]
        SXY = SXY + X[i] * Y[i]
    # appliquer K-H
    return (SXY/n) - (SX/n) * (SY/n)

def r2(X, Y):
    return covariance(X, Y)**2 / (variance(X) * variance(Y))

def r(X, Y):
    return covariance(X, Y) / (ecarttype(X) * ecarttype(Y))

# test
X = tips["total_bill"].to_list()
Y = tips["tip"].to_list()
print(covariance(X, Y))
print(r2(X, Y))
print(r(X, Y))

#%% exercice 11

# le nuage de points
tips.plot.scatter(x="total_bill", y="tip")
# le point moyen
xbar = moyenne(tips["total_bill"].to_list())
ybar = moyenne(tips["tip"].to_list())
plt.plot(xbar, ybar, marker="+", color="red", markersize=20, markeredgewidth=3)
# la droite de régression linéaire :
# a s'interpète comme le taux moyen de pourboire par rapport à la facture
a = covariance(tips["total_bill"].to_list(), tips["tip"].to_list()) / variance(tips["total_bill"].to_list())
print("a = ", a)
X = np.linspace(0, 50, 100)
Y = a * (X - xbar) + ybar
plt.plot(X, Y, color="green")
# afficher
plt.title("Pourboire en fonction de la facture totale")
plt.show()

#%% tout reprendre en filtrant, uniquement les hommes

tips[tips["sex"] == "Male"].plot.scatter(x="total_bill", y="tip")
xbar = moyenne(tips[tips["sex"] == "Male"]["total_bill"].to_list())
ybar = moyenne(tips[tips["sex"] == "Male"]["tip"].to_list())
plt.plot(xbar, ybar, marker="+", color="red", markersize=20, markeredgewidth=5)
a = covariance(tips[tips["sex"] == "Male"]["total_bill"].to_list(), tips[tips["sex"] == "Male"]["tip"].to_list()) / variance(tips[tips["sex"] == "Male"]["total_bill"].to_list())
print("a = ", a)
X = np.linspace(0, 50, 100)
Y = a * (X - xbar) + ybar
plt.plot(X, Y, color="green")
plt.title("Pourboire des hommes en fonction de la facture totale")
plt.show()

#%% idem pour les femmes

tips[tips["sex"] == "Female"].plot.scatter(x="total_bill", y="tip")
xbar = moyenne(tips[tips["sex"] == "Female"]["total_bill"].to_list())
ybar = moyenne(tips[tips["sex"] == "Female"]["tip"].to_list())
plt.plot(xbar, ybar, marker="+", color="red", markersize=20, markeredgewidth=5)
a = covariance(tips[tips["sex"] == "Female"]["total_bill"].to_list(), tips[tips["sex"] == "Female"]["tip"].to_list()) / variance(tips[tips["sex"] == "Female"]["total_bill"].to_list())
print("a = ", a)
X = np.linspace(0, 50, 100)
Y = a * (X - xbar) + ybar
plt.plot(X, Y, color="green")
plt.title("Pourboire des femmes en fonction de la facture totale")
plt.show()
