繁体   English   中英

Matplotlib图下的渐变填充

[英]Gradient Fill under Matplotlib Graphs

从这两篇SO文章中获得了很多有关在matplotlib中将渐变填充置于曲线下方的信息。 我尝试了同样的事情,在一个轴上绘制多个图,并按顺序排列它们及其alpha,以确保它们可见。 使用此代码输出此图时,PIL出现错误: 在此处输入图片说明

是否有可能使图下方的“填充”值进一步下降,并修复右下角的错误? 我已经通过将原始数据放在bpaste上而包含了本示例中使用的数据,因此即使很长,该示例也是完全独立的。

可能与所使用的后端有关吗?

谢谢贾里德

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from matplotlib.patches import Polygon
from matplotlib.ticker import Formatter, FuncFormatter
import matplotlib
import numpy as np
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFilter


df = pd.read_csv('https://bpaste.net/raw/87cbf69259ae')
df = df.set_index('Date', drop=True)
df.index = pd.to_datetime(df.index)


df1 = pd.read_csv('https://bpaste.net/raw/bc06b26b0b8b')
df1 = df1.set_index('Date', drop=True)
df1.index = pd.to_datetime(df1.index)

def zfunc(x, y, fill_color='k', alpha=1.0, xmin=None, xmax=None, ymin=None, ymax=None):

    if xmax is not None:
        xmax = int(xmax)

    if xmin is not None:
        xmin = int(xmin)

    if ymax is not None:
        ymax = int(ymax)

    if ymin is not None:
        ymin = int(ymin)

    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=25))

    # 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 = int(zalpha.shape[0] / 4)

    zalpha[:n] *= np.linspace(0, 10, n)[:, None]
    z[:,:,-1] = zalpha
    return z


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

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

    if ylabel is not None:
        ax.set_ylabel(ylabel, weight='bold', color='white')

    class DateFormatter(Formatter):
        def __init__(self, dates, fmt='%b \'%y'):
            self.dates = dates
            self.fmt = fmt

        def __call__(self, x, pos=0):
            'Return the label for time x at position pos'
            ind = int(round(x))
            if ind>=len(self.dates) or ind<0: return ''

            return self.dates[ind].strftime(self.fmt)

    def millions(x, pos):
        return '$%d' % x

    dollar_formatter = FuncFormatter(millions)     
    formatter = DateFormatter(df.index)
    ax.yaxis.grid(linestyle='-', alpha=0.5, color='white', zorder=-1) 

    line, = ax.plot(x, y, linewidth=2.0, c=fill_color, **kwargs)

    if fill_color is None:
        fill_color = line.get_color()

    zorder = line.get_zorder()
    if 'alpha' in kwargs:
        alpha = kwargs['alpha']
    else:
        alpha = line.get_alpha()
        alpha = 1.0 if alpha is None else alpha

    xmin, xmax, ymin, ymax = x.min(), x.max(), y.min(), y.max()
    diff = ymax - ymin
    ymin = ymin - diff*0.15
    ymax = diff*0.05 + ymax

    if zfunc is None:
        ## Grab an array of length (cols,rows,spacing) but don't initialize values
        z = np.empty((110, 1, 4), dtype=float)
        ## get color to fill for current axix line
        rgb = mcolors.colorConverter.to_rgb(fill_color)
        z[:,:,:3] = rgb
        z[:,:,-1] = np.linspace(0, alpha, 110)[:,None]
    else:
        z = zfunc(x, y, fill_color=fill_color, alpha=alpha, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)

    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)
    ax.patch.set_facecolor('black')
    im.set_clip_path(clip_path)


    ax.xaxis.set_major_formatter(formatter)
    ax.yaxis.set_major_formatter(dollar_formatter)

    for tick in ax.get_yticklabels():
        tick.set_color('white')

    for tick in ax.get_xticklabels():
        tick.set_color('white')

    w = 17.5 * 1.5  # approximate size in inches of 1280
    h = 7.5 * 1.5  # approximate size in inches of 720
    fig =  plt.gcf()
    fig.set_size_inches(w, h)
#     fig.autofmt_xdate()
    plt.rcParams['xtick.major.pad']='20'
    matplotlib.rcParams['ytick.major.pad']='20'
    matplotlib.rcParams.update({'font.size': 22})

    ax.set_ylim((ymin, ymax))
    #ax.autoscale(True)
    return line, im, ax

line, im, ax = gradient_fill(np.arange(len(df1.index)), df1['/CL_Close'], fill_color='#fdbf6f', ylabel='Crude Oil', alpha=1.0, zfunc=zfunc)
ax2 = ax.twinx()
gradient_fill(np.arange(len(df.index)), df['/ES_Close'], ax=ax2, fill_color='#cab2d6', ylabel='S&P', alpha=0.75, zfunc=zfunc)
ax2.yaxis.grid(False)

问题出在您的zfunc 您说要通过乘以np.linspace(0,10,n)来将alpha渐变为零。

尝试:

zalpha[:n] *= np.linspace(0, 1, n)[:, None]

那对我有用

这与您采取的方法不同,但是也许您可以使用具有不同强度的图像和使用alpha值的色图,如下所示:

import numpy as np
import scipy as sc

import matplotlib.pyplot as plt

x = np.linspace (0, 10, 100)
y = .5 * x + 4

plt.figure ()


yres = 100
ymax = np.max (y)
ymin = 0 
yy = np.linspace (ymin, ymax, yres)

fill_n = 10

xres = len(x)

# gradient image
gI = np.zeros ((yres, xres))
for xi,xx in enumerate(x):
  ym = y[xi]

  # find elment closest to curve
  ya = np.argmin (np.abs(yy - ym))

  gI[ya-fill_n:ya, xi] = np.linspace (0, 1, fill_n)

# make alpha cmap out of gray map
bb = np.linspace (0, 1, fill_n)
kk = []
for b in bb:
  kk.append ((b, b, b))

bb = tuple (kk) 
gr = { 'blue' : bb,
       'red' : bb,
       'green': bb,
       'alpha': bb }

plt.register_cmap (name = 'GrayAlpha', data = gr)

gI = np.flipud (gI)
plt.imshow (gI, vmin = 0, vmax = 1, cmap = 'GrayAlpha', interpolation = 'bicubic')
plt.show ()

在此处输入图片说明

暂无
暂无

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

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