Files
Server-scripts/work/generateAlinaChart.py
T
2026-09-14 12:43:38 +02:00

265 lines
8.3 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import pandas as pd
import matplotlib.pyplot as plt
import os
import socket
def interpolate_daily(df):
df = df.copy()
df["Datum"] = pd.to_datetime(df["Datum"], errors="coerce")
df = df.sort_values("Datum").set_index("Datum")
daily_index = pd.date_range(df.index.min(), df.index.max(), freq="D")
return (
df.reindex(daily_index)
.interpolate(method="time")
.reset_index()
.rename(columns={"index": "Datum"})
)
def plot_percentiles(ax, x, data_dict, p_keys, colors=None, linewidths=None, labels=None,
linestyle=None, fill=True, fill_color="lightcoral", fill_alpha=0.25):
"""
Plot multiple percentile curves from a dictionary like who_head_daily.
Parameters
----------
ax : matplotlib axis
x : array-like (x values)
data_dict : dict-like, e.g. who_head_daily
p_keys : list of keys, e.g. ["P3", "P50", "P97"]
colors : list of colors (optional)
linewidths : list of linewidths (optional)
fill : whether to fill between min and max percentile
:param labels:
"""
n = len(x)
if colors is None:
colors = [None] * len(p_keys)
if linewidths is None:
linewidths = [1.5] * len(p_keys)
# Plot lines
for i, key in enumerate(p_keys):
ax.plot(
x,
data_dict[key][:n],
color=colors[i],
linewidth=linewidths[i],
label=labels[i],
linestyle=linestyle[i]
)
# Fill between min and max percentile
if fill and len(p_keys) >= 2:
lower = data_dict[p_keys[0]][:n]
upper = data_dict[p_keys[-1]][:n]
ax.fill_between(
x,
lower,
upper,
color=fill_color,
alpha=fill_alpha
)
return ax
hostname = socket.gethostname()
if hostname == "quiethost":
# Build output path from ods_path
ods_path = "/data/Nextcloud/Documents/Dokumente/Shared/Alina/Wachstum/WachstumsDatenWHO.ods"
csv_path = "/data/Nextcloud/Documents/Dokumente/Shared/Alina/Wachstum/WachstumAlina.csv"
elif hostname == "serverhost":
print("Serverhost Mode")
import subprocess
subprocess.run(["bash", "/home/lucas/scripts/serverhost/syncNC.sh", "-al"])
ods_path = "/home/lucas/Documents/Dokumente/Shared/Alina/Wachstum/WachstumsDatenWHO.ods"
csv_path = "/home/lucas/Documents/Dokumente/Shared/Alina/Wachstum/WachstumAlina.csv"
output_dir = os.path.dirname(ods_path)
output_path = os.path.join(output_dir, "WachstumAlina.png")
# Load specific sheets into data structures (DataFrames)
alina_df = pd.read_csv(csv_path)
who_weight_df = pd.read_excel(ods_path,sheet_name="WHO Gewicht",engine="odf")
who_height_df = pd.read_excel(ods_path,sheet_name="WHO Größe",engine="odf")
who_head_df = pd.read_excel(ods_path,sheet_name="WHO Kopfumfang",engine="odf")
def calculateDateFromBrithday(df, birthday ):
new_df = pd.DataFrame(columns=["Datum"])
for idx, (val1, val2) in enumerate(zip(df["Month"], df["Week"])):
if not pd.isna(val1):
new_df.loc[idx, "Datum"] = (birthday + pd.DateOffset(months=val1))
if not pd.isna(val2):
new_df.loc[idx, "Datum"] = (birthday + pd.DateOffset(weeks=val2))
return pd.to_datetime(new_df["Datum"], format="%d.%m.%Y", errors="coerce")
alina_df["Datum"] = pd.to_datetime(alina_df["Datum"],format="%d.%m.%Y",errors="coerce")
# Ensure Datum is datetime (safe to run even if already converted)
who_weight_df["Datum"] = calculateDateFromBrithday(who_weight_df,alina_df["Datum"][0] )
who_height_df["Datum"] = calculateDateFromBrithday(who_height_df,alina_df["Datum"][0] )
who_head_df["Datum"] = calculateDateFromBrithday(who_head_df,alina_df["Datum"][0] )
who_weight_daily = interpolate_daily(who_weight_df)
who_height_daily = interpolate_daily(who_height_df)
who_head_daily = interpolate_daily(who_head_df)
print(alina_df["Datum"])
# Plot
last_alina_date = alina_df["Datum"].max() + pd.DateOffset(months=2)
x = who_weight_daily["Datum"][who_weight_daily["Datum"] <= last_alina_date]
fig, axes = plt.subplots(
nrows=3,
ncols=1,
figsize=(12, 10),
sharex=True
)
percentile_list = ['P1', 'P3', 'P10', 'P25',
'P50', 'P75', 'P90', 'P97', 'P99']
colors_list = ["lightcoral" if p != "P50" else "darkred" for p in percentile_list]
linewidths_list = [1.5 if p != "P50" else 2.5 for p in percentile_list]
label_list = [p if p == "P50" or p == "P3" or p == "P97" else "" for p in percentile_list]
linestyle_list = ["--" if p == "P3" or p == "P97" else "-" for p in percentile_list]
# ---------- 1. WEIGHT ----------
ax = axes[0]
plot_percentiles(
ax,
x,
who_weight_daily,
percentile_list,
colors=colors_list,
linewidths=linewidths_list,
labels=label_list,
linestyle=linestyle_list
)
ax.plot(alina_df["Datum"], alina_df["Gewicht"].interpolate(limit_area="inside"),
color="green", linewidth=1.5, marker="o", label="Alina")
last_idx = alina_df["Gewicht"].last_valid_index()
who_row = who_weight_daily.loc[who_weight_daily["Datum"] == alina_df["Datum"][last_idx]].iloc[0]
perzentil = (alina_df["Gewicht"][last_idx]-who_row["P50"])/((who_row["P97"]-who_row["P50"])/47)+50
ax.text(alina_df["Datum"].iloc[-1]+pd.DateOffset(days=1), alina_df["Gewicht"].iloc[-1]+0.5,f"{perzentil:.2f} %",
fontsize=10,
ha="left",
va="bottom",
color="green")
ax.set_ylabel("Gewicht [kg]")
ax.set_title("Wachstum")
# Enable minor ticks
ax.minorticks_on()
# Configure major grid (e.g., solid, darker)
ax.grid(which='major', color='black', linestyle='-', linewidth=0.8)
# Configure minor grid (e.g., dashed, lighter, thinner)
ax.grid(which='minor', color='gray', linestyle='--', linewidth=0.4, alpha=0.5)
ax.legend()
# ---------- 2. HEIGHT ----------
ax = axes[1]
plot_percentiles(
ax,
x,
who_height_daily,
percentile_list,
colors=colors_list,
linewidths=linewidths_list,
labels=label_list,
linestyle=linestyle_list
)
ax.plot(alina_df["Datum"], alina_df["Größe"].interpolate(limit_area="inside"),color="green", linewidth=1.5, marker="o")
last_idx = alina_df["Größe"].last_valid_index()
who_row = who_height_daily.loc[who_height_daily["Datum"] == alina_df["Datum"][last_idx]].iloc[0]
perzentil = (alina_df["Größe"][last_idx]-who_row["P50"])/((who_row["P97"]-who_row["P50"])/47)+50
ax.text(alina_df["Datum"].iloc[-1]+pd.DateOffset(days=1), alina_df["Größe"].iloc[-1]+0.5,f"{perzentil:.2f} %",
fontsize=10,
ha="left",
va="bottom",
color="green")
ax.set_ylabel("Größe [cm]")
# Enable minor ticks
ax.minorticks_on()
# Configure major grid (e.g., solid, darker)
ax.grid(which='major', color='black', linestyle='-', linewidth=0.8)
# Configure minor grid (e.g., dashed, lighter, thinner)
ax.grid(which='minor', color='gray', linestyle='--', linewidth=0.4, alpha=0.5)
# ---------- 3. HEAD CIRCUMFERENCE ----------
ax = axes[2]
plot_percentiles(
ax,
x,
who_head_daily,
percentile_list,
colors=colors_list,
linewidths=linewidths_list,
labels=label_list,
linestyle=linestyle_list
)
ax.plot(alina_df["Datum"], alina_df["Kopfumfang"].interpolate(limit_area="inside"),color="green", linewidth=1.5, marker="o")
last_idx = alina_df["Kopfumfang"].last_valid_index()
who_row = who_head_daily.loc[who_head_daily["Datum"] == alina_df["Datum"][last_idx]].iloc[0]
perzentil = (alina_df["Kopfumfang"][last_idx]-who_row["P50"])/((who_row["P97"]-who_row["P50"])/47)+50
ax.text(alina_df["Datum"].iloc[-1]+pd.DateOffset(days=1), alina_df["Kopfumfang"].iloc[-1]+0.5,f"{perzentil:.2f} %",
fontsize=10,
ha="left",
va="bottom",
color="green")
ax.set_ylabel("Kopfumfang [cm]")
# Enable minor ticks
ax.minorticks_on()
# Configure major grid (e.g., solid, darker)
ax.grid(which='major', color='black', linestyle='-', linewidth=0.8)
# Configure minor grid (e.g., dashed, lighter, thinner)
ax.grid(which='minor', color='gray', linestyle='--', linewidth=0.4, alpha=0.5)
# ---------- X‑AXIS ----------
axes[-1].set_xlabel("Datum")
N = 7 # label every Nth tick
axes[-1].set_xticks(x[::N])
axes[-1].set_xticklabels(
x.iloc[::N].dt.strftime("%d.%m.%Y"),
rotation=45,
ha="right"
)
plt.tight_layout()
plt.savefig(output_path, dpi=150)
plt.close()
if hostname == "serverhost":
print("Serverhost Mode")
import subprocess
subprocess.run(["bash", "/home/lucas/scripts/serverhost/syncNC.sh", "-al"])
print("Fin")