मैटलपोटलिब में गतिशील रूप से प्लॉट अपडेट करना


114

मैं पायथन में एक आवेदन कर रहा हूं जो एक सीरियल पोर्ट से डेटा एकत्र करता है और आगमन समय के खिलाफ एकत्र किए गए डेटा का एक ग्राफ प्लॉट करता है। डेटा के आने का समय अनिश्चित है। मैं चाहता हूं कि डेटा प्राप्त होने पर प्लॉट को अपडेट किया जाए। मैंने यह करने के लिए खोज की और दो तरीके पाए:

  1. प्लॉट को साफ़ करें और फिर से सभी बिंदुओं के साथ प्लॉट को फिर से ड्रा करें।
  2. एक विशेष अंतराल के बाद इसे बदलकर भूखंड को चेतन करें।

मैं पहले वाले को पसंद नहीं करता क्योंकि प्रोग्राम चलता है और एक लंबे समय (उदाहरण के लिए एक दिन) के लिए डेटा एकत्र करता है, और प्लॉट को फिर से तैयार करना बहुत धीमा होगा। दूसरा भी बेहतर नहीं है क्योंकि डेटा के आने का समय अनिश्चित है और मैं चाहता हूं कि डेटा प्राप्त होने पर ही अपडेट हो।

क्या कोई ऐसा तरीका है जिसमें मैं केवल डेटा प्राप्त होने पर केवल और अधिक अंक जोड़कर भूखंड को अपडेट कर सकता हूं?


जवाबों:


138

क्या कोई ऐसा तरीका है जिसमें मैं प्लॉट को केवल अधिक बिंदु [s] जोड़कर अपडेट कर सकता हूं ...

Matplotlib में डेटा को एनिमेट करने के कई तरीके हैं, जो आपके पास मौजूद संस्करण पर निर्भर करता है। क्या आपने matplotlib रसोई की किताब के उदाहरण देखे हैं? इसके अलावा, matplotlib प्रलेखन में अधिक आधुनिक एनीमेशन उदाहरण देखें । अंत में, एनीमेशन एपीआई एक फ़ंक्शन को परिभाषित करता है FuncAnimation जो समय में एक फ़ंक्शन को एनिमेट करता है। यह फ़ंक्शन केवल वह फ़ंक्शन हो सकता है जिसका उपयोग आप अपने डेटा को प्राप्त करने के लिए करते हैं।

प्रत्येक विधि मूल रूप dataसे खींची जा रही वस्तु की संपत्ति निर्धारित करती है , इसलिए स्क्रीन या आकृति को साफ करने की आवश्यकता नहीं होती है। dataसंपत्ति बस, बढ़ाया जा सकता है ताकि आप पिछले अंक रख सकते और सिर्फ अपनी लाइन (या छवि या जो भी आप बना रहे हैं) को जोड़ने रखने के लिए।

यह देखते हुए कि आप कहते हैं कि आपके आने का समय अनिश्चित है आपका सबसे अच्छा दांव शायद कुछ करने जैसा है:

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()

फिर जब आप सीरियल पोर्ट से डेटा प्राप्त करते हैं तो कॉल करें update_line


आखिरकार! मैं इस +1 के उत्तर की तलाश में हूँ :) हम कैसे प्लॉट पुनर्विक्रय को स्वचालित रूप से बनाते हैं। ax.set_autoscale_on (ट्रू) काम नहीं करता है।
एडवर्ड न्यूवेल

13
उत्तर मिला: कॉल करें ax.relim () तब ax.autoscale_view () डेटा को अपडेट करने के बाद लेकिन plt.draw को कॉल करने से पहले ()
एडवर्ड न्यूवेल

Matplotlib कुकबुक ( scipy.org/Cookbook/Matplotlib/Animations ) का लिंक टूटा हुआ प्रतीत हो रहा है (मुझे "निषिद्ध" त्रुटि मिली)
डेविड डोरिया

21
चूंकि दिखाने के लिए कोई कॉल नहीं है (), स्क्रीन पर कथानक कभी दिखाई नहीं देता है। अगर मैं शो () कहता हूं, तो यह अपडेट को ब्लॉक करता है और प्रदर्शन नहीं करता है। क्या मैं कुछ भूल रहा हूँ? gist.github.com/daviddoria/027b5c158b6f200527a4
डेविड डोरिया

2
कोड के साथ एक समान लेकिन अलग - अलग स्व-निहित उत्तर का लिंक जो आप चला सकते हैं (इस उत्तर में सही सामान्य विचार है लेकिन उदाहरण कोड नहीं चलाया जा सकता है)
ट्रेवर बॉयड स्मिथ

44

FuncAnimation के बिना ऐसा करने के लिए (जैसे आप कोड के अन्य हिस्सों को निष्पादित करना चाहते हैं जबकि प्लॉट का उत्पादन किया जा रहा है या आप एक ही समय में कई भूखंडों को अपडेट करना चाहते हैं), drawअकेले कॉलिंग प्लॉट का उत्पादन नहीं करता है (कम से कम) qt बैकएंड)।

निम्नलिखित मेरे लिए काम करता है:

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()

हाँ! अंत में एक समाधान जो स्पाइडर के साथ काम करता है! जो चीज़ मुझे याद आ रही थी वह था gcf ()। कैनवास .flush_events () ड्रा () - कमांड के बाद।
np8

इस महान उदाहरण के आधार पर मैंने एक छोटा सा पायथन मॉड्यूल लिखा, जिसमें पुनरावृत्ति की साजिश रचने की अनुमति थी: github.com/lorenzschmid/dynplot
lorenzli

1
एक सुंदर उदाहरण!
viv

स्पष्ट, संक्षिप्त, बहुमुखी, लचीला: यह स्वीकृत उत्तर होना चाहिए।
pfabri

Jupyter नोटबुक में इसका उपयोग करने के लिए , आपको %matplotlib notebookअपने matplotlib आयात विवरण के बाद मैजिक कमांड जोड़ना होगा ।
pfabri

3

यहाँ एक तरीका है जो एक निश्चित संख्या में बिंदुओं के बाद अंक निकालने की अनुमति देता है:

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

मुझे पता है कि मुझे इस प्रश्न का उत्तर देने में देर हो रही है, लेकिन आपके मुद्दे के लिए आप "जॉयस्टिक" पैकेज में देख सकते हैं। मैंने इसे सीरियल पोर्ट से डेटा की एक धारा की साजिश रचने के लिए डिज़ाइन किया था, लेकिन यह किसी भी स्ट्रीम के लिए काम करता है। यह इंटरेक्टिव टेक्स्ट लॉगिंग या इमेज प्लॉटिंग (ग्राफ प्लॉटिंग के अलावा) के लिए भी अनुमति देता है। अपने स्वयं के छोरों को एक अलग धागे में करने की आवश्यकता नहीं है, पैकेज इसकी देखभाल करता है, बस आपको अपडेट की जाने वाली आवृत्ति की इच्छा दें। प्लस टर्मिनल साजिश रचते समय निगरानी के लिए उपलब्ध रहता है। देखें http://www.github.com/ceyzeriat/joystick/ या https://pypi.python.org/pypi/joystick (उपयोग पिप स्थापित करने के लिए जॉयस्टिक स्थापित)

बस नीचे दिए गए कोड में सीरियल पोर्ट से पढ़े गए वास्तविक डेटा बिंदु द्वारा np.random.random () को प्रतिस्थापित करें:

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()
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.