Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4735755

Browse filesBrowse files
authored
Merge pull request #18511 from andrzejnovak/baselinearr
feat: StepPatch to take array as baseline
2 parents 6970023 + 998c648 commit 4735755
Copy full SHA for 4735755

File tree

Expand file treeCollapse file tree

5 files changed

+69
-67
lines changed
Filter options
Expand file treeCollapse file tree

5 files changed

+69
-67
lines changed

‎examples/lines_bars_and_markers/stairs_demo.py

Copy file name to clipboardExpand all lines: examples/lines_bars_and_markers/stairs_demo.py
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@
4343
ax.legend()
4444
plt.show()
4545

46+
#############################################################################
47+
# *baseline* can take an array to allow for stacked histogram plots
48+
A = [[0, 0, 0],
49+
[1, 2, 3],
50+
[2, 4, 6],
51+
[3, 6, 9]]
52+
53+
for i in range(len(A) - 1):
54+
plt.stairs(A[i+1], baseline=A[i], fill=True)
55+
4656
#############################################################################
4757
# Comparison of `.pyplot.step` and `.pyplot.stairs`
4858
# -------------------------------------------------

‎lib/matplotlib/axes/_axes.py

Copy file name to clipboardExpand all lines: lib/matplotlib/axes/_axes.py
+11-8Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6730,7 +6730,8 @@ def hist(self, x, bins=None, range=None, density=False, weights=None,
67306730
def stairs(self, values, edges=None, *,
67316731
orientation='vertical', baseline=0, fill=False, **kwargs):
67326732
"""
6733-
A stepwise constant line or filled plot.
6733+
A stepwise constant function as a line with bounding edges
6734+
or a filled plot.
67346735
67356736
Parameters
67366737
----------
@@ -6745,9 +6746,11 @@ def stairs(self, values, edges=None, *,
67456746
The direction of the steps. Vertical means that *values* are along
67466747
the y-axis, and edges are along the x-axis.
67476748
6748-
baseline : float or None, default: 0
6749-
Determines starting value of the bounding edges or when
6750-
``fill=True``, position of lower edge.
6749+
baseline : float, array-like or None, default: 0
6750+
The bottom value of the bounding edges or when
6751+
``fill=True``, position of lower edge. If *fill* is
6752+
True or an array is passed to *baseline*, a closed
6753+
path is drawn.
67516754
67526755
fill : bool, default: False
67536756
Whether the area under the step curve should be filled.
@@ -6776,8 +6779,8 @@ def stairs(self, values, edges=None, *,
67766779
if edges is None:
67776780
edges = np.arange(len(values) + 1)
67786781

6779-
edges, values = self._process_unit_info(
6780-
[("x", edges), ("y", values)], kwargs)
6782+
edges, values, baseline = self._process_unit_info(
6783+
[("x", edges), ("y", values), ("y", baseline)], kwargs)
67816784

67826785
patch = mpatches.StepPatch(values,
67836786
edges,
@@ -6789,9 +6792,9 @@ def stairs(self, values, edges=None, *,
67896792
if baseline is None:
67906793
baseline = 0
67916794
if orientation == 'vertical':
6792-
patch.sticky_edges.y.append(baseline)
6795+
patch.sticky_edges.y.append(np.min(baseline))
67936796
else:
6794-
patch.sticky_edges.x.append(baseline)
6797+
patch.sticky_edges.x.append(np.min(baseline))
67956798
self._request_autoscale_view()
67966799
return patch
67976800

‎lib/matplotlib/patches.py

Copy file name to clipboardExpand all lines: lib/matplotlib/patches.py
+36-51Lines changed: 36 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import math
55
from numbers import Number
66
import textwrap
7+
from collections import namedtuple
78

89
import numpy as np
910

@@ -926,7 +927,8 @@ class StepPatch(PathPatch):
926927
"""
927928
A path patch describing a stepwise constant function.
928929
929-
The path is unclosed. It starts and stops at baseline.
930+
By default the path is not closed and starts and stops at
931+
baseline value.
930932
"""
931933

932934
_edge_default = False
@@ -945,21 +947,23 @@ def __init__(self, values, edges, *,
945947
between which the curve takes on vals values.
946948
947949
orientation : {'vertical', 'horizontal'}, default: 'vertical'
948-
The direction of the steps. Vertical means that *values* are along
949-
the y-axis, and edges are along the x-axis.
950+
The direction of the steps. Vertical means that *values* are
951+
along the y-axis, and edges are along the x-axis.
950952
951-
baseline : float or None, default: 0
952-
Determines starting value of the bounding edges or when
953-
``fill=True``, position of lower edge.
953+
baseline : float, array-like or None, default: 0
954+
The bottom value of the bounding edges or when
955+
``fill=True``, position of lower edge. If *fill* is
956+
True or an array is passed to *baseline*, a closed
957+
path is drawn.
954958
955959
Other valid keyword arguments are:
956960
957961
%(Patch)s
958962
"""
959-
self.baseline = baseline
960963
self.orientation = orientation
961964
self._edges = np.asarray(edges)
962965
self._values = np.asarray(values)
966+
self._baseline = np.asarray(baseline) if baseline is not None else None
963967
self._update_path()
964968
super().__init__(self._path, **kwargs)
965969

@@ -972,13 +976,24 @@ def _update_path(self):
972976
f"`len(values) = {self._values.size}` and "
973977
f"`len(edges) = {self._edges.size}`.")
974978
verts, codes = [], []
975-
for idx0, idx1 in cbook.contiguous_regions(~np.isnan(self._values)):
979+
980+
_nan_mask = np.isnan(self._values)
981+
if self._baseline is not None:
982+
_nan_mask |= np.isnan(self._baseline)
983+
for idx0, idx1 in cbook.contiguous_regions(~_nan_mask):
976984
x = np.repeat(self._edges[idx0:idx1+1], 2)
977985
y = np.repeat(self._values[idx0:idx1], 2)
978-
if self.baseline is not None:
979-
y = np.hstack((self.baseline, y, self.baseline))
980-
else:
986+
if self._baseline is None:
981987
y = np.hstack((y[0], y, y[-1]))
988+
elif self._baseline.ndim == 0: # single baseline value
989+
y = np.hstack((self._baseline, y, self._baseline))
990+
elif self._baseline.ndim == 1: # baseline array
991+
base = np.repeat(self._baseline[idx0:idx1], 2)[::-1]
992+
x = np.concatenate([x, x[::-1]])
993+
y = np.concatenate([np.hstack((base[-1], y, base[0],
994+
base[0], base, base[-1]))])
995+
else: # no baseline
996+
raise ValueError('Invalid `baseline` specified')
982997
if self.orientation == 'vertical':
983998
xy = np.column_stack([x, y])
984999
else:
@@ -988,59 +1003,29 @@ def _update_path(self):
9881003
self._path = Path(np.vstack(verts), np.hstack(codes))
9891004

9901005
def get_data(self):
991-
"""Get `.StepPatch` values and edges."""
992-
return self._values, self._edges
1006+
"""Get `.StepPatch` values, edges and baseline as namedtuple."""
1007+
StairData = namedtuple('StairData', 'values edges baseline')
1008+
return StairData(self._values, self._edges, self._baseline)
9931009

994-
def set_data(self, values, edges=None):
1010+
def set_data(self, values=None, edges=None, baseline=None):
9951011
"""
996-
Set `.StepPatch` values and optionally edges.
1012+
Set `.StepPatch` values, edges and baseline.
9971013
9981014
Parameters
9991015
----------
10001016
values : 1D array-like or None
10011017
Will not update values, if passing None
10021018
edges : 1D array-like, optional
1019+
baseline : float, 1D array-like or None
10031020
"""
1021+
if values is None and edges is None and baseline is None:
1022+
raise ValueError("Must set *values*, *edges* or *baseline*.")
10041023
if values is not None:
10051024
self._values = np.asarray(values)
10061025
if edges is not None:
10071026
self._edges = np.asarray(edges)
1008-
self._update_path()
1009-
self.stale = True
1010-
1011-
def set_values(self, values):
1012-
"""
1013-
Set `.StepPatch` values.
1014-
1015-
Parameters
1016-
----------
1017-
values : 1D array-like
1018-
"""
1019-
self.set_data(values, edges=None)
1020-
1021-
def set_edges(self, edges):
1022-
"""
1023-
Set `.StepPatch` edges.
1024-
1025-
Parameters
1026-
----------
1027-
edges : 1D array-like
1028-
"""
1029-
self.set_data(None, edges=edges)
1030-
1031-
def get_baseline(self):
1032-
"""Get `.StepPatch` baseline value."""
1033-
return self.baseline
1034-
1035-
def set_baseline(self, baseline):
1036-
"""
1037-
Set `.StepPatch` baseline value.
1038-
1039-
Parameters
1040-
----------
1041-
baseline : float or None
1042-
"""
1043-
self.baseline = baseline
1027+
if baseline is not None:
1028+
self._baseline = np.asarray(baseline)
10441029
self._update_path()
10451030
self.stale = True
10461031

Loading

‎lib/matplotlib/tests/test_axes.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_axes.py
+12-8Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1892,15 +1892,15 @@ def test_stairs_update(fig_test, fig_ref):
18921892
test_ax = fig_test.add_subplot()
18931893
h = test_ax.stairs([1, 2, 3])
18941894
test_ax.set_ylim(ylim)
1895-
h.set_values([3, 2, 1])
1896-
h.set_edges(np.arange(4)+2)
1895+
h.set_data([3, 2, 1])
1896+
h.set_data(edges=np.arange(4)+2)
18971897
h.set_data([1, 2, 1], np.arange(4)/2)
18981898
h.set_data([1, 2, 3])
18991899
h.set_data(None, np.arange(4))
19001900
assert np.allclose(h.get_data()[0], np.arange(1, 4))
19011901
assert np.allclose(h.get_data()[1], np.arange(4))
1902-
h.set_baseline(-2)
1903-
assert h.get_baseline() == -2
1902+
h.set_data(baseline=-2)
1903+
assert h.get_data().baseline == -2
19041904

19051905
# Ref
19061906
ref_ax = fig_ref.add_subplot()
@@ -1921,13 +1921,13 @@ def test_stairs_invalid_mismatch():
19211921
def test_stairs_invalid_update():
19221922
h = plt.stairs([1, 2], [0, 1, 2])
19231923
with pytest.raises(ValueError, match='Nan values in "edges"'):
1924-
h.set_edges([1, np.nan, 2])
1924+
h.set_data(edges=[1, np.nan, 2])
19251925

19261926

19271927
def test_stairs_invalid_update2():
19281928
h = plt.stairs([1, 2], [0, 1, 2])
19291929
with pytest.raises(ValueError, match='Size mismatch'):
1930-
h.set_edges(np.arange(5))
1930+
h.set_data(edges=np.arange(5))
19311931

19321932

19331933
@image_comparison(['test_stairs_options.png'], remove_text=True)
@@ -1943,10 +1943,14 @@ def test_stairs_options():
19431943
ax.stairs(yn, x, color='orange', ls='--', lw=2, label="C")
19441944
ax.stairs(yn/3, x*3-2, ls='--', lw=2, baseline=0.5,
19451945
orientation='horizontal', label="D")
1946-
ax.stairs(y[::-1]*3+12, x, color='red', ls='--', lw=2, baseline=None,
1946+
ax.stairs(y[::-1]*3+13, x-1, color='red', ls='--', lw=2, baseline=None,
19471947
label="E")
1948+
ax.stairs(y[::-1]*3+14, x, baseline=26,
1949+
color='purple', ls='--', lw=2, label="F")
1950+
ax.stairs(yn[::-1]*3+15, x+1, baseline=np.linspace(27, 25, len(y)),
1951+
color='blue', ls='--', lw=2, label="G", fill=True)
19481952
ax.stairs(y[:-1][::-1]*2+11, x[:-1]+0.5, color='black', ls='--', lw=2,
1949-
baseline=12, hatch='//', label="F")
1953+
baseline=12, hatch='//', label="H")
19501954
ax.legend(loc=0)
19511955

19521956

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.