Skip to content
Advertisement

Matplotlib stacked histogram label

Here is my picture. I need to make label for those bars however every upper layer contains lower layer – so the label should containt grouped colors, i.e. blue – dataset 1, blue/orange – dataset 2, blue/orange/green – dataset 3 and finally blue/orange/green/purple – dataset 4. Is it plausible to make it? Thank you.

enter image description here

binwidth = 1
n, bins, patches = ax1.hist(C, bins=range(81, 105, binwidth), 
                   density=False, histtype='barstacked' ,
                        edgecolor='gray', 
                        color=barvy_histogram,linewidth=0.3)

hatches = ['//','x','..','oo']
for patch_set, hatch in zip(patches, hatches):
    for patch in patch_set.patches:
        patch.set_hatch(hatch)
        patch.set_linewidth=0.1
        patch.set_color='gray'
mpl.rcParams['hatch.linewidth'] = 0.5

Advertisement

Answer

The following approach uses the tuple legend handler (HandlerTuple) to combine the legend handles. It produces a horizontal layout, while maybe a vertical stacking would be more interesting.

The code starts with creating some test data, supposing C is an Nx4 array of integers. The bin edges are set at halves to make sure that floating point accuracy wouldn’t place values in the wrong bin.

import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.legend_handler import HandlerTuple
import numpy as np

# first, create some test data
C = (np.random.normal(0.001, 1, (100, 20)).cumsum(axis=0) * 1.2 + 90).astype(int).reshape(-1, 4)
c_min = C.min()
c_max = C.max()

mpl.rcParams['hatch.linewidth'] = 0.5
fig, ax1 = plt.subplots(figsize=(12, 5))
binwidth = 1
colors = plt.cm.Set2.colors[:C.shape[1]]
_, _, patches = ax1.hist(C, bins=np.arange(c_min - 0.5, c_max + binwidth, binwidth),
                         density=False, histtype='barstacked',
                         edgecolor='gray', color=colors, linewidth=0.3,
                         label=[f'N={p}' for p in range(25, 101, 25)])

hatches = ['//', 'x', '..', 'oo']
for patch_set, hatch in zip(patches, hatches):
     for patch in patch_set.patches:
          patch.set_hatch(hatch)
          patch.set_linewidth = 0.1

handles, labels = ax1.get_legend_handles_labels()
ax1.legend(handles=[tuple(handles[:i + 1]) for i in range(C.shape[1])], labels=labels,
           handlelength=6, handler_map={tuple: HandlerTuple(ndivide=None, pad=0)})
plt.show()

matplotlib legend using HandlerTuple

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement