【问题标题】:Gradient Fill under Matplotlib GraphsMatplotlib Graphs 下的渐变填充
【发布时间】:2015-11-10 17:11:47
【问题描述】:

我从these two posts on 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)

【问题讨论】:

  • 第一个数据链路是否工作?
  • 它对我有用,但可能是因为我是提出它的人。我会用更长的 TTL 重新加载另一个。
  • @gauteh 我已将数据编辑为在 bpaste 上永不过期。

标签: python matplotlib pillow


【解决方案1】:

问题在于您的zfunc。 你说你想通过将它们与np.linspace(0,10,n) 相乘来将你的 alpha 淡化为零。

尝试:

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

那么它对我有用...

【讨论】:

  • 是的,我认为可以。如果我现在想扩展淡入淡出(但仍保持为零),我在两条曲线上都看不到一致的水平。 Here 是我输出的图片——橙色很好看,紫色不太一致。
  • 这可能是因为第二行有alpha = 0.75。所以它会更快地褪色。如果您添加另一个参数 alpha_min 并将其用作上述 linspace 的第一个参数,您可以获得更多控制权。
【解决方案2】:

这是一种与您所采用的方法不同的方法,但也许您可以使用具有不同强度的图像和使用 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 ()

【讨论】:

    猜你喜欢
    • 2016-12-14
    • 1970-01-01
    • 1970-01-01
    • 2021-12-23
    • 2011-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多