Skip to content

First attempt for individual hatching styles for stackplot #27158

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

Merged
merged 1 commit into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions doc/users/next_whats_new/stackplot_hatch.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
``hatch`` parameter for stackplot
-------------------------------------------

The `~.Axes.stackplot` *hatch* parameter now accepts a list of strings describing hatching styles that will be applied sequentially to the layers in the stack:

.. plot::
:include-source: true
:alt: Two charts, identified as ax1 and ax2, showing "stackplots", i.e. one-dimensional distributions of data stacked on top of one another. The first plot, ax1 has cross-hatching on all slices, having been given a single string as the "hatch" argument. The second plot, ax2 has different styles of hatching on each slice - diagonal hatching in opposite directions on the first two slices, cross-hatching on the third slice, and open circles on the fourth.

import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10,5))

cols = 10
rows = 4
data = (
np.reshape(np.arange(0, cols, 1), (1, -1)) ** 2
+ np.reshape(np.arange(0, rows), (-1, 1))
+ np.random.random((rows, cols))*5
)
x = range(data.shape[1])
ax1.stackplot(x, data, hatch="x")
ax2.stackplot(x, data, hatch=["//","\\","x","o"])

ax1.set_title("hatch='x'")
ax2.set_title("hatch=['//','\\\\','x','o']")

plt.show()
5 changes: 4 additions & 1 deletion lib/matplotlib/pyplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -3792,12 +3792,15 @@ def spy(

# Autogenerated by boilerplate.py. Do not edit as changes will be lost.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the comment says, the signatures here are autogenerated

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, sorry! I think I ran boilerplate.py myself in the hope of fixing the stub github action, as I didn't know about the .pyi file... Should I revert this commit, or will the autogeneration take care of it anyway?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you are supposed run boilerplate.py yourself, and this comment is meant to indicate that you shouldn't manually edit the type hints here.

Copy link
Member

@story645 story645 Oct 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦‍♀️ I think Ruth is right given I edited this file in my PR too and we never call boilerplate.py as part of our build/install process. Sorry & I'll open up a follow up issue on documenting how this is used!

@_copy_docstring_and_deprecators(Axes.stackplot)
def stackplot(x, *args, labels=(), colors=None, baseline="zero", data=None, **kwargs):
def stackplot(
x, *args, labels=(), colors=None, hatch=None, baseline="zero", data=None, **kwargs
):
return gca().stackplot(
x,
*args,
labels=labels,
colors=colors,
hatch=hatch,
baseline=baseline,
**({"data": data} if data is not None else {}),
**kwargs,
Expand Down
21 changes: 19 additions & 2 deletions lib/matplotlib/stackplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@


def stackplot(axes, x, *args,
labels=(), colors=None, baseline='zero',
labels=(), colors=None, hatch=None, baseline='zero',
**kwargs):
"""
Draw a stacked area plot.
Expand Down Expand Up @@ -55,6 +55,15 @@ def stackplot(axes, x, *args,

If not specified, the colors from the Axes property cycle will be used.

hatch : list of str, default: None
A sequence of hatching styles. See
:doc:`/gallery/shapes_and_collections/hatch_style_reference`.
The sequence will be cycled through for filling the
stacked areas from bottom to top.
It need not be exactly the same length as the number
of provided *y*, in which case the styles will repeat from the
beginning.

data : indexable object, optional
DATA_PARAMETER_PLACEHOLDER

Expand All @@ -76,6 +85,11 @@ def stackplot(axes, x, *args,
else:
colors = (axes._get_lines.get_next_color() for _ in y)

if hatch is None or isinstance(hatch, str):
hatch = itertools.cycle([hatch])
else:
hatch = itertools.cycle(hatch)

# Assume data passed has not been 'stacked', so stack it here.
# We'll need a float buffer for the upcoming calculations.
stack = np.cumsum(y, axis=0, dtype=np.promote_types(y.dtype, np.float32))
Expand Down Expand Up @@ -113,7 +127,9 @@ def stackplot(axes, x, *args,

# Color between x = 0 and the first array.
coll = axes.fill_between(x, first_line, stack[0, :],
facecolor=next(colors), label=next(labels, None),
facecolor=next(colors),
hatch=next(hatch),
label=next(labels, None),
**kwargs)
coll.sticky_edges.y[:] = [0]
r = [coll]
Expand All @@ -122,6 +138,7 @@ def stackplot(axes, x, *args,
for i in range(len(y) - 1):
r.append(axes.fill_between(x, stack[i, :], stack[i + 1, :],
facecolor=next(colors),
hatch=next(hatch),
label=next(labels, None),
**kwargs))
return r
1 change: 1 addition & 0 deletions lib/matplotlib/stackplot.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def stackplot(
*args: ArrayLike,
labels: Iterable[str] = ...,
colors: Iterable[ColorType] | None = ...,
hatch: Iterable[str] | str | None = ...,
baseline: Literal["zero", "sym", "wiggle", "weighted_wiggle"] = ...,
**kwargs
) -> list[PolyCollection]: ...
21 changes: 21 additions & 0 deletions lib/matplotlib/tests/test_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3084,6 +3084,27 @@ def layers(n, m):
axs[1, 1].stackplot(range(100), d.T, baseline='weighted_wiggle')


@check_figures_equal()
def test_stackplot_hatching(fig_ref, fig_test):
x = np.linspace(0, 10, 10)
y1 = 1.0 * x
y2 = 2.0 * x + 1
y3 = 3.0 * x + 2
# stackplot with different hatching styles (issue #27146)
ax_test = fig_test.subplots()
ax_test.stackplot(x, y1, y2, y3, hatch=["x", "//", "\\\\"], colors=["white"])
ax_test.set_xlim((0, 10))
ax_test.set_ylim((0, 70))
# compare with result from hatching each layer individually
stack_baseline = np.zeros(len(x))
ax_ref = fig_ref.subplots()
ax_ref.fill_between(x, stack_baseline, y1, hatch="x", facecolor="white")
ax_ref.fill_between(x, y1, y1+y2, hatch="//", facecolor="white")
ax_ref.fill_between(x, y1+y2, y1+y2+y3, hatch="\\\\", facecolor="white")
ax_ref.set_xlim((0, 10))
ax_ref.set_ylim((0, 70))


def _bxp_test_helper(
stats_kwargs={}, transform_stats=lambda s: s, bxp_kwargs={}):
np.random.seed(937)
Expand Down