Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 786d0db

Browse files
committed
feat: HistLine
1 parent 71de09a commit 786d0db

File tree

4 files changed

+141
-1
lines changed

4 files changed

+141
-1
lines changed

lib/matplotlib/axes/_axes.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6880,6 +6880,40 @@ 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+
patches = []
6893+
for i, (idx0, idx1) in enumerate(cbook.contiguous_regions(~np.isnan(vals))):
6894+
if i != 0:
6895+
kwargs['label'] = "_nolegend_"
6896+
patch = mpatches.HistLine(vals[idx0:idx1],
6897+
bins[idx0:idx1+1],
6898+
baseline=baseline,
6899+
orientation=orientation,
6900+
fill=fill,
6901+
**kwargs)
6902+
6903+
patches.append(patch)
6904+
self.add_patch(patch)
6905+
6906+
if baseline is None:
6907+
baseline = 0
6908+
if orientation == 'horizontal':
6909+
patches[0].sticky_edges.y.append(baseline)
6910+
else:
6911+
patches[0].sticky_edges.x.append(baseline)
6912+
6913+
self._request_autoscale_view()
6914+
return patches
6915+
6916+
68836917
@_preprocess_data(replace_names=["x", "y", "weights"])
68846918
@docstring.dedent_interpd
68856919
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, HistLine
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+
HistLine: 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),
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: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,46 @@ def set_xy(self, xy):
10831083
doc='The vertices of the path as (N, 2) numpy array.')
10841084

10851085

1086+
class HistLine(Polygon):
1087+
1088+
def __init__(self, vals, bins=None, *, fill=False,
1089+
orientation='horizontal', baseline=0, **kwargs):
1090+
self.baseline = baseline
1091+
self.orientation = orientation
1092+
self._color = None
1093+
self._bins = bins
1094+
self._vals = vals
1095+
xy = self._update_data()
1096+
super(HistLine, self).__init__(xy, closed=False, fill=fill, **kwargs)
1097+
1098+
def _update_data(self):
1099+
if self._bins.size - 1 != self._vals.size:
1100+
raise ValueError('the length of the bins is wrong')
1101+
x = np.vstack((self._bins, self._bins)).T.flatten()
1102+
y = np.vstack((self._vals, self._vals)).T.flatten()
1103+
if self.baseline is not None:
1104+
y = np.hstack((self.baseline, y, self.baseline))
1105+
else:
1106+
y = np.hstack((y[0], y, y[-1]))
1107+
if self.orientation == 'horizontal':
1108+
return np.vstack([x, y]).T
1109+
else:
1110+
return np.vstack([y, x]).T
1111+
1112+
def set_bins(self, bins):
1113+
self._bins = bins
1114+
self.set_data(self._update_data())
1115+
1116+
def set_vals(self, vals):
1117+
self._vals = vals
1118+
self.set_data(self._update_data())
1119+
1120+
def set_vals_bins(self, vals, bins):
1121+
self._vals = vals
1122+
self._bins = bins
1123+
self.set_data(self._update_data())
1124+
1125+
10861126
class Wedge(Patch):
10871127
"""Wedge shaped patch."""
10881128

0 commit comments

Comments
 (0)