Matplotlib'de arsa dinamik olarak güncelleniyor


114

Python'da bir seri bağlantı noktasından veri toplayan ve toplanan verilerin varış zamanına göre grafiğini çizen bir uygulama yapıyorum. Verilerin varış zamanı belirsizdir. Veri alındığında grafiğin güncellenmesini istiyorum. Bunun nasıl yapılacağını araştırdım ve iki yöntem buldum:

  1. Grafiği temizleyin ve grafiği tekrar tüm noktaları ile yeniden çizin.
  2. Belirli bir aralıktan sonra değiştirerek grafiği canlandırın.

Program çalışıp uzun süre veri topladığından (örneğin bir gün) ilkini tercih etmiyorum ve arsa yeniden çizmek oldukça yavaş olacak. İkincisi de verinin varış zamanı belirsiz olduğundan ve grafiğin yalnızca veri alındığında güncellenmesini istediğim için tercih edilmez.

Grafiği sadece veri alındığında daha fazla nokta ekleyerek güncelleyebileceğim bir yol var mı?


Yanıtlar:


138

Konuya sadece daha fazla nokta ekleyerek güncelleyebileceğim bir yol var mı?

Sahip olduğunuz sürüme bağlı olarak matplotlib'de verileri canlandırmanın birkaç yolu vardır. Matplotlib yemek kitabı örneklerini gördünüz mü ? Ayrıca, matplotlib belgelerindeki daha modern animasyon örneklerini inceleyin . Son olarak, animasyon API'si , bir işlevi zamanında canlandıran bir FuncAnimation işlevini tanımlar . Bu işlev, yalnızca verilerinizi almak için kullandığınız işlev olabilir.

Her yöntem temelde dataçizilen nesnenin özelliğini ayarlar , bu nedenle ekranın veya şeklin temizlenmesini gerektirmez. dataÖnceki noktaları saklayıp yalnızca çizgi (veya resim ya da her neyse çizdiğiniz) ekleyerek tutmak, böylece mülkiyet basitçe uzatılabilir.

Veri varış zamanınızın belirsiz olduğunu söylediğiniz için en iyi seçeneğiniz muhtemelen aşağıdakiler gibi bir şey yapmaktır:

import matplotlib.pyplot as plt
import numpy

hl, = plt.plot([], [])

def update_line(hl, new_data):
    hl.set_xdata(numpy.append(hl.get_xdata(), new_data))
    hl.set_ydata(numpy.append(hl.get_ydata(), new_data))
    plt.draw()

Ardından seri porttan veri aldığınızda sadece arayın update_line.


En sonunda! Bu + 1'e bir cevap arıyordum :) Arsa nasıl otomatik olarak yeniden ölçeklendirilir? ax.set_autoscale_on (True) çalışmıyor gibi görünüyor.
Edward Newell

13
Cevabı buldum: veriyi güncelledikten sonra ancak plt.draw () 'ı çağırmadan önce ax.relim ()' i ve ardından ax.autoscale_view () 'i çağırın
Edward Newell

Matplotlib yemek kitabı bağlantısı ( scipy.org/Cookbook/Matplotlib/Animations ) bozuk görünüyor ("Yasak" hatası alıyorum)
David Doria

21
Gösterilecek bir çağrı olmadığından (), çizim hiçbir zaman ekranda görünmez. Show () 'u çağırırsam, güncellemeleri engeller ve gerçekleştirmez. Bir şey mi kaçırıyorum? gist.github.com/daviddoria/027b5c158b6f200527a4
David Doria

2
çalıştırabileceğiniz kodla benzer ancak farklı kendi kendine yeten bir cevaba bağlantı (bu cevap doğru genel fikre sahiptir ancak örnek kod çalıştırılamaz)
Trevor Boyd Smith

44

Bunu FuncAnimation olmadan yapmak için (örneğin, arsa üretilirken kodun diğer kısımlarını çalıştırmak veya aynı anda birkaç grafiği güncellemek istiyorsanız), drawtek başına çağırmak arsa oluşturmaz (en azından qt arka ucu).

Aşağıdakiler benim için çalışıyor:

import matplotlib.pyplot as plt
plt.ion()
class DynamicUpdate():
    #Suppose we know the x range
    min_x = 0
    max_x = 10

    def on_launch(self):
        #Set up plot
        self.figure, self.ax = plt.subplots()
        self.lines, = self.ax.plot([],[], 'o')
        #Autoscale on unknown axis and known lims on the other
        self.ax.set_autoscaley_on(True)
        self.ax.set_xlim(self.min_x, self.max_x)
        #Other stuff
        self.ax.grid()
        ...

    def on_running(self, xdata, ydata):
        #Update data (with the new _and_ the old points)
        self.lines.set_xdata(xdata)
        self.lines.set_ydata(ydata)
        #Need both of these in order to rescale
        self.ax.relim()
        self.ax.autoscale_view()
        #We need to draw *and* flush
        self.figure.canvas.draw()
        self.figure.canvas.flush_events()

    #Example
    def __call__(self):
        import numpy as np
        import time
        self.on_launch()
        xdata = []
        ydata = []
        for x in np.arange(0,10,0.5):
            xdata.append(x)
            ydata.append(np.exp(-x**2)+10*np.exp(-(x-7)**2))
            self.on_running(xdata, ydata)
            time.sleep(1)
        return xdata, ydata

d = DynamicUpdate()
d()

Evet! Sonunda Spyder ile çalışan bir çözüm! Eksik olan şey, draw () - komutundan sonra gcf (). Canvas.flush_events () idi.
np8

Bu harika örneğe dayanarak, tekrarlayan çizimlere izin veren küçük bir Python modülü yazdım: github.com/lorenzschmid/dynplot
lorenzli

1
Güzel bir örnek!
vvy

Açık, öz, çok yönlü, esnek: kabul edilen yanıt bu olmalıdır.
pfabri

Bunu bir Jupyter Defterinde kullanmak %matplotlib notebookiçin, matplotlib import deyiminizden sonra sihirli komutu eklemeniz gerekir .
pfabri

3

İşte belirli sayıda nokta çizildikten sonra noktaları kaldırmanın bir yolu:

import matplotlib.pyplot as plt
# generate axes object
ax = plt.axes()

# set limits
plt.xlim(0,10) 
plt.ylim(0,10)

for i in range(10):        
     # add something to axes    
     ax.scatter([i], [i]) 
     ax.plot([i], [i+1], 'rx')

     # draw the plot
     plt.draw() 
     plt.pause(0.01) #is necessary for the plot to update for some reason

     # start removing points if you don't want all shown
     if i>2:
         ax.lines[0].remove()
         ax.collections[0].remove()

2

Bu soruyu cevaplamakta geciktiğimi biliyorum, ancak sorununuz için "oyun çubuğu" paketine bakabilirsiniz. Seri bağlantı noktasından bir veri akışını çizmek için tasarladım, ancak herhangi bir akış için çalışıyor. Ayrıca etkileşimli metin günlüğü veya görüntü çizmeye (grafik çizmeye ek olarak) izin verir. Kendi döngülerinizi ayrı bir iş parçacığında yapmanıza gerek yok, paket bununla ilgileniyor, sadece istediğiniz güncelleme sıklığını verin. Ayrıca terminal, çizim sırasında komutları izlemek için kullanılabilir durumda kalır. Bkz. Http://www.github.com/ceyzeriat/joystick/ veya https://pypi.python.org/pypi/joystick (yüklemek için pip yükleme kumanda çubuğunu kullanın)

Sadece np.random.random () 'u aşağıdaki kodda seri bağlantı noktasından okunan gerçek veri noktanızla değiştirin:

import joystick as jk
import numpy as np
import time

class test(jk.Joystick):
    # initialize the infinite loop decorator
    _infinite_loop = jk.deco_infinite_loop()

    def _init(self, *args, **kwargs):
        """
        Function called at initialization, see the doc
        """
        self._t0 = time.time()  # initialize time
        self.xdata = np.array([self._t0])  # time x-axis
        self.ydata = np.array([0.0])  # fake data y-axis
        # create a graph frame
        self.mygraph = self.add_frame(jk.Graph(name="test", size=(500, 500), pos=(50, 50), fmt="go-", xnpts=10000, xnptsmax=10000, xylim=(None, None, 0, 1)))

    @_infinite_loop(wait_time=0.2)
    def _generate_data(self):  # function looped every 0.2 second to read or produce data
        """
        Loop starting with the simulation start, getting data and
    pushing it to the graph every 0.2 seconds
        """
        # concatenate data on the time x-axis
        self.xdata = jk.core.add_datapoint(self.xdata, time.time(), xnptsmax=self.mygraph.xnptsmax)
        # concatenate data on the fake data y-axis
        self.ydata = jk.core.add_datapoint(self.ydata, np.random.random(), xnptsmax=self.mygraph.xnptsmax)
        self.mygraph.set_xydata(t, self.ydata)

t = test()
t.start()
t.stop()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.