IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

matplotlib : autoscale


Sujet :

Python

  1. #1
    Membre Expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 910
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 910
    Par défaut matplotlib : autoscale
    Salut,

    J'ai un soucis avec la fonction autoscale de matplotlib...

    Elle ne fonctionne plus une fois que l'on modifie (met à jour avec set_data) les points d'une courbe (objet Line2D)...

    J'ai écrit un code pour mettre le problème en évidence (il y a beaucoup de code répétitif sinon il n'est pas très long) :

    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    import tkinter as tk
    from matplotlib.backends.backend_tkagg import (
        FigureCanvasTkAgg, NavigationToolbar2Tk)
    from matplotlib.figure import Figure
    from matplotlib.lines import Line2D
    import numpy as np
     
    root = tk.Tk()
    root.columnconfigure(1, weight=1)
    root.rowconfigure(1, weight=1)
     
    fig = Figure()              
    FigureCanvasTkAgg(fig, master=root).get_tk_widget().grid(row=1, column=1, sticky="wens")
     
    toolbar = NavigationToolbar2Tk(fig.canvas, root, pack_toolbar=False)  
    toolbar.grid(row=0, column=1, columnspan=2 ,sticky="wens")
     
    # ax1 = fig.add_subplot()
     
    ax1 = fig.add_subplot(2, 1, 1)
    ax2 = fig.add_subplot(2, 1, 2)
    ax1.grid()
    ax2.grid()
     
    x = np.arange(-30, 30, .01)
    y = np.sin(x)/x
     
     
    ###  Avec plot : fonctions  ###
     
    line_plot = None
     
    def trace_plot():
        global line_plot
        print("trace_plot")     
        clear_axes_ax1() 
        ax1.axis([-31, 31, -0.1, 0.9])
        line_plot = ax1.plot(x,y)[0]       
        fig.canvas.draw()
        liste_lines(ax1)  
     
     
    def set_data1_plot(): 
        global line_plot
        # ax1.axis([-21, 21, -0.1, 0.9])  
        print("set_data1")
     
        line_plot.set_data(x, y*1.5)
     
        fig.canvas.draw()
        liste_lines(ax1)  
     
     
    def set_data2_plot(): 
        global line_plot
        # ax1.axis([-21, 21, -0.1, 0.9])  
        print("set_data1")
     
        line_plot.set_data(2*x, y)
     
        fig.canvas.draw()
        liste_lines(ax1)     
     
     
    def set_data3_plot(): 
        global line_plot
        # ax1.axis([-21, 21, -0.1, 0.9])  
        print("set_data1")
     
        line_plot.set_data(0.7*x, y*0.7)
     
        fig.canvas.draw()
        liste_lines(ax1)    
     
    def autoscale_ax1():
        print("autoscale_ax1")     
        ax1.autoscale()
        fig.canvas.draw()
     
    def clear_axes_ax1():
        print("clear_ax1")  
        ax1.clear()
        ax1.grid()
        fig.canvas.draw()
        liste_lines(ax1)  
     
     
     
    ###  Avec line : fonctions  ###
     
    my_line = None
     
    def add_line():
        global my_line
     
        if my_line != None:
            return
     
        ax2.axis([-31, 31, -0.1, 0.9])
     
        my_line = Line2D([], [])     
        my_line.set_data(x, y)  # sans cette ligne cela ne fonctionne pas même la première fois...
        ax2.add_line(my_line)     
        # autoscale_ax2()
        fig.canvas.draw()
        liste_lines(ax2)      
     
     
    def set_data1(): 
        global my_line
        # ax1.axis([-21, 21, -0.1, 0.9])  
        print("set_data1")
     
        my_line.set_data(x, y*1.5)
     
        fig.canvas.draw()
        liste_lines(ax2)  
     
     
    def set_data2(): 
        global my_line
        # ax1.axis([-21, 21, -0.1, 0.9])  
        print("set_data1")
     
        my_line.set_data(2*x, y)
     
        fig.canvas.draw()
        liste_lines(ax2)  
     
    def set_data3(): 
        global my_line
        # ax1.axis([-21, 21, -0.1, 0.9])  
        print("set_data1")
     
        my_line.set_data(0.7*x, y*0.7)
     
        fig.canvas.draw()
        liste_lines(ax2)  
     
     
    def autoscale_ax2():
        print("autoscale_ax2")  
        ax2.autoscale()    
        fig.canvas.draw()
        liste_lines(ax2)
     
    def clear_axes_ax2():
        global my_line
        print("clear_ax2")  
        ax2.clear()
        ax2.grid()    
        my_line = None
        fig.canvas.draw()
     
        liste_lines(ax2)         
     
     
    ###
    def liste_lines(ax):
        lines = ax.get_lines() 
        print("\nlines:", lines)       
     
        if lines :       
            print("\tdata_x:", lines[0].get_xdata())
            print("\tdata_Y:", lines[0].get_ydata())
     
    ### GUI ###
     
    lbl_font = ("verdanna", 9, "bold")  
    panel_frame = tk.Frame(root)
    panel_frame.grid(row=1, column=0, columnspan=1 ,sticky="wens")
     
    # avec plot
     
    avec_plot_lblframe = tk.LabelFrame(panel_frame, text="Avec plot ", font=("verdanna", 9, "bold"), fg="green")
    avec_plot_lblframe.grid(column=0, row=0, sticky="we", ipadx=7, ipady=0, padx=3, pady=7)
     
    tk.Button(avec_plot_lblframe, text="Trace", font =lbl_font, fg="black", command=trace_plot).grid(column=0, row=0, columnspan=3, padx=(7,7), pady=11, sticky="we")
     
     
    tk.Button(avec_plot_lblframe, text="set_data1", font =lbl_font, fg="black", command=set_data1_plot).grid(column=0, row=1,   padx=(7,7), pady=11, sticky="we")
    tk.Button(avec_plot_lblframe, text="set_data2", font =lbl_font, fg="black", command=set_data2_plot).grid(column=1, row=1,   padx=(7,7), pady=11, sticky="we")
    tk.Button(avec_plot_lblframe, text="set_data3", font =lbl_font, fg="black", command=set_data3_plot).grid(column=2, row=1,   padx=(7,7), pady=11, sticky="we")
     
    tk.Button(avec_plot_lblframe, text="Autoscale", font =lbl_font, fg="black", command=autoscale_ax1).grid(column=0, row=2,  padx=(7,7), pady=11, sticky="we") 
    tk.Button(avec_plot_lblframe, text="Clear", font =lbl_font, fg="black", command=clear_axes_ax1).grid(column=1, row=2,  padx=(7,7), pady=11, sticky="we") 
     
    # avec line
     
    avec_line_lblframe = tk.LabelFrame(panel_frame, text="Avec line ", font=("verdanna", 9, "bold"), fg="green")
    avec_line_lblframe.grid(column=0, row=1, sticky="we", ipadx=7, ipady=0, padx=3, pady=7)
     
    tk.Button(avec_line_lblframe, text="add_line", font =lbl_font, fg="black", command=add_line).grid(column=0, row=0, columnspan=3, padx=(7,7), pady=11, sticky="we")
     
    tk.Button(avec_line_lblframe, text="set_data1", font =lbl_font, fg="black", command=set_data1).grid(column=0, row=1,   padx=(7,7), pady=11, sticky="we")
    tk.Button(avec_line_lblframe, text="set_data2", font =lbl_font, fg="black", command=set_data2).grid(column=1, row=1,   padx=(7,7), pady=11, sticky="we")
    tk.Button(avec_line_lblframe, text="set_data3", font =lbl_font, fg="black", command=set_data3).grid(column=2, row=1,   padx=(7,7), pady=11, sticky="we")
     
    tk.Button(avec_line_lblframe, text="Autoscale", font =lbl_font, fg="black", command=autoscale_ax2).grid(column=0, row=2,  padx=(7,7), pady=11, sticky="we") 
    tk.Button(avec_line_lblframe, text="Clear", font =lbl_font, fg="black", command=clear_axes_ax2).grid(column=1, row=2,  padx=(7,7), pady=11, sticky="we") 
     
    root.mainloop()
     
     
    # https://matplotlib.org/stable/users/explain/axes/autoscale.html

    Au début quand on clique sur le bouton "Trace" ou "add_line" et qu'on clique ensuite sur le bouton "Autoscale", cela fonctionne...

    Mais ensuite cela ne fonctionne plus si on modifie (met à jour en cliquant sur le bouton "set_data1", "set_data2"ou "set_data3") les points de la courbe (objet Line2D)...

    Est-ce un bug ou bien c'est moi qui utilise mal la fonction ?

    PS: Avec quelques bricolages cela fonctionne mais bon c'est pas top de devoir bricoler...

    On peut aussi refaire sa propre fonction "autoscale", j'en ai écrit une avec la possibilité d'ajouter une marge sur les 4 cotés, elle fonctionne mais là aussi c'est dommage de devoir refaire ce qui existe...

    Merci.

  2. #2
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 062
    Par défaut
    Bonjour,

    Peut-être qu'il faut recalculer les limites des axes en fonction des données actuelles des objets présents sur l'axe.

    La méthode relim peut éventuellement faire cela juste avant l'utilisation de la fonction autoscale, faut tester !

  3. #3
    Membre Expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 910
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 910
    Par défaut
    Citation Envoyé par fred1599 Voir le message
    La méthode relim peut éventuellement faire cela juste avant l'utilisation de la fonction autoscale, faut tester !
    Oui merci, effectivement entre temps j'ai vu et testé cette méthode et cela fonctionne...

    Mais il fallait la trouver celle-là, elle aurait quand même pu être intégrée à la fonction autoscale...



    PS : Est-ce quelqu'un sait si on peut faire un autoscale juste sur une partie de la courbe ?

  4. #4
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 062
    Par défaut
    Pas à ma connaissance, mais on peut créer sa fonction autoscale.

    Voici un petit test,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    import matplotlib.pyplot as plt
    import numpy as np
     
    from matplotlib.axes import Axes
     
    def autoscale_on_part(x: np.ndarray, y: np.ndarray, ax: Axes) -> None:
        x_part = x[(x >= -2) & (x <= 2)]
        y_part = y[(x >= -2) & (x <= 2)]
        x_min, x_max = x_part.min(), x_part.max()
        y_min, y_max = y_part.min(), y_part.max()
        padding_x = (x_max - x_min) * 0.1
        padding_y = (y_max - y_min) * 0.1
        ax.set_xlim(x_min - padding_x, x_max + padding_x)
        ax.set_ylim(y_min - padding_y, y_max + padding_y)
     
     
    x = np.linspace(-10, 10, 400)
    y = np.sin(x)
     
    fig, ax = plt.subplots()
    ax.plot(x, y)
     
    autoscale_on_part(x, y, ax)
     
    plt.show()

  5. #5
    Membre Expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 910
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 910
    Par défaut
    Citation Envoyé par fred1599 Voir le message
    Pas à ma connaissance, mais on peut créer sa fonction autoscale.

    Voici un petit test,
    Merci, oui jolie fonction, claire et concise...

    J'en avais écrit une aussi mais à l'ancienne sans utiliser la puissance des array numpy, je m'étais pris la tête, plusieurs lignes juste pour pouvoir faire un truc comme ça : x[(x >= -2) & (x <= 2)]...

    - Sinon il y aura juste une petite adaptation à faire pour le cas où on a plusieurs courbes sur un même "axe"...

    - Je pensais aussi à la solution qui consisterait à n'afficher que la(les) partie(s) de la(les) courbe(s) qui nous intéresse et alors utiliser la méthode autoscale native...

    Je ne sais pas si ça vaut le coup, ça devrait consommer moins de mémoire et peut-être que l’affichage sera plus rapide* ?

    * Je me demande si la fonction d'affichage est déjà optimisée, genre on a une courbe qui va pour x de 0 à 1000 et les limites de l'axe x sont par exemple 300 et 500, j'ose espérer que la fonction d'affichage va ignorer les 300 premiers points ainsi que les 500 derniers...

  6. #6
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 062
    Par défaut
    Je ne sais pas si ça vaut le coup, ça devrait consommer moins de mémoire et peut-être que l’affichage sera plus rapide* ?
    Il n'y a que le test pour déterminer cela

    Je me demande si la fonction d'affichage est déjà optimisée, genre on a une courbe qui va pour x de 0 à 1000 et les limites de l'axe x sont par exemple 300 et 500, j'ose espérer que la fonction d'affichage va ignorer les 300 premiers points ainsi que les 500 derniers...
    La seule fonction d'affichage que je vois est show, pourquoi ne serait elle pas optimisée ?

  7. #7
    Membre Expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 910
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 910
    Par défaut
    Salut,

    Il y a aussi canvas.draw par exemple... C'est ce que j'utilise après avoir mis à jour les points d'une courbe (objet Line2D) avec set_data...

    Quand on une acquisition en live on peut ne vouloir afficher que la dernière partie de la courbe (une sorte de zoom sur les derniers points), genre on a une courbe qui va pour x de 0 à 1000 et les limites de l'axe x sont par exemple [950 , 1050] alors je vois deux possibilités :

    - Soit je me casse pas la tête et j'utilise set_data pour enregistrer tous les points de la courbe et je laisse canvas.draw se débrouiller...
    - Soit je découpe : je prends seulement les points de l’intervalle [950 , 1050] et je les enregistre avec set_data...

    Il est possible qu'il n'y ait pas une grande différence...

Discussions similaires

  1. Autoscale graphe matplotlib
    Par foufounegirl dans le forum PyQt
    Réponses: 1
    Dernier message: 16/11/2011, 14h49
  2. autoscale avec matplotlib
    Par Invité dans le forum Général Python
    Réponses: 1
    Dernier message: 12/10/2009, 20h40
  3. [matplotlib] 3D
    Par Makino dans le forum Calcul scientifique
    Réponses: 1
    Dernier message: 05/04/2006, 16h36
  4. wxPython + MatPlotLib + py2exe
    Par bibile dans le forum Py2exe
    Réponses: 1
    Dernier message: 05/08/2005, 22h49
  5. Probleme python 2.4 et matplotlib : unicode encode error
    Par patfrat dans le forum Calcul scientifique
    Réponses: 1
    Dernier message: 12/06/2005, 00h30

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo