Skip to content

Commit a1db92c

Browse files
committed
Move replace back to generic.py
1 parent f1f11e3 commit a1db92c

File tree

3 files changed

+167
-346
lines changed

3 files changed

+167
-346
lines changed

pandas/core/frame.py

Lines changed: 5 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -238,30 +238,6 @@
238238
239239
"""
240240

241-
def _single_replace(self, to_replace, method, inplace, limit):
242-
if self.ndim != 1:
243-
raise TypeError('cannot replace {0} with method {1} on a {2}'
244-
.format(to_replace, method, type(self).__name__))
245-
246-
orig_dtype = self.dtype
247-
result = self if inplace else self.copy()
248-
fill_f = missing.get_fill_func(method)
249-
250-
mask = missing.mask_missing(result.values, to_replace)
251-
values = fill_f(result.values, limit=limit, mask=mask)
252-
253-
if values.dtype == orig_dtype and inplace:
254-
return
255-
256-
result = pd.Series(values, index=self.index,
257-
dtype=self.dtype).__finalize__(self)
258-
259-
if inplace:
260-
self._update_inplace(result._data)
261-
return
262-
263-
return result
264-
265241
# -----------------------------------------------------------------------
266242
# DataFrame class
267243

@@ -3189,8 +3165,8 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None,
31893165
31903166
See Also
31913167
--------
3192-
:func:`DataFrame.fillna` : Fill NA/NaN values
3193-
:func:`DataFrame.where` : Replace values based on boolean condition
3168+
DataFrame.fillna : Fill NA/NaN values
3169+
DataFrame.where : Replace values based on boolean condition
31943170
31953171
Returns
31963172
-------
@@ -3316,153 +3292,9 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None,
33163292
the correct type for replacement.
33173293
33183294
"""
3319-
inplace = validate_bool_kwarg(inplace, 'inplace')
3320-
if not is_bool(regex) and to_replace is not None:
3321-
raise AssertionError("'to_replace' must be 'None' if 'regex' is "
3322-
"not a bool")
3323-
if axis is not None:
3324-
warnings.warn('the "axis" argument is deprecated '
3325-
'and will be removed in'
3326-
'v0.13; this argument has no effect')
3327-
3328-
self._consolidate_inplace()
3329-
3330-
if value is None:
3331-
# passing a single value that is scalar like
3332-
# when value is None (GH5319), for compat
3333-
if not is_dict_like(to_replace) and not is_dict_like(regex):
3334-
to_replace = [to_replace]
3335-
3336-
if isinstance(to_replace, (tuple, list)):
3337-
return _single_replace(self, to_replace, method, inplace,
3338-
limit)
3339-
3340-
if not is_dict_like(to_replace):
3341-
if not is_dict_like(regex):
3342-
raise TypeError('If "to_replace" and "value" are both None'
3343-
' and "to_replace" is not a list, then '
3344-
'regex must be a mapping')
3345-
to_replace = regex
3346-
regex = True
3347-
3348-
items = list(compat.iteritems(to_replace))
3349-
keys, values = lzip(*items) or ([], [])
3350-
3351-
are_mappings = [is_dict_like(v) for v in values]
3352-
3353-
if any(are_mappings):
3354-
if not all(are_mappings):
3355-
raise TypeError("If a nested mapping is passed, all values"
3356-
" of the top level mapping must be "
3357-
"mappings")
3358-
# passed a nested dict/Series
3359-
to_rep_dict = {}
3360-
value_dict = {}
3361-
3362-
for k, v in items:
3363-
keys, values = lzip(*v.items()) or ([], [])
3364-
if set(keys) & set(values):
3365-
raise ValueError("Replacement not allowed with "
3366-
"overlapping keys and values")
3367-
to_rep_dict[k] = list(keys)
3368-
value_dict[k] = list(values)
3369-
3370-
to_replace, value = to_rep_dict, value_dict
3371-
else:
3372-
to_replace, value = keys, values
3373-
3374-
return self.replace(to_replace, value, inplace=inplace,
3375-
limit=limit, regex=regex)
3376-
else:
3377-
3378-
# need a non-zero len on all axes
3379-
for a in self._AXIS_ORDERS:
3380-
if not len(self._get_axis(a)):
3381-
return self
3382-
3383-
new_data = self._data
3384-
if is_dict_like(to_replace):
3385-
if is_dict_like(value): # {'A' : NA} -> {'A' : 0}
3386-
res = self if inplace else self.copy()
3387-
for c, src in compat.iteritems(to_replace):
3388-
if c in value and c in self:
3389-
# object conversion is handled in
3390-
# series.replace which is called recursivelly
3391-
res[c] = res[c].replace(to_replace=src,
3392-
value=value[c],
3393-
inplace=False,
3394-
regex=regex)
3395-
return None if inplace else res
3396-
3397-
# {'A': NA} -> 0
3398-
elif not is_list_like(value):
3399-
keys = [(k, src) for k, src in compat.iteritems(to_replace)
3400-
if k in self]
3401-
keys_len = len(keys) - 1
3402-
for i, (k, src) in enumerate(keys):
3403-
convert = i == keys_len
3404-
new_data = new_data.replace(to_replace=src,
3405-
value=value,
3406-
filter=[k],
3407-
inplace=inplace,
3408-
regex=regex,
3409-
convert=convert)
3410-
else:
3411-
raise TypeError('value argument must be scalar, dict, or '
3412-
'Series')
3413-
3414-
elif is_list_like(to_replace): # [NA, ''] -> [0, 'missing']
3415-
if is_list_like(value):
3416-
if len(to_replace) != len(value):
3417-
raise ValueError('Replacement lists must match '
3418-
'in length. Expecting %d got %d ' %
3419-
(len(to_replace), len(value)))
3420-
3421-
new_data = self._data.replace_list(src_list=to_replace,
3422-
dest_list=value,
3423-
inplace=inplace,
3424-
regex=regex)
3425-
3426-
else: # [NA, ''] -> 0
3427-
new_data = self._data.replace(to_replace=to_replace,
3428-
value=value, inplace=inplace,
3429-
regex=regex)
3430-
elif to_replace is None:
3431-
if not (is_re_compilable(regex) or
3432-
is_list_like(regex) or is_dict_like(regex)):
3433-
raise TypeError("'regex' must be a string or a compiled "
3434-
"regular expression or a list or dict of "
3435-
"strings or regular expressions, you "
3436-
"passed a"
3437-
" {0!r}".format(type(regex).__name__))
3438-
return self.replace(regex, value, inplace=inplace, limit=limit,
3439-
regex=True)
3440-
else:
3441-
3442-
# dest iterable dict-like
3443-
if is_dict_like(value): # NA -> {'A' : 0, 'B' : -1}
3444-
new_data = self._data
3445-
3446-
for k, v in compat.iteritems(value):
3447-
if k in self:
3448-
new_data = new_data.replace(to_replace=to_replace,
3449-
value=v, filter=[k],
3450-
inplace=inplace,
3451-
regex=regex)
3452-
3453-
elif not is_list_like(value): # NA -> 0
3454-
new_data = self._data.replace(to_replace=to_replace,
3455-
value=value, inplace=inplace,
3456-
regex=regex)
3457-
else:
3458-
msg = ('Invalid "to_replace" type: '
3459-
'{0!r}').format(type(to_replace).__name__)
3460-
raise TypeError(msg) # pragma: no cover
3461-
3462-
if inplace:
3463-
self._update_inplace(new_data)
3464-
else:
3465-
return self._constructor(new_data).__finalize__(self)
3295+
return super(DataFrame, self).replace(to_replace=to_replace,
3296+
value=value, inplace=inplace, limit=limit, regex=regex,
3297+
method=method, axis=axis)
34663298

34673299
@Appender(_shared_docs['shift'] % _shared_doc_kwargs)
34683300
def shift(self, periods=1, freq=None, axis=0):

pandas/core/generic.py

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@
6969

7070

7171
def _single_replace(self, to_replace, method, inplace, limit):
72+
"""
73+
Replaces values in a Series using the fill method specified when no
74+
replacement value is given in the replace method
75+
"""
7276
if self.ndim != 1:
7377
raise TypeError('cannot replace {0} with method {1} on a {2}'
7478
.format(to_replace, method, type(self).__name__))
@@ -4787,6 +4791,159 @@ def bfill(self, axis=None, inplace=False, limit=None, downcast=None):
47874791
return self.fillna(method='bfill', axis=axis, inplace=inplace,
47884792
limit=limit, downcast=downcast)
47894793

4794+
def replace(self, to_replace=None, value=None, inplace=False, limit=None,
4795+
regex=False, method='pad', axis=None):
4796+
"""
4797+
Replace values given in 'to_replace' with 'value'
4798+
"""
4799+
inplace = validate_bool_kwarg(inplace, 'inplace')
4800+
if not is_bool(regex) and to_replace is not None:
4801+
raise AssertionError("'to_replace' must be 'None' if 'regex' is "
4802+
"not a bool")
4803+
if axis is not None:
4804+
warnings.warn('the "axis" argument is deprecated '
4805+
'and will be removed in'
4806+
'v0.13; this argument has no effect')
4807+
4808+
self._consolidate_inplace()
4809+
4810+
if value is None:
4811+
# passing a single value that is scalar like
4812+
# when value is None (GH5319), for compat
4813+
if not is_dict_like(to_replace) and not is_dict_like(regex):
4814+
to_replace = [to_replace]
4815+
4816+
if isinstance(to_replace, (tuple, list)):
4817+
return _single_replace(self, to_replace, method, inplace,
4818+
limit)
4819+
4820+
if not is_dict_like(to_replace):
4821+
if not is_dict_like(regex):
4822+
raise TypeError('If "to_replace" and "value" are both None'
4823+
' and "to_replace" is not a list, then '
4824+
'regex must be a mapping')
4825+
to_replace = regex
4826+
regex = True
4827+
4828+
items = list(compat.iteritems(to_replace))
4829+
keys, values = lzip(*items) or ([], [])
4830+
4831+
are_mappings = [is_dict_like(v) for v in values]
4832+
4833+
if any(are_mappings):
4834+
if not all(are_mappings):
4835+
raise TypeError("If a nested mapping is passed, all values"
4836+
" of the top level mapping must be "
4837+
"mappings")
4838+
# passed a nested dict/Series
4839+
to_rep_dict = {}
4840+
value_dict = {}
4841+
4842+
for k, v in items:
4843+
keys, values = lzip(*v.items()) or ([], [])
4844+
if set(keys) & set(values):
4845+
raise ValueError("Replacement not allowed with "
4846+
"overlapping keys and values")
4847+
to_rep_dict[k] = list(keys)
4848+
value_dict[k] = list(values)
4849+
4850+
to_replace, value = to_rep_dict, value_dict
4851+
else:
4852+
to_replace, value = keys, values
4853+
4854+
return self.replace(to_replace, value, inplace=inplace,
4855+
limit=limit, regex=regex)
4856+
else:
4857+
4858+
# need a non-zero len on all axes
4859+
for a in self._AXIS_ORDERS:
4860+
if not len(self._get_axis(a)):
4861+
return self
4862+
4863+
new_data = self._data
4864+
if is_dict_like(to_replace):
4865+
if is_dict_like(value): # {'A' : NA} -> {'A' : 0}
4866+
res = self if inplace else self.copy()
4867+
for c, src in compat.iteritems(to_replace):
4868+
if c in value and c in self:
4869+
# object conversion is handled in
4870+
# series.replace which is called recursivelly
4871+
res[c] = res[c].replace(to_replace=src,
4872+
value=value[c],
4873+
inplace=False,
4874+
regex=regex)
4875+
return None if inplace else res
4876+
4877+
# {'A': NA} -> 0
4878+
elif not is_list_like(value):
4879+
keys = [(k, src) for k, src in compat.iteritems(to_replace)
4880+
if k in self]
4881+
keys_len = len(keys) - 1
4882+
for i, (k, src) in enumerate(keys):
4883+
convert = i == keys_len
4884+
new_data = new_data.replace(to_replace=src,
4885+
value=value,
4886+
filter=[k],
4887+
inplace=inplace,
4888+
regex=regex,
4889+
convert=convert)
4890+
else:
4891+
raise TypeError('value argument must be scalar, dict, or '
4892+
'Series')
4893+
4894+
elif is_list_like(to_replace): # [NA, ''] -> [0, 'missing']
4895+
if is_list_like(value):
4896+
if len(to_replace) != len(value):
4897+
raise ValueError('Replacement lists must match '
4898+
'in length. Expecting %d got %d ' %
4899+
(len(to_replace), len(value)))
4900+
4901+
new_data = self._data.replace_list(src_list=to_replace,
4902+
dest_list=value,
4903+
inplace=inplace,
4904+
regex=regex)
4905+
4906+
else: # [NA, ''] -> 0
4907+
new_data = self._data.replace(to_replace=to_replace,
4908+
value=value, inplace=inplace,
4909+
regex=regex)
4910+
elif to_replace is None:
4911+
if not (is_re_compilable(regex) or
4912+
is_list_like(regex) or is_dict_like(regex)):
4913+
raise TypeError("'regex' must be a string or a compiled "
4914+
"regular expression or a list or dict of "
4915+
"strings or regular expressions, you "
4916+
"passed a"
4917+
" {0!r}".format(type(regex).__name__))
4918+
return self.replace(regex, value, inplace=inplace, limit=limit,
4919+
regex=True)
4920+
else:
4921+
4922+
# dest iterable dict-like
4923+
if is_dict_like(value): # NA -> {'A' : 0, 'B' : -1}
4924+
new_data = self._data
4925+
4926+
for k, v in compat.iteritems(value):
4927+
if k in self:
4928+
new_data = new_data.replace(to_replace=to_replace,
4929+
value=v, filter=[k],
4930+
inplace=inplace,
4931+
regex=regex)
4932+
4933+
elif not is_list_like(value): # NA -> 0
4934+
new_data = self._data.replace(to_replace=to_replace,
4935+
value=value, inplace=inplace,
4936+
regex=regex)
4937+
else:
4938+
msg = ('Invalid "to_replace" type: '
4939+
'{0!r}').format(type(to_replace).__name__)
4940+
raise TypeError(msg) # pragma: no cover
4941+
4942+
if inplace:
4943+
self._update_inplace(new_data)
4944+
else:
4945+
return self._constructor(new_data).__finalize__(self)
4946+
47904947
_shared_docs['interpolate'] = """
47914948
Please note that only ``method='linear'`` is supported for
47924949
DataFrames/Series with a MultiIndex.

0 commit comments

Comments
 (0)