Plotting live data with Matplotlib
By: Thiago Carvalho
Tags: Animation, Data Visualization, Real Time Analytics, Matplotlib, Python

When we add FuncAnimation, it’ll repeatedly call our function to refresh the chart.

But when we use .plot() multiple times on the same axis, it won’t update the line but rather plot new ones.

CPU and memory line charts with a line for each measure — Image by the author

It’s hard to append to a line that’s already plotted, and other types of visualization like maps, bars, and pie charts, can be even more challenging to update.

A more straightforward solution is to clear the axis before plotting and draw a new plot at every iteration.

Adding axis.cla() to our function will give us that result.

That way, we don’t need to worry about texts, annotations, or other elements of our chart either. Every time we plot, the axis will reset, and the function will draw everything again.

# function to update the data

def my_function():

# get data

cpu.popleft()

cpu.append(psutil.cpu_percent(interval=1))

ram.popleft()

ram.append(psutil.virtual_memory().percent) # clear axis

ax.cla()

ax1.cla() # plot cpu

ax.plot(cpu)

ax.scatter(len(cpu)-1, cpu[-1])

ax.text(len(cpu)-1, cpu[-1]+2, "{}%".format(cpu[-1]))

ax.set_ylim(0,100) # plot memory

ax1.plot(ram)

ax1.scatter(len(ram)-1, ram[-1])

ax1.text(len(ram)-1, ram[-1]+2, "{}%".format(ram[-1]))

ax1.set_ylim(0,100) # start collections with zeros

cpu = collections.deque(np.zeros(10))

ram = collections.deque(np.zeros(10)) # define and adjust figure

fig = plt.figure(figsize=(12,6), facecolor='#DEDEDE')

ax = plt.subplot(121)

ax1 = plt.subplot(122)

ax.set_facecolor('#DEDEDE')

ax1.set_facecolor('#DEDEDE') # test

my_function()

my_function()

my_function()

plt.show()

CPU and memory line charts with multiple measures — Image by the author

We have everything we need to make our chart come to life.

Now we can add FuncAnimation and pass our figure and function as parameters.

ani = FuncAnimation(fig, my_function, interval=1000)

When we defined the figure, we assigned it to a variable called fig .

A different approach would be to skip that and rely on the default figure created by Matplotlib. Replacing the first parameter for plt.gcf() , which will automatically get the current figure for us.

Note that we also set an interval of 1,000 milliseconds at FuncAnimation. We’re not required to put it. By default, it’ll call the function every 200 milliseconds.

Since we already have an interval, we can also remove the one from .cpu_percent() .

Without getting into too much detail about psutil, when we don’t set the interval, it will calculate from the last time the function was called — Which means the first value it returns will be zero. After that, it’ll work as before.

Lastly, FuncAnimation will call our function and use frames as the first argument. Since we won’t define any custom values for this parameter, it will work as a counter, passing the iterations count.

The only thing we have to do is make sure our function can receive this argument, even if we’re not using it.

# function to update the data

def my_function(i):

# get data

cpu.popleft()

cpu.append(psutil.cpu_percent())

ram.popleft()

ram.append(psutil.virtual_memory().percent) # clear axis

ax.cla()

ax1.cla() # plot cpu

ax.plot(cpu)

ax.scatter(len(cpu)-1, cpu[-1])

ax.text(len(cpu)-1, cpu[-1]+2, "{}%".format(cpu[-1]))

ax.set_ylim(0,100) # plot memory

ax1.plot(ram)

ax1.scatter(len(ram)-1, ram[-1])

ax1.text(len(ram)-1, ram[-1]+2, "{}%".format(ram[-1]))

ax1.set_ylim(0,100) # start collections with zeros

cpu = collections.deque(np.zeros(10))

ram = collections.deque(np.zeros(10)) # define and adjust figure

fig = plt.figure(figsize=(12,6), facecolor='#DEDEDE')

ax = plt.subplot(121)

ax1 = plt.subplot(122)

ax.set_facecolor('#DEDEDE')

ax1.set_facecolor('#DEDEDE') # animate

ani = FuncAnimation(fig, my_function, interval=1000) plt.show()

CPU and memory visualization — Image by the author

It’s alive!