Skip to content

Commit 9b8f2af

Browse files
author
chris
committed
DEPR: Deprecate is_copy (pandas-dev#18801)
- Renamed 'is_copy' attribute to '_is_copy' for internal use - Setup getter and setter for 'is_copy' - Added tests for deprecation warning
1 parent b5f1e71 commit 9b8f2af

File tree

8 files changed

+64
-39
lines changed

8 files changed

+64
-39
lines changed

doc/source/whatsnew/v0.22.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ Deprecations
204204
- ``DataFrame.as_matrix`` is deprecated. Use ``DataFrame.values`` instead (:issue:`18458`).
205205
- ``Series.asobject``, ``DatetimeIndex.asobject``, ``PeriodIndex.asobject`` and ``TimeDeltaIndex.asobject`` have been deprecated. Use ``.astype(object)`` instead (:issue:`18572`)
206206
- ``Series.valid`` is deprecated. Use :meth:`Series.dropna` instead (:issue:`18800`).
207+
- The ``is_copy`` attribute is deprecated and will be removed in a future version (:issue:`18801`).
207208

208209
.. _whatsnew_0220.prior_deprecations:
209210

pandas/core/generic.py

+27-15
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ class NDFrame(PandasObject, SelectionMixin):
108108
axes : list
109109
copy : boolean, default False
110110
"""
111-
_internal_names = ['_data', '_cacher', '_item_cache', '_cache', 'is_copy',
111+
_internal_names = ['_data', '_cacher', '_item_cache', '_cache', '_is_copy',
112112
'_subtyp', '_name', '_index', '_default_kind',
113113
'_default_fill_value', '_metadata', '__array_struct__',
114114
'__array_interface__']
@@ -117,7 +117,7 @@ class NDFrame(PandasObject, SelectionMixin):
117117
_deprecations = frozenset(['as_blocks', 'blocks',
118118
'consolidate', 'convert_objects'])
119119
_metadata = []
120-
is_copy = None
120+
_is_copy = None
121121

122122
def __init__(self, data, axes=None, copy=False, dtype=None,
123123
fastpath=False):
@@ -132,10 +132,22 @@ def __init__(self, data, axes=None, copy=False, dtype=None,
132132
for i, ax in enumerate(axes):
133133
data = data.reindex_axis(ax, axis=i)
134134

135-
object.__setattr__(self, 'is_copy', None)
135+
object.__setattr__(self, '_is_copy', None)
136136
object.__setattr__(self, '_data', data)
137137
object.__setattr__(self, '_item_cache', {})
138138

139+
@property
140+
def is_copy(self):
141+
warnings.warn("Attribute 'is_copy' is deprecated and will be removed "
142+
"in a future version.", FutureWarning, stacklevel=2)
143+
return self._is_copy
144+
145+
@is_copy.setter
146+
def is_copy(self, msg):
147+
warnings.warn("Attribute 'is_copy' is deprecated and will be removed "
148+
"in a future version.", FutureWarning, stacklevel=2)
149+
self._is_copy = msg
150+
139151
def _repr_data_resource_(self):
140152
"""
141153
Not a real Jupyter special repr method, but we use the same
@@ -2153,7 +2165,7 @@ def _get_item_cache(self, item):
21532165
res._set_as_cached(item, self)
21542166

21552167
# for a chain
2156-
res.is_copy = self.is_copy
2168+
res._is_copy = self._is_copy
21572169
return res
21582170

21592171
def _set_as_cached(self, item, cacher):
@@ -2264,12 +2276,12 @@ def _set_item(self, key, value):
22642276

22652277
def _set_is_copy(self, ref=None, copy=True):
22662278
if not copy:
2267-
self.is_copy = None
2279+
self._is_copy = None
22682280
else:
22692281
if ref is not None:
2270-
self.is_copy = weakref.ref(ref)
2282+
self._is_copy = weakref.ref(ref)
22712283
else:
2272-
self.is_copy = None
2284+
self._is_copy = None
22732285

22742286
def _check_is_chained_assignment_possible(self):
22752287
"""
@@ -2288,7 +2300,7 @@ def _check_is_chained_assignment_possible(self):
22882300
self._check_setitem_copy(stacklevel=4, t='referant',
22892301
force=True)
22902302
return True
2291-
elif self.is_copy:
2303+
elif self._is_copy:
22922304
self._check_setitem_copy(stacklevel=4, t='referant')
22932305
return False
22942306

@@ -2323,7 +2335,7 @@ def _check_setitem_copy(self, stacklevel=4, t='setting', force=False):
23232335
23242336
"""
23252337

2326-
if force or self.is_copy:
2338+
if force or self._is_copy:
23272339

23282340
value = config.get_option('mode.chained_assignment')
23292341
if value is None:
@@ -2333,23 +2345,23 @@ def _check_setitem_copy(self, stacklevel=4, t='setting', force=False):
23332345
# the copy weakref
23342346
try:
23352347
gc.collect(2)
2336-
if not gc.get_referents(self.is_copy()):
2337-
self.is_copy = None
2348+
if not gc.get_referents(self._is_copy()):
2349+
self._is_copy = None
23382350
return
23392351
except Exception:
23402352
pass
23412353

23422354
# we might be a false positive
23432355
try:
2344-
if self.is_copy().shape == self.shape:
2345-
self.is_copy = None
2356+
if self._is_copy().shape == self.shape:
2357+
self._is_copy = None
23462358
return
23472359
except Exception:
23482360
pass
23492361

23502362
# a custom message
2351-
if isinstance(self.is_copy, string_types):
2352-
t = self.is_copy
2363+
if isinstance(self._is_copy, string_types):
2364+
t = self._is_copy
23532365

23542366
elif t == 'referant':
23552367
t = ("\n"

pandas/core/indexes/accessors.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,9 @@ def _delegate_property_get(self, name):
113113
result = Series(result, index=self.index, name=self.name)
114114

115115
# setting this object will show a SettingWithCopyWarning/Error
116-
result.is_copy = ("modifications to a property of a datetimelike "
117-
"object are not supported and are discarded. "
118-
"Change values on the original.")
116+
result._is_copy = ("modifications to a property of a datetimelike "
117+
"object are not supported and are discarded. "
118+
"Change values on the original.")
119119

120120
return result
121121

@@ -136,9 +136,9 @@ def _delegate_method(self, name, *args, **kwargs):
136136
result = Series(result, index=self.index, name=self.name)
137137

138138
# setting this object will show a SettingWithCopyWarning/Error
139-
result.is_copy = ("modifications to a method of a datetimelike object "
140-
"are not supported and are discarded. Change "
141-
"values on the original.")
139+
result._is_copy = ("modifications to a method of a datetimelike "
140+
"object are not supported and are discarded. "
141+
"Change values on the original.")
142142

143143
return result
144144

pandas/core/indexing.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ def _setitem_with_indexer(self, indexer, value):
366366
labels = index.insert(len(index), key)
367367
self.obj._data = self.obj.reindex(labels, axis=i)._data
368368
self.obj._maybe_update_cacher(clear=True)
369-
self.obj.is_copy = None
369+
self.obj._is_copy = None
370370

371371
nindexer.append(labels.get_loc(key))
372372

pandas/tests/indexes/test_multi.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -475,10 +475,10 @@ def test_set_value_keeps_names(self):
475475
columns=['one', 'two', 'three', 'four'],
476476
index=idx)
477477
df = df.sort_index()
478-
assert df.is_copy is None
478+
assert df._is_copy is None
479479
assert df.index.names == ('Name', 'Number')
480480
df.at[('grethe', '4'), 'one'] = 99.34
481-
assert df.is_copy is None
481+
assert df._is_copy is None
482482
assert df.index.names == ('Name', 'Number')
483483

484484
def test_copy_names(self):

pandas/tests/indexing/test_chaining_and_caching.py

+25-13
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from pandas import (compat, DataFrame, option_context,
99
Series, MultiIndex, date_range, Timestamp)
1010
from pandas.util import testing as tm
11+
from pandas.core.common import SettingWithCopyError, SettingWithCopyWarning
1112

1213

1314
class TestCaching(object):
@@ -136,7 +137,7 @@ def test_detect_chained_assignment(self):
136137
expected = DataFrame([[-5, 1], [-6, 3]], columns=list('AB'))
137138
df = DataFrame(np.arange(4).reshape(2, 2),
138139
columns=list('AB'), dtype='int64')
139-
assert df.is_copy is None
140+
assert df._is_copy is None
140141

141142
df['A'][0] = -5
142143
df['A'][1] = -6
@@ -145,15 +146,15 @@ def test_detect_chained_assignment(self):
145146
# test with the chaining
146147
df = DataFrame({'A': Series(range(2), dtype='int64'),
147148
'B': np.array(np.arange(2, 4), dtype=np.float64)})
148-
assert df.is_copy is None
149+
assert df._is_copy is None
149150

150151
with pytest.raises(com.SettingWithCopyError):
151152
df['A'][0] = -5
152153

153154
with pytest.raises(com.SettingWithCopyError):
154155
df['A'][1] = np.nan
155156

156-
assert df['A'].is_copy is None
157+
assert df['A']._is_copy is None
157158

158159
# Using a copy (the chain), fails
159160
df = DataFrame({'A': Series(range(2), dtype='int64'),
@@ -166,7 +167,7 @@ def test_detect_chained_assignment(self):
166167
df = DataFrame({'a': ['one', 'one', 'two', 'three',
167168
'two', 'one', 'six'],
168169
'c': Series(range(7), dtype='int64')})
169-
assert df.is_copy is None
170+
assert df._is_copy is None
170171

171172
with pytest.raises(com.SettingWithCopyError):
172173
indexer = df.a.str.startswith('o')
@@ -186,7 +187,7 @@ def test_detect_chained_assignment(self):
186187

187188
# gh-5475: Make sure that is_copy is picked up reconstruction
188189
df = DataFrame({"A": [1, 2]})
189-
assert df.is_copy is None
190+
assert df._is_copy is None
190191

191192
with tm.ensure_clean('__tmp__pickle') as path:
192193
df.to_pickle(path)
@@ -211,39 +212,39 @@ def random_text(nobs=100):
211212

212213
# Always a copy
213214
x = df.iloc[[0, 1, 2]]
214-
assert x.is_copy is not None
215+
assert x._is_copy is not None
215216

216217
x = df.iloc[[0, 1, 2, 4]]
217-
assert x.is_copy is not None
218+
assert x._is_copy is not None
218219

219220
# Explicitly copy
220221
indexer = df.letters.apply(lambda x: len(x) > 10)
221222
df = df.loc[indexer].copy()
222223

223-
assert df.is_copy is None
224+
assert df._is_copy is None
224225
df['letters'] = df['letters'].apply(str.lower)
225226

226227
# Implicitly take
227228
df = random_text(100000)
228229
indexer = df.letters.apply(lambda x: len(x) > 10)
229230
df = df.loc[indexer]
230231

231-
assert df.is_copy is not None
232+
assert df._is_copy is not None
232233
df['letters'] = df['letters'].apply(str.lower)
233234

234235
# Implicitly take 2
235236
df = random_text(100000)
236237
indexer = df.letters.apply(lambda x: len(x) > 10)
237238

238239
df = df.loc[indexer]
239-
assert df.is_copy is not None
240+
assert df._is_copy is not None
240241
df.loc[:, 'letters'] = df['letters'].apply(str.lower)
241242

242243
# Should be ok even though it's a copy!
243-
assert df.is_copy is None
244+
assert df._is_copy is None
244245

245246
df['letters'] = df['letters'].apply(str.lower)
246-
assert df.is_copy is None
247+
assert df._is_copy is None
247248

248249
df = random_text(100000)
249250
indexer = df.letters.apply(lambda x: len(x) > 10)
@@ -252,7 +253,7 @@ def random_text(nobs=100):
252253

253254
# an identical take, so no copy
254255
df = DataFrame({'a': [1]}).dropna()
255-
assert df.is_copy is None
256+
assert df._is_copy is None
256257
df['a'] += 1
257258

258259
# Inplace ops, originally from:
@@ -418,3 +419,14 @@ def test_cache_updating(self):
418419
tm.assert_frame_equal(df, expected)
419420
expected = Series([0, 0, 0, 2, 0], name='f')
420421
tm.assert_series_equal(df.f, expected)
422+
423+
def test_deprecate_is_copy(self):
424+
#GH18801
425+
df = DataFrame({"A": [1, 2, 3]})
426+
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
427+
# getter
428+
is_copy = df.is_copy
429+
430+
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
431+
# setter
432+
df.is_copy = "test deprecated is_copy"

pandas/tests/test_panel.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ def test_xs(self):
607607
# Mixed-type yields a copy.
608608
self.panel['strings'] = 'foo'
609609
result = self.panel.xs('D', axis=2)
610-
assert result.is_copy is not None
610+
assert result._is_copy is not None
611611

612612
def test_getitem_fancy_labels(self):
613613
with catch_warnings(record=True):

pandas/tests/test_panel4d.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ def test_xs(self):
510510
with catch_warnings(record=True):
511511
result = self.panel4d.xs('D', axis=3)
512512

513-
assert result.is_copy is not None
513+
assert result._is_copy is not None
514514

515515
def test_getitem_fancy_labels(self):
516516
with catch_warnings(record=True):

0 commit comments

Comments
 (0)