Skip to content

Commit b021a78

Browse files
committed
Move replace back to generic.py
1 parent 48d6006 commit b021a78

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
@@ -232,30 +232,6 @@
232232
233233
"""
234234

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

@@ -3144,8 +3120,8 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None,
31443120
31453121
See Also
31463122
--------
3147-
:func:`DataFrame.fillna` : Fill NA/NaN values
3148-
:func:`DataFrame.where` : Replace values based on boolean condition
3123+
DataFrame.fillna : Fill NA/NaN values
3124+
DataFrame.where : Replace values based on boolean condition
31493125
31503126
Returns
31513127
-------
@@ -3271,153 +3247,9 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None,
32713247
the correct type for replacement.
32723248
32733249
"""
3274-
inplace = validate_bool_kwarg(inplace, 'inplace')
3275-
if not is_bool(regex) and to_replace is not None:
3276-
raise AssertionError("'to_replace' must be 'None' if 'regex' is "
3277-
"not a bool")
3278-
if axis is not None:
3279-
warnings.warn('the "axis" argument is deprecated '
3280-
'and will be removed in'
3281-
'v0.13; this argument has no effect')
3282-
3283-
self._consolidate_inplace()
3284-
3285-
if value is None:
3286-
# passing a single value that is scalar like
3287-
# when value is None (GH5319), for compat
3288-
if not is_dict_like(to_replace) and not is_dict_like(regex):
3289-
to_replace = [to_replace]
3290-
3291-
if isinstance(to_replace, (tuple, list)):
3292-
return _single_replace(self, to_replace, method, inplace,
3293-
limit)
3294-
3295-
if not is_dict_like(to_replace):
3296-
if not is_dict_like(regex):
3297-
raise TypeError('If "to_replace" and "value" are both None'
3298-
' and "to_replace" is not a list, then '
3299-
'regex must be a mapping')
3300-
to_replace = regex
3301-
regex = True
3302-
3303-
items = list(compat.iteritems(to_replace))
3304-
keys, values = lzip(*items) or ([], [])
3305-
3306-
are_mappings = [is_dict_like(v) for v in values]
3307-
3308-
if any(are_mappings):
3309-
if not all(are_mappings):
3310-
raise TypeError("If a nested mapping is passed, all values"
3311-
" of the top level mapping must be "
3312-
"mappings")
3313-
# passed a nested dict/Series
3314-
to_rep_dict = {}
3315-
value_dict = {}
3316-
3317-
for k, v in items:
3318-
keys, values = lzip(*v.items()) or ([], [])
3319-
if set(keys) & set(values):
3320-
raise ValueError("Replacement not allowed with "
3321-
"overlapping keys and values")
3322-
to_rep_dict[k] = list(keys)
3323-
value_dict[k] = list(values)
3324-
3325-
to_replace, value = to_rep_dict, value_dict
3326-
else:
3327-
to_replace, value = keys, values
3328-
3329-
return self.replace(to_replace, value, inplace=inplace,
3330-
limit=limit, regex=regex)
3331-
else:
3332-
3333-
# need a non-zero len on all axes
3334-
for a in self._AXIS_ORDERS:
3335-
if not len(self._get_axis(a)):
3336-
return self
3337-
3338-
new_data = self._data
3339-
if is_dict_like(to_replace):
3340-
if is_dict_like(value): # {'A' : NA} -> {'A' : 0}
3341-
res = self if inplace else self.copy()
3342-
for c, src in compat.iteritems(to_replace):
3343-
if c in value and c in self:
3344-
# object conversion is handled in
3345-
# series.replace which is called recursivelly
3346-
res[c] = res[c].replace(to_replace=src,
3347-
value=value[c],
3348-
inplace=False,
3349-
regex=regex)
3350-
return None if inplace else res
3351-
3352-
# {'A': NA} -> 0
3353-
elif not is_list_like(value):
3354-
keys = [(k, src) for k, src in compat.iteritems(to_replace)
3355-
if k in self]
3356-
keys_len = len(keys) - 1
3357-
for i, (k, src) in enumerate(keys):
3358-
convert = i == keys_len
3359-
new_data = new_data.replace(to_replace=src,
3360-
value=value,
3361-
filter=[k],
3362-
inplace=inplace,
3363-
regex=regex,
3364-
convert=convert)
3365-
else:
3366-
raise TypeError('value argument must be scalar, dict, or '
3367-
'Series')
3368-
3369-
elif is_list_like(to_replace): # [NA, ''] -> [0, 'missing']
3370-
if is_list_like(value):
3371-
if len(to_replace) != len(value):
3372-
raise ValueError('Replacement lists must match '
3373-
'in length. Expecting %d got %d ' %
3374-
(len(to_replace), len(value)))
3375-
3376-
new_data = self._data.replace_list(src_list=to_replace,
3377-
dest_list=value,
3378-
inplace=inplace,
3379-
regex=regex)
3380-
3381-
else: # [NA, ''] -> 0
3382-
new_data = self._data.replace(to_replace=to_replace,
3383-
value=value, inplace=inplace,
3384-
regex=regex)
3385-
elif to_replace is None:
3386-
if not (is_re_compilable(regex) or
3387-
is_list_like(regex) or is_dict_like(regex)):
3388-
raise TypeError("'regex' must be a string or a compiled "
3389-
"regular expression or a list or dict of "
3390-
"strings or regular expressions, you "
3391-
"passed a"
3392-
" {0!r}".format(type(regex).__name__))
3393-
return self.replace(regex, value, inplace=inplace, limit=limit,
3394-
regex=True)
3395-
else:
3396-
3397-
# dest iterable dict-like
3398-
if is_dict_like(value): # NA -> {'A' : 0, 'B' : -1}
3399-
new_data = self._data
3400-
3401-
for k, v in compat.iteritems(value):
3402-
if k in self:
3403-
new_data = new_data.replace(to_replace=to_replace,
3404-
value=v, filter=[k],
3405-
inplace=inplace,
3406-
regex=regex)
3407-
3408-
elif not is_list_like(value): # NA -> 0
3409-
new_data = self._data.replace(to_replace=to_replace,
3410-
value=value, inplace=inplace,
3411-
regex=regex)
3412-
else:
3413-
msg = ('Invalid "to_replace" type: '
3414-
'{0!r}').format(type(to_replace).__name__)
3415-
raise TypeError(msg) # pragma: no cover
3416-
3417-
if inplace:
3418-
self._update_inplace(new_data)
3419-
else:
3420-
return self._constructor(new_data).__finalize__(self)
3250+
return super(DataFrame, self).replace(to_replace=to_replace,
3251+
value=value, inplace=inplace, limit=limit, regex=regex,
3252+
method=method, axis=axis)
34213253

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

pandas/core/generic.py

+157
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@
7272

7373

7474
def _single_replace(self, to_replace, method, inplace, limit):
75+
"""
76+
Replaces values in a Series using the fill method specified when no
77+
replacement value is given in the replace method
78+
"""
7579
if self.ndim != 1:
7680
raise TypeError('cannot replace {0} with method {1} on a {2}'
7781
.format(to_replace, method, type(self).__name__))
@@ -4353,6 +4357,159 @@ def bfill(self, axis=None, inplace=False, limit=None, downcast=None):
43534357
return self.fillna(method='bfill', axis=axis, inplace=inplace,
43544358
limit=limit, downcast=downcast)
43554359

4360+
def replace(self, to_replace=None, value=None, inplace=False, limit=None,
4361+
regex=False, method='pad', axis=None):
4362+
"""
4363+
Replace values given in 'to_replace' with 'value'
4364+
"""
4365+
inplace = validate_bool_kwarg(inplace, 'inplace')
4366+
if not is_bool(regex) and to_replace is not None:
4367+
raise AssertionError("'to_replace' must be 'None' if 'regex' is "
4368+
"not a bool")
4369+
if axis is not None:
4370+
warnings.warn('the "axis" argument is deprecated '
4371+
'and will be removed in'
4372+
'v0.13; this argument has no effect')
4373+
4374+
self._consolidate_inplace()
4375+
4376+
if value is None:
4377+
# passing a single value that is scalar like
4378+
# when value is None (GH5319), for compat
4379+
if not is_dict_like(to_replace) and not is_dict_like(regex):
4380+
to_replace = [to_replace]
4381+
4382+
if isinstance(to_replace, (tuple, list)):
4383+
return _single_replace(self, to_replace, method, inplace,
4384+
limit)
4385+
4386+
if not is_dict_like(to_replace):
4387+
if not is_dict_like(regex):
4388+
raise TypeError('If "to_replace" and "value" are both None'
4389+
' and "to_replace" is not a list, then '
4390+
'regex must be a mapping')
4391+
to_replace = regex
4392+
regex = True
4393+
4394+
items = list(compat.iteritems(to_replace))
4395+
keys, values = lzip(*items) or ([], [])
4396+
4397+
are_mappings = [is_dict_like(v) for v in values]
4398+
4399+
if any(are_mappings):
4400+
if not all(are_mappings):
4401+
raise TypeError("If a nested mapping is passed, all values"
4402+
" of the top level mapping must be "
4403+
"mappings")
4404+
# passed a nested dict/Series
4405+
to_rep_dict = {}
4406+
value_dict = {}
4407+
4408+
for k, v in items:
4409+
keys, values = lzip(*v.items()) or ([], [])
4410+
if set(keys) & set(values):
4411+
raise ValueError("Replacement not allowed with "
4412+
"overlapping keys and values")
4413+
to_rep_dict[k] = list(keys)
4414+
value_dict[k] = list(values)
4415+
4416+
to_replace, value = to_rep_dict, value_dict
4417+
else:
4418+
to_replace, value = keys, values
4419+
4420+
return self.replace(to_replace, value, inplace=inplace,
4421+
limit=limit, regex=regex)
4422+
else:
4423+
4424+
# need a non-zero len on all axes
4425+
for a in self._AXIS_ORDERS:
4426+
if not len(self._get_axis(a)):
4427+
return self
4428+
4429+
new_data = self._data
4430+
if is_dict_like(to_replace):
4431+
if is_dict_like(value): # {'A' : NA} -> {'A' : 0}
4432+
res = self if inplace else self.copy()
4433+
for c, src in compat.iteritems(to_replace):
4434+
if c in value and c in self:
4435+
# object conversion is handled in
4436+
# series.replace which is called recursivelly
4437+
res[c] = res[c].replace(to_replace=src,
4438+
value=value[c],
4439+
inplace=False,
4440+
regex=regex)
4441+
return None if inplace else res
4442+
4443+
# {'A': NA} -> 0
4444+
elif not is_list_like(value):
4445+
keys = [(k, src) for k, src in compat.iteritems(to_replace)
4446+
if k in self]
4447+
keys_len = len(keys) - 1
4448+
for i, (k, src) in enumerate(keys):
4449+
convert = i == keys_len
4450+
new_data = new_data.replace(to_replace=src,
4451+
value=value,
4452+
filter=[k],
4453+
inplace=inplace,
4454+
regex=regex,
4455+
convert=convert)
4456+
else:
4457+
raise TypeError('value argument must be scalar, dict, or '
4458+
'Series')
4459+
4460+
elif is_list_like(to_replace): # [NA, ''] -> [0, 'missing']
4461+
if is_list_like(value):
4462+
if len(to_replace) != len(value):
4463+
raise ValueError('Replacement lists must match '
4464+
'in length. Expecting %d got %d ' %
4465+
(len(to_replace), len(value)))
4466+
4467+
new_data = self._data.replace_list(src_list=to_replace,
4468+
dest_list=value,
4469+
inplace=inplace,
4470+
regex=regex)
4471+
4472+
else: # [NA, ''] -> 0
4473+
new_data = self._data.replace(to_replace=to_replace,
4474+
value=value, inplace=inplace,
4475+
regex=regex)
4476+
elif to_replace is None:
4477+
if not (is_re_compilable(regex) or
4478+
is_list_like(regex) or is_dict_like(regex)):
4479+
raise TypeError("'regex' must be a string or a compiled "
4480+
"regular expression or a list or dict of "
4481+
"strings or regular expressions, you "
4482+
"passed a"
4483+
" {0!r}".format(type(regex).__name__))
4484+
return self.replace(regex, value, inplace=inplace, limit=limit,
4485+
regex=True)
4486+
else:
4487+
4488+
# dest iterable dict-like
4489+
if is_dict_like(value): # NA -> {'A' : 0, 'B' : -1}
4490+
new_data = self._data
4491+
4492+
for k, v in compat.iteritems(value):
4493+
if k in self:
4494+
new_data = new_data.replace(to_replace=to_replace,
4495+
value=v, filter=[k],
4496+
inplace=inplace,
4497+
regex=regex)
4498+
4499+
elif not is_list_like(value): # NA -> 0
4500+
new_data = self._data.replace(to_replace=to_replace,
4501+
value=value, inplace=inplace,
4502+
regex=regex)
4503+
else:
4504+
msg = ('Invalid "to_replace" type: '
4505+
'{0!r}').format(type(to_replace).__name__)
4506+
raise TypeError(msg) # pragma: no cover
4507+
4508+
if inplace:
4509+
self._update_inplace(new_data)
4510+
else:
4511+
return self._constructor(new_data).__finalize__(self)
4512+
43564513
_shared_docs['interpolate'] = """
43574514
Please note that only ``method='linear'`` is supported for
43584515
DataFrames/Series with a MultiIndex.

0 commit comments

Comments
 (0)