繁体   English   中英

是否可以在 matplotlib 中获得曲线下的颜色渐变?

[英]Is it possible to get color gradients under curve in matplotlib?

我碰巧在这个页面上看到一个漂亮的图表,如下所示:

在此处输入图片说明

是否有可能在 matplotlib 中获得这样的颜色渐变?

之前有一些类似问题的答案(例如https://stackoverflow.com/a/22081678/325565 ),但他们推荐了一种次优方法。

以前的大多数答案都建议在pcolormesh填充上绘制一个白色多边形。 这不太理想,原因有两个:

  1. 轴的背景不能是透明的,因为上面有一个填充的多边形
  2. pcolormesh绘制速度相当慢,并且不能顺利插入。

这是更多的工作,但有一种方法可以更快地绘制并提供更好的视觉效果:设置使用imshow绘制的图像的剪辑路径。

举个例子:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from matplotlib.patches import Polygon
np.random.seed(1977)

def main():
    for _ in range(5):
        gradient_fill(*generate_data(100))
    plt.show()

def generate_data(num):
    x = np.linspace(0, 100, num)
    y = np.random.normal(0, 1, num).cumsum()
    return x, y

def gradient_fill(x, y, fill_color=None, ax=None, **kwargs):
    """
    Plot a line with a linear alpha gradient filled beneath it.

    Parameters
    ----------
    x, y : array-like
        The data values of the line.
    fill_color : a matplotlib color specifier (string, tuple) or None
        The color for the fill. If None, the color of the line will be used.
    ax : a matplotlib Axes instance
        The axes to plot on. If None, the current pyplot axes will be used.
    Additional arguments are passed on to matplotlib's ``plot`` function.

    Returns
    -------
    line : a Line2D instance
        The line plotted.
    im : an AxesImage instance
        The transparent gradient clipped to just the area beneath the curve.
    """
    if ax is None:
        ax = plt.gca()

    line, = ax.plot(x, y, **kwargs)
    if fill_color is None:
        fill_color = line.get_color()

    zorder = line.get_zorder()
    alpha = line.get_alpha()
    alpha = 1.0 if alpha is None else alpha

    z = np.empty((100, 1, 4), dtype=float)
    rgb = mcolors.colorConverter.to_rgb(fill_color)
    z[:,:,:3] = rgb
    z[:,:,-1] = np.linspace(0, alpha, 100)[:,None]

    xmin, xmax, ymin, ymax = x.min(), x.max(), y.min(), y.max()
    im = ax.imshow(z, aspect='auto', extent=[xmin, xmax, ymin, ymax],
                   origin='lower', zorder=zorder)

    xy = np.column_stack([x, y])
    xy = np.vstack([[xmin, ymin], xy, [xmax, ymin], [xmin, ymin]])
    clip_path = Polygon(xy, facecolor='none', edgecolor='none', closed=True)
    ax.add_patch(clip_path)
    im.set_clip_path(clip_path)

    ax.autoscale(True)
    return line, im

main()

在此处输入图片说明

请注意, Joe Kington 在此应得的大部分功劳; 我唯一的贡献是zfunc 他的方法为许多渐变/模糊/阴影效果打开了大门。 例如,要使线条具有均匀模糊的底面,您可以使用 PIL 构建一个 alpha 图层,该图层在线条附近为 1,在底部边缘附近为 0。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import matplotlib.patches as patches
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFilter

np.random.seed(1977)
def demo_blur_underside():
    for _ in range(5):
        # gradient_fill(*generate_data(100), zfunc=None) # original
        gradient_fill(*generate_data(100), zfunc=zfunc)
    plt.show()

def generate_data(num):
    x = np.linspace(0, 100, num)
    y = np.random.normal(0, 1, num).cumsum()
    return x, y

def zfunc(x, y, fill_color='k', alpha=1.0):
    scale = 10
    x = (x*scale).astype(int)
    y = (y*scale).astype(int)
    xmin, xmax, ymin, ymax = x.min(), x.max(), y.min(), y.max()

    w, h = xmax-xmin, ymax-ymin
    z = np.empty((h, w, 4), dtype=float)
    rgb = mcolors.colorConverter.to_rgb(fill_color)
    z[:,:,:3] = rgb

    # Build a z-alpha array which is 1 near the line and 0 at the bottom.
    img = Image.new('L', (w, h), 0)  
    draw = ImageDraw.Draw(img)
    xy = (np.column_stack([x, y]))
    xy -= xmin, ymin
    # Draw a blurred line using PIL
    draw.line(map(tuple, xy.tolist()), fill=255, width=15)
    img = img.filter(ImageFilter.GaussianBlur(radius=100))
    # Convert the PIL image to an array
    zalpha = np.asarray(img).astype(float) 
    zalpha *= alpha/zalpha.max()
    # make the alphas melt to zero at the bottom
    n = zalpha.shape[0] // 4
    zalpha[:n] *= np.linspace(0, 1, n)[:, None]
    z[:,:,-1] = zalpha
    return z

def gradient_fill(x, y, fill_color=None, ax=None, zfunc=None, **kwargs):
    if ax is None:
        ax = plt.gca()

    line, = ax.plot(x, y, **kwargs)
    if fill_color is None:
        fill_color = line.get_color()

    zorder = line.get_zorder()
    alpha = line.get_alpha()
    alpha = 1.0 if alpha is None else alpha

    if zfunc is None:
        h, w = 100, 1
        z = np.empty((h, w, 4), dtype=float)
        rgb = mcolors.colorConverter.to_rgb(fill_color)
        z[:,:,:3] = rgb
        z[:,:,-1] = np.linspace(0, alpha, h)[:,None]
    else:
        z = zfunc(x, y, fill_color=fill_color, alpha=alpha)
    xmin, xmax, ymin, ymax = x.min(), x.max(), y.min(), y.max()
    im = ax.imshow(z, aspect='auto', extent=[xmin, xmax, ymin, ymax],
                   origin='lower', zorder=zorder)

    xy = np.column_stack([x, y])
    xy = np.vstack([[xmin, ymin], xy, [xmax, ymin], [xmin, ymin]])
    clip_path = patches.Polygon(xy, facecolor='none', edgecolor='none', closed=True)
    ax.add_patch(clip_path)
    im.set_clip_path(clip_path)
    ax.autoscale(True)
    return line, im

demo_blur_underside()

产量

在此处输入图片说明

我试过一些东西:

import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure()

xData = range(100)
yData = range(100)
plt.plot(xData, yData)

NbData = len(xData)
MaxBL = [[MaxBL] * NbData for MaxBL in range(100)]
Max = [np.asarray(MaxBL[x]) for x in range(100)]

for x in range (50, 100):
  plt.fill_between(xData, Max[x], yData, where=yData >Max[x], facecolor='red', alpha=0.02)

for x in range (0, 50):
  plt.fill_between(xData, yData, Max[x], where=yData <Max[x], facecolor='green', alpha=0.02)

plt.fill_between([], [], [], facecolor='red', label="x > 50")
plt.fill_between([], [], [], facecolor='green', label="x < 50")

plt.legend(loc=4, fontsize=12)
plt.show()
fig.savefig('graph.png')

.. 结果:

结果

当然,通过改变feel_between函数的范围,梯度可以降到0。

只是重新打开这个线程,所以只是重新打开这个线程,我使用了绝大多数 joe 的代码,但是我一生都无法弄清楚如何在 X 轴上使用 0 而不是这个向上倾斜的限制来进行着色。

理想情况下,在数据的腹部,它应该是弱颜色,在大量的正负数处是极端颜色,我希望阴影开始/停止的线为水平(0)而不是我得到这个奇怪的倾斜轴我只是不明白如何纠正。

我有一个以前的图表,您可以看到(非渐变)我试图实现的目标。 和红色渐变图表我搞砸了。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from matplotlib.patches import Polygon

# Variables
AUM = df['#AHD_AUM'].head(104)
MM = df['#AHD_Managed_Money_Net'].head(104)
PRICE = df['#AHD_Price'].head(104)
DATES = df['DATES'].head(104)

# Date Friendly Variables for Plot
List_AUM = df['#AHD_AUM'].head(104).to_list()
List_MM = df['#AHD_Managed_Money_Net'].head(104).to_list()
List_DATES = df['DATES'].head(104).to_list()
X = 0 * df['#AHD_AUM'].head(104)


# Make a date list changing dates with numbers to avoid the issue with the plot 
interpreting dates
for i in range(len(df['DATES'].head(104))):
count = i
df['count'][i] = 120 - i


# X and Y data variables changed to arrays as when i had these set as dates 
matplotlib hates it    
x = df['count'].head(104).to_numpy()
y = df['#AHD_Managed_Money_Net'].head(104).to_numpy()

#DD = AUM.to_numpy()
#MMM = MM.to_numpy()

def main():
for _ in range(len(DD)):
    gradient_fill(x,y)
plt.show()


def gradient_fill(x,y, fill_color=None, ax=None, **kwargs):
"""

"""
if ax is None:
    ax = plt.gca()

line, = ax.plot(x, y, **kwargs)
if fill_color is None:
    fill_color = line.get_color()

zorder = line.get_zorder()
alpha = line.get_alpha()
alpha = 1.0 if alpha is None else alpha

z = np.empty((100, 1, 4), dtype=float)
rgb = mcolors.colorConverter.to_rgb(fill_color)
z[:,:,:3] = rgb
z[:,:,-1] = np.linspace(0, alpha, 100)[:,None]

xmin, xmax, ymin, ymax = x.min(), x.max(), y.min(), y.max()
im = ax.imshow(z, aspect='auto', extent=[xmin, xmax, ymin, ymax],
               origin='lower', zorder=zorder)

xy = np.column_stack([x, y])
#    xy = np.vstack([[xmin, ymin], xy, [xmax, ymin], [xmin, ymin]]) ### i dont 
need this so i have just commented it out
clip_path = Polygon(xy, facecolor='none', edgecolor='none', closed=True)
ax.add_patch(clip_path)
im.set_clip_path(clip_path)

ax.autoscale(True)
return line, im


main()

以前的版本测试一个我搞砸了

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM