Skip to content

Commit c8e5bf6

Browse files
committed
feat: StepPatch
1 parent 71de09a commit c8e5bf6

File tree

4 files changed

+181
-1
lines changed

4 files changed

+181
-1
lines changed

lib/matplotlib/axes/_axes.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6880,6 +6880,34 @@ def hist(self, x, bins=None, range=None, density=False, weights=None,
68806880
else "List[Polygon]")
68816881
return tops, bins, cbook.silent_list(patch_type, patches)
68826882

6883+
def histline(self, vals, bins=None, *,
6884+
orientation='horizontal', baseline=0, fill=False, **kwargs):
6885+
6886+
if 'color' not in kwargs:
6887+
kwargs['color'] = self._get_lines.get_next_color()
6888+
6889+
if bins is None:
6890+
bins = np.arange(len(vals)+1)
6891+
6892+
patch = mpatches.StepPatch(vals,
6893+
bins,
6894+
baseline=baseline,
6895+
orientation=orientation,
6896+
fill=fill,
6897+
**kwargs)
6898+
self.add_patch(patch)
6899+
6900+
if baseline is None:
6901+
baseline = 0
6902+
if orientation == 'horizontal':
6903+
patch.sticky_edges.y.append(baseline)
6904+
else:
6905+
patch.sticky_edges.x.append(baseline)
6906+
6907+
self._request_autoscale_view()
6908+
return patch
6909+
6910+
68836911
@_preprocess_data(replace_names=["x", "y", "weights"])
68846912
@docstring.dedent_interpd
68856913
def hist2d(self, x, y, bins=10, range=None, density=False, weights=None,

lib/matplotlib/legend.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from matplotlib.cbook import silent_list
3434
from matplotlib.font_manager import FontProperties
3535
from matplotlib.lines import Line2D
36-
from matplotlib.patches import Patch, Rectangle, Shadow, FancyBboxPatch
36+
from matplotlib.patches import Patch, Rectangle, Shadow, FancyBboxPatch, StepPatch
3737
from matplotlib.collections import (LineCollection, RegularPolyCollection,
3838
CircleCollection, PathCollection,
3939
PolyCollection)
@@ -623,6 +623,7 @@ def draw(self, renderer):
623623
ErrorbarContainer: legend_handler.HandlerErrorbar(),
624624
Line2D: legend_handler.HandlerLine2D(),
625625
Patch: legend_handler.HandlerPatch(),
626+
StepPatch: legend_handler.HandlerLinePatch(),
626627
LineCollection: legend_handler.HandlerLineCollection(),
627628
RegularPolyCollection: legend_handler.HandlerRegularPolyCollection(),
628629
CircleCollection: legend_handler.HandlerCircleCollection(),

lib/matplotlib/legend_handler.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,71 @@ def create_artists(self, legend, orig_handle,
302302
return [p]
303303

304304

305+
class HandlerLinePatch(HandlerBase):
306+
"""
307+
Handler for `.HistLine` instances.
308+
"""
309+
def __init__(self, patch_func=None, **kw):
310+
"""
311+
Parameters
312+
----------
313+
patch_func : callable, optional
314+
The function that creates the legend key artist.
315+
*patch_func* should have the signature::
316+
317+
def patch_func(legend=legend, orig_handle=orig_handle,
318+
xdescent=xdescent, ydescent=ydescent,
319+
width=width, height=height, fontsize=fontsize)
320+
321+
Subsequently the created artist will have its ``update_prop``
322+
method called and the appropriate transform will be applied.
323+
324+
Notes
325+
-----
326+
Any other keyword arguments are given to `HandlerBase`.
327+
"""
328+
super().__init__(**kw)
329+
self._patch_func = patch_func
330+
331+
def _create_patch(self, legend, orig_handle,
332+
xdescent, ydescent, width, height, fontsize):
333+
if self._patch_func is None:
334+
p = Rectangle(xy=(-xdescent, -ydescent), color=orig_handle.get_facecolor(),
335+
width=width, height=height)
336+
else:
337+
p = self._patch_func(legend=legend, orig_handle=orig_handle,
338+
xdescent=xdescent, ydescent=ydescent,
339+
width=width, height=height, fontsize=fontsize)
340+
return p
341+
342+
def _create_line(self, legend, orig_handle,
343+
xdescent, ydescent, width, height, fontsize):
344+
345+
# Overwrite manually because patch and line properties don't mix
346+
legline = Line2D([0, width], [height/2, height/2],
347+
color=orig_handle.get_edgecolor(),
348+
linestyle=orig_handle.get_linestyle(),
349+
linewidth=orig_handle.get_linewidth(),
350+
)
351+
352+
legline.set_drawstyle('default')
353+
legline.set_marker("")
354+
return legline
355+
356+
def create_artists(self, legend, orig_handle,
357+
xdescent, ydescent, width, height, fontsize, trans):
358+
if orig_handle.get_fill():
359+
p = self._create_patch(legend, orig_handle,
360+
xdescent, ydescent, width, height, fontsize)
361+
self.update_prop(p, orig_handle, legend)
362+
else:
363+
p = self._create_line(legend, orig_handle,
364+
xdescent, ydescent, width, height, fontsize)
365+
p.set_transform(trans)
366+
367+
return [p]
368+
369+
305370
class HandlerLineCollection(HandlerLine2D):
306371
"""
307372
Handler for `.LineCollection` instances.

lib/matplotlib/patches.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,52 @@ def set_path(self, path):
989989
self._path = path
990990

991991

992+
class StepPatch(PathPatch):
993+
def __init__(self, vals, bins=None, *,
994+
orientation='horizontal', baseline=0, **kwargs):
995+
self.baseline = baseline
996+
self.orientation = orientation
997+
self._bins = bins
998+
self._vals = vals
999+
verts, codes = self._update_data()
1000+
path = Path(verts, codes)
1001+
super().__init__(path, **kwargs)
1002+
1003+
def _update_data(self):
1004+
if self._bins.size - 1 != self._vals.size:
1005+
raise ValueError('the length of the bins is wrong')
1006+
verts, codes = [], []
1007+
for idx0, idx1 in cbook.contiguous_regions(~np.isnan(self._vals)):
1008+
x = np.vstack((self._bins[idx0:idx1+1],
1009+
self._bins[idx0:idx1+1])).T.flatten()
1010+
y = np.vstack((self._vals[idx0:idx1],
1011+
self._vals[idx0:idx1])).T.flatten()
1012+
if self.baseline is not None:
1013+
y = np.hstack((self.baseline, y, self.baseline))
1014+
else:
1015+
y = np.hstack((y[0], y, y[-1]))
1016+
if self.orientation == 'horizontal':
1017+
xy = np.vstack([x, y]).T
1018+
else:
1019+
xy = np.vstack([y, x]).T
1020+
verts.append(xy)
1021+
codes.append(np.array([Path.MOVETO] + [Path.LINETO]*(len(xy)-1)))
1022+
return np.vstack(verts), np.hstack(codes)
1023+
1024+
def set_bins(self, bins):
1025+
self._bins = bins
1026+
self._update_data()
1027+
1028+
def set_vals(self, vals):
1029+
self._vals = vals
1030+
self._update_data()
1031+
1032+
def set_vals_bins(self, vals, bins):
1033+
self._vals = vals
1034+
self._bins = bins
1035+
self._update_data()
1036+
1037+
9921038
class Polygon(Patch):
9931039
"""A general polygon patch."""
9941040

@@ -1083,6 +1129,46 @@ def set_xy(self, xy):
10831129
doc='The vertices of the path as (N, 2) numpy array.')
10841130

10851131

1132+
class HistLine(Polygon):
1133+
1134+
def __init__(self, vals, bins=None, *, fill=False,
1135+
orientation='horizontal', baseline=0, **kwargs):
1136+
self.baseline = baseline
1137+
self.orientation = orientation
1138+
self._color = None
1139+
self._bins = bins
1140+
self._vals = vals
1141+
xy = self._update_data()
1142+
super(HistLine, self).__init__(xy, closed=False, fill=fill, **kwargs)
1143+
1144+
def _update_data(self):
1145+
if self._bins.size - 1 != self._vals.size:
1146+
raise ValueError('the length of the bins is wrong')
1147+
x = np.vstack((self._bins, self._bins)).T.flatten()
1148+
y = np.vstack((self._vals, self._vals)).T.flatten()
1149+
if self.baseline is not None:
1150+
y = np.hstack((self.baseline, y, self.baseline))
1151+
else:
1152+
y = np.hstack((y[0], y, y[-1]))
1153+
if self.orientation == 'horizontal':
1154+
return np.vstack([x, y]).T
1155+
else:
1156+
return np.vstack([y, x]).T
1157+
1158+
def set_bins(self, bins):
1159+
self._bins = bins
1160+
self._update_data()
1161+
1162+
def set_vals(self, vals):
1163+
self._vals = vals
1164+
self._update_data()
1165+
1166+
def set_vals_bins(self, vals, bins):
1167+
self._vals = vals
1168+
self._bins = bins
1169+
self._update_data()
1170+
1171+
10861172
class Wedge(Patch):
10871173
"""Wedge shaped patch."""
10881174

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy