Vincent
Vincent

Reputation: 1734

Real time dynamic plotting

I have a question concerning updating dynamically scatter plots from matplotlib. I have the following class in Python

''' PolygonHandler.py - Python source for polygon handling '''
import numpy as np
import matplotlib.pyplot as plt


class PolygonHandler:
    # Constructor
    def __init__(self):
        self.X = []
        self.Y = []
        self.numberPoints = 0

    # Print the polygon
    def draw(self):
        plt.scatter(self.X,self.Y)
        plt.draw()

    def update(self):
        for i in range(1,self.numberPoints):
            self.X[i] += np.random.normal()*0.01
            self.Y[i] += np.random.normal()*0.01


    # append a point
    def add(self,x,y):
        self.numberPoints += 1
        self.X.append(x)
        self.Y.append(y)

This class is used in a real time loop that receives information and adds the points to the PolygonHandler class. Now for the purpose of an example, I want to design the following loop

P = PolygonHandler()
P.add(1,1)
P.add(2,2)
P.add(1,2)
plt.ion()
plt.show()
while (True):
    P.draw()
    P.update()

How can I tell the interpreter to draw the scatter plots, and once done to remove the former points after updating ? Right now, my plot draws the points and all their previous positions.

Vincent

Thanks a lot for your help

PS : An other problem I have is that the window that is opened by matplotlib freezes and stops answering as soon as I clicked on it (for example to move it to another place on my screen), is there a way to prevent that ?

Upvotes: 3

Views: 3046

Answers (2)

Miguel Tomás
Miguel Tomás

Reputation: 1911

you can do it like this. It accepts x,y as list and output a scatter plot plus a linear trend on the same plot.

from IPython.display import clear_output
from matplotlib import pyplot as plt
%matplotlib inline
    
def live_plot(x, y, figsize=(7,5), title=''):
    clear_output(wait=True)
    plt.figure(figsize=figsize)
    plt.xlim(0, training_steps)
    plt.ylim(0, 100)
    x= [float(i) for i in x]
    y= [float(i) for i in y]
    
    if len(x) > 1:
        plt.scatter(x,y, label='axis y', color='k') 
        m, b = np.polyfit(x, y, 1)
        plt.plot(x, [x * m for x in x] + b)

    plt.title(title)
    plt.grid(True)
    plt.xlabel('axis x')
    plt.ylabel('axis y')
    plt.show();

you just need to call live_plot(x, y) inside a loop. here's how it looks: enter image description here

Upvotes: 0

gg349
gg349

Reputation: 22671

Here you have one way to go, using animations from matplotlib.

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

class PolygonHandler:
    # Constructor
    def __init__(self):
        self.X = []
        self.Y = []
        self.numberPoints = 0
        self.fig , ax = plt.subplots()
        self.sc = ax.scatter(self.X,self.Y)
        ax.set_xlim(0,3)
        ax.set_ylim(0,3)

    # Print the polygon
    def update(self,_):
        for i in range(self.numberPoints):
            self.X[i] += np.random.normal()*0.01
            self.Y[i] += np.random.normal()*0.01
        self.sc.set_offsets(np.column_stack((self.X,self.Y)))
        return self.sc,

    # append a point
    def add(self,x,y):
        self.numberPoints += 1
        self.X.append(x)
        self.Y.append(y)

And this way you plot your 3 random walks:

P = PolygonHandler()
P.add(1,1)
P.add(2,2)
P.add(1,2)
ani = animation.FuncAnimation(P.fig, P.update, interval=10,blit=False)

the key element is the method set_offsets(), that substitutes the data of the scatter plot. Then such scatter object is returned by update(), so that matplotlib knows that it must be updated. For another source with a matplotlib animation handled by a class, see this matplotlib example.

With blit=True in the last line the animation is faster, but depending on your OS it may not work.

Upvotes: 1

Related Questions