Table of Contents
What is this post about?
Rule
- When creating a stacked bar chart, the choice of color palette to assign to each categorical level should matche the meaning of each categories
- The way of color assignment must align with the color taste of other team document
Problem
- The default color palette might not suite your color taste, although they provide several options
- You want to specift the color sequence, which is not provided by the matplotlib color palette list
Goal
Code in practice
Base line
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
category_names = ['Strongly disagree', 'Disagree',
'Neither agree nor disagree', 'Agree', 'Strongly agree']
results = {
'Question 1': [10, 15, 17, 32, 26],
'Question 2': [26, 22, 29, 10, 13],
'Question 3': [35, 37, 9, 5, 14],
'Question 4': [32, 11, 9, 15, 33],
'Question 5': [21, 29, 5, 5, 40],
'Question 6': [8, 19, 5, 30, 38]
}
def custom_div_cmap(numcolors=4, name='custom_div_cmap',
mincol='dimgray', midcol='white', maxcol='navy'):
""" Create a custom diverging colormap with three colors
Default is blue to white to red with 11 colors. Colors can be specified
in any way understandable by matplotlib.colors.ColorConverter.to_rgb()
"""
from matplotlib.colors import LinearSegmentedColormap
cmap = LinearSegmentedColormap.from_list(name=name,
colors =[mincol, midcol, maxcol],
N=numcolors)
return cmap
def survey(results, category_names):
"""
Parameters
----------
results : dict
A mapping from question labels to a list of answers per category.
It is assumed all lists contain the same number of entries and that
it matches the length of *category_names*.
category_names : list of str
The category labels.r
"""
labels = list(results.keys())
data = np.array(list(results.values()))
data_cum = data.cumsum(axis=1)
custom_map = custom_div_cmap(data.shape[1], mincol='dimgray', midcol='0.8' ,maxcol='navy')
category_colors = custom_map(np.linspace(0, 1, data.shape[1]))
fig, ax = plt.subplots(figsize=(9.2, 5))
ax.invert_yaxis()
ax.set_xlim(0, np.sum(data, axis=1).max())
for i, (colname, color) in enumerate(zip(category_names, category_colors)):
widths = data[:, i]
starts = data_cum[:, i] - widths
rects = ax.barh(labels, widths, left=starts, height=0.5,
label=colname, color=color)
r, g, b, _ = color
text_color = 'white' if r * g * b < 0.5 else 'darkgrey'
ax.bar_label(rects, fmt='%.0f%%', label_type='center', color='w')
ax.legend(title='Survey Results', edgecolor='white',
ncol=len(category_names), bbox_to_anchor=(0, 1),
loc='lower left', fontsize='small')
ax.get_legend()._legend_box.align = "left"
# Despine
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['bottom'].set_visible(True)
return fig, ax
survey(results, category_names)
plt.show()
pandas.DataFrame
Data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
category_names = ['Strongly disagree', 'Disagree',
'Neither agree nor disagree', 'Agree', 'Strongly agree']
results = {
'Question 1': [10, 15, 17, 32, 26],
'Question 2': [26, 22, 29, 10, 13],
'Question 3': [35, 37, 9, 5, 14],
'Question 4': [32, 11, 9, 15, 33],
'Question 5': [21, 29, 5, 5, 40],
'Question 6': [8, 19, 5, 30, 38]
}
df = pd.DataFrame(results).T
df.columns = category_names
df.head(6)
1
|Strongly disagree|Disagree|Neither agree nor disagree|Agree|Strongly agree ---|---|---|---|---|--- Question 1| 10|15|17|32|26 Question 2| 26|22|29|10|13 Question 3| 35|37|9|5|14 Question 4| 32|11|9|15|33 Question 5| 21|29|5|5|40 Question 6| 8|19|5|30|38
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
def custom_div_cmap(numcolors=4, name='custom_div_cmap',
mincol='dimgray', midcol='white', maxcol='navy'):
""" Create a custom diverging colormap with three colors
Default is blue to white to red with 11 colors. Colors can be specified
in any way understandable by matplotlib.colors.ColorConverter.to_rgb()
"""
from matplotlib.colors import LinearSegmentedColormap
cmap = LinearSegmentedColormap.from_list(name=name,
colors =[mincol, midcol, maxcol],
N=numcolors)
return cmap
fig, ax = plt.subplots(figsize=(9.2, 5))
ax.set_xlim(0, 100)
custom_map = custom_div_cmap(df.shape[1], mincol='dimgray', midcol='0.8' ,maxcol='navy')
category_colors = custom_map(np.linspace(0, 1, df.shape[1]))
df.plot.barh(stacked=True, ax=ax, color=category_colors)
for container in ax.containers:
ax.bar_label(container, fmt='%.0f%%', label_type='center', color='w')
ax.legend(title='Survey Results',
ncol=df.shape[1], bbox_to_anchor=(0, 1),
edgecolor='white',
loc='lower left', fontsize='small')
ax.get_legend()._legend_box.align = "left"
ax.invert_yaxis()
# Despine
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['bottom'].set_visible(True)
Appendix: Type of Palette
References
統計
Python
math
Linux
Ubuntu 20.04 LTS
Shell
English
git
方法論
Ubuntu 22.04 LTS
統計検定
競技プログラミング
フーリエ解析
前処理
SQL
coding
コミュニケーション
Network
ssh
将棋
Data visualization
Docker
Econometrics
VSCode
statistical inference
GitHub Pages
apt
development
システム管理
Coffee
cloud
数値計算
素数
Book
Font
Metrics
Poetry
Ubuntu 24.04 LTS
architecture
aws
shell
systemctl
テンプレート
データ構造
ポワソン分布
会計分析
文字コード
環境構築
論文
App
Bayesian
Dynamic Programming
Keyboard
Processing
R
Steam
filesystem
quarto
regex
(注意:GitHub Accountが必要となります)