117 lines
3.5 KiB
Python
117 lines
3.5 KiB
Python
import pandas as pd
|
|
import numpy as np
|
|
import matplotlib.pyplot as plt
|
|
from mpl_toolkits.mplot3d import Axes3D
|
|
from scipy.interpolate import make_interp_spline, BSpline
|
|
|
|
# === Chargement des données ===
|
|
chemin_fichier = "data.xlsx"
|
|
df = pd.read_excel(chemin_fichier)
|
|
|
|
# === Filtrer les sources ===
|
|
df = df[df["type"].str.lower() == "source"]
|
|
df = df.dropna(subset=["c25°C ", "Nom"])
|
|
|
|
# === Couleurs par source ===
|
|
couleurs_sources = {
|
|
"Bullac": "#D77D00",
|
|
"Corn": "#A1C935",
|
|
"Bual": "#F5D200",
|
|
"La diège": "#9271EA",
|
|
"Ayrissac": "#EB64C3",
|
|
"Pito": "#D52117",
|
|
"Ressel": "#1DC6C3",
|
|
"Marchepied": "#3FB94D",
|
|
"Anglades": "#0084C9",
|
|
"Liauzu": "#431D84",
|
|
"Pescalerie": "#D8676B",
|
|
"Sagne": "tab:purple",
|
|
}
|
|
|
|
# === Calcul des fréquences normalisées ===
|
|
freq_absolue = df.groupby(["Nom", "c25°C "]).size().reset_index(name="frequence")
|
|
freq_totale = freq_absolue.groupby("Nom")["frequence"].transform("sum")
|
|
freq_absolue["frequence_normalisee"] = freq_absolue["frequence"] / freq_totale
|
|
|
|
# === Sources à afficher dans l'ordre souhaité ===
|
|
noms_ordonnes = [
|
|
"Sagne", "Ressel", "Pito", "Pescalerie", "Marchepied",
|
|
"Liauzu", "La diège", "Corn", "Bullac", "Bual", "Ayrissac", "Anglades"
|
|
]
|
|
sources_disponibles = set(freq_absolue["Nom"].unique())
|
|
noms_valides = [nom for nom in noms_ordonnes if nom in sources_disponibles]
|
|
|
|
# === Graphique 3D ===
|
|
fig = plt.figure(figsize=(20, 12))
|
|
ax = fig.add_subplot(111, projection='3d')
|
|
|
|
y_spacing = 4.0
|
|
|
|
for idx, nom in enumerate(reversed(noms_valides)):
|
|
df_nom = freq_absolue[freq_absolue["Nom"] == nom].sort_values("c25°C ")
|
|
if df_nom.empty:
|
|
continue
|
|
|
|
xs = df_nom["c25°C "].to_numpy()
|
|
ys = df_nom["frequence_normalisee"].to_numpy()
|
|
color = couleurs_sources.get(nom, "gray")
|
|
y_val = (len(noms_valides) - 1 - idx) * y_spacing
|
|
|
|
# === Courbe type histogramme ===
|
|
x_curve = []
|
|
y_curve = []
|
|
|
|
for i in range(len(xs)):
|
|
x_curve.extend([xs[i] - 0.5, xs[i], xs[i] + 0.5])
|
|
y_curve.extend([0, ys[i], 0])
|
|
|
|
# Relier à la suivante si proche
|
|
if i < len(xs) - 1 and xs[i + 1] - xs[i] <= 1.5:
|
|
x_curve.extend([xs[i] + 0.5, xs[i + 1] - 0.5])
|
|
y_curve.extend([0, 0])
|
|
|
|
# X_Y_Spline = make_interp_spline(x_curve, y_curve)
|
|
# X_ = np.linspace(x_curve.min(), x_curve.max(), 500)
|
|
# Y_ = X_Y_Spline(X_)
|
|
|
|
# ax.plot(X_, [y_val] * len(Y_), Y_, color=color, linewidth=2, alpha=0.8)
|
|
|
|
res = {key: value for key, value in zip(x_curve, y_curve)}
|
|
x_curve = list(res.keys())
|
|
y_curve = list(res.values())
|
|
|
|
# Interpolation pour lisser la courbe
|
|
spline = make_interp_spline(x_curve, y_curve, k=1)
|
|
x_curve = np.linspace(min(x_curve), max(x_curve), 100)
|
|
y_curve = spline(x_curve)
|
|
|
|
|
|
ax.plot(x_curve, [y_val] * len(x_curve), y_curve, color=color, linewidth=2, alpha=0.8)
|
|
|
|
# Nom de la source à gauche
|
|
ax.text(
|
|
x=freq_absolue["c25°C "].min() - 30,
|
|
y=y_val,
|
|
z=0,
|
|
s=nom,
|
|
color=color,
|
|
fontsize=10,
|
|
ha='right',
|
|
va='center'
|
|
)
|
|
|
|
# === Configuration du graphique ===
|
|
ax.set_yticks([])
|
|
ax.set_xlabel("Conductivité (c25°C, µS/cm)", labelpad=15)
|
|
ax.set_ylabel("")
|
|
ax.set_zlabel("Fréquence normalisée", labelpad=10)
|
|
ax.set_title("Courbes 3D type histogrammes par source", pad=20)
|
|
|
|
ax.set_xlim(freq_absolue["c25°C "].min() - 35, freq_absolue["c25°C "].max() + 10)
|
|
ax.set_ylim(-y_spacing, len(noms_valides) * y_spacing)
|
|
ax.set_zlim(0, 0.4)
|
|
ax.view_init(elev=25, azim=-140)
|
|
# ax.autoscale()
|
|
plt.tight_layout()
|
|
plt.show()
|