1

I would like to know how I can dynamically update a stacked bar plot in matplotlib.

This question Dynamically updating a bar plot in matplotlib describes how it can be done for a normal bar chart, but not a stacked bar chart.

In a normal bar chart the update can be done via rect.set_height(h) assuming that rects = plt.bar(range(N), x, align='center')

But in a stacked bar chart we also need to set the bottom.

p2 = plt.bar(ind, womenMeans, width, color='y',
             bottom=menMeans, yerr=menStd)

How can I dynamically set the bottom? Unfortunately it seems that the 'Rectangle' object has no attribute 'set_bottom'. Is there any alternative way to handle this?

Community
  • 1
  • 1
Nickpick
  • 6,163
  • 16
  • 65
  • 116

1 Answers1

2

For some reason, the set_bottom() function you want is set_y under patches in the return object from bar. The minimal example, based on the link you suggest would look like,

import numpy as np
import matplotlib.pyplot as plt

def setup_backend(backend='TkAgg'):
    import sys
    del sys.modules['matplotlib.backends']
    del sys.modules['matplotlib.pyplot']
    import matplotlib as mpl
    mpl.use(backend)  # do this before importing pyplot
    import matplotlib.pyplot as plt
    return plt

N = 5
width = 0.35       # the width of the bars: can also be len(x) sequence

def animate():
    # http://www.scipy.org/Cookbook/Matplotlib/Animations
    mu, sigma = 100, 15
    h = mu + sigma * np.random.randn((N*2))
    p1 = plt.bar(np.arange(N), h[:N], width, color='r')
    p2 = plt.bar(np.arange(N), h[N:], width, color='b', bottom=h[:N])
    assert len(p1) == len(p2)
    maxh = 0.
    for i in range(50):
        for rect1, rect2 in zip(p1.patches, p2.patches):
            h = mu + sigma * np.random.randn(2)
            #Keep a record of maximum value of h
            maxh = max(h[0]+h[1],maxh)
            rect1.set_height(h[0])
            rect2.set_y(rect1.get_height())
            rect2.set_height(h[1])
        #Set y limits to maximum value
        ax.set_ylim((0,maxh))
        fig.canvas.draw()

plt = setup_backend()
fig, ax = plt.subplots(1,1)
win = fig.canvas.manager.window
win.after(10, animate)
plt.show()

Note, I change the height generation using random numbers each iteration so the two arrays of patches can be zipped instead (would get a bit messy otherwise).

Ed Smith
  • 12,716
  • 2
  • 43
  • 55
  • That is working very well. Many thanks. Is there also a way to update the axis? When the bars get higher than the axis, then they are no longer shown at the moment. – Nickpick Sep 05 '15 at 18:29
  • 1
    You can use `ax.relim()` to update maximum range and then `ax.autoscale_view()` to adjust the axis (where `ax` is the axis handle). You may find animation ends up a bit jerky and it's better to set a constant limit throughout (based on maximum expected from your data). I've changed the example above to use subplots (so you get `ax`) and update the limits only when it gets bigger than the previous maximum value. – Ed Smith Sep 05 '15 at 18:48