diff --git a/Atelier_MatPlotLib_2021_part1.py b/Atelier_MatPlotLib_2021_part1.py index 72218763cb5845f836d098df5f1a5ee8fc80f406..fb9148c0d072b99e7f373e8c235d45c9a403705c 100644 --- a/Atelier_MatPlotLib_2021_part1.py +++ b/Atelier_MatPlotLib_2021_part1.py @@ -17,10 +17,11 @@ import matplotlib.pyplot as plt # des éléments complémentaires (on verra leur utilité) import matplotlib.gridspec as gridspec -import matplotlib.dates as mpldates # https://matplotlib.org/examples/api/date_demo.html +import matplotlib.dates as mdates # https://matplotlib.org/examples/api/date_demo.html from matplotlib.dates import DateFormatter import matplotlib.colors as mocolor from matplotlib.patches import Rectangle +from matplotlib.lines import Line2D from matplotlib.colors import ListedColormap from matplotlib.collections import PatchCollection @@ -48,26 +49,36 @@ import pickle as pl # CONSTANTES - convention : en majuscule +DICO_NOM_MOIS = {1: 'janv', 2: 'févr', 3: 'mars', 4: 'avr', 5: 'mai', 6: 'juin', 7: 'juil', 8: 'août', + 9:'sept', 10:'oct', 11:'nov', 12:'déc'} -# les noms de fichiers de données -REP_DONNEES = "C:/WorkSpace/2021-Tuto-Python/Code/donnees" # remplacez par votre chemin -TD4_ENTPE_TPQ_original = "TD4-donneesTPQ.txt" # date au format "janv-00" : bon exercice pour "parser" en utilisant un dico -TD4_ENTPE_pv = "ChroniquesTPluieQ_pointvirgule.csv" # même données avec des dates au format JJ/MM/AAAA, séparateur = ; -TD4_ENTPE_tab = "ChroniquesTPQ_tabulations.txt" # même données avec des dates au format JJ/MM/AAAA, séparateur = blanc ou tabulation +DICO_COULEURS_MOIS = {1: "dimgrey", 2: "black", 3: "palegreen", 4: 'mediumspringgreen', 5: 'forestgreen', 6: 'gold', 7: 'orange', + 8:'orangered', 9:'deepskyblue', 10:'royalblue', 11:'navy', 12:'silver'} -HYDRO_QJO = "B2220010_qj_hydro2_2019.txt" -HYDRO_Qt = "B2220010_qtvar.txt" -HYDROQJ = "Y2372010_qj.csv" -CHAMP_BIN = "chronique_bloc_38_57_test2.bin" -CHAMP_GRID = "Panthere_20160512_LimBvGC_zoom1_0_Champ journalier.grd" - -trace_plt = True -trace_pd = True # CLASSES (même si en fait l'ordre importe peu...) +# pas encore de classe ici # FONCTIONS +def afficher_legende_code_couleur_mois(titre="Code couleur \n des mois"): + # on crée + plt.ion() + fig, ax = plt.subplots() + ax.axis('off') + handles = [Line2D([0], [0], marker='o', color="b", label=DICO_NOM_MOIS[mois], + markerfacecolor=DICO_COULEURS_MOIS[mois], markersize=15) for mois in range(1, 13)] + fig.legend(handles=handles, ncol=1, title=titre, fontsize=8, title_fontsize=10, + labelspacing=1.5) + # plt.show(block = False) + """ + if Path.is_file(Path(IKONKA)): + thismanager = plt.get_current_fig_manager() + thismanager.window.tk.call('wm', 'iconphoto', thismanager.window._w, Photoimage(file=IKONKA)) + """ + fig.canvas.draw() + plt.ioff() + # GRAPHIQUE SIMPLE # format Hydro2 "QJO" @@ -146,7 +157,7 @@ def lecteur_qj0_to_ts(nom_fichier, trace=True): return qj_series -def donnees_TD_ETP_csv(nom_fichier, separateur=";"): +def donnees_TD_ETP_csv_panda_seul(nom_fichier, separateur=";"): """ Lecteur du fichier TD4 ChroniquesTPluieQ_pointvirgule @@ -159,15 +170,9 @@ def donnees_TD_ETP_csv(nom_fichier, separateur=";"): """ #note : # https: // stackoverflow.com / questions / 25416955 / plot - pandas - dates - in -matplotlib - # fonction imbriquée dans la première, pour la mise en forme des données - # format the coords message box - def un_chiffre_apres_la_virgule(x): - # return '%1.1f unités' % x - return f"{x:.1f} unités" - DF_TD4 = pd.read_csv(nom_fichier, sep=separateur, decimal=',') - #on affiche les 5 premières lignes + #on affiche les 5 premières lignes pour vérifier DF_TD4.head(5) nom_colonnes = DF_TD4.columns.tolist() @@ -176,15 +181,8 @@ def donnees_TD_ETP_csv(nom_fichier, separateur=";"): # on affiche les 15 premières valeurs de la colonne n°0 print(DF_TD4[nom_colonnes[0]][:15]) - # pour assigner un format date à la première colonne, on a plusieurs possibilités : - # une façon "besogneuse" en découpant et collant les morceaux, comme à la première étape - # jmas = [date_str.split('/') for date_str in DF_TD4[nom_colonnes[0]]] # jmas est une liste de listes de 3 items - # dates_dt = [[datetime(int(a), int(m), int(j))] for j, m, a in jmas] # for j, m, a in jmas : on unpacke la liste - - # puisqu'il y a des séparateurs on peut aussi utiliser un lecteur de format du module datetime - # DF_TD4[nom_colonnes[0]].apply(lambda x: datetime.strptime(str(x), "%d/%m/%Y")) - - # ou enfin une méthode élégante de pandas, qui "se débrouille pour lire une date si c'est une date" + # pour assigner un format date à la première colonne, + # on s'en remet à pandas, qui "se débrouille pour lire une date si c'est une date" # date_au_format_pandas = pd.to_datetime(un_truc_nimporte_comment_qui_est_une_date) # piège ; on ne sait jamais si c'est MMDD ou DDMM : l'argument dayfirst=True permet de cadrer le déchiffrage @@ -197,13 +195,10 @@ def donnees_TD_ETP_csv(nom_fichier, separateur=";"): # une fois convaincus, on peut faire la conversion directement dans la colonne DF_TD4[nom_colonnes[0]] = pd.to_datetime(DF_TD4[nom_colonnes[0]], dayfirst=True) - # sinon il y aurait une façon "besogneuse" : - # DF_TD4[nom_colonnes[0]].apply(lambda x: datetime.strptime(str(x), "%d/%m/%Y")) - # on déclare comme index la colonne, avec les dates ; elle prend donc un statut particulier DF_TD4.set_index(nom_colonnes[0], inplace=True) - # on met à jour la liste des colonnes (on a supprimé une colonne "dates") + # on met à jour la liste des colonnes (on a supprimé une colonne "dates", qui est devenue l'index) nom_colonnes = DF_TD4.columns.tolist() print("Noms des colonnes après avoir défini l'index : ", nom_colonnes) @@ -212,15 +207,10 @@ def donnees_TD_ETP_csv(nom_fichier, separateur=";"): DF_TD4.head(5) - fig, ax = plt.subplots() # on verra juste après cette syntaxe - DF_TD4.plot(ax=ax) - fig.suptitle("En utilisant les possibilités de pandas") - + # idem, on laisse pandas se débrouiller complètement + DF_TD4.plot() + plt.suptitle("En utilisant les possibilités de pandas") plt.legend() - - # plt.gca().format_xdata = mpldates.DateFormatter('%Y/%m') - # plt.gca().format_ydata = un_chiffre_apres_la_virgule - # plt.gcf().autofmt_xdate() # sur figure plt.show() return DF_TD4 @@ -242,8 +232,23 @@ def donnees_TD_ETP_2subplots(nom_fichier): nom_colonnes = DF_TD4.columns.tolist() print("Noms de colonnes lus dans le csv : ", nom_colonnes) + + #première figure : PANDAS + # méthode plot de pandas + DF_TD4.plot() + plt.suptitle("En utilisant les possibilités de pandas") + plt.gcf().canvas.set_window_title("PTQ_avec_pandas") + plt.legend() + + # plt.gca().format_xdata = mpldates.DateFormatter('%Y/%m') + # plt.gca().format_ydata = un_chiffre_apres_la_virgule + # plt.gcf().autofmt_xdate() # sur figure + plt.show() + plt.close() + # on découpe la figure en deux subplots fig, (ax_pluie, ax_q) = plt.subplots(ncols=1, nrows=2, sharex=True) + fig.canvas.set_window_title("PTQ_2subplots") fig.subplots_adjust(bottom=0.15) fig.suptitle("Forçages en haut, réponse du bassin en bas") @@ -257,747 +262,780 @@ def donnees_TD_ETP_2subplots(nom_fichier): etiquette_temperatures = nom_colonnes[0] ax_t.set_ylabel(etiquette_temperatures) ax_t.plot(liste_dates, DF_TD4[etiquette_temperatures].tolist(), marker='*', color='orange', ls=':', - label=etiquette_temperatures, zorder=50) + label=etiquette_temperatures) etiquette_debit = nom_colonnes[2] ax_q.set_ylabel(etiquette_debit, color='blue') ax_q.plot(liste_dates, DF_TD4[etiquette_debit].tolist(), marker='>', color='blue', ls=':', label=etiquette_debit) fig.legend(bbox_to_anchor=(1, 0), loc="lower right", - bbox_transform=fig.transFigure, ncol=3) - - # plt.gca().format_xdata = mpldates.DateFormatter('%Y/%m') - # plt.gca().format_ydata = un_chiffre_apres_la_virgule - # plt.gcf().autofmt_xdate() # sur figure - - #le module pickle - # nom_fichier_pickle = Path(nom_fichier).with_suffix('.pickle') - # pl.dump(fig, open(nom_fichier_pickle, 'wb')) - # print(nom_fichier_pickle) + bbox_transform=fig.transFigure, ncol=4) plt.show() return DF_TD4 +def donnees_TD_ETP_2subplots_variante(DF_PTQ): + """ + # on prend en argument un DataFrame + et on fait un graphique avec d'autres solutions que plot et lines + """ + # couleurs par mois : prétexte pour manipuler dictionnaires et scatter + # équivalent "plus lisible" de : liste_couleurs = [ DICO_COULEURS_MOIS[date.month] for date in DF_PTQ.index] + liste_couleurs = [] + for date in DF_PTQ.index: + liste_couleurs.append(DICO_COULEURS_MOIS[date.month]) -def trace_avec_plt(liste_dates, valeurs, type_graphe="plot"): - temps = datetime.now() - debut - - # plt.suptitle("trace_avec_plt - fig.suptitle") - - if type_graphe == "plot": - - plt.plot(liste_dates, valeurs) - plt.title("Avec pyplot, plot") - - elif type_graphe == "stem": - plt.stem(liste_dates, valeurs) - plt.title("Avec pyplot, stem") - - elif type_graphe == "hlines": - plt.hlines(y=valeurs, xmin=liste_dates, xmax=[date + timedelta(days=30) for date in liste_dates]) - - elif type_graphe == "vlines": - plt.vlines(x=liste_dates, ymin=0, ymax=valeurs) - - # pour améliorer - plt.xticks(rotation=45) - plt.tight_layout(True) - # plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=1.0) - # plt.tight_layout({'rect': [0, .15, 1, 0.9]}, w_pad=0.5, h_pad=1.0) - plt.subplots_adjust(bottom=0.2) - - # fig.autofmt_xdate() - - val_moy = max(valeurs) / 2 - - # tests stem et vline sur qq valeurs - # plt.stem([liste_dates[0],liste_dates[-1]], [val_moy, val_moy ]) - # plt.vlines([liste_dates[5], liste_dates[-10]], [0, 0], [val_moy, val_moy], colors=["red", "green"]) - plt.title("Avec pyplot, " + type_graphe) - plt.gcf().canvas.set_window_title("Avec_pyplot_" + type_graphe) - - plt.show() - print("temps écoulé Pyplot avec plot:", temps) - - # plt.clf() - - return temps - + nom_colonnes = DF_PTQ.columns.tolist() + # on découpe la figure en deux subplots + fig, (ax_pluie, ax_q) = plt.subplots(ncols=1, nrows=2, sharex=True) + fig.canvas.set_window_title("PTQ_2subplots") + fig.subplots_adjust(bottom=0.15) + fig.suptitle("Forçages en haut, réponse du bassin en bas") -def trace_au_choix(serie, type_graphe="plot"): - temps = datetime.now() - debut + etiquette_pluie = nom_colonnes[1] + ax_pluie.set_ylabel(etiquette_pluie, color='lightblue') + ax_pluie.step(x=DF_PTQ.index, y=DF_PTQ[etiquette_pluie].tolist(), color='lightblue', lw=2, + label=etiquette_pluie, where='post') + ax_pluie.fill_between(DF_PTQ.index, DF_PTQ[etiquette_pluie].tolist(), step="post", alpha=0.4) + ax_pluie.invert_yaxis() - if type_graphe == "plot": + ax_t = ax_pluie.twinx() + etiquette_temperatures = nom_colonnes[0] + ax_t.set_ylabel(etiquette_temperatures) + ax_t.step(DF_PTQ.index, DF_PTQ[etiquette_temperatures].tolist(), marker='None', color='orange', ls=':', + label=etiquette_temperatures + " (step)", where="post") + # todo : on a un peu forcé le "scatter" pour s'exercer + ax_t.scatter(DF_PTQ.index, DF_PTQ[etiquette_temperatures].tolist(), marker='*', ls=':', + label=etiquette_temperatures, c=liste_couleurs) - serie.plot() + etiquette_debit = nom_colonnes[2] + ax_q.set_ylabel(etiquette_debit, color='blue') + ax_q.step(DF_PTQ.index, DF_PTQ[etiquette_debit].tolist(), marker='None', color='blue', ls='-', label=etiquette_debit, where='post') - elif type_graphe == "hlines": - plt.hlines(y=serie, xmin=serie.index, xmax=[date + timedelta(days=30) for date in serie.index]) + fig.legend(bbox_to_anchor=(1, 0), loc="lower right", + bbox_transform=fig.transFigure, ncol=4) - plt.title("Avec pyplot, hines") + #le module pickle + # nom_fichier_pickle = Path(nom_fichier).with_suffix('.pickle') + # pl.dump(fig, open(nom_fichier_pickle, 'wb')) + # print(nom_fichier_pickle) plt.show() + # pas d'instruction return, mais par défaut la fonction retourne quand même... None - print("temps écoulé, fonction trace_au_choix :", temps) - # plt.clf() - - return temps - - -def tracer_DF_lamedeau_moyennes_mobiles(series_or_df_lamedeau): - # format the coords message box - def un_chiffre_apres_la_virgule(x): - # return '%1.1f unités' % x - return f"{x:.1f} mm/5 min" - - # DF_moymobile_1j et 30 jours définis pour le plot mais non nommés - - if type(series_or_df_lamedeau) == pd.Series: - df_ler5min = series_or_df_lamedeau.to_frame() - else: - df_ler5min = series_or_df_lamedeau # deepcopy - df_ler5min.columns = ["Lame d'eau radar à 5 mm, en mm"] - df_ler5min.columns.name = 'Chroniques' - - fig, (ax_moy, ax_ler) = plt.subplots(nrows=2, ncols=1, sharex=True) - fig.subplots_adjust(bottom=0.3) +def donnees_TD_ETP_2subplots_variante2(DF_PTQ): + """ + # on prend en argument un DataFrame + et on fait un graphique avec d'autres solutions que plot et lines + """ + # couleurs par mois : prétexte pour manipuler dictionnaires et scatter + # équivalent "plus lisible" de : liste_couleurs = [ DICO_COULEURS_MOIS[date.month] for date in DF_PTQ.index] + liste_couleurs = [] + for date in DF_PTQ.index: + liste_couleurs.append(DICO_COULEURS_MOIS[date.month]) - fig.suptitle("Lames d'eau radar et cumuls, en utilisant pandas") - couleur_5min = 'blue' - ax_ler.set_ylabel("Lame d'eau radar \n (mm en 5 mm)", color=couleur_5min) - # méthode plot de pandas appliquée à un dataframe - df_ler5min.plot(ax=ax_ler, color=couleur_5min) + nom_colonnes = DF_PTQ.columns.tolist() + # on découpe la figure en deux subplots + fig, (ax_pluie, ax_codemois, ax_q) = plt.subplots(ncols=1, nrows=3, sharex=True) + fig.canvas.set_window_title("PTQ_2subplots") + fig.subplots_adjust(bottom=0.15) + fig.suptitle("Forçages en haut, réponse du bassin en bas") - couleur_24h = 'red' - ax_moy.set_ylabel("Cumuls sur 24 h (mm)", color=couleur_24h) - df_24h = df_ler5min.rolling('1d').sum() - df_24h.columns = ["Cumuls mobiles sur 24h"] - df_24h.plot(ax=ax_moy, color=couleur_24h, label="ça sert à rien") + etiquette_pluie = nom_colonnes[1] + ax_pluie.set_ylabel(etiquette_pluie, color='lightblue') + ax_pluie.step(x=DF_PTQ.index, y=DF_PTQ[etiquette_pluie].tolist(), color='lightblue', lw=2, + label=etiquette_pluie, where='post') + ax_pluie.fill_between(DF_PTQ.index, DF_PTQ[etiquette_pluie].tolist(), step="post", alpha=0.4) + ax_pluie.invert_yaxis() - couleur_30j = 'orange' - ax_moy_30j = ax_moy.twinx() - ax_moy_30j.set_ylabel("Cumuls sur 30 jours", color=couleur_30j) + ax_t = ax_pluie.twinx() + etiquette_temperatures = nom_colonnes[0] + ax_t.set_ylabel(etiquette_temperatures) + ax_t.step(DF_PTQ.index, DF_PTQ[etiquette_temperatures].tolist(), marker='None', color='orange', ls=':', + label=etiquette_temperatures + " (step)", where="post") + # todo : on a un peu forcé le "scatter" pour s'exercer + ax_t.scatter(DF_PTQ.index, DF_PTQ[etiquette_temperatures].tolist(), marker='*', ls=':', + label=etiquette_temperatures, c=liste_couleurs) - df_30j = df_ler5min.rolling('30d').sum() - df_30j.columns = ["Cumuls mobiles sur 30 jours"] - df_30j.plot(ax=ax_moy_30j, color=couleur_30j) + etiquette_debit = nom_colonnes[2] + ax_q.set_ylabel(etiquette_debit, color='blue') + ax_q.step(DF_PTQ.index, DF_PTQ[etiquette_debit].tolist(), marker='None', color='blue', ls='-', label=etiquette_debit, where='post') - for ax in [ax_ler, ax_moy, ax_moy_30j]: - ax.legend().set_visible(False) + fig.legend(bbox_to_anchor=(1, 0), loc="lower right", + bbox_transform=fig.transFigure, ncol=4) - fig.legend(loc='lower center', ncol=2) + # on enlève ax_codemois de la liaison + # https://stackoverflow.com/questions/42973223/how-to-share-x-axes-of-two-subplots-after-they-have-been-created + ax_codemois.autoscale() + ax_codemois.axis('off') + handles = [Line2D([0], [0], marker='*', ls="None", label=DICO_NOM_MOIS[mois],markerfacecolor=DICO_COULEURS_MOIS[mois], + markeredgecolor=DICO_COULEURS_MOIS[mois], markersize=7) for mois in range(1, 13)] + ax_codemois.legend(handles=handles, ncol=6, title="Code couleur des mois", fontsize=9, title_fontsize=9, + labelspacing=1.5) - ax_ler.format_ydata = un_chiffre_apres_la_virgule - ax_moy.format_ydata = un_chiffre_apres_la_virgule - ax_moy_30j.format_ydata = un_chiffre_apres_la_virgule + #le module pickle + # nom_fichier_pickle = Path(nom_fichier).with_suffix('.pickle') + # pl.dump(fig, open(nom_fichier_pickle, 'wb')) + # print(nom_fichier_pickle) - # plt.gca().format_xdata = mpldates.DateFormatter('%Y/%m') - # plt.gca().format_ydata = un_chiffre_apres_la_virgule - # plt.gcf().autofmt_xdate() # sur figure plt.show() + # pas d'instruction return, mais par défaut la fonction retourne quand même... None + +# LIRE UN FICHIER BINAIRE +def lire_lame_deau_radar_5min(nom_fichier_binaire): + PDT_OBS_EN_MIN = 5 + DIVISEUR = 10 + CODE_LACUNE_BINAIRE = 65535 # => -999.99 (lacune) + CODE_LACUNE = -999.99 + + nb_of_days = 3653 + pas_de_temps = 24 * 60 / PDT_OBS_EN_MIN # nb de pas de temps dans une journée ; = 288 + # nb_valeurs_cp = nb_of_days * pas_de_temps = 1514972160 + len_of_tvect = nb_of_days * pas_de_temps # 1052064 + recordSize = len_of_tvect * 2 # parce que ce sont des entiers codés sur 2 bytes + + date_premiere_valeur = datetime(2006, 7, 1, 0, 5) + liste_dates = [date_premiere_valeur + i * timedelta(minutes=PDT_OBS_EN_MIN) for i in + range(len_of_tvect)] # PDT_OBS_EN_MIN = 5 minutes + + if Path.is_file(Path(nom_fichier_binaire)): + print(f"nom_chemin_binaire1 : {nom_fichier_binaire} ") + + # lecture des valeurs + # cf fonctions Chronique.lire_chronique Lecture_Patnthere...chronique + uint16_valeurs = None + with open(nom_fichier_binaire, 'rb') as fic_binaire: + try: + uint16_valeurs = np.fromfile(fic_binaire, dtype=np.uint16, count=int(len(liste_dates))) + statut_lecture = "OK" + except: + statut_lecture = "erreur" + + # VERIF + print("Ensemble des valeurs lues avant traitement (lacunes, diviseur...) : ", set(uint16_valeurs)) + + if statut_lecture == "OK": + # Traitement des lacunes + nb_lacunes = 0 + for valeur in uint16_valeurs: + if int(valeur) == int(CODE_LACUNE_BINAIRE): + nb_lacunes += 1 + print("Nombre de lacunes = ", nb_lacunes) + + # on travaille avec des LAMES D'EAU en centième de mm, à passer en mm + np_valeurs = np.array( + [entier / DIVISEUR if entier != int(CODE_LACUNE_BINAIRE) else np.nan for entier in + uint16_valeurs]) + + # tracé + debut = datetime.now() + plt.step(liste_dates, np_valeurs) + plt.title("Fichier binaire "+ os.path.basename(nom_fichier_binaire) + "\n sans précaution pour les étiquettes de dates" ) + temps = datetime.now() - debut + plt.show() + plt.close() + print("temps écoulé, step:", temps) -def trace_3_courbes_avec_pandas(ts_lamedeau, ts_mensuel, ts_journalier): - print("Moyennes pdt fixes") - - fig = plt.figure("Chroniques_pandas", figsize=[10, 5]) # tight_layout=True, - fig.suptitle("Avec pandas, plus moyennes mensuelles en errorbars") - # https://matplotlib.org/gallery/userdemo/demo_gridspec03.html#sphx-glr-gallery-userdemo-demo-gridspec03-py - gs = gridspec.GridSpec(nrows=2, ncols=1, height_ratios=[10, 1]) - ax1 = fig.add_subplot(gs[0]) - - # ax1 = df_lamedeau.loc[:, cols[1]].plot(label=cols[1], - # title="Avec pandas, données moyennées (pas de temps fixe)", - # color='purple') - ts_lamedeau.plot(label="lame d'eau à 5 min", color='purple', ax=ax1) - (handles1, labels1) = ax1.get_legend_handles_labels() + return liste_dates, np_valeurs - ax2 = ax1.twinx() +# Tracer une longue série, applicable à toutes donnnées (lues dans Hydro2, dans un binaire ou tout autre format) +def tracer_proprement_une_longue_serie(dates, valeurs, info_chroniques="lames d'eau radar, mm en 5 min"): + """ démonstration de "concise formatter" - moyenne_des_mois = ts_mensuel.mean() - moyenne_des_ler5min = ts_lamedeau.mean() - print("moyenne des mois = ", moyenne_des_mois, " à comparer avec ", moyenne_des_ler5min) + """ + locator = mdates.AutoDateLocator(minticks=3, maxticks=12) + formatter = mdates.ConciseDateFormatter(locator) - ts_mensuel.plot(label=' cumul mensuel', color='lightblue', ax=ax2, ls=':') - ax2.errorbar(ts_mensuel.index, ts_mensuel, label=' cumul mensuel avec figuré errorbar', xerr=timedelta(days=30), - ls='None', color='blue', marker=">", xuplims=True) + # VERIF + if len(dates) != len(valeurs): + print(f"Les 2 vecteurs n'ont pas la même longueur : {len(dates)} dates et {len(valeurs)} valeurs") + return + # ça permet de quitter la fonction ; ce n'est pas la meilleure manière de faire (plutôt un else...) - debut_pour_plt = pd.to_datetime(date_debut_pd) - fin_pour_plt = pd.to_datetime(date_fin_pd) + print("Nombre de points : ", len(dates)) - # TESTS AVEC LES VLINE avec s - # ts_journalier.plot(label=' moyenne journalière', color='orange', ax=ax2) - # https: // stackoverflow.com / questions / 5902371 / matplotlib - bar - chart - with-dates - # ax1.xaxis_date() - ts_journalier.plot(label=' moyenne journalière', color='orange', ax=ax2) - (handles2, labels2) = ax2.get_legend_handles_labels() + # tracé + debut = datetime.now() + plt.ion() + fig_cf, ax_t = plt.subplots() + fig_cf.suptitle("Tracé d'une longue chronique, soin apporté aux étiquettes de dates \n" + info_chroniques) + ax_t.set_xlabel("dates, format date", fontsize=14) + ax_t.set_ylabel("info_valeurs", fontsize=14) + ax_t.xaxis.set_major_locator(locator) + ax_t.xaxis.set_major_formatter(formatter) + ax_t.step(dates, valeurs) - plt.legend(handles1 + handles2, labels1 + labels2, bbox_to_anchor=(0.9, 0.05), loc="lower right", # mode="expand", - bbox_transform=fig.transFigure, ncol=2) # loc='best') + temps = datetime.now() - debut + print("tracer_proprement_une_longue_serie, temps écoulé, step:", temps) + fig_cf.canvas.draw() + fig_cf.savefig("test.png") + plt.ioff() plt.show() - # plt.clf() - -def trace_3_courbes_avec_pandas_donc_vlines(ts_lamedeau, ts_mensuel, ts_journalier): - print("Moyennes pdt fixes, trace_3_courbes_avec_pandas_dont_vlines") + #plt.close() - # ax1 = df_lamedeau.loc[:, cols[1]].plot(label=cols[1], - # title="Avec pandas, données moyennées (pas de temps fixe)", - # color='purple') + # on ne retourne rien - fig = plt.figure("avec_pandas_jours_vlines", tight_layout=True) - gs = gridspec.GridSpec(nrows=2, ncols=1, height_ratios=[10, 1]) - ax1 = fig.add_subplot(gs[0]) - ts_lamedeau.plot(label="lame d'eau à 5 min", title="Avec pandas et vlines", color='purple', ax=ax1) - (handles, labels) = ax1.get_legend_handles_labels() +def tracer_une_longue_serie_en3_resolutions_tests(dates, valeurs, info_chroniques=" série temporelle", info_valeurs="lames d'eau radar, mm en 5 min"): + # met en lumière des problèmes liés à pandas.resample + # pour les données binaires, voir en particulier le pic max, situé un 30 juin à 17h30, avec une max mensuel rebasculé au mois suivant - ax2 = ax1.twinx() + locator = mdates.AutoDateLocator(minticks=3, maxticks=12) + formatter = mdates.ConciseDateFormatter(locator) - # ts_journalier.plot(label=' moyenne journalière', color='orange', ax=ax2) - # https: // stackoverflow.com / questions / 5902371 / matplotlib - bar - chart - with-dates - # ax1.xaxis_date() + couleur_originale = 'purple' + dico_resolutions = {"jour":"1D", "mois":"1M", "année":"1Y"} + dico_resolutions_couleurs = {"jour": "blue", "mois": "green", "année": "orange"} - moyenne_des_mois = ts_mensuel.mean() - print("moyenne des mois = ", moyenne_des_mois) + fig, (ax_original, ax_resample_max) = plt.subplots(nrows=2, ncols=1, sharex=True) + fig.subplots_adjust(bottom=0.3) - # graphique des mois - ts_mensuel.plot(label=' cumuls mensuels (pd plot)', color='lightblue', ax=ax2) - ax2.errorbar(ts_mensuel.index, ts_mensuel, label='cumuls mensuels (figuré errorbar)', xerr=timedelta(days=30), - ls='None', color='blue', marker=">", xuplims=True) + fig.suptitle(info_valeurs + "\n Rééchantillonnages sur pdt fixes, en utilisant pandas") + ax_resample_moy = ax_original.twinx() + ax_original.set_ylabel("Données originales", color=couleur_originale) + ax_resample_moy.set_ylabel("Moyennes sur pdt fixes", color='black') + ax_resample_max.set_ylabel("Max sur pdt fixes", color='black') + # méthode plot de pandas appliquée à un dataframe - debut_pour_plt = pd.to_datetime(date_debut_pd) - fin_pour_plt = pd.to_datetime(date_fin_pd) + ax_original.step(dates, valeurs, c=couleur_originale, label="résolution du fichier") + #ax_original.plot(dates, valeurs, c=couleur_originale, label="résolution du fichier", marker='*', markersize=10, ls="None") + ax_original.legend() + + ts= pd.Series(data=valeurs, index=dates, name=info_valeurs) + # test d'un axe supplémentaire (3e) avec twinx : il va être superposé à l'axe déjà défini par twinx + if False: + ax_test = ax_original.twinx() + ax_test.set_ylabel("test", color="sienna") + ts_test = ts.resample('1D', closed='left').mean() + ax_test.plot(ts_test.index.to_pydatetime(), ts_test.to_numpy(), c='sienna', + label="test", marker='None', ls="None") + # on définit une liste ax_moy dans le cas où l'on veut tester un "ax" par résolution : résultat, les axes des y sont superposés + #ax_moy=[] + + for duree, reso in dico_resolutions.items(): + ts_moy = ts.copy().resample(reso, closed='left',label='left').mean() + #ax_moy.append(ax_original.twinx()) + print(f"Rééchantillonnage par moyenne sur pas de temps {duree}, première date = {ts_moy.index[0]}") + # ax_resample_moy.step(ts_moy.index, ts_moy.to_numpy(), c=dico_resolutions_couleurs[duree], label=duree) + ax_resample_moy.plot(ts_moy.index.to_pydatetime(), ts_moy.to_numpy(), c=dico_resolutions_couleurs[duree], + label=duree, marker='*', ls="None") + + if reso=="1D": + ax_resample_moy.step(ts_moy.index.to_pydatetime(), ts_moy.to_numpy(), c=dico_resolutions_couleurs[duree], + label=duree, where='post') + else: + ax_resample_moy.step(ts_moy.index.to_pydatetime(), ts_moy.to_numpy(), c=dico_resolutions_couleurs[duree], + label=duree) + """ + ax_moy[-1].plot(ts_moy.index.to_pydatetime(), ts_moy.to_numpy(), c=dico_resolutions_couleurs[duree], + label=duree, marker='*', ls="None") + ax_moy[-1].step(ts_moy.index.to_pydatetime(), ts_moy.to_numpy(), c=dico_resolutions_couleurs[duree], label=duree) # , where='post') + """ + ts_max = ts.resample(reso, closed='left', label='left').max() + print(f"Rééchantillonnage par max sur pas de temps {duree}, première date = {ts_max.index[0]}") + # ax_resample_max.step(ts_max.index, ts_max.to_numpy(), c=dico_resolutions_couleurs[duree], label=duree) + ax_resample_max.plot(ts_max.index.to_pydatetime(), ts_max.to_numpy(), c=dico_resolutions_couleurs[duree], + label=duree, marker='*', ls="None") + if reso=="1D": + ax_resample_max.step(ts_max.index.to_pydatetime(), ts_max.to_numpy(), c=dico_resolutions_couleurs[duree], label=duree, where='post') + else: + ax_resample_max.step(ts_max.index.to_pydatetime(), ts_max.to_numpy(), c=dico_resolutions_couleurs[duree], + label=duree) - # TESTS AVEC LES AX.H/V.LINE avec s - # il faut un autre plot déjà sur le même ax - # ts_journalier.plot(label=' moyenne journalière', color='orange', ax=ax2, ls=':') - ax2.vlines(x=ts_journalier.index, ymin=0, ymax=ts_journalier, label="cumuls journaliers (vlines)", color='orange', - lw=5, alpha=0.8) - # PB avec STEM : - # ValueError: view limit minimum -453626.8500000001 is less than 1 and is an invalid Matplotlib date value. This often happens if you pass a non-datetime value to an axis that has datetime units - # + stem() got an unexpected keyword argument 'use_line_collection' - # on teste d'abord avec les mois ?? - # ax2.stem(ts_journalier.index, ts_journalier) # use_line_collection=True) #, color='red') - # print(type(ts_mensuel.index[0])) - # print(ts_mensuel.index) - # ax2.stem([pd.to_datetime(date) for date in ts_mensuel.index], ts_mensuel) + ax_resample_max.legend() - (handles2, labels2) = ax2.get_legend_handles_labels() + ax_original.xaxis.set_major_locator(locator) + ax_original.xaxis.set_major_formatter(formatter) - plt.legend(handles + handles2, labels + labels2, bbox_to_anchor=(1, 0.1), loc="lower right", # mode="expand", - bbox_transform=fig.transFigure, ncol=2) # loc='best') plt.show() - # plt.clf() - - -# moyennes par MOIS avec des rectangles -def trace_mensuel_avec_rectangles(ts_lamedeau, ts_mensuel, ts_journalier=None, trace_rect_mensuels=False): - debut = datetime.now() +def tracer_une_longue_serie_en3_resolutions_mieux(dates, valeurs, info_chroniques="lames d'eau radar, mm en 5 min"): """ - si DF - ax1 = df_lamedeau.loc[:, cols[1]].plot(label=cols[1], - title="Avec rectangles,5 min et cumuls mensuels (pas de temps fixe)", - color='purple', zorder=10) + solutions proposées au pb identifié """ - fig, ax1 = plt.subplots("chroniques_mois_rectangles", figsize=(7, 4)) - ts_lamedeau.plot(title="trace_mensuel_avec_rectangles (cumuls pas de temps fixe)", label="lames d'eau à 5 min", - color='purple', zorder=10, ax=ax1) - ax1.set(ylabel="Lames d'eau à 5 min") - (handles, labels) = plt.gca().get_legend_handles_labels() - - # pas possible de mettre un 2e axe rien qu'avec des rectangles... DONC il faut mettre une ligne "guide" avec un plot - # moyenne_des_mois = ds_mensuel["lames d'eau en mm"].mean() - date_debut_pd = ts_lamedeau.index[0] - date_fin_pd = ts_lamedeau.index[-1] - - ax2 = ax1.twinx() - - ts_debutfin = pd.Series([ts_mensuel.mean(), ts_mensuel.mean()], index=[date_debut_pd, date_fin_pd]) - - ts_debutfin.plot(ls=':', label='moyenne des cumuls mensuels', ax=ax2) - ax2.set(ylabel="cumuls (sur la durée, 1 j ou 1 mois)") - - for debut_rect, valeur in zip(ts_mensuel.index, ts_mensuel): - if debut_rect.month in [12, 1, 2]: - code_couleur = 'blue' - elif debut_rect.month in [3, 4, 5]: - code_couleur = 'pink' - elif debut_rect.month in [6, 7, 8]: - code_couleur = 'yellow' - else: - code_couleur = 'orange' - - print(debut_rect, valeur, code_couleur) - # patch = patches.Rectangle(xy, w, h, linewidth=1, edgecolor=edgecolor, facecolor=facecolor) - if trace_rect_mensuels: - ax2.add_patch(Rectangle((debut_rect, 0.1), -timedelta(days=30), valeur, color=code_couleur, alpha=0.8)) - - # pause=input("fin des rectangles par mois ! ") - """ - # avec PatchCollection (pb avec dates ??) - notre_cmap = ListedColormap(color_list) - patches_collection = PatchCollection(patch_list, cmap=notre_cmap, alpha=1) - patches_collection.set_array(np.arange(len(patch_list))) - ax1.add_collection(patches_collection, zorder=5) - """ - ts_journalier.plot(color='red', ax=ax2, label='cumul journalier') - - (handles2, labels2) = plt.gca().get_legend_handles_labels() - # ax1.plot(ts_journalier.index, ts_journalier, label=' pyplot', - # ls=':', color='yellow', lw=5, alpha=0.5) - - # ax1.errorbar(ts_journalier.index, ts_journalier, label=' moyenne journalière par errorbar', xerr=timedelta(days=1), - # ls='None', color='orange', marker=">", xuplims=True) - - # Legende commune - # plt.gca().legend(handles+handles2,labels+labels2) - box = plt.gca().get_position() - plt.gca().set_position([box.x0, box.y0, box.width * 0.8, box.height]) - fig.legend(loc=7) - - # max_valeur = max(df_lamedeau["lames d'eau en mm"].max(), df_mensuel["lames d'eau en mm"].max()) - # min_valeur = min(0, df_lamedeau["lames d'eau en mm"].min(), df_mensuel["lames d'eau en mm"].min()) - # ax1.set_ylim(min_valeur, df_mensuel["lames d'eau en mm"].max()*1.1) - # ax1.set_ylim(min_valeur, max_valeur * 1.1) - - ax1.set_ylim(0, ts_lamedeau.max() * 1.1) - ax2.set_ylim(0, ts_mensuel.max() * 1.1) - - # jours : Memoruy Error avec vlines ? - """ - color_list = [] - for debut_rect, valeur in zip(df_journalier.index, df_journalier["lames d'eau en mm"]): - if debut_rect.month in [12, 1, 2]: - code_couleur = 'blue' - elif debut_rect.month in [3, 4, 5]: - code_couleur = 'pink' - elif debut_rect.month in [6, 7, 8]: - code_couleur = 'yellow' - else: - code_couleur = 'orange' + # format the coords message box + def un_chiffre_apres_la_virgule(x): + # return '%1.1f unités' % x + return f"{x:.1f} mm/5 min" - color_list.append(code_couleur) + # imports spécifiques + from mpl_toolkits.axes_grid1 import host_subplot + import mpl_toolkits.axisartist as AA + + locator = mdates.AutoDateLocator(minticks=3, maxticks=12) + formatter = mdates.ConciseDateFormatter(locator) + + fig = plt.figure(dpi=100) # , frameon=False figsize=(20, 4), + fig.patch.set_facecolor('white') + fig.subplots_adjust(right=0.75, bottom=0.15) # on définit des marges + fig.suptitle(info_chroniques + "\n Rééchantillonnages sur pdt fixes, en utilisant pandas") + + # VIGNETTE DU HAUT + host_original = host_subplot(211, axes_class=AA.Axes) + par_j = host_original.twinx() + par_m = host_original.twinx() + par_a = host_original.twinx() + + offset = 40 + new_fixed_axis_j = par_j.get_grid_helper().new_fixed_axis + par_j.axis["right"] = new_fixed_axis_j(loc="right", axes=par_j, offset=(0, 0)) + par_j.axis["right"].toggle(all=True) + new_fixed_axis_m = par_m.get_grid_helper().new_fixed_axis + par_m.axis["right"] = new_fixed_axis_m(loc="right", axes=par_m, offset=(offset, 0)) + par_m.axis["right"].toggle(all=True) + new_fixed_axis_a = par_a.get_grid_helper().new_fixed_axis + par_a.axis["right"] = new_fixed_axis_a(loc="right", axes=par_a, offset=(2*offset, 0)) + par_a.axis["right"].toggle(all=True) + + # vignette du bas + ax_unique = fig.add_subplot(212, sharex =host_original) + + # tracé donnée à la résolution originale, + ts = pd.Series(data=valeurs, index=dates, name=info_chroniques) + couleur_originale = 'purple' + host_original.set_ylabel("Données originales", color=couleur_originale) + host_original.step(dates, valeurs, c=couleur_originale) + ax_unique.set_ylabel("Max sur pas de temps fixe", color='black') + ax_unique.step(dates, valeurs, c=couleur_originale, label="résolution du fichier") + + dico_resolutions = {"jour":"1D", "mois":"1M", "année":"1Y"} + dico_resolutions_couleurs = {"jour": "blue", "mois": "green", "année": "orange"} + + # vignette du haut, moyennes + + par_j.set_ylabel("Moyennes journalières", color=dico_resolutions_couleurs["jour"]) + par_m.set_ylabel("Moyennes mensuelles", color=dico_resolutions_couleurs["mois"]) + par_a.set_ylabel("Moyennes annuelles", color=dico_resolutions_couleurs["année"]) + + ts_j_moy = ts.resample("1D").mean() + print(f"Rééchantillonnage par moyenne sur pas de temps {'jour'}, première date = {ts_j_moy.index[0]}") + par_j.plot(ts_j_moy.index.to_pydatetime(), ts_j_moy.to_numpy(), c=dico_resolutions_couleurs["jour"], + marker='*', ls="None") + par_j.step(ts_j_moy.index.to_pydatetime(), ts_j_moy.to_numpy(), c=dico_resolutions_couleurs["jour"], + label="jour", where='post') + + ts_m_moy = ts_j_moy.resample("1M").mean() + print(f"Rééchantillonnage par moyenne sur pas de temps {'mois'}, première date = {ts_m_moy.index[0]}") + par_m.plot(ts_m_moy.index.shift(1, freq='D').to_pydatetime(), ts_m_moy.to_numpy(), c=dico_resolutions_couleurs["mois"], + marker='*', ls="None") + par_m.step(ts_m_moy.index.shift(1, freq='D').to_pydatetime(), ts_m_moy.to_numpy(), c=dico_resolutions_couleurs["mois"], + label="mois") + + ts_a_moy = ts_m_moy.resample("1Y").mean() + print(f"Rééchantillonnage par moyenne sur pas de temps {'année'}, première date = {ts_a_moy.index[0]}") + par_a.plot(ts_a_moy.index.shift(1, freq='D').to_pydatetime(), ts_a_moy.to_numpy(), c=dico_resolutions_couleurs["année"], + marker='*', ls="None") + par_a.step(ts_a_moy.index.shift(1, freq='D').to_pydatetime(), ts_a_moy.to_numpy(), c=dico_resolutions_couleurs["année"], + label="année") + + # vignette du bas, max + ts_j_max = ts.resample("1D").max() + print(f"Rééchantillonnage par max sur pas de temps {'jour'}, première date = {ts_j_moy.index[0]}") + # ax_resample_moy.step(ts_moy.index, ts_moy.to_numpy(), c=dico_resolutions_couleurs[duree], label=duree) + ax_unique.plot(ts_j_max.index.to_pydatetime(), ts_j_max.to_numpy(), c=dico_resolutions_couleurs["jour"], + marker='*', ls="None") + ax_unique.step(ts_j_max.index.to_pydatetime(), ts_j_max.to_numpy(), c=dico_resolutions_couleurs["jour"], + where='post') + + ts_m_max = ts_j_max.resample("1M").max() + print(f"Rééchantillonnage par max sur pas de temps {'mois'}, première date = {ts_m_moy.index[0]}") + # ax_resample_moy.step(ts_moy.index, ts_moy.to_numpy(), c=dico_resolutions_couleurs[duree], label=duree) + ax_unique.plot(ts_m_max.index.shift(1, freq='D').to_pydatetime(), ts_m_max.to_numpy(), c=dico_resolutions_couleurs["mois"], + marker='*', ls="None") + ax_unique.step(ts_m_max.index.shift(1, freq='D').to_pydatetime(), ts_m_max.to_numpy(), c=dico_resolutions_couleurs["mois"]) + ts_a_max = ts_m_max.resample("1Y").max() + print(f"Rééchantillonnage par max sur pas de temps {'année'}, première date = {ts_a_moy.index[0]}") + ax_unique.plot(ts_a_max.index.shift(1, freq='D').to_pydatetime(), ts_a_max.to_numpy(), c=dico_resolutions_couleurs["année"], + marker='*', ls="None") + ax_unique.step(ts_a_max.index.shift(1, freq='D').to_pydatetime(), ts_a_max.to_numpy(), c=dico_resolutions_couleurs["année"]) + + host_original.xaxis.set_major_locator(locator) + host_original.xaxis.set_major_formatter(formatter) + + fig.legend(loc='lower center', ncol=4) + + host_original.format_ydata = un_chiffre_apres_la_virgule - # colors avec un s car il y a une liste - ax2.vlines(df_journalier.index, 0, df_journalier["lames d'eau en mm"], colors=color_list) - """ - temps = datetime.now() - debut plt.show() - print("temps écoulé Pandas avec mois en rectangles", temps) - # plt.close(fig) - - -def trace_mensuel_avec_hlines(ts_lamedeau, ts_mensuel, ts_journalier=None, trace_hlines=False): - debut = datetime.now() +def tracer_une_longue_serie_lame_deau_en3_resolutions_verifs(dates, valeurs, info_resolution_min=5, couverture_mini = 0.8, info_chroniques="lames d'eau radar, mm en 5 min"): """ - si DF - ax1 = df_lamedeau.loc[:, cols[1]].plot(label=cols[1], - title="Avec rectangles,5 min et cumuls mensuels (pas de temps fixe)", - color='purple', zorder=10) + Cas de lames d'eau : on aggrège par CUMUL + solutions proposées au pb identifié : + + boucle "propre", possible car on ne distingue plus le cas journalier des autres + + vérif nb points disponibles avec resample().count + https://stackoverflow.com/questions/49019245/resample-pandas-with-minimum-required-number-of-observations + todo : reste un défaut, les 0 ne sont pas forcément alignés sur la même horizontale ! """ - fig = plt.figure(figsize=(7, 4)) - # on crée un emplacement vide - gs = gridspec.GridSpec(nrows=2, ncols=1, height_ratios=[10, 1]) - ax1 = fig.add_subplot(gs[0]) - ts_lamedeau.plot(title="tracé cumuls_mensuel_avec_hlines ", label="lames d'eau à 5 min", color='purple', - zorder=10, ax=ax1) - ax1.set(ylabel="Lames d'eau à 5 min") - (handles, labels) = plt.gca().get_legend_handles_labels() - - # pas possible de mettre un 2e axe rien qu'avec des rectangles... DONC il faut mettre une ligne "guide" avec un plot - # moyenne_des_mois = ds_mensuel["lames d'eau en mm"].mean() - date_debut_pd = ts_lamedeau.index[0] - date_fin_pd = ts_lamedeau.index[-1] - - ax2 = ax1.twinx() - ts_debutfin = pd.Series([ts_mensuel.mean(), ts_mensuel.mean()], index=[date_debut_pd, date_fin_pd]) - ts_debutfin.plot(ls=':', label='moyenne des cumuls mensuels', ax=ax2) - ax2.set(ylabel="cumuls (sur la durée, 1 j ou 1 mois)") + # format the coords message box + def un_chiffre_apres_la_virgule(x): + # return '%1.1f unités' % x + return f"{x:.1f} mm/5 min" - codes_couleur = [] - for debut_rect, valeur in zip(ts_mensuel.index, ts_mensuel): - if debut_rect.month in [12, 1, 2]: - code_couleur = 'blue' - elif debut_rect.month in [3, 4, 5]: - code_couleur = 'pink' - elif debut_rect.month in [6, 7, 8]: - code_couleur = 'yellow' + # imports spécifiques + from mpl_toolkits.axes_grid1 import host_subplot + import mpl_toolkits.axisartist as AA + + locator = mdates.AutoDateLocator(minticks=3, maxticks=12) + formatter = mdates.ConciseDateFormatter(locator) + + fig = plt.figure(dpi=100) # , frameon=False figsize=(20, 4), + fig.patch.set_facecolor('white') + fig.subplots_adjust(right=0.75, bottom=0.15) # on définit des marges + fig.suptitle(info_chroniques + f"\n Rééchantillonnages sur pdt fixes, taux de couverture mini = {int(couverture_mini*100)}%") + + # VIGNETTE DU HAUT + host_original = host_subplot(211, axes_class=AA.Axes) + par_j = host_original.twinx() + par_m = host_original.twinx() + par_a = host_original.twinx() + + offset = 40 + new_fixed_axis_j = par_j.get_grid_helper().new_fixed_axis + par_j.axis["right"] = new_fixed_axis_j(loc="right", axes=par_j, offset=(0, 0)) + par_j.axis["right"].toggle(all=True) + new_fixed_axis_m = par_m.get_grid_helper().new_fixed_axis + par_m.axis["right"] = new_fixed_axis_m(loc="right", axes=par_m, offset=(offset, 0)) + par_m.axis["right"].toggle(all=True) + new_fixed_axis_a = par_a.get_grid_helper().new_fixed_axis + par_a.axis["right"] = new_fixed_axis_a(loc="right", axes=par_a, offset=(2*offset, 0)) + par_a.axis["right"].toggle(all=True) + + # vignette du bas + ax_unique = fig.add_subplot(212, sharex =host_original) + + # tracé donnée à la résolution originale, + ts = pd.Series(data=valeurs, index=dates, name=info_chroniques) + couleur_originale = 'purple' + host_original.set_ylabel("Données originales", color=couleur_originale) + host_original.step(dates, valeurs, c=couleur_originale) + ax_unique.set_ylabel("Max sur pas de temps fixe", color='black') + ax_unique.step(dates, valeurs, c=couleur_originale, label="résolution du fichier") + + dico_resolutions = {"jour":"1D", "mois":"1M", "année":"1Y"} + dico_resolutions_couleurs = {"jour": "blue", "mois": "green", "année": "orange"} + + # vignette du haut, moyennes + + par_j.set_ylabel("Cumuls journaliers", color=dico_resolutions_couleurs["jour"]) + par_m.set_ylabel("Cumuls mensuels", color=dico_resolutions_couleurs["mois"]) + par_a.set_ylabel("Cumuls annuels", color=dico_resolutions_couleurs["année"]) + + nb_minutes_attendues_par_jour = couverture_mini * 24*60 / info_resolution_min + + for ((reso, code_reso), axe_parasite) in zip(dico_resolutions.items(), [par_j, par_m, par_a]): + + # vignette du haut, moyennes + df_reso_moy = ts.resample(code_reso).agg(['sum', 'count']) # une colonne moyenne, une colonne nombre de points dans la fenêtre + + # définition d'un "masque" de booléens par une condition + if reso=="jour": + invalid = df_reso_moy['count'] <= nb_minutes_attendues_par_jour + elif reso=="mois": + invalid = df_reso_moy['count'] <= df_reso_moy.index.days_in_month * nb_minutes_attendues_par_jour else: - code_couleur = 'orange' + invalid = df_reso_moy['count'] <= 365 * nb_minutes_attendues_par_jour - codes_couleur.append(code_couleur) + # restrict to the sum and null out invalid entries, using invalid as a mask + # https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy + df_reso_moy.loc[invalid, 'sum'] = np.nan + ts_reso_moy = df_reso_moy['sum'] + print(f"Rééchantillonnage par moyenne sur pas de temps {reso}, première date = {ts_reso_moy.index[0]}") + axe_parasite.plot(ts_reso_moy.index.shift(1, freq='D').to_pydatetime(), ts_reso_moy.to_numpy(), c=dico_resolutions_couleurs[reso], + marker='*', ls="None") + axe_parasite.step(df_reso_moy.index.shift(1, freq='D').to_pydatetime(), ts_reso_moy.to_numpy(), c=dico_resolutions_couleurs[reso], + label=reso) # , where='post') - print(debut_rect, valeur, code_couleur) + # vignette du bas, max - if trace_hlines: - ax2.hlines(y=ts_mensuel, xmax=ts_mensuel.index, - xmin=[date - timedelta(days=30) for date in ts_mensuel.index], - label="cumuls mensuels", color=codes_couleur, lw=3, alpha=0.8) + ts_reso_max = ts.resample(code_reso).max() + ts_reso_max[invalid] = np.nan + print(f"Rééchantillonnage par max sur pas de temps {'jour'}, première date = {ts_reso_max[0]}") + ax_unique.plot(ts_reso_max.index.shift(1, freq='D').to_pydatetime(), ts_reso_max.to_numpy(), c=dico_resolutions_couleurs[reso], + marker='*', ls="None") + ax_unique.step(ts_reso_max.index.shift(1, freq='D').to_pydatetime(), ts_reso_max.to_numpy(), c=dico_resolutions_couleurs[reso]) - ts_journalier.plot(color='red', ax=ax2, label='cumuls journaliers') - (handles2, labels2) = plt.gca().get_legend_handles_labels() + host_original.xaxis.set_major_locator(locator) + host_original.xaxis.set_major_formatter(formatter) - # Legende commune + fig.legend(loc='lower center', ncol=4) - plt.legend(handles + handles2, labels + labels2, bbox_to_anchor=(1, 0.01), loc="lower right", # mode="expand", - bbox_transform=fig.transFigure, ncol=2) - """ - box = plt.gca().get_position() - plt.gca().set_position([box.x0, box.y0, box.width * 0.8, box.height]) - fig.legend(loc=7) - """ - ax1.set_ylim(0, ts_lamedeau.max() * 1.1) - ax2.set_ylim(0, ts_mensuel.max() * 1.1) + host_original.format_ydata = un_chiffre_apres_la_virgule - temps = datetime.now() - debut plt.show() - print("temps écoulé Pandas avec mois en rectangles", temps) - # plt.clf() - - -def HautBas_trace_mensuel_avec_rectangles(ts_lamedeau, ts_mensuel, ts_journalier=None, trace_rect_mensuels=False): - debut = datetime.now() +def tracer_une_longue_serie_q_en3_resolutions_verifs(dates, valeurs, info_resolution_min=60, couverture_mini = 0.8, info_chroniques="débit moyen sur une journée"): """ - si DF - ax1 = df_lamedeau.loc[:, cols[1]].plot(label=cols[1], - title="Avec rectangles,5 min et cumuls mensuels (pas de temps fixe)", - color='purple', zorder=10) - """ - # d'après cheatsheet pbpython - fig, (ax_cumul, ax_5min) = plt.subplots(nrows=2, ncols=1, sharex=True, figsize=(7, 4)) - fig.suptitle("Lames d'eau à 5 min (bas) et cumuls journaliers et mensuels (haut)") - ax_cumul.set(ylabel="cumuls") - ax_5min.set(ylabel="lame d'eau \n à 5 min") - - ts_lamedeau.plot(label="lames d'eau à 5 min", color='purple', zorder=10, ax=ax_5min) - plt.legend() + Cas de débits : on aggrège par MOYENNE + solutions proposées au pb identifié : + + boucle "propre", possible car on ne distingue plus le cas journalier des autres + + vérif nb points disponibles avec resample().count - # pas possible de mettre un 2e axe rien qu'avec des rectangles... DONC il faut mettre une ligne "guide" avec un plot - # moyenne_des_mois = ds_mensuel["lames d'eau en mm"].mean() - date_debut_pd = ts_lamedeau.index[0] - date_fin_pd = ts_lamedeau.index[-1] + https://stackoverflow.com/questions/49019245/resample-pandas-with-minimum-required-number-of-observations + todo : reste un défaut, les 0 ne sont pas forcément alignés sur la même horizontale ! + """ - ts_debutfin = pd.Series([ts_mensuel.mean(), ts_mensuel.mean()], index=[date_debut_pd, date_fin_pd]) - ts_debutfin.plot(ls=':', label='moyenne des cumuls mensuels', ax=ax_cumul) + # format the coords message box + def un_chiffre_apres_la_virgule(x): + # return '%1.1f unités' % x + return f"{x:.1f} mm/5 min" - # TESTS : on enlève les rectangles - for debut_rect, valeur in zip(ts_mensuel.index, ts_mensuel): - if debut_rect.month in [12, 1, 2]: - code_couleur = 'blue' - elif debut_rect.month in [3, 4, 5]: - code_couleur = 'pink' - elif debut_rect.month in [6, 7, 8]: - code_couleur = 'yellow' + # imports spécifiques + from mpl_toolkits.axes_grid1 import host_subplot + import mpl_toolkits.axisartist as AA + + locator = mdates.AutoDateLocator(minticks=3, maxticks=12) + formatter = mdates.ConciseDateFormatter(locator) + + fig = plt.figure(dpi=100) # , frameon=False figsize=(20, 4), + fig.patch.set_facecolor('white') + fig.subplots_adjust(right=0.75, bottom=0.15) # on définit des marges + fig.suptitle(info_chroniques + f"\n Rééchantillonnages sur pdt fixes, taux de couverture mini = {int(couverture_mini*100)}%") + + # VIGNETTE DU HAUT + host_original = host_subplot(211, axes_class=AA.Axes) + par_j = host_original.twinx() + par_m = host_original.twinx() + par_a = host_original.twinx() + + offset = 40 + new_fixed_axis_j = par_j.get_grid_helper().new_fixed_axis + par_j.axis["right"] = new_fixed_axis_j(loc="right", axes=par_j, offset=(0, 0)) + par_j.axis["right"].toggle(all=True) + new_fixed_axis_m = par_m.get_grid_helper().new_fixed_axis + par_m.axis["right"] = new_fixed_axis_m(loc="right", axes=par_m, offset=(offset, 0)) + par_m.axis["right"].toggle(all=True) + new_fixed_axis_a = par_a.get_grid_helper().new_fixed_axis + par_a.axis["right"] = new_fixed_axis_a(loc="right", axes=par_a, offset=(2*offset, 0)) + par_a.axis["right"].toggle(all=True) + + # vignette du bas + ax_unique = fig.add_subplot(212, sharex =host_original) + + # tracé donnée à la résolution originale, + ts = pd.Series(data=valeurs, index=dates, name=info_chroniques) + couleur_originale = 'purple' + host_original.set_ylabel("Données originales", color=couleur_originale) + host_original.step(dates, valeurs, c=couleur_originale) + ax_unique.set_ylabel("Max sur pas de temps fixe", color='black') + ax_unique.step(dates, valeurs, c=couleur_originale, label="résolution du fichier") + + dico_resolutions = {"jour":"1D", "mois":"1M", "année":"1Y"} + dico_resolutions_couleurs = {"jour": "blue", "mois": "green", "année": "orange"} + + # vignette du haut, moyennes + + par_j.set_ylabel("Moyennes journalières", color=dico_resolutions_couleurs["jour"]) + par_m.set_ylabel("Moyennes mensuelles", color=dico_resolutions_couleurs["mois"]) + par_a.set_ylabel("Moyennes annuelles", color=dico_resolutions_couleurs["année"]) + + nb_minutes_attendues_par_jour = couverture_mini * 24*60 / info_resolution_min + + for ((reso, code_reso), axe_parasite) in zip(dico_resolutions.items(), [par_j, par_m, par_a]): + + # vignette du haut, moyennes + df_reso_moy = ts.resample(code_reso).agg(['mean', 'count']) # une colonne moyenne, une colonne nombre de points dans la fenêtre + + # définition d'un "masque" de booléens par une condition + if reso=="jour": + invalid = df_reso_moy['count'] <= nb_minutes_attendues_par_jour + elif reso=="mois": + invalid = df_reso_moy['count'] <= df_reso_moy.index.days_in_month * nb_minutes_attendues_par_jour else: - code_couleur = 'orange' + invalid = df_reso_moy['count'] <= 365 * nb_minutes_attendues_par_jour - print(debut_rect, valeur, code_couleur) - # patch = patches.Rectangle(xy, w, h, linewidth=1, edgecolor=edgecolor, facecolor=facecolor) - if trace_rect_mensuels: - ax_cumul.add_patch(Rectangle((debut_rect, 0.1), -timedelta(days=30), valeur, color=code_couleur, alpha=0.8)) + # restrict to the sum and null out invalid entries, using invalid as a mask + # https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy + df_reso_moy.loc[invalid, 'mean'] = np.nan + ts_reso_moy = df_reso_moy['mean'] + print(f"Rééchantillonnage par moyenne sur pas de temps {reso}, première date = {ts_reso_moy.index[0]}") + axe_parasite.plot(ts_reso_moy.index.shift(1, freq='D').to_pydatetime(), ts_reso_moy.to_numpy(), c=dico_resolutions_couleurs[reso], + marker='*', ls="None") + axe_parasite.step(df_reso_moy.index.shift(1, freq='D').to_pydatetime(), ts_reso_moy.to_numpy(), c=dico_resolutions_couleurs[reso], + label=reso) # , where='post') - # pause=input("fin des rectangles par mois ! ") + # vignette du bas, max - # TESTS : on enlève aussi le journalier - plot ou vlines ? - # ts_journalier.plot(color='red', ax=ax_cumul, label='cumul journalier') + ts_reso_max = ts.resample(code_reso).max() + ts_reso_max[invalid] = np.nan + print(f"Rééchantillonnage par max sur pas de temps {'jour'}, première date = {ts_reso_max[0]}") + ax_unique.plot(ts_reso_max.index.shift(1, freq='D').to_pydatetime(), ts_reso_max.to_numpy(), c=dico_resolutions_couleurs[reso], + marker='*', ls="None") + ax_unique.step(ts_reso_max.index.shift(1, freq='D').to_pydatetime(), ts_reso_max.to_numpy(), c=dico_resolutions_couleurs[reso]) - # ax_cumul.plot(ts_journalier.index, ts_journalier, label=' pyplot', - # ls=':', color='yellow', lw=5, alpha=0.5) - ax_cumul.vlines(x=ts_journalier.index, ymin=0, ymax=ts_journalier, label="cumuls journaliers (vlines)", - color='red', lw=1, alpha=0.8) - - ax_cumul.legend() - - # max_valeur = max(df_lamedeau["lames d'eau en mm"].max(), df_mensuel["lames d'eau en mm"].max()) - # min_valeur = min(0, df_lamedeau["lames d'eau en mm"].min(), df_mensuel["lames d'eau en mm"].min()) - # ax1.set_ylim(min_valeur, df_mensuel["lames d'eau en mm"].max()*1.1) - # ax1.set_ylim(min_valeur, max_valeur * 1.1) - - ax_5min.set_ylim(0, ts_lamedeau.max() * 1.1) - ax_cumul.set_ylim(0, ts_mensuel.max() * 1.1) + host_original.xaxis.set_major_locator(locator) + host_original.xaxis.set_major_formatter(formatter) - # jours : Memoruy Error avec vlines ? - """ - color_list = [] - for debut_rect, valeur in zip(df_journalier.index, df_journalier["lames d'eau en mm"]): - if debut_rect.month in [12, 1, 2]: - code_couleur = 'blue' - elif debut_rect.month in [3, 4, 5]: - code_couleur = 'pink' - elif debut_rect.month in [6, 7, 8]: - code_couleur = 'yellow' - else: - code_couleur = 'orange' + fig.legend(loc='lower center', ncol=4) - color_list.append(code_couleur) + host_original.format_ydata = un_chiffre_apres_la_virgule - # colors avec un s car il y a une liste - ax2.vlines(df_journalier.index, 0, df_journalier["lames d'eau en mm"], colors=color_list) - """ - temps = datetime.now() - debut plt.show() - print("temps écoulé Pandas avec mois en rectangles", temps) - # plt.clf() +def get_month_end(dt): + first_of_month = datetime(dt.year, dt.month, 1) + next_month_date = first_of_month + timedelta(days=32) + new_dt = datetime(next_month_date.year, next_month_date.month, 1) + return new_dt - timedelta(days=1) -def HautBas_trace_mensuel_avec_hlines(ts_lamedeau, ts_mensuel, ts_journalier=None, trace_hlines_mensuels=True): - debut = datetime.now() +def dernier_jour_du_mois(dt): + first_of_month = datetime(dt.year, dt.month, 1) + next_month_date = first_of_month + timedelta(days=32) + new_dt = datetime(next_month_date.year, next_month_date.month, 1) + return (new_dt - timedelta(days=1)).day +# moyennes par MOIS avec des rectangles +def trace_mensuel_avec_rectangles(ts_original, trace_journalier=False, trace_rect_mensuels=False): """ - si DF - ax1 = df_lamedeau.loc[:, cols[1]].plot(label=cols[1], - title="Avec rectangles,5 min et cumuls mensuels (pas de temps fixe)", - color='purple', zorder=10) + test ici : Rectangles, axline et hlines """ - # d'après cheatsheet pbpython - fig, (ax_cumul, ax_5min) = plt.subplots(nrows=2, ncols=1, sharex=True, figsize=(7, 4)) - fig.suptitle("Lames d'eau à 5 min (bas) et cumuls journaliers et mensuels (haut)") - ax_cumul.set(ylabel="cumuls") - ax_5min.set(ylabel="lame d'eau \n à 5 min") + debut = datetime.now() - ts_lamedeau.plot(label="lames d'eau à 5 min", color='purple', zorder=10, ax=ax_5min) - plt.legend() + ts_mensuel= ts_original.resample("1M").sum() + ts_journalier = ts_original.resample("1D").sum() - # pas possible de mettre un 2e axe rien qu'avec des rectangles... DONC il faut mettre une ligne "guide" avec un plot - # moyenne_des_mois = ds_mensuel["lames d'eau en mm"].mean() - date_debut_pd = ts_lamedeau.index[0] - date_fin_pd = ts_lamedeau.index[-1] + fig, ax1 = plt.subplots(figsize=(7, 4)) + fig.suptitle("chroniques_mois_rectangles") - ts_debutfin = pd.Series([ts_mensuel.mean(), ts_mensuel.mean()], index=[date_debut_pd, date_fin_pd]) + ax1.plot(ts_original.index.to_pydatetime(), ts_original.to_numpy(), label="lames d'eau à 5 min", + color='purple', zorder=10) + ax1.set(ylabel="Lames d'eau à 5 min") - ts_debutfin.plot(ls=':', label='moyenne des cumuls mensuels', ax=ax_cumul) + (handles1, labels1) = plt.gca().get_legend_handles_labels() - # TESTS : on enlève les rectangles - couleurs = [] - for debut_rect, valeur in zip(ts_mensuel.index, ts_mensuel): - if debut_rect.month in [12, 1, 2]: - code_couleur = 'blue' - elif debut_rect.month in [3, 4, 5]: - code_couleur = 'pink' - elif debut_rect.month in [6, 7, 8]: - code_couleur = 'yellow' - else: - code_couleur = 'orange' - couleurs.append(code_couleur) - # patch = patches.Rectangle(xy, w, h, linewidth=1, edgecolor=edgecolor, facecolor=facecolor) + # pas possible de mettre un 2e axe rien qu'avec des rectangles... DONC il faut mettre une ligne "guide" avec un plot + ax2 = ax1.twinx() + ax2.axhline(ts_mensuel.mean(), color="red", linestyle="--", label="moyenne des moyennes mensuelles") + + ax2.set(ylabel="cumuls sur la durée (1 j ou 1 mois)") + + if trace_rect_mensuels: + print("tracé des cumuls mensuels par des rectangles") + for date_debut_rect, valeur in zip(ts_mensuel.index.to_pydatetime(), ts_mensuel): + if date_debut_rect.month in [12, 1, 2]: + code_couleur = 'blue' + elif date_debut_rect.month in [3, 4, 5]: + code_couleur = 'pink' + elif date_debut_rect.month in [6, 7, 8]: + code_couleur = 'yellow' + else: + code_couleur = 'orange' + + nb_jours_du_mois = dernier_jour_du_mois(date_debut_rect) + print(date_debut_rect, valeur, code_couleur, nb_jours_du_mois) + # patch = patches.Rectangle(xy, w, h, linewidth=1, edgecolor=edgecolor, facecolor=facecolor) + ax2.add_patch(Rectangle((date_debut_rect+timedelta(days=1), 0.1), -timedelta(days=nb_jours_du_mois), valeur, color=code_couleur, alpha=0.8)) + + if trace_journalier: + print("tracé des cumuls journaliers par des verticales") + ax2.vlines(x=ts_journalier.index.to_pydatetime(), ymin=0, ymax=ts_journalier.to_numpy(), color="blue", linestyle="--", label="moyennes journalières") + + trace_hlines = False + if trace_hlines: + print("tracé des cumuls mensuels par des segments horizontaux") + ax2.hlines(y=ts_mensuel, xmax=ts_mensuel.index, + xmin=[date - timedelta(days=30) for date in ts_mensuel.index.to_pydatetime()], + label="cumuls mensuels", lw=3, alpha=0.8) # color=codes_couleur, - # TESTS : on enlève aussi le journalier - plot ou vlines ? - # ts_journalier.plot(color='red', ax=ax_cumul, label='cumul journalier') + (handles2, labels2) = plt.gca().get_legend_handles_labels() - # ax_cumul.plot(ts_journalier.index, ts_journalier, label=' pyplot', - # ls=':', color='yellow', lw=5, alpha=0.5) - if trace_hlines_mensuels: - ax_cumul.hlines(y=ts_mensuel, xmax=ts_mensuel.index, - xmin=[date - timedelta(days=30) for date in ts_mensuel.index], label="cumuls mensuels (hlines)", - color=couleurs, lw=1, alpha=0.9) - ax_cumul.vlines(x=ts_journalier.index, ymin=0, ymax=ts_journalier, label="cumuls journaliers (vlines)", - color='red', lw=1, alpha=0.8) + # Legende commune + plt.gca().legend(handles1+handles2, labels1+labels2) - ax_cumul.legend() + #fig.legend(loc=7) - # max_valeur = max(df_lamedeau["lames d'eau en mm"].max(), df_mensuel["lames d'eau en mm"].max()) - # min_valeur = min(0, df_lamedeau["lames d'eau en mm"].min(), df_mensuel["lames d'eau en mm"].min()) - # ax1.set_ylim(min_valeur, df_mensuel["lames d'eau en mm"].max()*1.1) - # ax1.set_ylim(min_valeur, max_valeur * 1.1) + ax1.set_ylim(0, ts_original.max() * 1.1) + ax2.set_ylim(0, max(ts_mensuel.max(), ts_journalier.max()) * 1.1) - ax_5min.set_ylim(0, ts_lamedeau.max() * 1.1) - ax_cumul.set_ylim(0, ts_mensuel.max() * 1.1) + # jours : Memory Error avec vlines ? temps = datetime.now() - debut plt.show() - print("temps écoulé Pandas avec mois en hlines", temps) - + print("temps écoulé Pandas avec mois en rectangles", temps) + # plt.close(fig) -# todo : HautBas_trace_mensuel_avec_hlines_2y_en_haut -def HautBas_trace_mensuel_avec_hlines_2y_en_haut(ts_lamedeau, ts_mensuel, ts_journalier=None, - trace_hlines_mensuels=True): - debut = datetime.now() +def HautBas_trace_mensuel_avec_rectangles(ts_original, agg_par_cumul=True): """ - si DF - ax1 = df_lamedeau.loc[:, cols[1]].plot(label=cols[1], - title="Avec rectangles,5 min et cumuls mensuels (pas de temps fixe)", - color='purple', zorder=10) + Avec des imperfections à corriger : + * étiquettes de dates : ajouter ConciseDateFormatter + * étiquettes des axes des y """ - # https://stackoverflow.com/questions/22511550/gridspec-with-shared-axes-in-python - # fig, (ax_cumul, ax_5min) = plt.subplots(nrows=3, ncols=1, sharex=True, figsize=(7,4), gridspec_kw={'height_ratios': [5,5, 1]}) - fig, (ax_cumul, ax_5min) = plt.subplots(nrows=2, ncols=1, sharex=True, figsize=(8, 5)) - # tight_layout docs: [left, bottom, right, top] in normalized (0, 1) figure coordinates - # You can increase the space between suptitle and axes even more by lowering the rightest value: - fig.set_tight_layout({'rect': [0, .15, 1, 0.9], 'pad': 1.5, 'h_pad': 1.5}) - # fig.subplots_adjust(bottom=0.9) - fig.suptitle("Lames d'eau à 5 min (bas) et cumuls journaliers et mensuels (haut)") - ax_cumul.set(ylabel="cumuls mensuels)") - ax_5min.set(ylabel="lame d'eau \n à 5 min") - - ts_lamedeau.plot(label="lames d'eau à 5 min", color='purple', zorder=10, ax=ax_5min) - # plt.legend() + debut = datetime.now() + if agg_par_cumul: + ts_mensuel= ts_original.resample("1M").sum() + ts_journalier = ts_original.resample("1D").sum() + else: + ts_mensuel = ts_original.resample("1M").mean() + ts_journalier = ts_original.resample("1D").mean() - # pas possible de mettre un 2e axe rien qu'avec des rectangles... DONC il faut mettre une ligne "guide" avec un plot - # moyenne_des_mois = ds_mensuel["lames d'eau en mm"].mean() - date_debut_pd = ts_lamedeau.index[0] - date_fin_pd = ts_lamedeau.index[-1] + # d'après cheatsheet pbpython + fig, (ax_agg, ax_original) = plt.subplots(nrows=2, ncols=1, sharex=True, figsize=(7, 4)) + if agg_par_cumul: + fig.suptitle("Lames d'eau à 5 min (bas) et cumuls journaliers et mensuels (haut)") + ax_agg.set(ylabel="cumuls") + ax_original.set(ylabel="lame d'eau \n à 5 min") + etiquette="lames d'eau à 5 min" + else: + fig.suptitle("Débit (bas) et débits moyens journaliers et mensuels (haut)") + ax_agg.set(ylabel="moyennes") + ax_original.set(ylabel="débit") + etiquette = "débit" - ts_debutfin = pd.Series([ts_mensuel.mean(), ts_mensuel.mean()], index=[date_debut_pd, date_fin_pd]) + ax_original.plot(ts_original.index.to_pydatetime(), ts_original.to_numpy(), label=etiquette, + color='purple', zorder=10) + plt.legend() - ts_debutfin.plot(ls=':', label='moyenne des cumuls mensuels', ax=ax_cumul) + ax_agg.axhline(ts_mensuel.mean(), color="red", linestyle="--", label="moyenne des moyennes mensuelles") # TESTS : on enlève les rectangles - couleurs = [] - for debut_rect, valeur in zip(ts_mensuel.index, ts_mensuel): - if debut_rect.month in [12, 1, 2]: + for date_debut_rect, valeur in zip(ts_mensuel.index, ts_mensuel): + if date_debut_rect.month in [12, 1, 2]: code_couleur = 'blue' - elif debut_rect.month in [3, 4, 5]: + elif date_debut_rect.month in [3, 4, 5]: code_couleur = 'pink' - elif debut_rect.month in [6, 7, 8]: + elif date_debut_rect.month in [6, 7, 8]: code_couleur = 'yellow' else: code_couleur = 'orange' - couleurs.append(code_couleur) - - # patch = patches.Rectangle(xy, w, h, linewidth=1, edgecolor=edgecolor, facecolor=facecolor) - - # TESTS : on enlève aussi le journalier - plot ou vlines ? - # ts_journalier.plot(color='red', ax=ax_cumul, label='cumul journalier') - - # ax_cumul.plot(ts_journalier.index, ts_journalier, label=' pyplot', - # ls=':', color='yellow', lw=5, alpha=0.5) - if trace_hlines_mensuels: - ax_cumul.hlines(y=ts_mensuel, xmax=ts_mensuel.index, - xmin=[date - timedelta(days=30) for date in ts_mensuel.index], label="cumuls mensuels (hlines)", - color=couleurs, lw=1, alpha=0.9) - ax_cumul.set_ylim(0, ts_mensuel.max() * 1.1) - - ax_cumul_j = ax_cumul.twinx() - ax_cumul_j.set(ylabel=("cumuls journaliers")) - ts_debutfin_j = pd.Series([ts_journalier.mean(), ts_journalier.mean()], index=[date_debut_pd, date_fin_pd]) - - ts_debutfin.plot(ls=':', label='moyenne des cumuls journaliers', ax=ax_cumul_j, color='orange') - ax_cumul_j.vlines(x=ts_journalier.index, ymin=0, ymax=ts_journalier, label="cumuls journaliers (vlines)", - color='red', lw=1, alpha=0.8) - - # ax_cumul.legend() - - # max_valeur = max(df_lamedeau["lames d'eau en mm"].max(), df_mensuel["lames d'eau en mm"].max()) - # min_valeur = min(0, df_lamedeau["lames d'eau en mm"].min(), df_mensuel["lames d'eau en mm"].min()) - # ax1.set_ylim(min_valeur, df_mensuel["lames d'eau en mm"].max()*1.1) - # ax1.set_ylim(min_valeur, max_valeur * 1.1) - - ax_5min.set_ylim(0, ts_lamedeau.max() * 1.1) - ax_cumul.set_ylim(0, ts_mensuel.max() * 1.1) - - # légende de figure - fig.legend(loc='lower center', mode="expand", borderaxespad=0, ncol=2) - - temps = datetime.now() - debut - plt.show() - print("temps écoulé Pandas avec mois en hlines, 2 aces des y en haut", temps) - -def trace_bars_hack_rectangles(ts_lamedeau, ts_mensuel, ts_journalier, hack_rect_mensuels=False): - debut = datetime.now() + nb_jours_du_mois = dernier_jour_du_mois(date_debut_rect) + print(date_debut_rect, valeur, code_couleur) + ax_agg.add_patch(Rectangle((date_debut_rect+timedelta(days=1), 0.1), -timedelta(days=nb_jours_du_mois), valeur, color=code_couleur, alpha=0.8)) - """ - si DF - ax1 = df_lamedeau.loc[:, cols[1]].plot(label=cols[1], - title="Avec rectangles,5 min et cumuls mensuels (pas de temps fixe)", - color='purple', zorder=10) - """ - ax1 = ts_lamedeau.plot(label="lames d'eau à 5 min", - title="Avec collection de rectangles,5 min et cumuls mensuels (pas de temps fixe)", - color='purple', zorder=10) - - # pas possible de mettre un 2e axe rien qu'avec des rectangles... DONC il faut mettre une ligne "guide" avec un plot - - ax2 = ax1.twinx() - - # moyenne_des_mois = ds_mensuel["lames d'eau en mm"].mean() - date_debut_pd = ts_lamedeau.index[0] - date_fin_pd = ts_lamedeau.index[-1] - - ts_debutfin = pd.Series([ts_mensuel.mean(), ts_mensuel.mean()], index=[date_debut_pd, date_fin_pd]) - - ts_debutfin.plot(ls=':', ax=ax2) + ax_j = ax_agg.twinx() + ax_j.vlines(x=ts_journalier.index, ymin=0, ymax=ts_journalier, label="cumuls journaliers (vlines)", + color='red', lw=1, alpha=0.8) - # on prépare les rectangles - liste_rectangles = [] - for debut_rect, valeur in zip(ts_mensuel.index, ts_mensuel): - if debut_rect.month in [12, 1, 2]: - code_couleur = 'blue' - elif debut_rect.month in [3, 4, 5]: - code_couleur = 'pink' - elif debut_rect.month in [6, 7, 8]: - code_couleur = 'yellow' - else: - code_couleur = 'orange' - if hack_rect_mensuels: - ax2.add_patch(Rectangle((debut_rect, 0.1), timedelta(days=30), valeur, color=code_couleur)) - - # print(debut_rect, valeur, code_couleur) - # alternative avec liste_rectangles.append puis ax2.add_collection - # descriptif : patch = patches.Rectangle(xy, w, h, linewidth=1, edgecolor=edgecolor, facecolor=facecolor) - # if hack_rect_mensuels: - # liste_rectangles.append(Rectangle((debut_rect, 0.1), timedelta(days=30), valeur, color=code_couleur)) - - # avec PatchCollection (pb avec dates ??) - # notre_cmap = ListedColormap(color_list) - # patches_collection = PatchCollection(liste_rectangles, match_original=True, alpha=1) - # patches_collection.set_array(np.arange(len(liste_rectangles))) - # ax2.add_collection(patches_collection, zorder=5) - - # ts_journalier.plot(color='red') - # ts_journalier.plot(label=' cumul journalier, pd', - # ls=':', color='yellow', lw=2, alpha=0.5, ax=ax2) - ax2.vlines(x=ts_journalier.index, ymin=0, ymax=ts_journalier, label="cumuls journaliers (vlines)", - color='orange', lw=5, alpha=0.8) + fig.legend() # max_valeur = max(df_lamedeau["lames d'eau en mm"].max(), df_mensuel["lames d'eau en mm"].max()) # min_valeur = min(0, df_lamedeau["lames d'eau en mm"].min(), df_mensuel["lames d'eau en mm"].min()) # ax1.set_ylim(min_valeur, df_mensuel["lames d'eau en mm"].max()*1.1) # ax1.set_ylim(min_valeur, max_valeur * 1.1) - ax1.set_ylim(0, ts_lamedeau.max() * 1.1) - ax2.set_ylim(0, ts_mensuel.max() * 1.1) - plt.legend() - - # jours : Memoruy Error avec vlines ? - """ - color_list = [] - for debut_rect, valeur in zip(df_journalier.index, df_journalier["lames d'eau en mm"]): - if debut_rect.month in [12, 1, 2]: - code_couleur = 'blue' - elif debut_rect.month in [3, 4, 5]: - code_couleur = 'pink' - elif debut_rect.month in [6, 7, 8]: - code_couleur = 'yellow' - else: - code_couleur = 'orange' + ax_original.set_ylim(0, ts_original.max() * 1.1) + ax_agg.set_ylim(0, ts_mensuel.max() * 1.1) + ax_j.set_ylim(0, ts_journalier.max() * 1.1) - color_list.append(code_couleur) - # colors avec un s car il y a une liste - ax2.vlines(df_journalier.index, 0, df_journalier["lames d'eau en mm"], colors=color_list) - """ temps = datetime.now() - debut plt.show() - print("temps écoulé Pandas avec mois en rectangles 'patch collection' :", temps) - - return temps + print("temps écoulé Pandas avec mois en rectangles", temps) + # plt.clf() if __name__ == '__main__': @@ -1013,257 +1051,32 @@ if __name__ == '__main__': print("chemin ", chemin_complet, " non valide") # Deuxième atelier, figure avec plusieurs courbes, 2e axe des y - deuxieme_atelier = True - chemin_complet = askopenfilename() - # donnees_TD_ETP_csv(chemin_complet) - donnees_TD_ETP_2subplots(chemin_complet) - -""" - -# données TD4 ENTPE -# nom_fichier_TD4 = "D:/2020-TraitementChroniques/TestsVisuChroniques/donnees/ChroniquesPluieT.csv" -nom_fichier_TD4 = "D:/2020-TraitementChroniques/TestsVisuChroniques/donnees/donnees.csv" -nom_rep = "D:/2020-TraitementChroniques/TestsVisuChroniques/donnees" - -print("Le chemin existe ? ... ", Path.is_dir(Path(nom_rep))) - -tracer_td4 = False - -if Path.is_file(Path(nom_fichier_TD4)) and tracer_td4: - print(f"traitement du fichier date, P, T : {nom_fichier_TD4} ") - - # DF_ETP = donnees_TD_ETP_besogneux(nom_fichier_TD4) - # DF_ETP = donnees_TD_ETP_elegant(nom_fichier_TD4) - DF_ETP = donnees_TD_ETP_2subplots(nom_fichier_TD4) - - print(DF_ETP.head(10)) -print("phase suivante, fichier binaire de lames d'eau radar") - -tracer_binaire = True - -# recherche du nom du fichier binaire -if Path.is_file(Path(nom_fichier_binaire)) and tracer_binaire: - print(f"nom_chemin_binaire1 : {nom_fichier_binaire} ") - - # vérification des dates - - # lecture des valeurs - # cf fonctions Chronique.lire_chronique Lecture_Patnthere...chronique - uint16_valeurs = None - with open(nom_fichier_binaire, 'rb') as fic_binaire: - try: - uint16_valeurs = np.fromfile(fic_binaire, dtype=np.uint16, count=int(len(liste_dates))) - statut_lecture = "OK" - - except: - statut_lecture = "erreur" - - # VERIF - print("Ensemble des valeurs lues avant traitement (lacunes, diviseur...) : ", set(uint16_valeurs)) - - if statut_lecture == "OK": - - # Traitement des lacunes - nb_lacunes = 0 - for valeur in uint16_valeurs: - if int(valeur) == int(CODE_LACUNE_BINAIRE): - nb_lacunes += 1 - print("Nombre de lacunes = ", nb_lacunes) - - # on travaille avec des LAMES D'EAU en centième de mm, à passer en mm - valeurs = np.array( - [entier / DIVISEUR if entier != int(CODE_LACUNE_BINAIRE) else np.nan for entier in - uint16_valeurs]) - - # tracé - # PlotLy - debut = datetime.now() - if trace_plt: - trace_avec_plt(liste_dates, valeurs) - # trace_avec_plt(liste_dates, valeurs, "stem") - # trace_avec_plt(liste_dates, valeurs, "hlines") - - # Pandas, dont DataShader - # préparation d'un DataFrame avec les données - - if trace_ds or trace_bok or trace_pd: - # toute la liste ou juste DEBUT FIN - # liste_date_pd = pd.DatetimeIndex(liste_dates, freq='infer') - - date_debut_pd = pd.to_datetime(date_premiere_valeur) - date_fin_pd = pd.to_datetime(date_premiere_valeur + (len_of_tvect - 1) * timedelta(minutes=PDT_OBS_EN_MIN)) - print("dates de début et fin", date_debut_pd, date_fin_pd) - - range_dates_pd_pour_index = pd.date_range(date_debut_pd, date_fin_pd, freq='5min') - print(len(range_dates_pd_pour_index), len(valeurs), len_of_tvect) - - # les données doivent être dans un format DataFrame - # ts_lamedeau = pd.Series(valeurs, index=liste_date_pd) - ts_lamedeau = pd.Series(valeurs, index=range_dates_pd_pour_index) - - print(ts_lamedeau.head(10)) - print("Verification de l'analyse des dates : ", date_debut_pd, " mois ", date_debut_pd.month) - - if trace_pd: - # POUR DATAFRAMES - # cols = list(df_lamedeau) - # print(cols) - - tracer_DF_lamedeau_moyennes_mobiles(pd.DataFrame(ts_lamedeau)) - - # Deux courbes - debut = datetime.now() - ax1 = df_lamedeau.loc[:,cols[1]].plot (label=cols[1], title ="Avec pandas, 2 courbes", color='purple') - ax2 = ax1.twinx() - df_lamedeau.loc[:, cols[2]].plot(label=cols[1]) - - plt.show() - temps = datetime.now() - debut - print ("temps écoulé, Pandas 2 courbes (sans création DF):", temps) - - - # Une seule courbe - debut = datetime.now() - df_lamedeau.loc[:, cols[1]].plot(label=cols[1], title="Avec pandas", color='purple') - temps = datetime.now() - debut - plt.show() - - print("temps écoulé Pandas (sans création DF):", temps) - - - # MOYENNES PDT FIXE - # Resample - # https: // towardsdatascience.com / playing - with-time - series - data - in -python - 959e2485bff8 - - print("Cumuls; pdt fixes") - - # ax1 = df_lamedeau.loc[:, cols[1]].plot(label=cols[1], - # title="Avec pandas, données moyennées (pas de temps fixe)", - # color='purple') - - ts_mensuel = ts_lamedeau.resample('M').sum() - ts_journalier = ts_lamedeau.resample('D').sum() - print("trace_3_courbes_avec_pandas(ts_lamedeau, ts_mensuel, ts_journalier)") - trace_3_courbes_avec_pandas(ts_lamedeau, ts_mensuel, ts_journalier) - print("trace_3_courbes_avec_pandas_donc_vlines(ts_lamedeau, ts_mensuel, ts_journalier)") - trace_3_courbes_avec_pandas_donc_vlines(ts_lamedeau, ts_mensuel, ts_journalier) - - # barplots : une catastrophe - - #ax1 = df_lamedeau.loc[:, cols[1]].plot(label=cols[1], - # title="Avec pandas données moyennées (pas de temps fixe)", - # color='purple') - #ax2 = ax1.twinx() - - ax = df_mensuel.loc[:, cols[1]].plot(label=cols[1] + ' cumul mensuel', color='blue', kind='bar') - df_journalier.loc[:, cols[1]].plot(label=cols[1] + ' cumul journalier', color='orange', kind='bar') - # https: // stackoverflow.com / questions / 5902371 / matplotlib - bar - chart - with-dates - # ax.xaxis_date() - - plt.legend() - plt.show() - - - # =============================================================== - # plot_moyenne_mois_avec_rectangles - # trace_mensuel_avec_rectangles marche - print( - " on passe trace_mensuel_avec_rectangles(ts_lamedeau,ts_mensuel,ts_journalier,trace_rect_mensuels=True)") - if False: - trace_mensuel_avec_rectangles(ts_lamedeau, ts_mensuel, ts_journalier, trace_rect_mensuels=True) - - print("trace_mensuel_avec_hlines(ts_lamedeau, ts_mensuel, ts_journalier, trace_hlines=True)") - trace_mensuel_avec_hlines(ts_lamedeau, ts_mensuel, ts_journalier, trace_hlines=True) - print( - "HautBas_trace_mensuel_avec_rectangles(ts_lamedeau, ts_mensuel, ts_journalier, trace_rect_mensuels=True)") - HautBas_trace_mensuel_avec_rectangles(ts_lamedeau, ts_mensuel, ts_journalier, trace_rect_mensuels=True) - - print( - "HautBas_trace_mensuel_avec_hlines(ts_lamedeau, ts_mensuel, ts_journalier, trace_hlines_mensuels=True)") - - HautBas_trace_mensuel_avec_hlines(ts_lamedeau, ts_mensuel, ts_journalier, trace_hlines_mensuels=True) - - # =============================================================== - - # Marche pô, MemoryError même pour les mois ?... - print("et le dernier qui ne marche pas, pb avec collections et ax.add_collection avec un x en dates") - trace_bars_hack_rectangles(ts_lamedeau, ts_mensuel, ts_journalier, hack_rect_mensuels=True) - - # pause = input("on continue vers les jours ?") - - - # jours : memory error ? - - trace_rect_m = False - debut = datetime.now() - ax1 = ts_lamedeau.plot(label="lames d'eau à 5 min", - title="Avec rectangles corps du programme, moyennes journalières (pas de temps fixe)", - color='purple', zorder=10) - - ax2 = ax1.twinx() - date_debut_pd = ts_lamedeau.index[0] - date_fin_pd = ts_lamedeau.index[-1] - - ts_debutfin = pd.Series([ts_mensuel.mean(), ts_mensuel.mean()], index=[date_debut_pd, date_fin_pd]) - ts_debutfin.plot(ls=':', ax=ax2) - - # https://stackoverflow.com/questions/10550477/how-do-i-set-color-to-rectangle-in-matplotlib - color_list = [] - #patch_list = [] - for debut_rect, valeur in zip(ts_journalier.index, ts_journalier): - if debut_rect.month in [12,1,2]: - code_couleur = 'blue' - elif debut_rect.month in [3,4,5]: - code_couleur = 'pink' - elif debut_rect.month in [6,7,8]: - code_couleur = 'yellow' - else: - code_couleur = 'orange' - - #color_list.append(code_couleur) - - if trace_rect_m : - ax2.add_patch(Rectangle((debut_rect, 0.1), timedelta(days=30), valeur, color=code_couleur, alpha=0.8)) - - # print(debut_rect, valeur, code_couleur) - # patch = patches.Rectangle(xy, w, h, linewidth=1, edgecolor=edgecolor, facecolor=facecolor) - #patch_list.append(Rectangle((debut_rect, 0.1), timedelta(days=30), valeur)) - - #dates = [pd.to_datetime(datepd) for datepd in df_journalier.index] - - # tests différentes solutions - #ax2.vlines(df_journalier.index, 0, df_journalier["lames d'eau en mm"],color=color_list) - #ax2.stem(df_journalier.index, df_journalier["lames d'eau en mm"]) - - - notre_cmap = ListedColormap(color_list) - patches_collection = PatchCollection(patch_list, cmap=notre_cmap, alpha=1 ) - patches_collection.set_array(np.arange(len(patch_list))) - ax1.add_collection(patches_collection, zorder=5) - - max_valeur = max(ts_lamedeau.max(), ts_journalier.max()) - min_valeur = min(0, ts_lamedeau.min(), ts_journalier.min()) - ax1.set_ylim(0, ts_lamedeau.max() * 1.1) - ax2.set_ylim(0, ts_mensuel.max() * 1.1) - - temps = datetime.now() - debut - plt.show() - - print("temps écoulé Pandas, jours, avec vlines", temps) - - - # MOYENNES MOBILES - # Roll - - # DataShader + bokeh, interactif - if trace_bok: - print("DataShader interactif ? ") - # def create_image_une_serie(df, x_range, y_range, w=900, h=300, col_x="pdt", col_y="lames d'eau en mm", how='linear') - p = base_plot(t_bokeh, y_range) - print(t_bokeh, x_range, y_range) - - # # InteractiveImage rend p interactive en établissant un LIEN entre l'objet figure p et le callback défini - InteractiveImage(p, create_image_une_serie) + deuxieme_atelier = False + if deuxieme_atelier: + chemin_complet = askopenfilename() + # donnees_TD_ETP_csv(chemin_complet) + print("Dans un premier temps, on s'en remet entièrement à pandas") + donnees_TD_ETP_csv_panda_seul(chemin_complet) + print("Fermez la fenêtre Tk ; on continue") + + # on peut récupérer la DataFrame ou pas... + DF_PTQ = donnees_TD_ETP_2subplots(chemin_complet) + + # mais par exemple on en a besoin pour cette variante + donnees_TD_ETP_2subplots_variante(DF_PTQ) + + quatrieme_atelier = True + binaire_chemin_complet = askopenfilename() + if Path.is_file(Path(binaire_chemin_complet)): + # CHRONIQUES + # lire_lame_deau_radar_5min(binaire_chemin_complet) # trace la figure mais on ne récupère pas les objets retournés + dates, valeurs = lire_lame_deau_radar_5min(binaire_chemin_complet) # trace la figure et on récupère les objets retournés + # tracer_proprement_une_longue_serie(dates, valeurs, info_chroniques=" série temporelle") # donc on peut les utiliser + # tracer_une_longue_serie_en3_resolutions_mieux(dates, valeurs, info_chroniques=" lame d'eau radar à 5 min") + tracer_une_longue_serie_lame_deau_en3_resolutions_verifs(dates, valeurs, info_chroniques=" lame d'eau radar à 5 min") + #trace_mensuel_avec_rectangles(pd.Series(valeurs, index=dates), trace_journalier=True, trace_rect_mensuels=True) + #HautBas_trace_mensuel_avec_rectangles(pd.Series(valeurs, index=dates)) + + -"""