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

Skip to content

Commit ed56f95

Browse files
authored
Merge pull request #28073 from Impaler343/histogram
Add support for multiple hatches, edgecolors and linewidths in histograms
2 parents aca8b7a + 43ce57d commit ed56f95

File tree

4 files changed

+236
-9
lines changed

4 files changed

+236
-9
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
Vectorized ``hist`` style parameters
2+
------------------------------------
3+
4+
The parameters *hatch*, *edgecolor*, *facecolor*, *linewidth* and *linestyle*
5+
of the `~matplotlib.axes.Axes.hist` method are now vectorized.
6+
This means that you can pass in individual parameters for each histogram
7+
when the input *x* has multiple datasets.
8+
9+
10+
.. plot::
11+
:include-source: true
12+
:alt: Four charts, each displaying stacked histograms of three Poisson distributions. Each chart differentiates the histograms using various parameters: top left uses different linewidths, top right uses different hatches, bottom left uses different edgecolors, and bottom right uses different facecolors. Each histogram on the left side also has a different edgecolor.
13+
14+
import matplotlib.pyplot as plt
15+
import numpy as np
16+
np.random.seed(19680801)
17+
18+
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(9, 9))
19+
20+
data1 = np.random.poisson(5, 1000)
21+
data2 = np.random.poisson(7, 1000)
22+
data3 = np.random.poisson(10, 1000)
23+
24+
labels = ["Data 1", "Data 2", "Data 3"]
25+
26+
ax1.hist([data1, data2, data3], bins=range(17), histtype="step", stacked=True,
27+
edgecolor=["red", "green", "blue"], linewidth=[1, 2, 3])
28+
ax1.set_title("Different linewidths")
29+
ax1.legend(labels)
30+
31+
ax2.hist([data1, data2, data3], bins=range(17), histtype="barstacked",
32+
hatch=["/", ".", "*"])
33+
ax2.set_title("Different hatch patterns")
34+
ax2.legend(labels)
35+
36+
ax3.hist([data1, data2, data3], bins=range(17), histtype="bar", fill=False,
37+
edgecolor=["red", "green", "blue"], linestyle=["--", "-.", ":"])
38+
ax3.set_title("Different linestyles")
39+
ax3.legend(labels)
40+
41+
ax4.hist([data1, data2, data3], bins=range(17), histtype="barstacked",
42+
facecolor=["red", "green", "blue"])
43+
ax4.set_title("Different facecolors")
44+
ax4.legend(labels)
45+
46+
plt.show()

galleries/examples/statistics/histogram_multihist.py

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
select these parameters:
1616
http://docs.astropy.org/en/stable/visualization/histogram.html
1717
"""
18-
18+
# %%
1919
import matplotlib.pyplot as plt
2020
import numpy as np
2121

@@ -45,6 +45,94 @@
4545
fig.tight_layout()
4646
plt.show()
4747

48+
# %%
49+
# -----------------------------------
50+
# Setting properties for each dataset
51+
# -----------------------------------
52+
#
53+
# You can style the histograms individually by passing a list of values to the
54+
# following parameters:
55+
#
56+
# * edgecolor
57+
# * facecolor
58+
# * hatch
59+
# * linewidth
60+
# * linestyle
61+
#
62+
#
63+
# edgecolor
64+
# .........
65+
66+
fig, ax = plt.subplots()
67+
68+
edgecolors = ['green', 'red', 'blue']
69+
70+
ax.hist(x, n_bins, fill=False, histtype="step", stacked=True,
71+
edgecolor=edgecolors, label=edgecolors)
72+
ax.legend()
73+
ax.set_title('Stacked Steps with Edgecolors')
74+
75+
plt.show()
76+
77+
# %%
78+
# facecolor
79+
# .........
80+
81+
fig, ax = plt.subplots()
82+
83+
facecolors = ['green', 'red', 'blue']
84+
85+
ax.hist(x, n_bins, histtype="barstacked", facecolor=facecolors, label=facecolors)
86+
ax.legend()
87+
ax.set_title("Bars with different Facecolors")
88+
89+
plt.show()
90+
91+
# %%
92+
# hatch
93+
# .....
94+
95+
fig, ax = plt.subplots()
96+
97+
hatches = [".", "o", "x"]
98+
99+
ax.hist(x, n_bins, histtype="barstacked", hatch=hatches, label=hatches)
100+
ax.legend()
101+
ax.set_title("Hatches on Stacked Bars")
102+
103+
plt.show()
104+
105+
# %%
106+
# linewidth
107+
# .........
108+
109+
fig, ax = plt.subplots()
110+
111+
linewidths = [1, 2, 3]
112+
edgecolors = ["green", "red", "blue"]
113+
114+
ax.hist(x, n_bins, fill=False, histtype="bar", linewidth=linewidths,
115+
edgecolor=edgecolors, label=linewidths)
116+
ax.legend()
117+
ax.set_title("Bars with Linewidths")
118+
119+
plt.show()
120+
121+
# %%
122+
# linestyle
123+
# .........
124+
125+
fig, ax = plt.subplots()
126+
127+
linestyles = ['-', ':', '--']
128+
129+
ax.hist(x, n_bins, fill=False, histtype='bar', linestyle=linestyles,
130+
edgecolor=edgecolors, label=linestyles)
131+
ax.legend()
132+
ax.set_title('Bars with Linestyles')
133+
134+
plt.show()
135+
48136
# %%
49137
#
50138
# .. admonition:: References

lib/matplotlib/axes/_axes.py

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6946,7 +6946,13 @@ def hist(self, x, bins=None, range=None, density=False, weights=None,
69466946
DATA_PARAMETER_PLACEHOLDER
69476947
69486948
**kwargs
6949-
`~matplotlib.patches.Patch` properties
6949+
`~matplotlib.patches.Patch` properties. The following properties
6950+
additionally accept a sequence of values corresponding to the
6951+
datasets in *x*:
6952+
*edgecolors*, *facecolors*, *lines*, *linestyles*, *hatches*.
6953+
6954+
.. versionadded:: 3.10
6955+
Allowing sequences of values in above listed Patch properties.
69506956
69516957
See Also
69526958
--------
@@ -7219,15 +7225,35 @@ def hist(self, x, bins=None, range=None, density=False, weights=None,
72197225
# If None, make all labels None (via zip_longest below); otherwise,
72207226
# cast each element to str, but keep a single str as it.
72217227
labels = [] if label is None else np.atleast_1d(np.asarray(label, str))
7228+
7229+
if histtype == "step":
7230+
edgecolors = itertools.cycle(np.atleast_1d(kwargs.get('edgecolor',
7231+
colors)))
7232+
else:
7233+
edgecolors = itertools.cycle(np.atleast_1d(kwargs.get("edgecolor", None)))
7234+
7235+
facecolors = itertools.cycle(np.atleast_1d(kwargs.get('facecolor', colors)))
7236+
hatches = itertools.cycle(np.atleast_1d(kwargs.get('hatch', None)))
7237+
linewidths = itertools.cycle(np.atleast_1d(kwargs.get('linewidth', None)))
7238+
linestyles = itertools.cycle(np.atleast_1d(kwargs.get('linestyle', None)))
7239+
72227240
for patch, lbl in itertools.zip_longest(patches, labels):
7223-
if patch:
7224-
p = patch[0]
7241+
if not patch:
7242+
continue
7243+
p = patch[0]
7244+
kwargs.update({
7245+
'hatch': next(hatches),
7246+
'linewidth': next(linewidths),
7247+
'linestyle': next(linestyles),
7248+
'edgecolor': next(edgecolors),
7249+
'facecolor': next(facecolors),
7250+
})
7251+
p._internal_update(kwargs)
7252+
if lbl is not None:
7253+
p.set_label(lbl)
7254+
for p in patch[1:]:
72257255
p._internal_update(kwargs)
7226-
if lbl is not None:
7227-
p.set_label(lbl)
7228-
for p in patch[1:]:
7229-
p._internal_update(kwargs)
7230-
p.set_label('_nolegend_')
7256+
p.set_label('_nolegend_')
72317257

72327258
if nx == 1:
72337259
return tops[0], bins, patches[0]

lib/matplotlib/tests/test_axes.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4632,6 +4632,64 @@ def test_hist_stacked_bar():
46324632
ax.legend(loc='upper right', bbox_to_anchor=(1.0, 1.0), ncols=1)
46334633

46344634

4635+
@pytest.mark.parametrize('kwargs', ({'facecolor': ["b", "g", "r"]},
4636+
{'edgecolor': ["b", "g", "r"]},
4637+
{'hatch': ["/", "\\", "."]},
4638+
{'linestyle': ["-", "--", ":"]},
4639+
{'linewidth': [1, 1.5, 2]},
4640+
{'color': ["b", "g", "r"]}))
4641+
@check_figures_equal(extensions=["png"])
4642+
def test_hist_vectorized_params(fig_test, fig_ref, kwargs):
4643+
np.random.seed(19680801)
4644+
xs = [np.random.randn(n) for n in [20, 50, 100]]
4645+
4646+
(axt1, axt2) = fig_test.subplots(2)
4647+
(axr1, axr2) = fig_ref.subplots(2)
4648+
4649+
for histtype, axt, axr in [("stepfilled", axt1, axr1), ("step", axt2, axr2)]:
4650+
_, bins, _ = axt.hist(xs, bins=10, histtype=histtype, **kwargs)
4651+
4652+
kw, values = next(iter(kwargs.items()))
4653+
for i, (x, value) in enumerate(zip(xs, values)):
4654+
axr.hist(x, bins=bins, histtype=histtype, **{kw: value},
4655+
zorder=(len(xs)-i)/2)
4656+
4657+
4658+
@pytest.mark.parametrize('kwargs, patch_face, patch_edge',
4659+
# 'C0'(blue) stands for the first color of the
4660+
# default color cycle as well as the patch.facecolor rcParam
4661+
# When the expected edgecolor is 'k'(black),
4662+
# it corresponds to the patch.edgecolor rcParam
4663+
[({'histtype': 'stepfilled', 'color': 'r',
4664+
'facecolor': 'y', 'edgecolor': 'g'}, 'y', 'g'),
4665+
({'histtype': 'step', 'color': 'r',
4666+
'facecolor': 'y', 'edgecolor': 'g'}, ('y', 0), 'g'),
4667+
({'histtype': 'stepfilled', 'color': 'r',
4668+
'edgecolor': 'g'}, 'r', 'g'),
4669+
({'histtype': 'step', 'color': 'r',
4670+
'edgecolor': 'g'}, ('r', 0), 'g'),
4671+
({'histtype': 'stepfilled', 'color': 'r',
4672+
'facecolor': 'y'}, 'y', 'k'),
4673+
({'histtype': 'step', 'color': 'r',
4674+
'facecolor': 'y'}, ('y', 0), 'r'),
4675+
({'histtype': 'stepfilled',
4676+
'facecolor': 'y', 'edgecolor': 'g'}, 'y', 'g'),
4677+
({'histtype': 'step', 'facecolor': 'y',
4678+
'edgecolor': 'g'}, ('y', 0), 'g'),
4679+
({'histtype': 'stepfilled', 'color': 'r'}, 'r', 'k'),
4680+
({'histtype': 'step', 'color': 'r'}, ('r', 0), 'r'),
4681+
({'histtype': 'stepfilled', 'facecolor': 'y'}, 'y', 'k'),
4682+
({'histtype': 'step', 'facecolor': 'y'}, ('y', 0), 'C0'),
4683+
({'histtype': 'stepfilled', 'edgecolor': 'g'}, 'C0', 'g'),
4684+
({'histtype': 'step', 'edgecolor': 'g'}, ('C0', 0), 'g'),
4685+
({'histtype': 'stepfilled'}, 'C0', 'k'),
4686+
({'histtype': 'step'}, ('C0', 0), 'C0')])
4687+
def test_hist_color_semantics(kwargs, patch_face, patch_edge):
4688+
_, _, patches = plt.figure().subplots().hist([1, 2, 3], **kwargs)
4689+
assert all(mcolors.same_color([p.get_facecolor(), p.get_edgecolor()],
4690+
[patch_face, patch_edge]) for p in patches)
4691+
4692+
46354693
def test_hist_barstacked_bottom_unchanged():
46364694
b = np.array([10, 20])
46374695
plt.hist([[0, 1], [0, 1]], 2, histtype="barstacked", bottom=b)
@@ -4643,6 +4701,15 @@ def test_hist_emptydata():
46434701
ax.hist([[], range(10), range(10)], histtype="step")
46444702

46454703

4704+
def test_hist_unused_labels():
4705+
# When a list with one dataset and N elements is provided and N labels, ensure
4706+
# that the first label is used for the dataset and all other labels are ignored
4707+
fig, ax = plt.subplots()
4708+
ax.hist([[1, 2, 3]], label=["values", "unused", "also unused"])
4709+
_, labels = ax.get_legend_handles_labels()
4710+
assert labels == ["values"]
4711+
4712+
46464713
def test_hist_labels():
46474714
# test singleton labels OK
46484715
fig, ax = plt.subplots()

0 commit comments

Comments
 (0)