Skip to content

Commit 4d3ca3a

Browse files
committed
Merge branch 'master' into fix-14763
2 parents 725f1b8 + 856476b commit 4d3ca3a

File tree

18 files changed

+138
-44
lines changed

18 files changed

+138
-44
lines changed

ci/build_docs.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ if [ x"$DOC_BUILD" != x"" ]; then
5555
touch .nojekyll
5656
git add --all .
5757
git commit -m "Version" --allow-empty
58-
git remote add origin "https://$GH_TOKEN@github.com/pandas-docs/pandas-docs-travis"
58+
git remote add origin "https://pandas-docs:$GH_TOKEN@github.com/pandas-docs/pandas-docs-travis"
5959
git push origin gh-pages -f
6060
fi
6161

doc/source/advanced.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,12 +528,14 @@ return a copy of the data rather than a view:
528528
jim joe
529529
1 z 0.64094
530530
531+
.. _advanced.unsorted:
532+
531533
Furthermore if you try to index something that is not fully lexsorted, this can raise:
532534

533535
.. code-block:: ipython
534536
535537
In [5]: dfm.loc[(0,'y'):(1, 'z')]
536-
KeyError: 'Key length (2) was greater than MultiIndex lexsort depth (1)'
538+
UnsortedIndexError: 'Key length (2) was greater than MultiIndex lexsort depth (1)'
537539
538540
The ``is_lexsorted()`` method on an ``Index`` show if the index is sorted, and the ``lexsort_depth`` property returns the sort depth:
539541

doc/source/whatsnew/v0.20.0.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,15 @@ Other enhancements
5050
- ``Series.sort_index`` accepts parameters ``kind`` and ``na_position`` (:issue:`13589`, :issue:`14444`)
5151

5252
- ``pd.read_excel`` now preserves sheet order when using ``sheetname=None`` (:issue:`9930`)
53+
54+
- New ``UnsortedIndexError`` (subclass of ``KeyError``) raised when indexing/slicing into an
55+
unsorted MultiIndex (:issue:`11897`). This allows differentiation between errors due to lack
56+
of sorting or an incorrect key. See :ref:`here <advanced.unsorted>`
57+
5358
- ``pd.cut`` and ``pd.qcut`` now support datetime64 and timedelta64 dtypes (issue:`14714`)
5459
- ``Series`` provides a ``to_excel`` method to output Excel files (:issue:`8825`)
5560
- The ``usecols`` argument in ``pd.read_csv`` now accepts a callable function as a value (:issue:`14154`)
61+
- ``pd.DataFrame.plot`` now prints a title above each subplot if ``suplots=True`` and ``title`` is a list of strings (:issue:`14753`)
5662

5763
.. _whatsnew_0200.api_breaking:
5864

@@ -70,6 +76,9 @@ Backwards incompatible API changes
7076
Other API Changes
7177
^^^^^^^^^^^^^^^^^
7278

79+
- Change error message text when indexing via a
80+
boolean ``Series`` that has an incompatible index (:issue:`14491`)
81+
7382
.. _whatsnew_0200.deprecations:
7483

7584
Deprecations

pandas/core/common.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,16 @@ class UnsupportedFunctionCall(ValueError):
9797
pass
9898

9999

100+
class UnsortedIndexError(KeyError):
101+
""" Error raised when attempting to get a slice of a MultiIndex
102+
and the index has not been lexsorted. Subclass of `KeyError`.
103+
104+
.. versionadded:: 0.20.0
105+
106+
"""
107+
pass
108+
109+
100110
class AbstractMethodError(NotImplementedError):
101111
"""Raise this error instead of NotImplementedError for abstract methods
102112
while keeping compatibility with Python 2 and Python 3.

pandas/core/frame.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2483,7 +2483,7 @@ def assign(self, **kwargs):
24832483
Notes
24842484
-----
24852485
Since ``kwargs`` is a dictionary, the order of your
2486-
arguments may not be preserved. The make things predicatable,
2486+
arguments may not be preserved. To make things predicatable,
24872487
the columns are inserted in alphabetical order, at the end of
24882488
your DataFrame. Assigning multiple columns within the same
24892489
``assign`` is possible, but you cannot reference other columns

pandas/core/generic.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3635,14 +3635,17 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None,
36353635
require that you also specify an `order` (int),
36363636
e.g. df.interpolate(method='polynomial', order=4).
36373637
These use the actual numerical values of the index.
3638-
* 'krogh', 'piecewise_polynomial', 'spline', 'pchip' and 'akima' are all
3639-
wrappers around the scipy interpolation methods of similar
3640-
names. These use the actual numerical values of the index. See
3641-
the scipy documentation for more on their behavior
3642-
`here <http://docs.scipy.org/doc/scipy/reference/interpolate.html#univariate-interpolation>`__ # noqa
3643-
`and here <http://docs.scipy.org/doc/scipy/reference/tutorial/interpolate.html>`__ # noqa
3638+
* 'krogh', 'piecewise_polynomial', 'spline', 'pchip' and 'akima'
3639+
are all wrappers around the scipy interpolation methods of
3640+
similar names. These use the actual numerical values of the
3641+
index. For more information on their behavior, see the
3642+
`scipy documentation
3643+
<http://docs.scipy.org/doc/scipy/reference/interpolate.html#univariate-interpolation>`__
3644+
and `tutorial documentation
3645+
<http://docs.scipy.org/doc/scipy/reference/tutorial/interpolate.html>`__
36443646
* 'from_derivatives' refers to BPoly.from_derivatives which
3645-
replaces 'piecewise_polynomial' interpolation method in scipy 0.18
3647+
replaces 'piecewise_polynomial' interpolation method in
3648+
scipy 0.18
36463649
36473650
.. versionadded:: 0.18.1
36483651
@@ -3656,7 +3659,7 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None,
36563659
* 1: fill row-by-row
36573660
limit : int, default None.
36583661
Maximum number of consecutive NaNs to fill.
3659-
limit_direction : {'forward', 'backward', 'both'}, defaults to 'forward'
3662+
limit_direction : {'forward', 'backward', 'both'}, default 'forward'
36603663
If limit is specified, consecutive NaNs will be filled in this
36613664
direction.
36623665
@@ -4159,6 +4162,9 @@ def resample(self, rule, how=None, axis=0, fill_method=None, closed=None,
41594162
41604163
.. versionadded:: 0.19.0
41614164
4165+
Notes
4166+
-----
4167+
41624168
To learn more about the offset strings, please see `this link
41634169
<http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases>`__.
41644170
@@ -4346,7 +4352,7 @@ def rank(self, axis=0, method='average', numeric_only=None,
43464352
43474353
Parameters
43484354
----------
4349-
axis: {0 or 'index', 1 or 'columns'}, default 0
4355+
axis : {0 or 'index', 1 or 'columns'}, default 0
43504356
index to direct ranking
43514357
method : {'average', 'min', 'max', 'first', 'dense'}
43524358
* average: average rank of group

pandas/core/indexing.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1814,7 +1814,9 @@ def check_bool_indexer(ax, key):
18141814
result = result.reindex(ax)
18151815
mask = isnull(result._values)
18161816
if mask.any():
1817-
raise IndexingError('Unalignable boolean Series key provided')
1817+
raise IndexingError('Unalignable boolean Series provided as '
1818+
'indexer (index of the boolean Series and of '
1819+
'the indexed object do not match')
18181820
result = result.astype(bool)._values
18191821
elif is_sparse(result):
18201822
result = result.to_dense()

pandas/core/ops.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1006,7 +1006,7 @@ def wrapper(self, other):
10061006
10071007
Parameters
10081008
----------
1009-
other: Series or scalar value
1009+
other : Series or scalar value
10101010
fill_value : None or float value, default None (NaN)
10111011
Fill missing (NaN) values with this value. If both Series are
10121012
missing, the result will be missing

pandas/core/series.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2033,9 +2033,9 @@ def reorder_levels(self, order):
20332033
20342034
Parameters
20352035
----------
2036-
order: list of int representing new level order.
2036+
order : list of int representing new level order.
20372037
(reference level by number or key)
2038-
axis: where to reorder levels
2038+
axis : where to reorder levels
20392039
20402040
Returns
20412041
-------

pandas/indexes/multi.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
from pandas.core.common import (_values_from_object,
2626
is_bool_indexer,
2727
is_null_slice,
28-
PerformanceWarning)
28+
PerformanceWarning,
29+
UnsortedIndexError)
2930

3031

3132
from pandas.core.base import FrozenList
@@ -1936,9 +1937,10 @@ def get_locs(self, tup):
19361937

19371938
# must be lexsorted to at least as many levels
19381939
if not self.is_lexsorted_for_tuple(tup):
1939-
raise KeyError('MultiIndex Slicing requires the index to be fully '
1940-
'lexsorted tuple len ({0}), lexsort depth '
1941-
'({1})'.format(len(tup), self.lexsort_depth))
1940+
raise UnsortedIndexError('MultiIndex Slicing requires the index '
1941+
'to be fully lexsorted tuple len ({0}), '
1942+
'lexsort depth ({1})'
1943+
.format(len(tup), self.lexsort_depth))
19421944

19431945
# indexer
19441946
# this is the list of all values that we want to select

pandas/io/tests/parser/common.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1453,7 +1453,7 @@ def test_as_recarray(self):
14531453
FutureWarning, check_stacklevel=False):
14541454
data = 'a,b\n1,a\n2,b'
14551455
expected = np.array([(1, 'a'), (2, 'b')],
1456-
dtype=[('a', '<i8'), ('b', 'O')])
1456+
dtype=[('a', '=i8'), ('b', 'O')])
14571457
out = self.read_csv(StringIO(data), as_recarray=True)
14581458
tm.assert_numpy_array_equal(out, expected)
14591459

@@ -1462,7 +1462,7 @@ def test_as_recarray(self):
14621462
FutureWarning, check_stacklevel=False):
14631463
data = 'a,b\n1,a\n2,b'
14641464
expected = np.array([(1, 'a'), (2, 'b')],
1465-
dtype=[('a', '<i8'), ('b', 'O')])
1465+
dtype=[('a', '=i8'), ('b', 'O')])
14661466
out = self.read_csv(StringIO(data), as_recarray=True, index_col=0)
14671467
tm.assert_numpy_array_equal(out, expected)
14681468

@@ -1471,7 +1471,7 @@ def test_as_recarray(self):
14711471
FutureWarning, check_stacklevel=False):
14721472
data = '1,a\n2,b'
14731473
expected = np.array([(1, 'a'), (2, 'b')],
1474-
dtype=[('a', '<i8'), ('b', 'O')])
1474+
dtype=[('a', '=i8'), ('b', 'O')])
14751475
out = self.read_csv(StringIO(data), names=['a', 'b'],
14761476
header=None, as_recarray=True)
14771477
tm.assert_numpy_array_equal(out, expected)
@@ -1482,15 +1482,15 @@ def test_as_recarray(self):
14821482
FutureWarning, check_stacklevel=False):
14831483
data = 'b,a\n1,a\n2,b'
14841484
expected = np.array([(1, 'a'), (2, 'b')],
1485-
dtype=[('b', '<i8'), ('a', 'O')])
1485+
dtype=[('b', '=i8'), ('a', 'O')])
14861486
out = self.read_csv(StringIO(data), as_recarray=True)
14871487
tm.assert_numpy_array_equal(out, expected)
14881488

14891489
# overrides the squeeze parameter
14901490
with tm.assert_produces_warning(
14911491
FutureWarning, check_stacklevel=False):
14921492
data = 'a\n1'
1493-
expected = np.array([(1,)], dtype=[('a', '<i8')])
1493+
expected = np.array([(1,)], dtype=[('a', '=i8')])
14941494
out = self.read_csv(StringIO(data), as_recarray=True, squeeze=True)
14951495
tm.assert_numpy_array_equal(out, expected)
14961496

@@ -1500,7 +1500,7 @@ def test_as_recarray(self):
15001500
data = 'a,b\n1,a\n2,b'
15011501
conv = lambda x: int(x) + 1
15021502
expected = np.array([(2, 'a'), (3, 'b')],
1503-
dtype=[('a', '<i8'), ('b', 'O')])
1503+
dtype=[('a', '=i8'), ('b', 'O')])
15041504
out = self.read_csv(StringIO(data), as_recarray=True,
15051505
converters={'a': conv})
15061506
tm.assert_numpy_array_equal(out, expected)
@@ -1509,7 +1509,7 @@ def test_as_recarray(self):
15091509
with tm.assert_produces_warning(
15101510
FutureWarning, check_stacklevel=False):
15111511
data = 'a,b\n1,a\n2,b'
1512-
expected = np.array([(1,), (2,)], dtype=[('a', '<i8')])
1512+
expected = np.array([(1,), (2,)], dtype=[('a', '=i8')])
15131513
out = self.read_csv(StringIO(data), as_recarray=True,
15141514
usecols=['a'])
15151515
tm.assert_numpy_array_equal(out, expected)

pandas/tests/indexes/test_multi.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from pandas import (DataFrame, date_range, period_range, MultiIndex, Index,
1010
CategoricalIndex, compat)
11-
from pandas.core.common import PerformanceWarning
11+
from pandas.core.common import PerformanceWarning, UnsortedIndexError
1212
from pandas.indexes.base import InvalidIndexError
1313
from pandas.compat import range, lrange, u, PY3, long, lzip
1414

@@ -2535,3 +2535,19 @@ def test_dropna(self):
25352535
msg = "invalid how option: xxx"
25362536
with tm.assertRaisesRegexp(ValueError, msg):
25372537
idx.dropna(how='xxx')
2538+
2539+
def test_unsortedindex(self):
2540+
# GH 11897
2541+
mi = pd.MultiIndex.from_tuples([('z', 'a'), ('x', 'a'), ('y', 'b'),
2542+
('x', 'b'), ('y', 'a'), ('z', 'b')],
2543+
names=['one', 'two'])
2544+
df = pd.DataFrame([[i, 10 * i] for i in lrange(6)], index=mi,
2545+
columns=['one', 'two'])
2546+
2547+
with assertRaises(UnsortedIndexError):
2548+
df.loc(axis=0)['z', :]
2549+
df.sort_index(inplace=True)
2550+
self.assertEqual(len(df.loc(axis=0)['z', :]), 2)
2551+
2552+
with assertRaises(KeyError):
2553+
df.loc(axis=0)['q', :]

pandas/tests/indexing/test_indexing.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
MultiIndex, Timestamp, Timedelta)
2424
from pandas.formats.printing import pprint_thing
2525
from pandas import concat
26-
from pandas.core.common import PerformanceWarning
26+
from pandas.core.common import PerformanceWarning, UnsortedIndexError
2727

2828
import pandas.util.testing as tm
2929
from pandas import date_range
@@ -2230,7 +2230,7 @@ def f():
22302230
df = df.sortlevel(level=1, axis=0)
22312231
self.assertEqual(df.index.lexsort_depth, 0)
22322232
with tm.assertRaisesRegexp(
2233-
KeyError,
2233+
UnsortedIndexError,
22342234
'MultiIndex Slicing requires the index to be fully '
22352235
r'lexsorted tuple len \(2\), lexsort depth \(0\)'):
22362236
df.loc[(slice(None), df.loc[:, ('a', 'bar')] > 5), :]
@@ -2417,7 +2417,7 @@ def test_per_axis_per_level_doc_examples(self):
24172417
def f():
24182418
df.loc['A1', (slice(None), 'foo')]
24192419

2420-
self.assertRaises(KeyError, f)
2420+
self.assertRaises(UnsortedIndexError, f)
24212421
df = df.sortlevel(axis=1)
24222422

24232423
# slicing
@@ -3480,8 +3480,12 @@ def test_iloc_mask(self):
34803480
('index', '.loc'): '0b11',
34813481
('index', '.iloc'): ('iLocation based boolean indexing '
34823482
'cannot use an indexable as a mask'),
3483-
('locs', ''): 'Unalignable boolean Series key provided',
3484-
('locs', '.loc'): 'Unalignable boolean Series key provided',
3483+
('locs', ''): 'Unalignable boolean Series provided as indexer '
3484+
'(index of the boolean Series and of the indexed '
3485+
'object do not match',
3486+
('locs', '.loc'): 'Unalignable boolean Series provided as indexer '
3487+
'(index of the boolean Series and of the '
3488+
'indexed object do not match',
34853489
('locs', '.iloc'): ('iLocation based boolean indexing on an '
34863490
'integer type is not available'),
34873491
}

pandas/tests/plotting/test_misc.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,11 @@
1616
from pandas.tests.plotting.common import (TestPlotBase, _check_plot_works,
1717
_ok_for_gaussian_kde)
1818

19-
2019
""" Test cases for misc plot functions """
2120

2221

2322
@tm.mplskip
2423
class TestSeriesPlots(TestPlotBase):
25-
2624
def setUp(self):
2725
TestPlotBase.setUp(self)
2826
import matplotlib as mpl
@@ -54,7 +52,6 @@ def test_bootstrap_plot(self):
5452

5553
@tm.mplskip
5654
class TestDataFramePlots(TestPlotBase):
57-
5855
@slow
5956
def test_scatter_plot_legacy(self):
6057
tm._skip_if_no_scipy()
@@ -277,6 +274,32 @@ def test_radviz(self):
277274
handles, labels = ax.get_legend_handles_labels()
278275
self._check_colors(handles, facecolors=colors)
279276

277+
@slow
278+
def test_subplot_titles(self):
279+
df = self.iris.drop('Name', axis=1).head()
280+
# Use the column names as the subplot titles
281+
title = list(df.columns)
282+
283+
# Case len(title) == len(df)
284+
plot = df.plot(subplots=True, title=title)
285+
self.assertEqual([p.get_title() for p in plot], title)
286+
287+
# Case len(title) > len(df)
288+
self.assertRaises(ValueError, df.plot, subplots=True,
289+
title=title + ["kittens > puppies"])
290+
291+
# Case len(title) < len(df)
292+
self.assertRaises(ValueError, df.plot, subplots=True, title=title[:2])
293+
294+
# Case subplots=False and title is of type list
295+
self.assertRaises(ValueError, df.plot, subplots=False, title=title)
296+
297+
# Case df with 3 numeric columns but layout of (2,2)
298+
plot = df.drop('SepalWidth', axis=1).plot(subplots=True, layout=(2, 2),
299+
title=title[:-1])
300+
title_list = [ax.get_title() for sublist in plot for ax in sublist]
301+
self.assertEqual(title_list, title[:3] + [''])
302+
280303

281304
if __name__ == '__main__':
282305
nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],

pandas/tests/series/test_datetime_values.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ def test_strftime(self):
326326
period_index = period_range('20150301', periods=5)
327327
result = period_index.strftime("%Y/%m/%d")
328328
expected = np.array(['2015/03/01', '2015/03/02', '2015/03/03',
329-
'2015/03/04', '2015/03/05'], dtype='<U10')
329+
'2015/03/04', '2015/03/05'], dtype='=U10')
330330
self.assert_numpy_array_equal(result, expected)
331331

332332
s = Series([datetime(2013, 1, 1, 2, 32, 59), datetime(2013, 1, 2, 14,

0 commit comments

Comments
 (0)