Matplotlib设置alpha<1时如何防止网格线穿透填充图形显示
问题原因
通常使用ax.set_axisbelow(True) 或 plt.rc('axes', axisbelow=True) 可以将网格线调整到填充区域图层下方,但当填充元素设置alpha<1时,网格线仍会透显到图层前方。
这是因为上述方法仅调整了图层的z轴排序,matplotlib默认渲染逻辑下,若上层填充元素(直方图填充、fill_between生成的填充区域等)设置了全局alpha<1参数,渲染时会对该元素和其下方所有可见元素(包括网格线)做alpha混合,最终导致网格线透过半透明区域显示,该现象和zorder顺序无关。
问题复现效果如下:
复现代码:
import numpy as np import matplotlib.pyplot as plt np.random.seed(2022) x1 = np.random.normal(0, 0.8, 1000) x2 = np.random.normal(-2, 1, 1000) x3 = np.random.normal(3, 2, 1000) kwargs = dict(histtype='stepfilled', alpha=.3, density=True, bins=40) fig, ax = plt.subplots(figsize=(9, 6)) ax.hist(x1, **kwargs) ax.hist(x2, **kwargs) ax.hist(x3, **kwargs) ax.set_axisbelow(True) ax.yaxis.grid(color='gray', linestyle='dashed') ax.xaxis.grid(color='gray', linestyle='dashed')
解决方案
以下两种方案均兼容hist、fill_between等所有生成填充区域的接口,可彻底解决网格透显问题。
方案1:将透明度写入颜色RGBA通道(最简单,适配绝大多数场景)
不要通过alpha参数单独设置填充透明度,而是将透明度值直接写入填充颜色的RGBA通道中,即可避免网格线透显,同时保留填充区域的半透明效果。
实现代码:
import numpy as np import matplotlib.pyplot as plt from matplotlib.colors import to_rgba np.random.seed(2022) x1 = np.random.normal(0, 0.8, 1000) x2 = np.random.normal(-2, 1, 1000) x3 = np.random.normal(3, 2, 1000) # 直接定义带透明度的RGBA填充色,透明度设为0.3 color1 = to_rgba('#1f77b4', 0.3) color2 = to_rgba('#ff7f0e', 0.3) color3 = to_rgba('#2ca02c', 0.3) kwargs = dict(histtype='stepfilled', density=True, bins=40) fig, ax = plt.subplots(figsize=(9, 6)) ax.hist(x1, color=color1, **kwargs) ax.hist(x2, color=color2, **kwargs) ax.hist(x3, color=color3, **kwargs) ax.set_axisbelow(True) ax.yaxis.grid(color='gray', linestyle='dashed') ax.xaxis.grid(color='gray', linestyle='dashed') plt.show()
该方案的渲染逻辑是:填充元素本身为不透明渲染,仅颜色值预乘了透明度,渲染时不会将下方网格线纳入混合计算,网格会被完全遮挡在填充层下方。
方案2:保留多填充块之间的半透明叠加效果
如果需要多个重叠填充块之间保留半透明叠色效果,同时不让网格透出,可以先绘制网格,再统一调整所有填充元素的透明度和层级:
import numpy as np import matplotlib.pyplot as plt np.random.seed(2022) x1 = np.random.normal(0, 0.8, 1000) x2 = np.random.normal(-2, 1, 1000) x3 = np.random.normal(3, 2, 1000) fig, ax = plt.subplots(figsize=(9, 6)) # 先绘制最底层网格 ax.set_axisbelow(True) ax.yaxis.grid(color='gray', linestyle='dashed') ax.xaxis.grid(color='gray', linestyle='dashed') # 绘制填充元素时不设置alpha参数 kwargs = dict(histtype='stepfilled', density=True, bins=40) ax.hist(x1, **kwargs) ax.hist(x2, **kwargs) ax.hist(x3, **kwargs) # 统一设置所有填充块透明度,调整层级高于网格 ax.grid(zorder=0) for patch in ax.patches: patch.set_alpha(0.3) patch.set_zorder(1) plt.show()
如果是fill_between生成的填充区域,只需要将遍历对象从ax.patches替换为ax.collections即可,逻辑完全一致。
内容的提问来源于stack exchange,提问作者mss
相关产品推荐
相关产品推荐

