datawrapper.de
可视化服务中,可以使用“Split-Bars”图;我想使用 matplotlib (等)在 Python 中重新创建可视化绘图类型(以编程方式)。SO上没有找到相关问题。
问题描述:结构看起来是表格格式;每个单元格都填充有阴影区域;到给定的填充百分比。轴线被移除。值文本颜色根据背景颜色而变化。值文本位置根据百分比值而变化。
尝试:ChatGPT 建议ax.imshow()
生成带有颜色的每个单元格,但是(据我所知)它缺乏%填充功能。我恢复到
ax.fill_between()
,但是这只适用于沿轴(X或是)。最后我尝试了
ax.add_patch(Rectangle(...))
,这似乎是外部“传奇”色块和内部
ax
%填充色块的理想选择。使用此方法的解决方案如下所示。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
from matplotlib.patches import Rectangle
import matplotlib.font_manager as font_manager
# Add every font at the specified location
font_dir = ['path/to/AppData/Local/Microsoft/Windows/Fonts/']
for font in font_manager.findSystemFonts(font_dir):
font_manager.fontManager.addfont(font)
from matplotlib.font_manager import findfont, FontProperties
font = findfont(FontProperties(family=['Roboto']))
matplotlib.rcParams['font.family'] = 'sans-serif'
matplotlib.rcParams['font.sans-serif'] = 'Roboto'
def plot_split_bar(data, metrics, categories,
colors=[(0.6431, 0.9412, 1.0),
(0.0, 0.6392, 0.6902),
(0.0, 0.349, 0.3765),
(0.2039, 0.749, 0.8118),
(0.0, 0.4392, 0.4745),
(0.3686, 0.8549, 0.9255),
(0.0, 0.5373, 0.5804)],
precision=2, fig_size=None):
numrows = data.shape[0]
numcols = data.shape[1]
data_max = np.max(data)*1.025
fig, ax = plt.subplots()
if fig_size:
fig.set_size_inches(fig_size)
# Set labels and ticks
ax.set_xticks(np.arange(len(categories))-0.14)
ax.set_xticklabels(categories, fontsize=8, fontname='Roboto', ha='left', va='center', y=1.001, weight='bold')
ax.xaxis.tick_top()
ax.xaxis.set_ticks_position('none')
ax.set_yticks(np.arange(len(metrics))+0.26)
ax.set_yticklabels(metrics, fontsize=7, fontname='Roboto', x=0.025, ha='right')
ax.spines[['right', 'top','bottom','left']].set_visible(False)
# Add text annotation
for i in range(len(data)):
for j in range(len(data[i])):
perc = data[i][j]/data_max
ax.add_patch(Rectangle((j-.29, i-.25), perc, 0.95,
facecolor = colors[j],
alpha=1,
fill=True,
))
text_pos = (j-0.26)+perc if perc < 0.5 else (j-(0.26)+.1)
text_col = '#333' if perc < 0.5 else 'white'
text_col = '#333' if text_col=='white' and np.mean(colors[j])>0.5 else text_col
text_bold = None if perc < 0.5 else 'bold'
text_perc = f'{data[i][j]:,.{precision}f}' if f'{data[i][j]:,.1f}'[-2:]!='.0' else f'{data[i][j]:,.0f}'
ax.text(text_pos, i+.15, text_perc, ha='left', va='center',
fontname='Roboto', color=text_col, fontsize=7, weight=text_bold)
ax.grid(False)
ax.set_ylim(0-0.5, numrows)
ax.set_xlim(0-0.5, numcols)
for j in range(len(categories)):
ax.add_patch(Rectangle((j-0.31, len(data)+.4), 0.15, .7,
facecolor = colors[j],
clip_on=False,
alpha=1,
fill=True,
))
plt.show()
化石燃料数据中全球二氧化碳排放量的使用:data = pd.DataFrame(arr[1:],columns=arr[0] ).set_index('year').iloc[::-1]
plot_split_bar(data=data.astype(float).to_numpy(),
metrics=data.index.tolist(),
categories=data.columns,
precision=2,
fig_size=(5, 3)
)
与伪随机生成的数据一起使用:cols = 3
data = pd.DataFrame( np.random.rand(5,cols), columns=[chr(65+i) for i in range(cols)]).iloc[::-1]
plot_split_bar(data=data.to_numpy(),
metrics=data.index.tolist(),
categories=data.columns,
precision=2,
fig_size=(5, 1)
)