Skip to content

Commit daf551d

Browse files
committed
Merge pull request matplotlib#1671 from dmcdougall/feature_stack_base
Feature stack base
2 parents b7ff5d4 + 6b4678b commit daf551d

File tree

8 files changed

+3508
-5
lines changed

8 files changed

+3508
-5
lines changed

CHANGELOG

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
2013-01-07 Add framealpha keyword argument to legend - PO
66

7+
2013-01-16 Till Stensitzki added a baseline feature to stackplot
8+
79
2012-12-22 Added classes for interpolation within triangular grids
810
(LinearTriInterpolator) and to find the triangles in which points
911
lie (TrapezoidMapTriFinder) to matplotlib.tri module. - IMT

doc/users/whats_new.rst

+7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ revision, see the :ref:`github-stats`.
2222
new in matplotlib-1.3
2323
=====================
2424

25+
Baselines for stackplot
26+
-----------------------
27+
Till Stensitzki added non-zero baselines to :func:`~matplotlib.pyplot.stackplot`.
28+
They may be symmetric or weighted.
29+
30+
.. plot:: mpl_examples/pylab_examples/stackplot_demo2.py
31+
2532
Initialize a rotated rectangle
2633
------------------------------
2734
Damon McDougall extended the :class:`~matplotlib.patches.Rectangle` constructor
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import numpy as np
2+
import matplotlib.pyplot as plt
3+
4+
np.random.seed(0)
5+
def layers(n, m):
6+
"""
7+
Return *n* random Gaussian mixtures, each of length *m*.
8+
"""
9+
def bump(a):
10+
x = 1 / (.1 + np.random.random())
11+
y = 2 * np.random.random() - .5
12+
z = 10 / (.1 + np.random.random())
13+
for i in range(m):
14+
w = (i / float(m) - y) * z
15+
a[i] += x * np.exp(-w * w)
16+
a = np.zeros((m, n))
17+
for i in range(n):
18+
for j in range(5):
19+
bump(a[:, i])
20+
return a
21+
22+
d = layers(3, 100)
23+
24+
plt.subplots()
25+
plt.stackplot(range(100), d.T, baseline='wiggle')
26+
plt.show()

lib/matplotlib/stackplot.py

+49-5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ def stackplot(axes, x, *args, **kwargs):
2525
2626
Keyword arguments:
2727
28+
*baseline* : ['zero', 'sym', 'wiggle', 'weighted_wiggle']
29+
Method used to calculate the baseline. 'zero' is just a
30+
simple stacked plot. 'sym' is symmetric around zero and
31+
is sometimes called `ThemeRiver`. 'wiggle' minimizes the
32+
sum of the squared slopes. 'weighted_wiggle' does the
33+
same but weights to account for size of each layer.
34+
It is also called `Streamgraph`-layout. More details
35+
can be found at http://www.leebyron.com/else/streamgraph/.
36+
37+
2838
*colors* : A list or tuple of colors. These will be cycled through and
2939
used to colour the stacked areas.
3040
All other keyword arguments are passed to
@@ -44,17 +54,51 @@ def stackplot(axes, x, *args, **kwargs):
4454
if colors is not None:
4555
axes.set_color_cycle(colors)
4656

57+
baseline = kwargs.pop('baseline', 'zero')
4758
# Assume data passed has not been 'stacked', so stack it here.
48-
y_stack = np.cumsum(y, axis=0)
59+
stack = np.cumsum(y, axis=0)
4960

5061
r = []
62+
if baseline == 'zero':
63+
first_line = 0.
64+
65+
elif baseline == 'sym':
66+
first_line = -np.sum(y, 0) * 0.5
67+
stack += first_line[None, :]
68+
69+
elif baseline == 'wiggle':
70+
m = y.shape[0]
71+
first_line = (y * (m - 0.5 - np.arange(0, m)[:, None])).sum(0)
72+
first_line /= -m
73+
stack += first_line
74+
75+
elif baseline == 'weighted_wiggle':
76+
m, n = y.shape
77+
center = np.zeros(n)
78+
total = np.sum(y, 0)
79+
increase = np.hstack((y[:, 0:1], np.diff(y)))
80+
below_size = total - stack
81+
below_size += 0.5 * y
82+
move_up = below_size / total
83+
move_up[:, 0] = 0.5
84+
center = (move_up - 0.5) * increase
85+
center = np.cumsum(center.sum(0))
86+
first_line = center - 0.5 * total
87+
stack += first_line
88+
else:
89+
errstr = "Baseline method %s not recognised. " % baseline
90+
errstr += "Expected 'zero', 'sym', 'wiggle' or 'weighted_wiggle'"
91+
raise ValueError(errstr)
5192

5293
# Color between x = 0 and the first array.
53-
r.append(axes.fill_between(x, 0, y_stack[0, :],
54-
facecolor=axes._get_lines.color_cycle.next(), **kwargs))
94+
r.append(axes.fill_between(x, first_line, stack[0, :],
95+
facecolor=axes._get_lines.color_cycle.next(),
96+
**kwargs))
5597

5698
# Color between array i-1 and array i
5799
for i in xrange(len(y) - 1):
58-
r.append(axes.fill_between(x, y_stack[i, :], y_stack[i + 1, :],
59-
facecolor=axes._get_lines.color_cycle.next(), **kwargs))
100+
color = axes._get_lines.color_cycle.next()
101+
r.append(axes.fill_between(x, stack[i, :], stack[i + 1, :],
102+
facecolor= color,
103+
**kwargs))
60104
return r
Binary file not shown.
Loading

0 commit comments

Comments
 (0)