Skip to content

Commit 7d9e9fa

Browse files
committed
Merge pull request #5859 from jreback/series_view
rework Series.sort is_view check on underlying ndarray inplace sort (GH5856)
2 parents c57df68 + 986d7d1 commit 7d9e9fa

File tree

6 files changed

+43
-14
lines changed

6 files changed

+43
-14
lines changed

doc/source/release.rst

+3
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ New features
5656
API Changes
5757
~~~~~~~~~~~
5858

59+
- ``Series.sort`` will raise a ``ValueError`` (rather than a ``TypeError``) on sorting an
60+
object that is a view of another (:issue:`5856`, :issue:`5853`)
61+
5962
.. _release.bug_fixes-0.13.1:
6063

6164
Experimental Features

pandas/core/frame.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -1604,10 +1604,15 @@ def _ixs(self, i, axis=0, copy=False):
16041604
values = self._data.iget(i)
16051605
if not len(values):
16061606
values = np.array([np.nan] * len(self.index), dtype=object)
1607-
return self._constructor_sliced.from_array(
1607+
result = self._constructor_sliced.from_array(
16081608
values, index=self.index,
16091609
name=label, fastpath=True)
16101610

1611+
# this is a cached value, mark it so
1612+
result._set_as_cached(i, self)
1613+
1614+
return result
1615+
16111616
def iget_value(self, i, j):
16121617
return self.iat[i, j]
16131618

pandas/core/generic.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -983,9 +983,14 @@ def _get_item_cache(self, item):
983983
values = self._data.get(item)
984984
res = self._box_item_values(item, values)
985985
cache[item] = res
986-
res._cacher = (item, weakref.ref(self))
986+
res._set_as_cached(item, self)
987987
return res
988988

989+
def _set_as_cached(self, item, cacher):
990+
""" set the _cacher attribute on the calling object with
991+
a weakref to cacher """
992+
self._cacher = (item, weakref.ref(cacher))
993+
989994
def _box_item_values(self, key, values):
990995
raise NotImplementedError
991996

@@ -994,6 +999,12 @@ def _maybe_cache_changed(self, item, value):
994999
maybe it has changed """
9951000
self._data.set(item, value)
9961001

1002+
@property
1003+
def _is_cached(self):
1004+
""" boolean : return if I am cached """
1005+
cacher = getattr(self, '_cacher', None)
1006+
return cacher is not None
1007+
9971008
def _maybe_update_cacher(self, clear=False):
9981009
""" see if we need to update our parent cacher
9991010
if clear, then clear our cache """

pandas/core/series.py

+7-11
Original file line numberDiff line numberDiff line change
@@ -1664,20 +1664,16 @@ def sort(self, axis=0, kind='quicksort', order=None, ascending=True):
16641664
--------
16651665
pandas.Series.order
16661666
"""
1667-
sortedSeries = self.order(na_last=True, kind=kind,
1668-
ascending=ascending)
16691667

1670-
true_base = self.values
1671-
while true_base.base is not None:
1672-
true_base = true_base.base
1668+
# GH 5856/5863
1669+
if self._is_cached:
1670+
raise ValueError("This Series is a view of some other array, to "
1671+
"sort in-place you must create a copy")
16731672

1674-
if (true_base is not None and
1675-
(true_base.ndim != 1 or true_base.shape != self.shape)):
1676-
raise TypeError('This Series is a view of some other array, to '
1677-
'sort in-place you must create a copy')
1673+
result = self.order(na_last=True, kind=kind,
1674+
ascending=ascending)
16781675

1679-
self._data = sortedSeries._data.copy()
1680-
self.index = sortedSeries.index
1676+
self._update_inplace(result)
16811677

16821678
def sort_index(self, ascending=True):
16831679
"""

pandas/tests/test_frame.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -9547,7 +9547,7 @@ def test_sort_datetimes(self):
95479547

95489548
def test_frame_column_inplace_sort_exception(self):
95499549
s = self.frame['A']
9550-
with assertRaisesRegexp(TypeError, "This Series is a view"):
9550+
with assertRaisesRegexp(ValueError, "This Series is a view"):
95519551
s.sort()
95529552

95539553
cp = s.copy()

pandas/tests/test_indexing.py

+14
Original file line numberDiff line numberDiff line change
@@ -2019,6 +2019,20 @@ def f():
20192019
zed['eyes']['right'].fillna(value=555, inplace=True)
20202020
self.assertRaises(com.SettingWithCopyError, f)
20212021

2022+
# GH 5856/5863
2023+
# Series.sort operating on a view
2024+
df = DataFrame(np.random.randn(10,4))
2025+
s = df.iloc[:,0]
2026+
def f():
2027+
s.sort()
2028+
self.assertRaises(ValueError, f)
2029+
2030+
df = DataFrame(np.random.randn(10,4))
2031+
s = df.iloc[:,0]
2032+
s = s.order()
2033+
assert_series_equal(s,df.iloc[:,0].order())
2034+
assert_series_equal(s,df[0].order())
2035+
20222036
pd.set_option('chained_assignment','warn')
20232037

20242038
def test_float64index_slicing_bug(self):

0 commit comments

Comments
 (0)