Matplotlib: Geschwindigkeitsproblem

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
locutus
User
Beiträge: 19
Registriert: Mittwoch 21. Oktober 2015, 08:39

Moin Moin,

zunächst einmal: ich bin ein Anfänger in Python und ganz besonders mit matplotlib ;-)
Also: Im Zuge eines Projektes mit einem zweiarmigen Roboteraufbau habe ich ein kleines Programm geschrieben, was aus den Positionsdaten der einzelnen Gelenke auslesen soll, was für eine Bewegung gemacht wurde.
Dazu werden die Daten für jeden Arm jeweils in einer Matrix geordnet nach Gelenken eingelesen, die aktuelle Positionen mit denen vor t Samples verglichen und überprüft, ob die Differenz einen Schwellwert (hier provisorisch ein Bruchteil der jeweiligen Standardabweichung) übersteigt. Die maximale Auslenkung entscheidet dann darüber, welche "Bewegung" gerade gemacht wird. Das Programm ist noch nicht besonders schick, aber es tut, was es soll (also da erstmal nicht kritisieren :-P). Wenn ich als Ausgabe einfache print Nachrichten mache, ist das Programm auch fix nach gut 3 Sekunden durch. Nun wollte ich das ganze visualisieren, indem die sich bewegenden Gelenke auf einem Schema des Aufbaus aufleuchten, wenn sie ihren Schwellwert übertreten haben. Zurzeit habe ich es auf ein Gelenk und Bewegung/Nicht-Bewegung reduziert, und der Plot macht auch das, was er soll, allerdings wird er ziemlich schnell ziemlich langsam, daher vermute ich stack overflow. Wie gesagt, ich kenne noch nicht viel von matplotlib und die Lösungen zu ähnlichen Problemen hier im Forum konnte ich nicht erfolgreich auf mein Problem anwenden. Daher an dieser Stelle die Frage: Was kann man machen, damit das Polten (bzw. das Updaten des Plots) schneller geht?

Vielen Dank schon einmal!

(PS: verwende Python 2.7, falls das für matplotlib von Bedeutung ist)

Code: Alles auswählen

import numpy as np

import matplotlib.pyplot as plt

from copy import deepcopy

#####

J_309_310 = [13, 5]

J_409_410 = [7, 5]

fig1 = plt.figure()

plt.plot([10, 10, 10, 10, 10, 10, 11, 13, 14, 14, 14, 13, 14, 14, 14, 14, 13, 9, 7, 6, 6, 6, 7, 6, 6, 6, 6, 7],    # Koordinaten der Gelenke
         [7, 9, 11, 13, 16, 17, 15, 15, 14, 13, 11, 10, 9, 8, 7, 6, 5, 15, 15, 14, 13, 11, 10, 9, 8, 7, 6, 5], 'ko',
         markerfacecolor='w', markersize=15)
plt.axis([0, 20, 0, 22])
plt.xlabel('x-axis')
plt.ylabel('y-axis')

plt.ion()
plt.show()

#####

input1 = np.load('/Users/neckardt/Documents/joints1.npy')

input2 = np.load('/Users/neckardt/Documents/joints2.npy')

arm1 = deepcopy(input1)

arm2 = deepcopy(input2)

t = 50

mv1 = []

for i in range(0, len(arm1[:, 1])):
    mv1 += [np.std(arm1[i, :])]

mv1 = np.asarray(mv1)

mv1 /= 5.5

mv2 = []

for i in range(0, len(arm2[:, 1])):
    mv2 += [np.std(arm2[i, :])]

mv2 = np.asarray(mv2)

mv2 /= 5.5

#####

for i in range(2206 + t, len(arm1[5, :])):

    array1 = np.amax(abs(arm1[:, i] - arm1[:, i - t]))
    array2 = np.amax(abs(arm2[:, i] - arm2[:, i - t]))

    if array1 > array2:

        ind = np.argmax(abs(arm1[:, i] - arm1[:, i - t]))
    else:
        ind = np.argmax(abs(arm2[:, i] - arm2[:, i - t]))

    if abs(arm1[ind, i] - arm1[ind, i - t]) >= mv1[ind] and abs(arm2[ind, i] - arm2[ind, i - t]) >= mv2[ind]:

        # Beide Arme

        plt.plot([J_309_310[0]], [J_309_310[1]], 'go', markersize=15)
        plt.plot([J_409_410[0]], [J_309_310[1]], 'go', markersize=15)

    elif abs(arm1[ind, i] - arm1[ind, i - t]) >= mv1[ind]:

        # nur linker Arm

        plt.plot([J_409_410[0]], [J_409_410[1]], 'ko', markerfacecolor='w', markersize=15)
        plt.plot([J_309_310[0]], [J_309_310[1]], 'go', markersize=15)

    elif abs(arm2[ind, i] - arm2[ind, i - t]) >= mv2[ind]:

        # nur rechter Arm

        plt.plot([J_309_310[0]], [J_409_410[1]], 'ko', markerfacecolor='w', markersize=15)
        plt.plot([J_409_410[0]], [J_309_310[1]], 'go', markersize=15)

    else:

        print "{0:00}. -".format(i)

        plt.plot([J_309_310[0]], [J_309_310[1]], 'ko', markerfacecolor='w', markersize=15)
        plt.plot([J_409_410[0]], [J_409_410[1]], 'ko', markerfacecolor='w', markersize=15)

    plt.draw()
Zuletzt geändert von locutus am Mittwoch 21. Oktober 2015, 09:40, insgesamt 1-mal geändert.
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Ich denke plt.draw() in der Schleife ist das Problem, probier mal stattdessen plt.show() nach der Schleife.
Oder soll das eine Animation sein?
a fool with a tool is still a fool, www.magben.de, YouTube
locutus
User
Beiträge: 19
Registriert: Mittwoch 21. Oktober 2015, 08:39

MagBen hat geschrieben:Ich denke plt.draw() in der Schleife ist das Problem, probier mal stattdessen plt.show() nach der Schleife.
Oder soll das eine Animation sein?
Man soll sozusagen "zuschauen" können, während die Daten durchgegangen werden, wann welches Gelenk aufblinkt, also könnte man das wohl Animation nennen. Allerdings verstehe ich die Beispiele zum Thema Animation auf matplotlib.org nicht bzw. krieg sie nicht zum laufen.
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

In jedem Schleifendurchlauf fügst Du neue Punkte hinzu. Matplotlib muss immer alle Punkte plotten, obwohl wahrscheinlich nur die Punkte vom aktuellen Schleifendurchlauf benötigt werden. Die Liste der zu plottenden Punkte wird immer länger und entsprechend braucht Matplotlib immer länger zum Plotten.
locutus hat geschrieben:Allerdings verstehe ich die Beispiele zum Thema Animation auf matplotlib.org nicht bzw. krieg sie nicht zum laufen.
Probier das mal:
http://matplotlib.org/1.4.1/examples/an ... mated.html
a fool with a tool is still a fool, www.magben.de, YouTube
locutus
User
Beiträge: 19
Registriert: Mittwoch 21. Oktober 2015, 08:39

klingt logisch, mit den Punkten ;-)
Bei der Animation schafft er es zwar, das Raster noch zu plotten, danach bekomme ich dann nur permanent den Fehler
'FigureCanvasMac' object has no attribute 'restore_region'
ausgeworfen.
Die Module sind alle da :-/
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Welche Matplotlib Version?

Code: Alles auswählen

import matplotlib
print matplotlib.__version__
Schau Dir das mal an
http://stackoverflow.com/questions/1321 ... estore-reg
a fool with a tool is still a fool, www.magben.de, YouTube
locutus
User
Beiträge: 19
Registriert: Mittwoch 21. Oktober 2015, 08:39

MagBen hat geschrieben:Welche Matplotlib Version?

Code: Alles auswählen

import matplotlib
print matplotlib.__version__
Schau Dir das mal an
http://stackoverflow.com/questions/1321 ... estore-reg
1.4.3

ok, die zweite Lösung dort, das backend zu ändern, funktioniert super :-)

Wie würde das bei meinem Programm etwa dann aussehen?
mein Programm, also die Entscheidung, welcher Punkt aufblinkt wäre im Vergleich die Funktion animate()?

Update:
So, hab mal versucht an Anlehnung an das Beispiel eine einfache Animation zu machen, die einfach nur so einen Punkt aufblinken lässt:

Code: Alles auswählen

#…#

fig = plt.figure()

joint1 = [(11,15),(13,15),(14,14),(14,13),(14,11),(13,10),(14,9),(14,8),(14,7),(14,6),(13,5)]

#…#

joint, = plt.plot([], [], 'go', markersize=15)


def init():

    joint.set_data([], [])

    return joint

joints_on = joint1


def animate(i):

    joint.set_data(joints_on[i][0], joints_on[i][1])

    return joint

ani = animation.FuncAnimation(fig, animate, blit=True, init_func=init)

plt.show()
jetzt kommt der Fehler "'Line2D' object is not iterable".
Ich erkenne nicht, welches Objekt er meint.

Update 2:
Ok, jetzt weiß ich es. Aber irgendwie bekomme ich nicht hin, dass halt kein 2D-Objekt, sondern die Koordinaten übergeben werden.
Ich probiere mal weiter.
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

animate muss eine Liste von Matplotlib-Objekten zurückgeben:

Code: Alles auswählen

def animate(i):
    joint.set_data(joints_on[i][0], joints_on[i][1])
    return [joint]
a fool with a tool is still a fool, www.magben.de, YouTube
locutus
User
Beiträge: 19
Registriert: Mittwoch 21. Oktober 2015, 08:39

aah, ok, und bei init auch. jetzt funktioniert es. Zumindest diese kleine Protoanimation, den Rest krieg ich wohl hin.
Vielen Dank :)
locutus
User
Beiträge: 19
Registriert: Mittwoch 21. Oktober 2015, 08:39

ok, soweit funktioniert es. allerdings frage ich mich jetzt, ob man die Farbe des Markes ebenfalls neben den Koordinaten als Parameter übergeben kann. Vermutlich ja, denke ich, aber wie kriegt man einen String da erfolgreich rein?

also das man statt:
joint.set_data(joints_on[ind][0], joints_on[ind][1])
joint.set_data(joints_on[ind][0], joints_on[ind][1], 'go')
z.B. dann in animate() setzen kann. Vermutlich übersehe ich wieder eine Möglichkeit…

Update:
genauer: joint.set_data([) kann nicht mehr als zwei Werte verarbeiten, was wäre die alternative für drei?
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Hier kannst Du nachlesen, welche Methoden matplotlib.lines.Line2D hat:
http://matplotlib.org/api/lines_api.html
Probier doch mal set_color
a fool with a tool is still a fool, www.magben.de, YouTube
locutus
User
Beiträge: 19
Registriert: Mittwoch 21. Oktober 2015, 08:39

ok, vielen Dank, das klappt, irgendwie denke ich immer zu krumm ;-)
durch die ganzen if Abfragen in animate() ist es inzwischen sogar als animation recht langsam, aber zumindest konstant^^
Das ganze andersherum zu stülpen, als das if-else-Konstrukt außen und die animation-Funktion würde wohl nicht helfen/funktionieren, oder?
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

locutus hat geschrieben:urch die ganzen if Abfragen in animate() ist es inzwischen sogar als animation recht langsam
Ich glaube nicht, dass es die if-Abfragen sind. Je nachdem wie groß die Arrays sind könnten es die Funktionen numpy.amax und numpy.argmax sein (Zeile 59 bis 66 in Deinem ersten Beitrag), die das ganze langsam machen.
a fool with a tool is still a fool, www.magben.de, YouTube
locutus
User
Beiträge: 19
Registriert: Mittwoch 21. Oktober 2015, 08:39

Daran liegt es nicht, hab es probiert, der Array ist auch nur 8 Stellen lang :-/
Es schafft etwa drei Werte pro Sekunde, dass ist gegen die ursprüngliche Aufnahmefrequenz von 100 Hz nicht viel^^

hier der aktuelle Code:

Code: Alles auswählen

import matplotlib

matplotlib.use('TkAgg')

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from copy import deepcopy

fig = plt.figure()

joint1 = [(11, 15), (13, 15), (14, 14), (14, 13), (14, 11), (13, 10), (14, 9), (14, 8), (14, 7), (14, 6), (13, 5)]

joint2 = [(9, 15), (7, 15), (6, 14), (6, 13), (6, 11), (7, 10), (6, 9), (6, 8), (6, 7), (6, 6), (7, 5)]

plt.plot([10, 10, 10, 10, 10, 10, 11, 13, 14, 14, 14, 13, 14, 14, 14, 14, 13, 9, 7, 6, 6, 6, 7, 6, 6, 6, 6, 7],
         [7, 9, 11, 13, 16, 17, 15, 15, 14, 13, 11, 10, 9, 8, 7, 6, 5, 15, 15, 14, 13, 11, 10, 9, 8, 7, 6, 5], 'ko',
         markerfacecolor='w', markersize=15)
plt.axis([0, 20, 0, 22])
plt.xlabel('x-axis')
plt.ylabel('y-axis')

joint, = plt.plot([], [], "o", markersize=15)

input1 = np.load('/Users/neckardt/Documents/joints1.npy')

input2 = np.load('/Users/neckardt/Documents/joints2.npy')

arm1 = deepcopy(input1)

arm2 = deepcopy(input2)

t = 50

mv1 = []

for i in range(0, len(arm1[:, 1])):
    mv1 += [np.std(arm1[i, :])]

mv1 = np.asarray(mv1)

mv1 /= 5.5

mv2 = []

for i in range(0, len(arm2[:, 1])):
    mv2 += [np.std(arm2[i, :])]

mv2 = np.asarray(mv2)

mv2 /= 5.5

print len(arm1[:, 1])


def init():
    joint.set_data([], [])

    return [joint]


def animate(i):

    i = i + 2206

    print i

    array1 = np.amax(abs(arm1[:, i] - arm1[:, i - t]))
    array2 = np.amax(abs(arm2[:, i] - arm2[:, i - t]))

    if array1 > array2:
        
        ind = np.argmax(abs(arm1[:, i] - arm1[:, i - t]))
        
        joints_on = joint1
        joints_off = joint2

    else:

        ind = np.argmax(abs(arm2[:, i] - arm2[:, i - t]))
        
        joints_on = joint2
        joints_off = joint1

    if abs(arm1[ind, i] - arm1[ind, i - t]) >= mv1[ind] and abs(arm2[ind, i] - arm2[ind, i - t]) >= mv2[ind]:

        joint.set_data(joints_on[ind][0], joints_on[ind][1])
        joint.set_data(joints_off[ind][0], joints_off[ind][1])
        joint.set_color('g')

    elif abs(arm1[ind, i] - arm1[ind, i - t]) >= mv1[ind]:

        joint.set_data(joints_on[ind][0], joints_on[ind][1])
        joint.set_color('g')

    elif abs(arm2[ind, i] - arm2[ind, i - t]) >= mv2[ind]:

        joint.set_data(joints_on[ind][0], joints_on[ind][1])
        joint.set_color('g')

    else:

        joint.set_data(joints_on[ind][0], joints_on[ind][1])

        joint.set_color('w')

    return [joint]

ani = animation.FuncAnimation(fig, animate, blit=True, init_func=init)

plt.show()
Wie kann man den Iterator beeinflussen, durch den animate läuft? Zu Präsentationszwecken könnte man überlegen, nur jeden fünften Wert oder ähnlich zu verarbeiten, damit es scheinbar "schneller" wird.
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

locutus hat geschrieben:Wie kann man den Iterator beeinflussen, durch den animate läuft?
Du kannst bei der Matplotlib-Animation (irgendwo) eine maximale Abspielfrequenz einstellen.
locutus hat geschrieben:Es schafft etwa drei Werte pro Sekunde, dass ist gegen die ursprüngliche Aufnahmefrequenz von 100 Hz nicht viel^^
Du könntest alles vorberechnen und dann in der Funktion animate einfach nur die vorberechneten Objekte zurückgeben. Die Animation würde dann zwar zum Starten ein paar Sekunden brauchen, danach kannst Du sie aber fast beliebig schnell abspielen. Mehr als 25 Bilder pro Sekunde machen aber keinen Sinn. Du könntest die ganze Animation auch komplett vor der Präsentation berechnen und als mp4 abspeichern.
a fool with a tool is still a fool, www.magben.de, YouTube
locutus
User
Beiträge: 19
Registriert: Mittwoch 21. Oktober 2015, 08:39

Das mit dem Vorberechnen hatte ich mir auch gedacht, daher habe ich es so umgebaut:

Code: Alles auswählen


##### vorher alles gleich

def init():

    joint.set_data([], [])

    return [joint]


def animate(i):

    print i

    joint.set_data(array_out[i][0], array_out[i][1])
    if array_out[i][2] == 1:
        joint.set_color('g')
    elif array_out[i][2] == 2:
        joint.set_color('r')
    else:
        joint.set_color('w')

    return [joint]

array_out = []

#####

for i in range(t+2206, len(arm1[5, :])):

    array1 = np.amax(abs(arm1[:, i] - arm1[:, i - t]))
    array2 = np.amax(abs(arm2[:, i] - arm2[:, i - t]))

    if array1 > array2:
        ind = np.argmax(abs(arm1[:, i] - arm1[:, i - t]))
        joints_on = joint1
    else:
        ind = np.argmax(abs(arm2[:, i] - arm2[:, i - t]))
        joints_on = joint2

    if abs(arm1[ind, i] - arm1[ind, i - t]) >= mv1[ind] and abs(arm2[ind, i] - arm2[ind, i - t]) >= mv2[ind]:

        if (arm1[ind, i] - arm1[ind, i - t]) and (arm2[ind, i] - arm2[ind, i - t]) > 0:
            array_out += [joints_on[ind][0]], [joints_on[ind][1]], [1]

        elif (arm1[ind, i] - arm1[ind, i - t]) and (arm2[ind, i] - arm2[ind, i - t]) < 0:
            array_out += [joints_on[ind][0]], [joints_on[ind][1]], [1]

        elif (arm1[ind, i] - arm1[ind, i - t]) > 0 > (arm2[ind, i] - arm2[ind, i - t]):
            array_out += [joints_on[ind][0]], [joints_on[ind][1]], [1]

        else:
            array_out += [joints_on[ind][0]], [joints_on[ind][1]], [1]

    elif abs(arm1[ind, i] - arm1[ind, i - t]) >= mv1[ind]:

        if (arm1[ind, i] - arm1[ind, i - t]) > 0:
            array_out += [joints_on[ind][0]], [joints_on[ind][1]], [1]
        else:
            array_out += [joints_on[ind][0]], [joints_on[ind][1]], [2]

    elif abs(arm2[ind, i] - arm2[ind, i - t]) >= mv2[ind]:

        if (arm2[ind, i] - arm2[ind, i - t]) > 0:
            array_out += [joints_on[ind][0]], [joints_on[ind][1]], [1]
        else:
            array_out += [joints_on[ind][0]], [joints_on[ind][1]], [2]

    else:

        array_out += [joints_on[ind][0]], [joints_on[ind][1]], [0]

#####

array_out = np.reshape(array_out,(len(array_out)/3,3))

ani = animation.FuncAnimation(fig, animate, blit=True, init_func=init)

plt.show()
Mit zusätzlicher Funktion "vor = grün, zurück = rot", der Array ist auch schnell in 1-2 Sekunden fertig, die Frames der Animation sind aber ähnlich langsam wie vorher :-/
25 pro Sekunde wären schon klasse^^
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Mit Vorberechnet meinte ich eher sowas:

Code: Alles auswählen

def animate(i):
    return [joints[i]]
D.h. Du veränderst nicht die Line2D Objekte während der Animation, sonderst Du hast für jedes Frame ein eigenes Line2D Objekt.
a fool with a tool is still a fool, www.magben.de, YouTube
locutus
User
Beiträge: 19
Registriert: Mittwoch 21. Oktober 2015, 08:39

ah, ok, verstehe, aber wie initialisiert man ein line2D object so, dass man darüber iterieren kann?
So allein kommt ja verständlicher Weise dann: "TypeError: 'Line2D' object does not support indexing"

Update: Also es liegt eigentlich nur an plt.show(), was das ganze langsam macht. Lasse ich es ungesehen durchrechnen, geht es ganz gut und die gespeicherte animation klappt dann auch.
Wenigstens das :-)
Antworten