Matplotlib Graphs下的渐变填充
Gradient Fill under Matplotlib Graphs
我从 about putting a gradient fill below a curve in matplotlib. I tried the same thing plotting multiple plots on one axis and working the order of them and their alpha to ensure they're visible. I'm getting errors with PIL with this code that outputs this graph:
那里得到了很多信息
能不能把剧情下面的'fill'再往下一点,修正右下角的错误?我通过将原始数据放在 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)
这与您采用的方法不同,但也许您可以使用具有不同强度的图像和使用 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 ()
问题出在您的 zfunc
上。
你说你想通过将它们与 np.linspace(0,10,n)
相乘来将你的 alpha 淡化为零。
尝试:
zalpha[:n] *= np.linspace(0, 1, n)[:, None]
那么对我有用...
我从
能不能把剧情下面的'fill'再往下一点,修正右下角的错误?我通过将原始数据放在 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)
这与您采用的方法不同,但也许您可以使用具有不同强度的图像和使用 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 ()
问题出在您的 zfunc
上。
你说你想通过将它们与 np.linspace(0,10,n)
相乘来将你的 alpha 淡化为零。
尝试:
zalpha[:n] *= np.linspace(0, 1, n)[:, None]
那么对我有用...