Skip to content

Commit 618c253

Browse files
committed
Move replace back to generic.py
1 parent d3f9a4a commit 618c253

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

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

34203252
@Appender(_shared_docs['shift'] % _shared_doc_kwargs)
34213253
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__))
@@ -4354,6 +4358,159 @@ def bfill(self, axis=None, inplace=False, limit=None, downcast=None):
43544358
return self.fillna(method='bfill', axis=axis, inplace=inplace,
43554359
limit=limit, downcast=downcast)
43564360

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

0 commit comments

Comments
 (0)