Skip to content

Commit

Permalink
API/DEPR: replace "raise_conflict" with "errors" for df.update (#23657)
Browse files Browse the repository at this point in the history
  • Loading branch information
h-vetinari authored and jreback committed Nov 15, 2018
1 parent ee5e79f commit e50b5fe
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 27 deletions.
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.24.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,7 @@ Deprecations
- The ``fastpath`` keyword of the different Index constructors is deprecated (:issue:`23110`).
- :meth:`Timestamp.tz_localize`, :meth:`DatetimeIndex.tz_localize`, and :meth:`Series.tz_localize` have deprecated the ``errors`` argument in favor of the ``nonexistent`` argument (:issue:`8917`)
- The class ``FrozenNDArray`` has been deprecated. When unpickling, ``FrozenNDArray`` will be unpickled to ``np.ndarray`` once this class is removed (:issue:`9031`)
- The methods :meth:`DataFrame.update` and :meth:`Panel.update` have deprecated the ``raise_conflict=False|True`` keyword in favor of ``errors='ignore'|'raise'`` (:issue:`23585`)
- Deprecated the `nthreads` keyword of :func:`pandas.read_feather` in favor of
`use_threads` to reflect the changes in pyarrow 0.11.0. (:issue:`23053`)
- :func:`pandas.read_excel` has deprecated accepting ``usecols`` as an integer. Please pass in a list of ints from 0 to ``usecols`` inclusive instead (:issue:`23527`)
Expand Down
28 changes: 22 additions & 6 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -5213,8 +5213,10 @@ def combiner(x, y):

return self.combine(other, combiner, overwrite=False)

@deprecate_kwarg(old_arg_name='raise_conflict', new_arg_name='errors',
mapping={False: 'ignore', True: 'raise'})
def update(self, other, join='left', overwrite=True, filter_func=None,
raise_conflict=False):
errors='ignore'):
"""
Modify in place using non-NA values from another DataFrame.
Expand All @@ -5238,17 +5240,28 @@ def update(self, other, join='left', overwrite=True, filter_func=None,
* False: only update values that are NA in
the original DataFrame.
filter_func : callable(1d-array) -> boolean 1d-array, optional
filter_func : callable(1d-array) -> bool 1d-array, optional
Can choose to replace values other than NA. Return True for values
that should be updated.
raise_conflict : bool, default False
If True, will raise a ValueError if the DataFrame and `other`
errors : {'raise', 'ignore'}, default 'ignore'
If 'raise', will raise a ValueError if the DataFrame and `other`
both contain non-NA data in the same place.
.. versionchanged :: 0.24.0
Changed from `raise_conflict=False|True`
to `errors='ignore'|'raise'`.
Returns
-------
None : method directly changes calling object
Raises
------
ValueError
When `raise_conflict` is True and there's overlapping non-NA data.
* When `errors='raise'` and there's overlapping non-NA data.
* When `errors` is not either `'ignore'` or `'raise'`
NotImplementedError
* If `join != 'left'`
See Also
--------
Expand Down Expand Up @@ -5319,6 +5332,9 @@ def update(self, other, join='left', overwrite=True, filter_func=None,
# TODO: Support other joins
if join != 'left': # pragma: no cover
raise NotImplementedError("Only left join is supported")
if errors not in ['ignore', 'raise']:
raise ValueError("The parameter errors must be either "
"'ignore' or 'raise'")

if not isinstance(other, DataFrame):
other = DataFrame(other)
Expand All @@ -5332,7 +5348,7 @@ def update(self, other, join='left', overwrite=True, filter_func=None,
with np.errstate(all='ignore'):
mask = ~filter_func(this) | isna(that)
else:
if raise_conflict:
if errors == 'raise':
mask_this = notna(that)
mask_that = notna(this)
if any(mask_this & mask_that):
Expand Down
49 changes: 33 additions & 16 deletions pandas/core/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
create_block_manager_from_blocks)
from pandas.core.series import Series
from pandas.core.reshape.util import cartesian_product
from pandas.util._decorators import Appender, Substitution
from pandas.util._decorators import Appender, Substitution, deprecate_kwarg
from pandas.util._validators import validate_axis_style_args

_shared_doc_kwargs = dict(
Expand Down Expand Up @@ -1235,7 +1235,12 @@ def reindex(self, *args, **kwargs):
kwargs.update(axes)
kwargs.pop('axis', None)
kwargs.pop('labels', None)
return super(Panel, self).reindex(**kwargs)

with warnings.catch_warnings():
warnings.simplefilter("ignore", FutureWarning)
# do not warn about constructing Panel when reindexing
result = super(Panel, self).reindex(**kwargs)
return result

@Substitution(**_shared_doc_kwargs)
@Appender(NDFrame.rename.__doc__)
Expand Down Expand Up @@ -1377,25 +1382,37 @@ def join(self, other, how='left', lsuffix='', rsuffix=''):
return concat([self] + list(other), axis=0, join=how,
join_axes=join_axes, verify_integrity=True)

@deprecate_kwarg(old_arg_name='raise_conflict', new_arg_name='errors',
mapping={False: 'ignore', True: 'raise'})
def update(self, other, join='left', overwrite=True, filter_func=None,
raise_conflict=False):
errors='ignore'):
"""
Modify Panel in place using non-NA values from passed
Panel, or object coercible to Panel. Aligns on items
Modify Panel in place using non-NA values from other Panel.
May also use object coercible to Panel. Will align on items.
Parameters
----------
other : Panel, or object coercible to Panel
join : How to join individual DataFrames
{'left', 'right', 'outer', 'inner'}, default 'left'
overwrite : boolean, default True
If True then overwrite values for common keys in the calling panel
filter_func : callable(1d-array) -> 1d-array<boolean>, default None
The object from which the caller will be udpated.
join : {'left', 'right', 'outer', 'inner'}, default 'left'
How individual DataFrames are joined.
overwrite : bool, default True
If True then overwrite values for common keys in the calling Panel.
filter_func : callable(1d-array) -> 1d-array<bool>, default None
Can choose to replace values other than NA. Return True for values
that should be updated
raise_conflict : bool
If True, will raise an error if a DataFrame and other both
contain data in the same place.
that should be updated.
errors : {'raise', 'ignore'}, default 'ignore'
If 'raise', will raise an error if a DataFrame and other both.
.. versionchanged :: 0.24.0
Changed from `raise_conflict=False|True`
to `errors='ignore'|'raise'`.
See Also
--------
DataFrame.update : Similar method for DataFrames.
dict.update : Similar method for dictionaries.
"""

if not isinstance(other, self._constructor):
Expand All @@ -1406,8 +1423,8 @@ def update(self, other, join='left', overwrite=True, filter_func=None,
other = other.reindex(**{axis_name: axis_values})

for frame in axis_values:
self[frame].update(other[frame], join, overwrite, filter_func,
raise_conflict)
self[frame].update(other[frame], join=join, overwrite=overwrite,
filter_func=filter_func, errors=errors)

def _get_join_index(self, other, how):
if how == 'left':
Expand Down
21 changes: 19 additions & 2 deletions pandas/tests/frame/test_combine_concat.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,17 @@ def test_update_filtered(self):
[1.5, nan, 7.]])
assert_frame_equal(df, expected)

def test_update_raise(self):
@pytest.mark.parametrize('bad_kwarg, exception, msg', [
# errors must be 'ignore' or 'raise'
({'errors': 'something'}, ValueError, 'The parameter errors must.*'),
({'join': 'inner'}, NotImplementedError, 'Only left join is supported')
])
def test_update_raise_bad_parameter(self, bad_kwarg, exception, msg):
df = DataFrame([[1.5, 1, 3.]])
with pytest.raises(exception, match=msg):
df.update(df, **bad_kwarg)

def test_update_raise_on_overlap(self):
df = DataFrame([[1.5, 1, 3.],
[1.5, nan, 3.],
[1.5, nan, 3],
Expand All @@ -322,7 +332,14 @@ def test_update_raise(self):
other = DataFrame([[2., nan],
[nan, 7]], index=[1, 3], columns=[1, 2])
with pytest.raises(ValueError, match="Data overlaps"):
df.update(other, raise_conflict=True)
df.update(other, errors='raise')

@pytest.mark.parametrize('raise_conflict', [True, False])
def test_update_deprecation(self, raise_conflict):
df = DataFrame([[1.5, 1, 3.]])
other = DataFrame()
with tm.assert_produces_warning(FutureWarning):
df.update(other, raise_conflict=raise_conflict)

def test_update_from_non_df(self):
d = {'a': Series([1, 2, 3, 4]), 'b': Series([5, 6, 7, 8])}
Expand Down
23 changes: 20 additions & 3 deletions pandas/tests/test_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -2341,16 +2341,33 @@ def test_update_filtered(self):

assert_panel_equal(pan, expected)

def test_update_raise(self):
@pytest.mark.parametrize('bad_kwarg, exception, msg', [
# errors must be 'ignore' or 'raise'
({'errors': 'something'}, ValueError, 'The parameter errors must.*'),
({'join': 'inner'}, NotImplementedError, 'Only left join is supported')
])
def test_update_raise_bad_parameter(self, bad_kwarg, exception, msg):
pan = Panel([[[1.5, np.nan, 3.]]])
with pytest.raises(exception, match=msg):
pan.update(pan, **bad_kwarg)

def test_update_raise_on_overlap(self):
pan = Panel([[[1.5, np.nan, 3.], [1.5, np.nan, 3.],
[1.5, np.nan, 3.],
[1.5, np.nan, 3.]],
[[1.5, np.nan, 3.], [1.5, np.nan, 3.],
[1.5, np.nan, 3.],
[1.5, np.nan, 3.]]])

pytest.raises(Exception, pan.update, *(pan, ),
**{'raise_conflict': True})
with pytest.raises(ValueError, match='Data overlaps'):
pan.update(pan, errors='raise')

@pytest.mark.parametrize('raise_conflict', [True, False])
def test_update_deprecation(self, raise_conflict):
pan = Panel([[[1.5, np.nan, 3.]]])
other = Panel([[[]]])
with tm.assert_produces_warning(FutureWarning):
pan.update(other, raise_conflict=raise_conflict)

def test_all_any(self):
assert (self.panel.all(axis=0).values == nanall(
Expand Down

0 comments on commit e50b5fe

Please sign in to comment.