stacked bar plot using matplotlib
You need the bottom
of each dataset to be the sum of all the datasets that came before. you may also need to convert the datasets to numpy arrays to add them together.
p1 = plt.bar(ind, dataset[1], width, color='r')p2 = plt.bar(ind, dataset[2], width, bottom=dataset[1], color='b')p3 = plt.bar(ind, dataset[3], width, bottom=np.array(dataset[1])+np.array(dataset[2]), color='g')p4 = plt.bar(ind, dataset[4], width, bottom=np.array(dataset[1])+np.array(dataset[2])+np.array(dataset[3]), color='c')
Alternatively, you could convert them to numpy arrays before you start plotting.
dataset1 = np.array(dataset[1])dataset2 = np.array(dataset[2])dataset3 = np.array(dataset[3])dataset4 = np.array(dataset[4])p1 = plt.bar(ind, dataset1, width, color='r')p2 = plt.bar(ind, dataset2, width, bottom=dataset1, color='b')p3 = plt.bar(ind, dataset3, width, bottom=dataset1+dataset2, color='g')p4 = plt.bar(ind, dataset4, width, bottom=dataset1+dataset2+dataset3, color='c')
Or finally if you want to avoid converting to numpy arrays, you could use a list comprehension:
p1 = plt.bar(ind, dataset[1], width, color='r')p2 = plt.bar(ind, dataset[2], width, bottom=dataset[1], color='b')p3 = plt.bar(ind, dataset[3], width, bottom=[sum(x) for x in zip(dataset[1],dataset[2])], color='g')p4 = plt.bar(ind, dataset[4], width, bottom=[sum(x) for x in zip(dataset[1],dataset[2],dataset[3])], color='c')
I found this such a pain that I wrote a function to do it. I'm sharing it in the hope that others find it useful:
import numpy as npimport matplotlib.pyplot as pltdef plot_stacked_bar(data, series_labels, category_labels=None, show_values=False, value_format="{}", y_label=None, colors=None, grid=True, reverse=False): """Plots a stacked bar chart with the data and labels provided. Keyword arguments: data -- 2-dimensional numpy array or nested list containing data for each series in rows series_labels -- list of series labels (these appear in the legend) category_labels -- list of category labels (these appear on the x-axis) show_values -- If True then numeric value labels will be shown on each bar value_format -- Format string for numeric value labels (default is "{}") y_label -- Label for y-axis (str) colors -- List of color labels grid -- If True display grid reverse -- If True reverse the order that the series are displayed (left-to-right or right-to-left) """ ny = len(data[0]) ind = list(range(ny)) axes = [] cum_size = np.zeros(ny) data = np.array(data) if reverse: data = np.flip(data, axis=1) category_labels = reversed(category_labels) for i, row_data in enumerate(data): color = colors[i] if colors is not None else None axes.append(plt.bar(ind, row_data, bottom=cum_size, label=series_labels[i], color=color)) cum_size += row_data if category_labels: plt.xticks(ind, category_labels) if y_label: plt.ylabel(y_label) plt.legend() if grid: plt.grid() if show_values: for axis in axes: for bar in axis: w, h = bar.get_width(), bar.get_height() plt.text(bar.get_x() + w/2, bar.get_y() + h/2, value_format.format(h), ha="center", va="center")
Example:
plt.figure(figsize=(6, 4))series_labels = ['Series 1', 'Series 2']data = [ [0.2, 0.3, 0.35, 0.3], [0.8, 0.7, 0.6, 0.5]]category_labels = ['Cat A', 'Cat B', 'Cat C', 'Cat D']plot_stacked_bar( data, series_labels, category_labels=category_labels, show_values=True, value_format="{:.1f}", colors=['tab:orange', 'tab:green'], y_label="Quantity (units)")plt.savefig('bar.png')plt.show()
This is probably your most convenient solution if you are willing to use Pandas:
import pandas as pdimport numpy as npimport matplotlib.pyplot as plt X_AXIS = ('60.0', '65.0', '70.0', '75.0', '80.0', '85.0', '90.0', '95.0', '100.0', '105.0', '110.0', '115.0', '120.0', '125.0', '130.0', '135.0', '140.0', '145.0', '150.0', '155.0', '160.0', '165.0', '170.0', '175.0', '180.0', '185.0', '190.0', '195.0', '200.0')index = pd.Index(X_AXIS, name='test')data = {'a': (0.0, 25.0, 48.94, 83.02, 66.67, 66.67, 70.97, 84.62, 93.33, 85.0, 92.86, 93.75, 95.0, 100.0, 100.0, 100.0, 100.0, 80.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0), 'b': (0.0, 50.0, 36.17, 11.32, 26.67, 33.33, 29.03, 15.38, 6.67, 15.0, 7.14, 6.25, 5.0, 0.0, 0.0, 0.0, 0.0, 20.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), 'c': (0.0, 12.5, 10.64, 3.77, 4.45, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), 'd': (100.0, 12.5, 4.26, 1.89, 2.22, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)}df = pd.DataFrame(data, index=index)ax = df.plot(kind='bar', stacked=True, figsize=(10, 6))ax.set_ylabel('foo')plt.legend(title='labels', bbox_to_anchor=(1.0, 1), loc='upper left')# plt.savefig('stacked.png') # if neededplt.show()