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

Skip to content

Commit 088d672

Browse files
committed
feat: StepPatch to take array as baseline
1 parent 3f92f43 commit 088d672

File tree

5 files changed

+53
-28
lines changed

5 files changed

+53
-28
lines changed

examples/lines_bars_and_markers/stairs_demo.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@
4343
ax.legend()
4444
plt.show()
4545

46+
#############################################################################
47+
# ``baseline`` can take an array to allow for stacked histogram plots
48+
49+
A = np.tile([1, 2, 3], (3, 1)) * np.arange(1, 4).reshape(-1, 1)
50+
A = np.vstack([np.zeros(3), A])
51+
52+
for i in range(3):
53+
plt.stairs(A[i+1], baseline=A[i], fill=True)
54+
4655
#############################################################################
4756
# Comparison of `.pyplot.step` and `.pyplot.stairs`
4857
# -------------------------------------------------

lib/matplotlib/axes/_axes.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6729,8 +6729,8 @@ def stairs(self, values, edges=None, *,
67296729
The direction of the steps. Vertical means that *values* are along
67306730
the y-axis, and edges are along the x-axis.
67316731
6732-
baseline : float or None, default: 0
6733-
Determines starting value of the bounding edges or when
6732+
baseline : float, array-like or None, default: 0
6733+
Determines bottom value of the bounding edges or when
67346734
``fill=True``, position of lower edge.
67356735
67366736
fill : bool, default: False
@@ -6760,8 +6760,8 @@ def stairs(self, values, edges=None, *,
67606760
if edges is None:
67616761
edges = np.arange(len(values) + 1)
67626762

6763-
edges, values = self._process_unit_info(
6764-
[("x", edges), ("y", values)], kwargs)
6763+
edges, values, baseline = self._process_unit_info(
6764+
[("x", edges), ("y", values), ("y", baseline)], kwargs)
67656765

67666766
patch = mpatches.StepPatch(values,
67676767
edges,
@@ -6773,9 +6773,9 @@ def stairs(self, values, edges=None, *,
67736773
if baseline is None:
67746774
baseline = 0
67756775
if orientation == 'vertical':
6776-
patch.sticky_edges.y.append(baseline)
6776+
patch.sticky_edges.y.append(np.min(baseline))
67776777
else:
6778-
patch.sticky_edges.x.append(baseline)
6778+
patch.sticky_edges.x.append(np.min(baseline))
67796779
self._request_autoscale_view()
67806780
return patch
67816781

lib/matplotlib/patches.py

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,18 +1013,18 @@ def __init__(self, values, edges, *,
10131013
The direction of the steps. Vertical means that *values* are along
10141014
the y-axis, and edges are along the x-axis.
10151015
1016-
baseline : float or None, default: 0
1017-
Determines starting value of the bounding edges or when
1016+
baseline : float, 1D array-like or None, default: 0
1017+
Determines bottom value of the bounding edges or when
10181018
``fill=True``, position of lower edge.
10191019
10201020
Other valid keyword arguments are:
10211021
10221022
%(Patch)s
10231023
"""
1024-
self.baseline = baseline
10251024
self.orientation = orientation
10261025
self._edges = np.asarray(edges)
10271026
self._values = np.asarray(values)
1027+
self._baseline = np.asarray(baseline) if baseline is not None else None
10281028
self._update_path()
10291029
super().__init__(self._path, **kwargs)
10301030

@@ -1037,13 +1037,24 @@ def _update_path(self):
10371037
f"`len(values) = {self._values.size}` and "
10381038
f"`len(edges) = {self._edges.size}`.")
10391039
verts, codes = [], []
1040-
for idx0, idx1 in cbook.contiguous_regions(~np.isnan(self._values)):
1040+
1041+
_nan_mask = np.isnan(self._values)
1042+
if self._baseline is not None:
1043+
_nan_mask |= np.isnan(self._baseline)
1044+
for idx0, idx1 in cbook.contiguous_regions(~_nan_mask):
10411045
x = np.repeat(self._edges[idx0:idx1+1], 2)
10421046
y = np.repeat(self._values[idx0:idx1], 2)
1043-
if self.baseline is not None:
1044-
y = np.hstack((self.baseline, y, self.baseline))
1045-
else:
1047+
if self._baseline is None:
10461048
y = np.hstack((y[0], y, y[-1]))
1049+
elif self._baseline.ndim == 0: # single baseline value
1050+
y = np.hstack((self._baseline, y, self._baseline))
1051+
elif self._baseline.ndim == 1: # baseline array
1052+
base = np.repeat(self._baseline[idx0:idx1], 2)[::-1]
1053+
x = np.concatenate([x, x[::-1]])
1054+
y = np.concatenate([np.hstack((base[-1], y, base[0],
1055+
base[0], base, base[-1]))])
1056+
else: # no baseline
1057+
raise ValueError('Invalid `baseline` specified')
10471058
if self.orientation == 'vertical':
10481059
xy = np.column_stack([x, y])
10491060
else:
@@ -1053,23 +1064,26 @@ def _update_path(self):
10531064
self._path = Path(np.vstack(verts), np.hstack(codes))
10541065

10551066
def get_data(self):
1056-
"""Get `.StepPatch` values and edges."""
1057-
return self._values, self._edges
1067+
"""Get `.StepPatch` values, edges and baseline."""
1068+
return self._values, self._edges, self._baseline
10581069

1059-
def set_data(self, values, edges=None):
1070+
def set_data(self, values, edges=None, baseline=None):
10601071
"""
1061-
Set `.StepPatch` values and optionally edges.
1072+
Set `.StepPatch` values and optionally edges and baseline.
10621073
10631074
Parameters
10641075
----------
10651076
values : 1D array-like or None
10661077
Will not update values, if passing None
10671078
edges : 1D array-like, optional
1079+
baseline : float, 1D array-like or None
10681080
"""
10691081
if values is not None:
10701082
self._values = np.asarray(values)
10711083
if edges is not None:
10721084
self._edges = np.asarray(edges)
1085+
if baseline is not None:
1086+
self._baseline = np.asarray(baseline)
10731087
self._update_path()
10741088
self.stale = True
10751089

@@ -1081,7 +1095,7 @@ def set_values(self, values):
10811095
----------
10821096
values : 1D array-like
10831097
"""
1084-
self.set_data(values, edges=None)
1098+
self.set_data(values, edges=None, baseline=None)
10851099

10861100
def set_edges(self, edges):
10871101
"""
@@ -1091,23 +1105,21 @@ def set_edges(self, edges):
10911105
----------
10921106
edges : 1D array-like
10931107
"""
1094-
self.set_data(None, edges=edges)
1108+
self.set_data(None, edges=edges, baseline=None)
10951109

10961110
def get_baseline(self):
1097-
"""Get `.StepPatch` baseline value."""
1098-
return self.baseline
1111+
"""Get `.StepPatch` baseline."""
1112+
return self._baseline
10991113

11001114
def set_baseline(self, baseline):
11011115
"""
1102-
Set `.StepPatch` baseline value.
1116+
Set `.StepPatch` baseline.
11031117
11041118
Parameters
11051119
----------
1106-
baseline : float or None
1120+
baseline : float, array-like or None, default: 0
11071121
"""
1108-
self.baseline = baseline
1109-
self._update_path()
1110-
self.stale = True
1122+
self.set_data(None, edges=None, baseline=baseline)
11111123

11121124

11131125
class Polygon(Patch):

lib/matplotlib/tests/test_axes.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1932,10 +1932,14 @@ def test_stairs_options():
19321932
ax.stairs(yn, x, color='orange', ls='--', lw=2, label="C")
19331933
ax.stairs(yn/3, x*3-2, ls='--', lw=2, baseline=0.5,
19341934
orientation='horizontal', label="D")
1935-
ax.stairs(y[::-1]*3+12, x, color='red', ls='--', lw=2, baseline=None,
1935+
ax.stairs(y[::-1]*3+13, x-1, color='red', ls='--', lw=2, baseline=None,
19361936
label="E")
1937+
ax.stairs(y[::-1]*3+14, x, baseline=26,
1938+
color='purple', ls='--', lw=2, label="F")
1939+
ax.stairs(yn[::-1]*3+15, x+1, baseline=np.linspace(27, 25, len(y)),
1940+
color='blue', ls='--', lw=2, label="G", fill=True)
19371941
ax.stairs(y[:-1][::-1]*2+11, x[:-1]+0.5, color='black', ls='--', lw=2,
1938-
baseline=12, hatch='//', label="F")
1942+
baseline=12, hatch='//', label="H")
19391943
ax.legend(loc=0)
19401944

19411945

0 commit comments

Comments
 (0)