0

I want to update a plot every x seconds and keep the shared x axis. The problem is that when using a cla() command the sharedx gets lost and when not using the cla(), the plot is not updated, but "overplotted", as in this minimal example:

import matplotlib.pyplot as plt
import pandas as pd

data = pd.DataFrame([[1,2,1],[3,1,3]], index = [1,2])

n_plots = data.shape[1]

fig, axs = plt.subplots(n_plots , 1, sharex = True)
axs = axs.ravel()

while True:
    for i in range(n_plots):
        #axs[i].cla()
        axs[i].plot(data.iloc[:,i])
        axs[i].grid()

    plt.tight_layout()
    plt.draw()

    plt.pause(5)

    data = pd.concat([data,data]).reset_index(drop = True)

The behaviour can be seen by uncommenting the axs[i].cla() line.

So the question is: How can I update a plot (without predefined number of subplots) in a while loop (I want to update some data) and keep a shared x-axis?

Thanks in advance

Daniel
  • 304
  • 2
  • 12

1 Answers1

0

First, to produce animations with matplotlib you should have a look at FuncAnimation. You'll find lots of posts on SO on this subject, for instance: Dynamically updating plot in matplotlib

The general guideline is to not repetitively call plt.plot() but instead use the set_data() function of the Line2D object returned by plot(). In other words, in a first part of your code you instantiate an object with an empty plot

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

and then, whenever you need to update your plot, you keep the same object (do not clear the axes, do not make a new plot() call), and simply update its content:

l.set_data(X,Y)
# alternatively, if only Y-data changes
l.set_ydata(Y)  # make sure that len(Y)==len(l.get_xdata())!

EDIT: Here is a minimal example showing 3 axes with shared x-axis, like you are trying to do

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

N_points_to_display = 20
N_lines = 3
line_colors = ['r','g','b']

fig, axs = plt.subplots(N_lines, 1, sharex=True)
my_lines = []
for ax,c in zip(axs,line_colors):
    l, = ax.plot([], [], c, animated=True)
    my_lines.append(l)

def init():
    for ax in axs:
        ax.set_xlim((0,N_points_to_display))
        ax.set_ylim((-1.5,1.5))
    return my_lines

def update(frame):
    #generates a random number to simulate new incoming data
    new_data = np.random.random(size=(N_lines,))
    for l,datum in zip(my_lines,new_data):
        xdata, ydata = l.get_data()
        ydata = np.append(ydata, datum)
        #keep only the last N_points_to_display
        ydata = ydata[-N_points_to_display:]
        xdata = range(0,len(ydata))
        # update the data in the Line2D object
        l.set_data(xdata,ydata)
    return my_lines

anim = FuncAnimation(fig, update, interval=200,
                    init_func=init, blit=True)

enter image description here

Diziet Asahi
  • 38,379
  • 7
  • 60
  • 75