用渐变填充条形图的径向条
Posted
技术标签:
【中文标题】用渐变填充条形图的径向条【英文标题】:Fill radial bars of barplot with gradient 【发布时间】:2022-01-19 12:09:47 【问题描述】:我正在尝试生成显示各种颜色范围的径向(圆形)条形图,以便比较它们。使用径向条形图可以更好地理解色调值的圆形性质并表示超出范围的范围,例如 [340°, 10°]
为了用这张图片进行说明,想法是将左侧条形图的条的黑色替换为对应的彩色圆圈部分(显示在右侧)。
这是我用来生成径向条形图的代码:
import matplotlib.pyplot as plt
from numpy import pi
start_values = [0, 3*pi/4, pi/6]
range_values = [pi/3, 3*pi/10, pi/2]
ax = plt.subplot(projection='polar')
for i in range(len(start_values)):
ax.barh(i, range_values[i], left=start_values[i], color=(0, 0, 0))
我想应用 Matplotlib 的 hsv 颜色图,但 barh() 的颜色参数不接受。
在互联网上,我只找到了将渐变或 cmaps 应用于带有矩形条的常规条形图的技术(例如:How to fill matplotlib bars with a gradient? 但是,这些技术不适用于圆形条。
【问题讨论】:
【参考方案1】:感谢皮埃尔,我使用您的技术构建了一个图表,该图表也将饱和度和值表示为半径方向上的梯度。 这是给定示例的返回值(添加了饱和度和值范围)
代码:
def radius_barplot(lower_color, upper_color, ax, index=None, angle_precision=1, full_color=0.9, plot_index=(1, 1, 1)):
ax = plt.subplot(plot_index[0], plot_index[1], plot_index[2], projection="polar")
#print("Building graph")
if index is None:
if isinstance(lower_color[0], pd.Series):
index = lower_color[0].index
lower_color = [parameter.tolist() for parameter in lower_color]
upper_color = [parameter.tolist() for parameter in upper_color]
else:
index= range(len(lower_color[0]))
pi2 = 2*pi
hue_only = 1 - full_color
for i in range(len(index)):
#print("Frame n°", i+1, sep='')
min_hue, max_hue = lower_color[0][i] / 179 * 2 * pi, upper_color[0][i] / 179 * 2 * pi
min_sat, min_val = lower_color[1][i] / 255, lower_color[2][i] / 255
range_hue, range_sat, range_val = max_hue - min_hue, round(upper_color[1][i] - lower_color[1][i]), round(upper_color[2][i] - lower_color[2][i])
n_height = min(max(range_sat, range_val), 20)
pas_height = 1*full_color/n_height
large_pas_angle, large_pas_height = angle_precision*1.5/180*pi, pas_height*1.25
pas_sat, pas_val = range_sat / 255 / n_height, range_val / 255 / n_height
#print("Angle range: [", round(min_hue/pi*180), "; ", round((max_hue)/pi*180), "]", sep='')
# For each degree of the angle
for angle in np.arange(min_hue, max_hue, angle_precision/180*pi):
# To avoid an overflow on the end of the interval
if angle + large_pas_angle > max_hue: large_pas_angle = max_hue - angle
# Keep a positive angle for the hue value
if angle < 0: angle = 2*pi + angle
# For each line inside the current bar
if full_color > 0:
for height in range(n_height):
"""
print("Height: [", round(i + height*pas_height, 2), ", ", round(i + height*pas_height + pas_height, 2),
"], angle: ", round(angle/pi*180, 3),
"°: color [", round(angle*360), ", ", round((min_sat + height*pas_sat)*100), ", ", round((min_val + height*pas_val)*100), "]", sep='')
"""
ax.barh(y=i + height*pas_height,
width=large_pas_angle, left=angle, height=large_pas_height, align='edge',
color=colorsys.hsv_to_rgb(angle/pi2,
min_sat + height*pas_sat,
min_val + height*pas_val)
)
# To display the reference hue (with full saturation and value)
ax.barh(y=i + full_color,
width=large_pas_angle, left=angle, height=hue_only, align='edge',
color=(colorsys.hsv_to_rgb(angle/pi2, 1, 1)))
# """
ax.set_rgrids(range(len(index)), labels=index)
radius_barplot(
((0, (3*pi/4)/(2*pi)*179, (pi/6)/(2*pi)*179), (0, 200, 50), (0, 50, 200)), # Minimal values of colors ranges
(((pi/3)/(2*pi)*179, (21*pi/20)/(2*pi)*179, (2*pi/3)/(2*pi)*179), (255, 250, 100), (255, 100, 250)), # Maximal values of colors ranges
axes)
图表:
这种技术的主要问题是它的复杂性:在我的计算机上返回这张图需要 1 分 45 秒,而获得我感兴趣的图则需要将近 5 分钟。
因此,如果有人有更好、更优化的方法来获得类似的结果,我仍然会感兴趣。
【讨论】:
【参考方案2】:不是最优雅的版本,但它可以完成工作
import matplotlib.pyplot as plt
from numpy import pi
import colorsys
start_values = [0, 3*pi/4, pi/6]
range_values = [pi/3, 3*pi/10, pi/2]
ax = plt.subplot(projection='polar')
n = 100
for i in range(len(start_values)):
ax.barh(i, [range_values[i]/n]*n,
left=[start_values[i]+j*range_values[i]/n for j in range(n)],
color=[colorsys.hsv_to_rgb((start_values[i]+(j+0.5)*range_values[i]/n)/(2*pi),1,1) for j in range(n)])
plt.show()
输出:
【讨论】:
以上是关于用渐变填充条形图的径向条的主要内容,如果未能解决你的问题,请参考以下文章