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")