2016-11-30 2 views
0

Mein Programm zeichnet die Positionen von Partikeln in meiner Datei für jeden Zeitschritt auf. Leider wird es immer langsamer, obwohl ich matplotlib.animation verwendet habe. Wo ist der Flaschenhals?Python: Animiertes 3D Scatterplot wird langsam

Meine Datendatei für zwei Teilchen sieht wie folgt aus:

#  x y z 
# t1 1 2 4 
#  4 1 3 
# t2 4 0 4 
#  3 2 9 
# t3 ... 

Mein Skript:

import numpy as np       
import matplotlib.pyplot as plt    
from mpl_toolkits.mplot3d import Axes3D 
import mpl_toolkits.mplot3d.axes3d as p3 
import matplotlib.animation as animation 

# Number of particles 
numP = 2 
# Dimensions 
DIM = 3 
timesteps = 2000 

with open('//home//data.dat', 'r') as fp: 
    particleData = [] 
    for line in fp: 
     line = line.split() 
     particleData.append(line) 

x = [float(item[0]) for item in particleData] 
y = [float(item[1]) for item in particleData] 
z = [float(item[2]) for item in particleData]  

# Attaching 3D axis to the figure 
fig = plt.figure() 
ax = p3.Axes3D(fig) 

# Setting the axes properties 
border = 1 
ax.set_xlim3d([-border, border]) 
ax.set_ylim3d([-border, border]) 
ax.set_zlim3d([-border, border]) 


def animate(i): 
    global x, y, z, numP 
    #ax.clear() 
    ax.set_xlim3d([-border, border]) 
    ax.set_ylim3d([-border, border]) 
    ax.set_zlim3d([-border, border]) 
    idx0 = i*numP 
    idx1 = numP*(i+1) 
    ax.scatter(x[idx0:idx1],y[idx0:idx1],z[idx0:idx1]) 

ani = animation.FuncAnimation(fig, animate, frames=timesteps, interval=1, blit=False, repeat=False) 
plt.show() 

Antwort

2

Ich würde pyqtgraph in diesem Fall zu verwenden, vor. Zitat aus der Dokumentation:

Die Hauptziele sind: 1) schnelle, interaktive Grafiken für Anzeigen von Daten (Grundstücke zur Verfügung zu stellen, Video, etc.) und 2) Werkzeuge zu schaffen, in schnellen Anwendungsentwicklung zu unterstützen (für Beispiel: Eigenschaftsbäume wie , die in Qt Designer verwendet werden).

Sie können einige Beispiele nach der Installation finden Sie unter:

import pyqtgraph.examples 
pyqtgraph.examples.run() 

Dieser kleine Code-Schnipsel erzeugen 1000 zufällige Punkte und zeigt sie in einem 3D-Streudiagramm durch die Opazität ständig aktualisiert wird, ähnlich wie bei der 3D-Streuung Grundstück Beispiel in pyqtgraph.examples:

from pyqtgraph.Qt import QtCore, QtGui 
import pyqtgraph.opengl as gl 
import numpy as np 

app = QtGui.QApplication([]) 
w = gl.GLViewWidget() 
w.show() 
g = gl.GLGridItem() 
w.addItem(g) 

#generate random points from -10 to 10, z-axis positive 
pos = np.random.randint(-10,10,size=(1000,3)) 
pos[:,2] = np.abs(pos[:,2]) 

sp2 = gl.GLScatterPlotItem(pos=pos) 
w.addItem(sp2) 

#generate a color opacity gradient 
color = np.zeros((pos.shape[0],4), dtype=np.float32) 
color[:,0] = 1 
color[:,1] = 0 
color[:,2] = 0.5 
color[0:100,3] = np.arange(0,100)/100. 

def update(): 
    ## update volume colors 
    global color 
    color = np.roll(color,1, axis=0) 
    sp2.setData(color=color) 

t = QtCore.QTimer() 
t.timeout.connect(update) 
t.start(50) 


## Start Qt event loop unless running in interactive mode. 
if __name__ == '__main__': 
    import sys 
    if (sys.flags.interactive != 1) or not hasattr(QtCore, PYQT_VERSION'): 
     QtGui.QApplication.instance().exec_() 

kleinen gif Ihnen eine Vorstellung von der Leistung zu geben:

enter image description here

EDIT:

mehr Punkte an jedem einzelnen Zeitschritt ist ein wenig kompliziert, da die gl.GLScatterPlotItem dauert nur (N,3)-arrays als Punktstellen finden here angezeigt. Sie könnten versuchen, ein Dictionary von ScatterPlotItems zu machen, wo jeder von ihnen alle Zeitschritte für einen bestimmten Punkt enthält. Dann müsste man die Update-Funktion entsprechend anpassen. Unten finden Sie ein Beispiel, in dem pos eine (100,10,3)-array ist, die 100 Zeitschritte für jeden Punkt darstellt. Ich habe die Aktualisierungszeit für eine langsamere Animation auf 1000 ms reduziert.

from pyqtgraph.Qt import QtCore, QtGui 
import pyqtgraph.opengl as gl 
import numpy as np 

app = QtGui.QApplication([]) 
w = gl.GLViewWidget() 
w.show() 
g = gl.GLGridItem() 
w.addItem(g) 

pos = np.random.randint(-10,10,size=(100,10,3)) 
pos[:,:,2] = np.abs(pos[:,:,2]) 

ScatterPlotItems = {} 
for point in np.arange(10): 
    ScatterPlotItems[point] = gl.GLScatterPlotItem(pos=pos[:,point,:]) 
    w.addItem(ScatterPlotItems[point]) 

color = np.zeros((pos.shape[0],10,4), dtype=np.float32) 
color[:,:,0] = 1 
color[:,:,1] = 0 
color[:,:,2] = 0.5 
color[0:5,:,3] = np.tile(np.arange(1,6)/5., (10,1)).T 

def update(): 
    ## update volume colors 
    global color 
    for point in np.arange(10): 
     ScatterPlotItems[point].setData(color=color[:,point,:]) 
    color = np.roll(color,1, axis=0) 

t = QtCore.QTimer() 
t.timeout.connect(update) 
t.start(1000) 


## Start Qt event loop unless running in interactive mode. 
if __name__ == '__main__': 
    import sys 
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): 
    QtGui.QApplication.instance().exec_() 

Beachten Sie, dass in diesen Beispielen, all Punkte im Streudiagramm dargestellt sind, jedoch ist die Farbe Opazität (4. Dimension in der Farb Array) in jedem Zeitschritt aktualisiert, um eine Animation zu erhalten. Sie könnten auch versuchen, die Punkte anstelle der Farbe zu aktualisieren, um bessere Leistung zu erhalten ...

+0

Wie würde ich fortfahren, wenn meine Liste "pos" die Positionen für 10 Punkte für 100 Zeitschritte enthält. Wo würde ich das in deinen Code integrieren? Für jeden Zeitschritt würde ich etwas wie "Plot (pos [Zeitschritt0: Zeitschritt1])" brauchen. Es wäre toll, wenn Sie mir einen Hinweis geben könnten! – Samuel

+0

Ich habe ein Beispiel in das Update aufgenommen –

1

Ich würde schätzen, Ihr Flaschenhals ruft ax.scatter und ax.set_xlim3d und ähnlich in jedem Rahmen in der Animation.

Idealerweise sollten Sie einen Anruf zu scatter einmal machen, dann das durch Streuung und seine set_... Eigenschaften in der animate Funktion (more details here) zurückgegebene Objekt verwenden.

Ich kann nicht herausfinden, wie es geht, mit scatter, aber wenn Sie ax.plot(x, y, z, 'o') stattdessen verwenden, können Sie dann die Demo-Methode here folgen.

Verwendung einiger zufälliger Daten für x, y, z. Es würde so funktionieren

import numpy as np 
import matplotlib.pyplot as plt 
from mpl_toolkits.mplot3d import Axes3D 
import mpl_toolkits.mplot3d.axes3d as p3 
import matplotlib.animation as animation 
from numpy.random import random 

# Number of particles 
numP = 2 
# Dimensions 
DIM = 3 
timesteps = 2000 

x, y, z = random(timesteps), random(timesteps), random(timesteps) 

# Attaching 3D axis to the figure 
fig = plt.figure() 
ax = p3.Axes3D(fig) 

# Setting the axes properties 
border = 1 
ax.set_xlim3d([-border, border]) 
ax.set_ylim3d([-border, border]) 
ax.set_zlim3d([-border, border]) 
line = ax.plot(x[:1], y[:1], z[:1], 'o')[0] 


def animate(i): 
    global x, y, z, numP 
    idx1 = numP*(i+1) 
    # join x and y into single 2 x N array 
    xy_data = np.c_[x[:idx1], y[:idx1]].T 
    line.set_data(xy_data) 
    line.set_3d_properties(z[:idx1]) 

ani = animation.FuncAnimation(fig, animate, frames=timesteps, interval=1, blit=False, repeat=False) 
plt.show()