Skip to content

Commit 89f3d98

Browse files
committed
Fix asymmetric error bars for series (closes #9536)
This fix is for handling asymmetric error bars for series. It adapts to the syntax for http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.errorbar where a sequence of shape 2xN is expected in case of asymmetric error bars. If a single series is to be plotted and the error sequence is of shape 2xN it will be used as asymmetric error bars. Previously a 2xN error sequence was assumed to be 2 symmetric error sequences for 2 series. Thus in the end only the first error sequence was used. This commit improves the docstring of the `_parse_errorbars` method as well as the general pandas doc on error bars.
1 parent 48f39af commit 89f3d98

File tree

4 files changed

+50
-16
lines changed

4 files changed

+50
-16
lines changed

doc/source/visualization.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1366,9 +1366,10 @@ Horizontal and vertical errorbars can be supplied to the ``xerr`` and ``yerr`` k
13661366

13671367
- As a :class:`DataFrame` or ``dict`` of errors with column names matching the ``columns`` attribute of the plotting :class:`DataFrame` or matching the ``name`` attribute of the :class:`Series`
13681368
- As a ``str`` indicating which of the columns of plotting :class:`DataFrame` contain the error values
1369+
- As a single ``number`` which is used as the error for every value
13691370
- As raw values (``list``, ``tuple``, or ``np.ndarray``). Must be the same length as the plotting :class:`DataFrame`/:class:`Series`
13701371

1371-
Asymmetrical error bars are also supported, however raw error values must be provided in this case. For a ``M`` length :class:`Series`, a ``Mx2`` array should be provided indicating lower and upper (or left and right) errors. For a ``MxN`` :class:`DataFrame`, asymmetrical errors should be in a ``Mx2xN`` array.
1372+
Asymmetrical error bars are also supported, however raw error values must be provided in this case. For a ``N`` length :class:`Series`, a ``2xN`` array should be provided indicating lower and upper (or left and right) errors. For a ``MxN`` :class:`DataFrame`, asymmetrical errors should be in a ``Mx2xN`` array.
13721373

13731374
Here is an example of one way to easily plot group means with standard deviations from the raw data.
13741375

doc/source/whatsnew/v0.18.1.txt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,13 @@ Other Enhancements
6060
- ``pd.read_msgpack()`` now always gives writeable ndarrays even when compression is used (:issue:`12359`).
6161
- ``Index.take`` now handles ``allow_fill`` and ``fill_value`` consistently (:issue:`12631`)
6262

63-
.. ipython:: python
63+
.. ipython:: python
6464

65-
idx = pd.Index([1., 2., 3., 4.], dtype='float')
66-
idx.take([2, -1]) # default, allow_fill=True, fill_value=None
67-
idx.take([2, -1], fill_value=True)
65+
idx = pd.Index([1., 2., 3., 4.], dtype='float')
66+
idx.take([2, -1]) # default, allow_fill=True, fill_value=None
67+
idx.take([2, -1], fill_value=True)
6868

69+
- ``Series.plot`` allows now asymmetric error bars in the shape of 2xN array (:issue:`9536`)
6970

7071
.. _whatsnew_0181.api:
7172

pandas/tests/test_graphics.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,20 @@ def test_errorbar_plot(self):
11801180
with tm.assertRaises((ValueError, TypeError)):
11811181
s.plot(yerr=s_err)
11821182

1183+
def test_errorbar_asymmetrical(self):
1184+
# github issue #9536
1185+
s = Series(np.random.randn(5))
1186+
err = np.random.rand(2, 5)
1187+
1188+
ax = _check_plot_works(s.plot, yerr=err, xerr=(err / 2))
1189+
self._check_has_errorbars(ax, yerr=1, xerr=1)
1190+
1191+
assert_allclose(ax.lines[2].get_ydata(), s.values - err[0])
1192+
assert_allclose(ax.lines[3].get_ydata(), s.values + err[1])
1193+
1194+
assert_allclose(ax.lines[0].get_xdata(), s.index - (err[0] / 2))
1195+
assert_allclose(ax.lines[1].get_xdata(), s.index + (err[1] / 2))
1196+
11831197
def test_table(self):
11841198
_check_plot_works(self.series.plot, table=True)
11851199
_check_plot_works(self.series.plot, table=self.series)

pandas/tools/plotting.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,10 +1419,17 @@ def _parse_errorbars(self, label, err):
14191419
Error bars can be specified in several ways:
14201420
Series: the user provides a pandas.Series object of the same
14211421
length as the data
1422-
ndarray: provides a np.ndarray of the same length as the data
1422+
list_like (list/tuple/ndarray/iterator): either a list like of the
1423+
same length N as the data has to be provided
1424+
or a list like of the shape Mx2xN for asymmetrical error
1425+
bars when plotting a DataFrame of shape MxN
1426+
or a list like of the shape 2xN for asymmetrical error bars
1427+
when plotting a Series.
14231428
DataFrame/dict: error values are paired with keys matching the
14241429
key in the plotted DataFrame
14251430
str: the name of the column within the plotted DataFrame
1431+
numeric scalar: the error provided as a number is used for every
1432+
data point
14261433
'''
14271434

14281435
if err is None:
@@ -1458,22 +1465,33 @@ def match_labels(data, e):
14581465

14591466
elif com.is_list_like(err):
14601467
if com.is_iterator(err):
1461-
err = np.atleast_2d(list(err))
1468+
err = np.asanyarray(list(err))
14621469
else:
14631470
# raw error values
1464-
err = np.atleast_2d(err)
1471+
err = np.asanyarray(err)
14651472

1466-
err_shape = err.shape
1473+
if self.nseries == 1 and err.ndim == 2 and len(err) == 2:
1474+
# asymmetrical errors bars for a series as a 2xN array
1475+
err = np.expand_dims(err, 0)
1476+
err_shape = err.shape
14671477

1468-
# asymmetrical error bars
1469-
if err.ndim == 3:
1470-
if (err_shape[0] != self.nseries) or \
1471-
(err_shape[1] != 2) or \
1472-
(err_shape[2] != len(self.data)):
1478+
if err_shape[2] != len(self.data):
14731479
msg = "Asymmetrical error bars should be provided " + \
1474-
"with the shape (%u, 2, %u)" % \
1475-
(self.nseries, len(self.data))
1480+
"with the shape (2, %u)" % (len(self.data))
14761481
raise ValueError(msg)
1482+
else:
1483+
err = np.atleast_2d(err)
1484+
err_shape = err.shape
1485+
1486+
# asymmetrical error bars
1487+
if err.ndim == 3:
1488+
if (err_shape[0] != self.nseries) or \
1489+
(err_shape[1] != 2) or \
1490+
(err_shape[2] != len(self.data)):
1491+
msg = "Asymmetrical error bars should be provided " + \
1492+
"with the shape (%u, 2, %u)" % \
1493+
(self.nseries, len(self.data))
1494+
raise ValueError(msg)
14771495

14781496
# broadcast errors to each data series
14791497
if len(err) == 1:

0 commit comments

Comments
 (0)