簡體   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