Zwei Funktionen in einem Plot, wie realisieren?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

Hi,

ich möchte gerne zwei Funktionen in einem Plot darstellen. Das Ausgangsbeispiel ist dieses hier (Quelle:http://matplotlib.org/examples/animatio ... decay.html ):

Code: Alles auswählen

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

def data_gen():
    t = data_gen.t
    cnt = 0
    while cnt < 1000:
        cnt+=1
        t += 0.05
        yield t, np.sin(2*np.pi*t) * np.exp(-t/10.)
data_gen.t = 0

fig, ax = plt.subplots()
line, = ax.plot([], [], lw=2)
ax.set_ylim(-1.1, 1.1)
ax.set_xlim(0, 5)
ax.grid()
xdata, ydata = [], []
def run(data):
    # update the data
    t,y = data
    xdata.append(t)
    ydata.append(y)
    xmin, xmax = ax.get_xlim()

    if t >= xmax:
        ax.set_xlim(xmin, 2*xmax)
        ax.figure.canvas.draw()
    line.set_data(xdata, ydata)

    return line,

ani = animation.FuncAnimation(fig, run, data_gen, blit=True, interval=10,
    repeat=False)
plt.show()
Ich habe leider keine Idee, wie ich zwei Funktionen dort hineinbekomme :-(

Wenn ich einfach so Funktionen darstellen will, dann kann ich einfach das machen:

plt.plot (x1, y1, param1, x2, y2, param2, x3, y3, param3)
Wobei x1..3 und y1..3 immer Arrays mit Werten sind und alle Arrays die gleiche Dimension haben müssen. Soweit ok. Leider funktioniert das mit der Anweisung "line, = ax.plot([], [], lw=2)" nicht. Ich hatte es zunächst so probiert:

line, = ax.plot([], [], [], [])

Das wird nicht akzeptiert.

Dann hab ich versucht, in einen der Kästen immer mehrere Funktionspaare reinzutun:

line.set_data( (x1,x2, x3), (y1, y2, y3) )
Das zeigt zwar alle Funktionen der Plots an - ABER es werden zwischen dem letzten Punkt von (x1, y1) und dem ersten Punkt von (x2, y2) auch eine Verbindungslinie gezeichnet - ebenso zw. den Punkten (x2_letzer, y2_letzer) und (x3_erster, y3_erster). Also kann ich so die Werte auch nicht übergeben .

Ich hab echt keine Idee mehr, wie ich unterschiedliche Funktionen in einen Plot anzeigen kann :-(
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Ich habe das Beispiel von http://matplotlib.org/1.3.0/examples/an ... _anim.html um eine 2.Kurve erweitert:

Code: Alles auswählen

"""
A simple example of an animated plot
"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

fig, ax = plt.subplots()

x = np.arange(0, 2*np.pi, 0.01)        # x-array    
line1, = ax.plot(x, np.sin(x))
line2, = ax.plot(x, np.sin(3*x))

def animate(i):
    line1.set_ydata(np.sin(x+i/10.0))  # update the data
    line2.set_ydata(np.sin(3*(x+i/10.0)))  # update the data
    return line1,line2

#Init only required for blitting to give a clean slate.
def init():
    line1.set_ydata(np.ma.array(x, mask=True))
    line2.set_ydata(np.ma.array(x, mask=True))
    return line1,line2

ani = animation.FuncAnimation(fig, animate, np.arange(1, 200), init_func=init,
    interval=25, blit=True)
plt.show()
a fool with a tool is still a fool, www.magben.de, YouTube
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

Vielen Dank, dass Du mir ein Beispiel gebastelt hast, mit dem ich zwei und mehr Funktionen in einen Graphen anzeigen kann. Ich hab mich sehr drüber gefreut.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

Dank Eurer Hilfe hab ich ein für meine Verhältnisse schon recht cooles Programm hinbekommen :-)

Falls ihr noch ein paar Verbessrungs- oder Korrekturvorschläge für mich habt, würde ich mich sehr darüber freuen.

Code: Alles auswählen

try:
    import tkinter as tk
except:
    import Tkinter as tk
    
import numpy as np
import matplotlib.animation as animation
import matplotlib.pyplot as plt

import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.backends.backend_tkagg import NavigationToolbar2
from matplotlib.backend_bases import key_press_handler

import random
class FIFOBuffer:
    """
    A circular FIFO buffer implemented on top of numpy.
    """
    # __instance=None
    
    def __init__(self, shape, dtype=np.float32, filled=False):
        # print ("in FIFOBuffer")
        self._cache = np.zeros(shape, dtype)
        self._values = np.zeros(shape, dtype)
        self.shape = shape
        self._cached=True
        if filled:
            self._ind = self.shape[0]
        else:
            self._ind = 0
            self._cached = False
    
    def add(self, value):
        """
        Add a value to the buffer.
        """
        ind = self._ind % self.shape[0]
        self._values[ind] = value
        self._ind += 1
        self._cached = False
        
    def array(self):
        """
        Returns a numpy array containing the last stored values.
        """
        if self._ind < self.shape[0]:
            return self._values[:self._ind]
        if not self._cached:
            ind = self._ind % self.shape[0]
            self._cache[:self.shape[0] - ind] = self._values[ind:]
            self._cache[self.shape[0] - ind:] = self._values[:ind]
            self._cached = True
        return self._cache
    
    def __len__(self):
        return min(self._ind, self.shape[0])

class GraphenAnzeige(tk.Frame):
    
    def __init__(self, master, fifo_container, refreshtime=500):
        self._master = master
        self._fifo_container = fifo_container
        self._refreshtime = refreshtime
        
        self._x_values = np.arange(len(self._fifo_container[0]), dtype=np.float16 )
        
        self._init_plot()

        show_data_container(self._fifo_container)
        self.start_animation()
        
    def _init_plot(self):
        self._fig = plt.figure()
        self._ax = plt.axes(xlim=(0, len(self._fifo_container[0])-1), ylim=(0, 11))
        self._canvas = FigureCanvasTkAgg(self._fig, master=self._master)
        self._canvas.get_tk_widget().pack()
        self._canvas.show()
        
        # init line_container
        self._line_container = []
        for i in range( len(self._fifo_container) ):
            line, = self._ax.plot([], [], lw=i+1)
            self._line_container.append(line,)
        
    def start_animation(self):
        self.anim = animation.FuncAnimation(self._fig, self._animate, init_func=self._init_animation,
                               frames=100, interval=self._refreshtime, blit=True, repeat=True)
    
    def stop_animation(self):
        pass

    def _init_animation(self):
        return_value=[]
        for line in self._line_container:
            line.set_data([], [])
            return_value.append(line,)
        return return_value
    
    def _animate(self, i):
        return_value=[]
        i = 0
        for line in self._line_container:
            line.set_data(self._x_values, self._fifo_container[i].array())
            i += 1
            return_value.append(line,)
        return return_value
            
       
def show_data_container(data_container):
    for elem in data_container:
        print (elem.array())
        
def create_data (data_container):
    for elem in data_container:
        elem.add(random.random()*10)


def main ():
    root = tk.Tk()
    
    # init subframes
    button_frame = tk.Frame(root)
    plot_frame = tk.Frame(root)
    
    # init fifo
    fifolength = 15
    number_of_fifos = 5
    data_container = []
    for i in range (0, number_of_fifos):
        data_container.append( FIFOBuffer([fifolength], filled=True, dtype=np.float16) )
    create_data (data_container)
    create_data (data_container)
    create_data (data_container)        
     
    def do_exit ():
        root.quit()
        root.destroy()
        
    def do_put_data():
        create_data(data_container)
        show_data_container(data_container)
        
    plot_graph = GraphenAnzeige(plot_frame, data_container)
        
    exit_button = tk.Button (button_frame, text="Beenden", width=20, command=do_exit)
    exit_button.pack() 
    createdata_button = tk.Button (button_frame, text="Erzeuge Daten", width=20, command=do_put_data)
    createdata_button.pack() 
    button_frame.grid(row=0, column=2)
    plot_frame.grid(row=0, column=1)
        
        
    root.mainloop()
        
    
if __name__ == '__main__':
    main ()
    print ("... Programm beendet ...")
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Ich würde an Deiner Stelle nichts mehr am Programm ändern. Schreibe lieber Dein nächstes Programm.

Die Imports der Zeilen 13 und 14 werden nicht benutzt.

_init_plot ist eigentlich Teil des Konstruktors, weil Du aber eine extra Methode daraus gemacht hast, musst Du vorher noch den Konstruktor Parameter master in der Objektvariablen _master speichern. Wenn Du den Code von _init_plot direkt in den Konstruktor schreibst, sparst Du deshalb 3 Zeilen Code und es ist klarer was gemeint ist.

Der Underscore von _init_animation und _animate suggeriert, dass es interne Methoden von GraphenAnzeige sind. Sind es aber nicht, da Du diese ja in Zeile 88 an FuncAnimation übergibst.

Den größten Teil des Codes der Funktion main würde ich eher in eine eigene Klasse MainWindow packen.

In der Klasse FIFOBuffer benutzt Du von shape immer nur shape[0]. Da könntest Du auch gleich in Zeile 27 self.length = shape[0] schreiben.

Du implementierst einen leeren Methodenrumpf auf Vorrat, den Du nie benutzt, Zeile 91: stop_animation
Niemals was auf Vorrat implementieren, jede nicht benutzte Code-Zeile kostet bei der Wartung Geld.

Der Button "Beenden" und damit auch die Funktion exit sind überflüssig, das Fenster schließen ist ausreichend um das Programm zu beenden (und man klickt beim Testen nicht auf den falschen Konpf).
a fool with a tool is still a fool, www.magben.de, YouTube
BlackJack

@MagBen: `_init_animation()` und `_animate()` kann man IMHO schon als interne API ansehen. Die Frage ist ja nicht ob das Objekt selbst diese Methoden als Rückruffunktionen irgendwo hin übergibt, sondern ob ein Benutzer von dem Objekt die direkt aufrufen darf.
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

BlackJack hat geschrieben:@MagBen: `_init_animation()` und `_animate()` kann man IMHO schon als interne API ansehen. Die Frage ist ja nicht ob das Objekt selbst diese Methoden als Rückruffunktionen irgendwo hin übergibt, sondern ob ein Benutzer von dem Objekt die direkt aufrufen darf.
Der Underscore wird oft als Python Variante von protected oder private bezeichnet. Callback Funktionen sind aber in der Regel immer public.
Bei der Fehlersuche ist es hilfreich, wenn man davon ausgehen kann, dass eine interne Methode wirklich auch nur intern aufgerufen wird.
a fool with a tool is still a fool, www.magben.de, YouTube
BlackJack

@MagBen: Der Underscore ist ein Kennzeichen das eine Methode nicht zur öffentlichen API gehört, von aussen also (möglichst) niemand über diesen Namen auf das Attribut zugreift, und das tut ja auch niemand. Callbacks sind zum Beispiel in Java nur deshalb ``public`` weil sie es sein müssen, nicht weil sie tatsächlich unbedingt zur öffentlichen API eines Objekts gehören. Die Frage ob Unterstrich oder nicht entscheidet sich eher daran ob jemand der so ein Objekt erstellt oder in die Finger bekommt, die Methode aufrufen soll/darf, oder ob das keinen Sinn macht die öffentlich anzubieten.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@MagBen: Callbacks sind in der Regel nie public, weil es meist keinen Sinn macht, diese explizit aufzurufen. Python kennt keine Zugriffsbeschränkungen; und man sollte nicht künstlich Konzepte von anderen Programmiersprachen auf Python übertragen. Sobald das Funktionsobjekt an eine andere Funktion übergeben wird, geht ja auch die Information, ob mit oder ohne Unterstrich verloren.

Zur FIFOBuffer-Klasse: "shape" ist ein typisches Beispiel für ein property, das man dann auch in "buffer_length" umschreiben kann:

Code: Alles auswählen

    @property
    def buffer_length(self):
        return self._values.shape[0]
Antworten