Skip to content

Commit 7142db6

Browse files
committed
Move replace back to generic.py
1 parent aeb3763 commit 7142db6

File tree

3 files changed

+167
-346
lines changed

3 files changed

+167
-346
lines changed

pandas/core/frame.py

+5-173
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

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

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

pandas/core/generic.py

+157
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)