-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
add drawstyle option to fill_between function #643
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
+1 |
I notice lines have a drawstyle, but patches do not. In short, to implement this functionality, I believe one would want to implement drawstyles on patches. |
+1 I don't think it's needed to implement drawstyles on patches (this is probably not well defined anyway). One should rather get back the x,y coordinates of the step-lines (i.e [x0,x1,x1,x2,x2,...],[y0,y0,y1,y1,y2,...]) and give it to |
Hello, |
I think what would need to be done is a MEP describing exactly what a drawstyle would mean for a Patch object. I am not familiar enough with what exactly a drawstyle means (as opposed to a linestyle). Should it also be included for Polygons? What about Collections? These things need to be worked out prior to adding the top-level feature to fill_between. |
I don't think drawstyles on all patches makes sense (on an ellipse? what would that mean?). However, it may make sense to have a "FilledLine" class that inherits from line, except is filled above or below (maybe left or right as well). This would then get all of the features of Line. |
Maybe go a different route and add a |
Any news about this? I made a little convenience function that works in most cases - it's primitive but gets the work done, and that is what I care about right now. https://gist.github.com/thriveth/8352565 |
@thriveth What cases does that method not work in? Copied the code (with minor edit to fix return value) from the linked just below just in case.
|
@tacaswell I don't know if there are any, I just haven't tested it thoroughly. But I am using that method extensively in my current work. |
@tacaswell For one thing, it only works for evenly spaced data points. I suspect it should be possible to extend so it would work for unevenly spaced data too but I haven\t had any need for it so far so I have not looked into it. |
My workaround for this uses PathPatch which has the ability to handle nonuniform steps - also the potential to create a collection which allows the blending of alphas (needed when rasterizing for use in eps figures). Here is a quick and dirty example. One could change y0 to an array and have the points iterate backwards to complete the path if you really wanted fill between two "curves" and not just fill between the data and a constant value. When creating these plots, I typically use the keyword "polygon" for drawstyle. This might be the word you are looking for here, or it might be too general since all the angles are 90 degrees. import numpy as np
from numpy.random import rand
import itertools as it
from scipy import stats
import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.patches import PathPatch
np.random.seed(1)
data = stats.gamma(3).rvs(5000)
h,e = np.histogram(data,70)
x = e
y0 = 0
y1 = h
xx = it.chain.from_iterable(it.izip(*it.tee(x)))
yy = it.chain.from_iterable(it.izip(*it.tee(y1)))
next(xx,None)
points = list(it.chain(
[(x[0],y0)],
it.izip(xx,yy),
[(x[-1],y0),
(x[0],y0),
(x[0],y0)]))
extent = (min(x),max(x),min([y0,min(y1)]),max([y0,max(y1)]))
codes = [Path.MOVETO] \
+ [Path.LINETO]*(len(points) - 3) \
+ [Path.MOVETO,
Path.CLOSEPOLY]
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
data_patch = PathPatch(Path(points, codes), color='steelblue', alpha=0.6, linewidth=0)
_ = ax.add_patch(data_patch)
_ = ax.set_xlim(extent[0],extent[1])
_ = ax.set_ylim(extent[2],1.05*extent[3])
### compare to this:
#_ = ax.hist(data, 70, histtype="stepfilled", edgecolor='none',alpha=.7)
### also to this:
#_ = ax.plot(e,list(h)+[h[-1]],linestyle='steps-post',color='red',alpha=0.9) |
For completeness, here is an example for the fill_between two curves case: import numpy as np
from numpy.random import rand
import itertools as it
from scipy import stats
import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.patches import PathPatch
np.random.seed(1)
data0 = stats.gamma(3).rvs(5000)
data1 = stats.gamma(3).rvs(10000)
h0,e = np.histogram(data0,70)
h1,_ = np.histogram(data1,e)
x = e
y0 = h0
y1 = h1
xx = list(it.chain.from_iterable(it.izip(*it.tee(x))))
yy0 = list(it.chain.from_iterable(it.izip(*it.tee(y0))))
yy1 = list(it.chain.from_iterable(it.izip(*it.tee(y1))))
points = list(it.chain(
[(xx[0],yy0[0])],
it.izip(xx[1:],yy1),
it.izip(xx[-2::-1],yy0[::-1]),
[(xx[0],yy0[0])]))
extent = (min(x),max(x),min([min(y0),min(y1)]),max([max(y0),max(y1)]))
codes = [Path.MOVETO] \
+ [Path.LINETO]*(len(points) - 3) \
+ [Path.MOVETO,
Path.CLOSEPOLY]
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
data_patch = PathPatch(Path(points, codes), facecolor='steelblue', alpha=0.6, linewidth=0)
_ = ax.add_patch(data_patch)
_ = ax.set_xlim(extent[0],extent[1])
_ = ax.set_ylim(extent[2],1.05*extent[3])
# compare with:
#_ = ax.hist(data0, 70, histtype="stepfilled", edgecolor='none',alpha=.7)
#_ = ax.hist(data1, e, histtype="stepfilled", edgecolor='none',alpha=.7)
#_ = ax.plot(e,list(h0)+[h0[-1]],linestyle='steps-post',color='red',alpha=0.9)
#_ = ax.plot(e,list(h1)+[h1[-1]],linestyle='steps-post',color='red',alpha=0.9) |
Here is a more complete solution which deals with all of the stepping methods of def fill_between_steps(ax, x, y1, y2=0, step_where='pre', **kwargs):
''' fill between a step plot and
Parameters
----------
ax : Axes
The axes to draw to
x : array-like
Array/vector of index values.
y1 : array-like or float
Array/vector of values to be filled under.
y2 : array-Like or float, optional
Array/vector or bottom values for filled area. Default is 0.
step_where : {'pre', 'post', 'mid'}
where the step happens, same meanings as for `step`
**kwargs will be passed to the matplotlib fill_between() function.
Returns
-------
ret : PolyCollection
The added artist
'''
if step_where not in {'pre', 'post', 'mid'}:
raise ValueError("where must be one of {{'pre', 'post', 'mid'}} "
"You passed in {wh}".format(wh=step_where))
# make sure y values are up-converted to arrays
if np.isscalar(y1):
y1 = np.ones_like(x) * y1
if np.isscalar(y2):
y2 = np.ones_like(x) * y2
# temporary array for up-converting the values to step corners
# 3 x 2N - 1 array
vertices = np.vstack((x, y1, y2))
# this logic is lifted from lines.py
# this should probably be centralized someplace
if step_where == 'pre':
steps = ma.zeros((3, 2 * len(x) - 1), np.float)
steps[0, 0::2], steps[0, 1::2] = vertices[0, :], vertices[0, :-1]
steps[1:, 0::2], steps[1:, 1:-1:2] = vertices[1:, :], vertices[1:, 1:]
elif step_where == 'post':
steps = ma.zeros((3, 2 * len(x) - 1), np.float)
steps[0, ::2], steps[0, 1:-1:2] = vertices[0, :], vertices[0, 1:]
steps[1:, 0::2], steps[1:, 1::2] = vertices[1:, :], vertices[1:, :-1]
elif step_where == 'mid':
steps = ma.zeros((3, 2 * len(x)), np.float)
steps[0, 1:-1:2] = 0.5 * (vertices[0, :-1] + vertices[0, 1:])
steps[0, 2::2] = 0.5 * (vertices[0, :-1] + vertices[0, 1:])
steps[0, 0] = vertices[0, 0]
steps[0, -1] = vertices[0, -1]
steps[1:, 0::2], steps[1:, 1::2] = vertices[1:, :], vertices[1:, :]
else:
raise RuntimeError("should never hit end of if-elif block for validated input")
# un-pack
xx, yy1, yy2 = steps
# now to the plotting part:
return ax.fill_between(xx, yy1, y2=yy2, **kwargs)
fig, ax_list = plt. subplots(3, 1)
x = y = np.arange(5)
for ax, where in zip(ax_list, ['pre', 'post', 'mid']):
ax.step(x, y, where=where, color='r', zorder=5, lw=5)
fill_between_steps(ax, x, y, 0, step_where=where) I will get around to putting this in a PR eventually |
This would be a very nice addition, @tacaswell do you think this would be implemented soon? I've written something similar myself (and step style is more or less required in my field, so I'm sure quite a few others have as well), but it would be great to see a standard solution. |
I would just use the function above for now. The only hold up is I am not sure if this should be it's own function |
I would vote for an additional kwarg in fill_between, and presumably in fill_betweenx as well. |
Add ability to fill between 'step' plots. Closes matplotlib#643 and matplotlib#1709
Add ability to fill between 'step' plots. Closes matplotlib#643 and matplotlib#1709
Add ability to fill between 'step' plots. Closes matplotlib#643 and matplotlib#1709
Great solution @tacaswell. I would additionally request the ability to specify N+1 step edges for N step values. Currently I am using your solution by plotting the first bin with |
Hi,
I think the fill_between function should take a drawstyle option. It would be both handy and consistent.
Here is a post from someone else requesting the feature, with a figure illustrating why this is needed:
http://old.nabble.com/fill_between-with-drawstyle-steps--td30172277.html
thank you
The text was updated successfully, but these errors were encountered: