Does the fill_between function in matplotlib have a gradient feature? [duplicate] - python

I happened to see a beautiful graph on this page which is shown below:
Is it possible to get such color gradients in matplotlib?

There have been a handful of previous answers to similar questions (e.g. https://stackoverflow.com/a/22081678/325565), but they recommend a sub-optimal approach.
Most of the previous answers recommend plotting a white polygon over a pcolormesh fill. This is less than ideal for two reasons:
The background of the axes can't be transparent, as there's a filled polygon overlying it
pcolormesh is fairly slow to draw and isn't smoothly interpolated.
It's a touch more work, but there's a method that draws much faster and gives a better visual result: Set the clip path of an image plotted with imshow.
As an example:
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()

Please note Joe Kington deserves the lion's share of the credit here; my sole contribution is zfunc.
His method opens to door to many gradient/blur/drop-shadow
effects. For example, to make the lines have an evenly blurred underside, you
could use PIL to build an alpha layer which is 1 near the line and 0 near the bottom edge.
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(list(map(tuple, xy)), 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()
yields

I've tried something :
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')
.. and the result:
Of course the gradient could go down to 0 by changing the range of feel_between function.

Related

How to apply gradient fill with datetime on x-axis using matplotlib?

I am trying to use the gradient fill under curves in matplotlib with datetime values on x-axis using this answer. I tried converting the datetime values to float to fix this, but that was not helpful.
The MWE and the error is given below. How to apply gradient fill under curves with datetime values on x-axis in matplotlib?
MWE
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
from datetime import datetime, timedelta
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
# x = np.linspace(0, 100, 100)
# y = np.sin(x) + x
x = np.arange(datetime(2010,7,1), datetime(2015,7,1), timedelta(hours=12)).astype(datetime)
y = np.linspace(0, 100, len(x))
fig, ax = plt.subplots()
ax.plot(x, y)
gradient_fill(x, y, ax=ax, zfunc=zfunc)
ax.grid()
fig.savefig("test.pdf")
plt.show()
Error
File "./gradient_fill_test.py", line 95, in <module>
gradient_fill(x, y, ax=ax, zfunc=zfunc)
File "./radient_fill_test.py", line 69, in gradient_fill
z = zfunc(x, y, fill_color=fill_color, alpha=alpha)
File "./gradient_fill_test.py", line 24, in zfunc
x = (x*scale).astype(int)
TypeError: unsupported operand type(s) for *: 'datetime.datetime' and 'int'
Try something like:
x = np.arange(datetime(2010,7,1), datetime(2015,7,1), timedelta(hours=12)).astype(datetime)
y = np.linspace(0, 100, len(x))
fig, ax = plt.subplots()
ax.plot(x, y)
gradient_fill(mdates.date2num(x), y, ax=ax, zfunc=zfunc)
I couldn't actually get that to work because your zfunc seems to have a bug in it, but if you fix that it will get you properly labeled date ticks.

Add an iterative legend to a python function

I have 2 functions, 'one function calling other' to plot some curves. What I want is to have legends in these functions that can be seen as one in the final plot, but I am unable to achieve that. Can someone help me with this? Code below (works fine without the label/legend lines).
import matplotlib.colors as mcolors
from matplotlib.patches import Polygon
def gradient_fill(x, y, fill_color=None, ax=None, label = None, **kwargs):
"""
Plot a line with a linear alpha gradient filled beneath it.
Parameters
----------
x, y : array-like
The data values of the line.
"""
if ax is None:
ax = plt.gca()
line, = ax.plot(x, y, **kwargs, color='black', lw = 1.5)
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*1.12],
origin='lower', zorder=zorder,alpha = 0.8, label = label)
ax.legend([str(label)])
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
def gaussian_kde(data,bandwidth = 20,x_range_st = -50,x_range_end = 1000, N = 1000, fill_color = None, label = None):
from sklearn.neighbors import KernelDensity
# instantiate and fit the KDE model
kde = KernelDensity(bandwidth=bandwidth, kernel='gaussian')
kde.fit(data[:, None])
x_d = np.linspace(x_range_st, x_range_end, N)
# score_samples returns the log of the probability density
logprob = kde.score_samples(x_d[:, None])
#plt.fill_between(x_d, np.exp(logprob), alpha=0.5)
gradient_fill(x_d,np.exp(logprob), fill_color = fill_color, label = label)
plt.gca().invert_yaxis()
Example of the code:
gaussian_kde(data1, label = 'Apple')
gaussian_kde(data2, label = 'Banana', fill_color = 'red')
gaussian_kde(data3, label = 'Mango', fill_color = 'green')
I want to add label directly in the gaussian_kde function so that it appears in the final figure. The error I get with the code TypeError: zip argument #2 must support iteration. The code works fine, but only displays the last label. Can someone provide some guidance on how to get all the labels as also defined in the figure?

Matplotlib is not rendering gradient under the curve correctly when directly saving

I'm using some sample code I got from:
https://stackoverflow.com/a/29331211/9469766
I'm attempting to save as pdf but whenever I do the gradient colors are formatted incorrectly. But if I simply show instead of saving or save the file as .png instead, the gradients appear correctly. Which leads me to believe that its the .pdf format that's the issue. Are there specific parameters that I need to set when saving as pdf?
Expected Image Output:
Actual Image Output
My code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from matplotlib.patches import Polygon
np.random.seed(1977)
import os
def main():
home_dir = os.path.expanduser('~/Desktop/test2.pdf')
x, y = generate_data(100)
for _ in range(5):
gradient_fill(*generate_data(100))
# plt.show()
plt.savefig(home_dir, rasterized = True, dpi=300)
plt.close()
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):
"""
"""
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()
The problem is the same as in https://github.com/matplotlib/matplotlib/issues/7151.
The solution is to use the "image.composite_image" rc parameter and set it to False.
plt.rcParams["image.composite_image"] =False

Changing the notation of clabel in contourplot, processed by KDE

I'm very new to Python and especially Matplotlib but I would like to change the amount of significant digits in a contour plot or preferably change the notation to scientific.
I found sth like this, refering to Matlab: http://de.mathworks.com/matlabcentral/newsreader/view_thread/33019
Is there any chance to do this? Additionally, I would like to change the background of the axis. So only the areas of contour are colored in different shades of blue. Is this possible.
Here is my code:
import numpy as np
import matplotlib.pyplot as pl
import scipy.stats as st
from matplotlib.patches import Ellipse
data = np.loadtxt(filename)
x = data[:, 0]
y = data[:, 1]
xmin, xmax = 265, 675
ymin, ymax = 45,450
# Set Parameters from Autotracking
a1 = 277
a2 = 664
b1 = 51
b2 = 437
a = (a2-a1)
b = (b2-b1)
xm = a1+(a/2)
ym = b1+(b/2)
# Peform the kernel density estimate
xx, yy = np.mgrid[xmin:xmax:200j, ymin:ymax:200j]
positions = np.vstack([xx.ravel(), yy.ravel()])
values = np.vstack([x, y])
kernel = st.gaussian_kde(values)
f = np.reshape(kernel(positions).T, xx.shape)
fig = pl.figure()
ax = fig.gca()
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
# Contourf plot
cfset = ax.contourf(xx, yy, f, cmap='Blues')
cset = ax.contour(xx, yy, f, colors='k')
# Label plot
ax.clabel(cset, inline=1, fontsize=10, format='%.4f')
ax.set_xlabel('Bewegung in $x$-Richtung [px]')
ax.set_ylabel('Bewegung in $y$-Richtung [px]')
# Plot ellipse as border of system
ellipse = Ellipse(xy=(xm, ym), width=a, height=b,
edgecolor='black', fc='None', lw=1.5)
pl.gca().add_patch(ellipse)
pl.gca().set_aspect('equal', adjustable='box')
pl.show()
This is my output:
Graph
After trying to follow the instructions of the example, I came up with sth like this:
Graph2
But this is not really what I would like to get. I just want to change the notation of the contour lines, because the kernel destiny is so small (approximately 0,0000334 e.g.). So there would be two possible ways:
1) Changing it to scientific notation: 3,34 * 10^(-5) (preferred way)
2) Expanding the amount of significant digits to be displayed
My code so far:
import numpy as np
import matplotlib.pyplot as pl
import scipy.stats as st
from matplotlib.patches import Ellipse
import matplotlib.ticker as ticker
data = np.loadtxt(filename)
x = data[:, 0]
y = data[:, 1]
xmin, xmax = 265, 675
ymin, ymax = 45,450
# Set Parameters from Autotracking
a1 = 277
a2 = 664
b1 = 51
b2 = 437
a = (a2-a1)
b = (b2-b1)
xm = a1+(a/2)
ym = b1+(b/2)
# Peform the kernel density estimate
xx, yy = np.mgrid[xmin:xmax:200j, ymin:ymax:200j]
positions = np.vstack([xx.ravel(), yy.ravel()])
values = np.vstack([x, y])
kernel = st.gaussian_kde(values)
f = np.reshape(kernel(positions).T, xx.shape)
fig = pl.figure()
ax = fig.gca()
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
# Contourf plot
cfset = ax.contourf(xx, yy, f, cmap='Blues')
cset = ax.contour(xx, yy, f, colors='k', locator=pl.LogLocator())
# Label plot
fmt = ticker.LogFormatterMathtext()
fmt.create_dummy_axis()
ax.clabel(cset, inline=1, fontsize=10, fmt=fmt)
ax.set_xlabel('Bewegung in $x$-Richtung [px]')
ax.set_ylabel('Bewegung in $y$-Richtung [px]')
# Plot ellipse as border of system
ellipse = Ellipse(xy=(xm, ym), width=a, height=b,
edgecolor='black', fc='None', lw=1.5)
pl.gca().add_patch(ellipse)
pl.gca().set_aspect('equal', adjustable='box')
pl.show()
Thank you for your help!

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

I happened to see a beautiful graph on this page which is shown below:
Is it possible to get such color gradients in matplotlib?
There have been a handful of previous answers to similar questions (e.g. https://stackoverflow.com/a/22081678/325565), but they recommend a sub-optimal approach.
Most of the previous answers recommend plotting a white polygon over a pcolormesh fill. This is less than ideal for two reasons:
The background of the axes can't be transparent, as there's a filled polygon overlying it
pcolormesh is fairly slow to draw and isn't smoothly interpolated.
It's a touch more work, but there's a method that draws much faster and gives a better visual result: Set the clip path of an image plotted with imshow.
As an example:
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()
Please note Joe Kington deserves the lion's share of the credit here; my sole contribution is zfunc.
His method opens to door to many gradient/blur/drop-shadow
effects. For example, to make the lines have an evenly blurred underside, you
could use PIL to build an alpha layer which is 1 near the line and 0 near the bottom edge.
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(list(map(tuple, xy)), 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()
yields
I've tried something :
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')
.. and the result:
Of course the gradient could go down to 0 by changing the range of feel_between function.

Categories

Resources