This is my current plot. There are 3 major groups (v1, v2, v3), each with 3 classes (a, b, c). A class can also have a subclass (a2, c2), but not necessarily.
I need to plot in stacked format, and want to have subclasses grouped together. That is, I need to remove the white gap between a/a2 c/c2 and have a common x-label (a and c, remove a2 and c2), like in the plot below (edited with Paint).
I have no idea how to do it. I thought about manually changing the white gap, but it seems it must be the same across all bars. I tried manually specifying the positioning of the bars but I couldn't. Now I thought about multiple grouping but I am having trouble. Any help is really appreciated.
from matplotlib.colors import to_rgb
import matplotlib.pyplot as plt
import seaborn as sns
import itertools
import numpy as np
import pandas as pd
idx = ['a', 'a2', 'b', 'c', 'c2']
groups = ['v1', 'v2', 'v3']
def model_to_patterns(k):
return 'o' if '2' in k else ''
patterns = tuple(model_to_patterns(k) for k in idx)
edgecolor = ['black' if '2' not in m else 'white'
for m in idx]
barplot_colors = ['#1f77b4', 'tab:purple', '#ff7f0e']
palette_bars = itertools.cycle(barplot_colors)
color_map = dict()
ys = dict()
errs = dict()
for f in groups:
color_map.update({f: next(palette_bars)})
ys.update({f: []})
errs.update({f: []})
for m in idx:
ys[f].append(np.random.rand())
errs[f].append([0, np.random.rand() / 10])
# ------------------------------------------------------------------------------
k = list(ys.keys())
for i in reversed(range(1, len(ys))):
for j in range(len(ys[k[i]])):
ys[k[i]][j] = np.maximum(1e-6, ys[k[i]][j] - ys[k[i-1]][j])
for k in list(errs.keys())[:-1]:
errs[k] = [[np.nan, np.nan]] * len(errs[k])
df = pd.DataFrame(ys,
index=idx
)
errs = list(errs.values())
errs = np.swapaxes(errs, 1, 2)
error_args = dict(lw=1, capsize=2, capthick=1)
color = []
for c in df.columns.map(color_map):
tmp_color_list = [c if '2' not in m else list(to_rgb(c)) + [0.7]
for m in idx]
color.append(tmp_color_list)
ax = df.interpolate().plot(kind='bar',
color=color,
figsize=(12,4),
width=0.7,
edgecolor=edgecolor,
yerr=errs,
error_kw=error_args,
stacked=True)
ax.autoscale(enable=True, axis='x', tight=True)
bars = ax.patches
for bar, hatch in zip(bars, patterns * len(groups)):
bar.set_hatch(hatch)
plt.draw()
plt.show()
EDIT
This question was marked as duplicate, but none of the linked questions actually solves my problem.
- This question is closed and does not even have a solution.
- This and this are not exactly what I am looking for. None of them, in fact, deals with missing 'subclasses'. In my case, I have subclasses only for groups 'a' and 'c', not 'b', and these solutions leave an empty bars for 'b2' (which is missing).
- Even if this would not be a big problem, the solutions with
matplotlib
raise errors in my case, and the ones withaltair
(that look much better) do not include the standard error markers in grouped stacked charts.