Repository: pandas-dev/pandas

Issue:
Regression for pd.Series.replace(np.nan, inplace=True)
This code was working in pd 0.12.0, but is failing in current master ('0.12.0-955-ga96cd66'):

``` python
df.last_el_cmd.replace(np.nan, inplace=True)
```

The error:

``` python
/usr/local/epd/lib/python2.7/site-packages/pandas-0.12.0_955_ga96cd66-py2.7-linux-x86_64.egg/pandas/core/generic.pyc in replace(self, to_replace, value, inplace, limit, regex, method, axis)
   1903             if not is_dictlike(to_replace):
   1904                 if not is_dictlike(regex):
-> 1905                     raise TypeError('If "to_replace" and "value" are both None'
   1906                                     ' and "to_replace" is not a list, then '
   1907                                     'regex must be a mapping')

TypeError: If "to_replace" and "value" are both None and "to_replace" is not a list, then regex must be a mapping
```

I saw that some filter related stuff was updated to use regex for 0.13dev, but I don't understand why this makes my command failing? 



Discussion:
`df.last_el_cmd.replace([np.nan], inplace=True)` seems to be the way now, correct?

So this is working for you now? (Despite the confusing error message) Regardless that TypeError doesn't make sense, how can to_replace simultaneously be None and also potentially a list... I might take a glance at some point.

Its type depends on the other arguments. To replace could be none if you pass a string to regex. 

I can take a look tonight 

I think I know what the issue is.

Filter is unrlated to this

The literal text says that to_replace is None and to_replace is not a list. First statement always implies the second right? 

And type error is valid there. This is a bug that is incorrectly triggering a typewrror

Oh I c. Yup that is an error reporting big :)

Actually I'm surprised this isn't covered. It's such a simple case. 

But: Why is this a bug now and was working in 0.12? What DID change?

in 0.13, Series went from a sub-class of ndarray to a sub-class of NDFrame (which sub-class the rest of pandas objects). As a result we were able to move most methods to a generic class, and have consisten type signatures for all objects. In this case, this the frame method was much more complete and accepts much more input. This is merely a validation issue, AFAIK. @cpcloud will address shortly. see here: http://pandas.pydata.org/pandas-docs/dev/whatsnew.html#internal-refactoring

Bump. This is still an API change (at least for me), because before, `df.last_el_cmd.replace(np.nan, inplace=True)` was working, now I have to `df.last_el_cmd.replace([np.nan], inplace=True)`.
Do I have to encapsulate the argument in a list or not?

@cpcloud what did we decide about this?

is this still broken? i can't really test right now ... can look later

oh i c, nvm

this should work, so it's a bug 


Resolving patch:
diff --git a/pandas/core/generic.py b/pandas/core/generic.py
--- a/pandas/core/generic.py
+++ b/pandas/core/generic.py
@@ -41,6 +41,10 @@ def is_dictlike(x):
 
 
 def _single_replace(self, to_replace, method, inplace, limit):
+    if self.ndim != 1:
+        raise TypeError('cannot replace {0} with method {1} on a {2}'.format(to_replace,
+                                                                             method,type(self).__name__))
+
     orig_dtype = self.dtype
     result = self if inplace else self.copy()
     fill_f = com._get_fill_func(method)
@@ -2033,6 +2037,11 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None,
         self._consolidate_inplace()
 
         if value is None:
+            # passing a single value that is scalar like
+            # when value is None (GH5319), for compat
+            if not is_dictlike(to_replace) and not is_dictlike(regex):
+                to_replace = [ to_replace ]
+
             if isinstance(to_replace, (tuple, list)):
                 return _single_replace(self, to_replace, method, inplace,
                                        limit)


Test patch:
diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py
--- a/pandas/tests/test_frame.py
+++ b/pandas/tests/test_frame.py
@@ -6975,6 +6975,7 @@ def test_replace_inplace(self):
         assert_frame_equal(tsframe, self.tsframe.fillna(0))
 
         self.assertRaises(TypeError, self.tsframe.replace, nan, inplace=True)
+        self.assertRaises(TypeError, self.tsframe.replace, nan)
 
         # mixed type
         self.mixed_frame['foo'][5:20] = nan
diff --git a/pandas/tests/test_series.py b/pandas/tests/test_series.py
--- a/pandas/tests/test_series.py
+++ b/pandas/tests/test_series.py
@@ -5140,6 +5140,18 @@ def test_replace(self):
         result = ser.replace([0, 1, 2, 3, 4], [4, 3, 2, 1, 0])
         assert_series_equal(result, Series([4, 3, 2, 1, 0]))
 
+        # API change from 0.12?
+        # GH 5319
+        ser = Series([0, np.nan, 2, 3, 4])
+        expected = ser.ffill()
+        result = ser.replace([np.nan])
+        assert_series_equal(result, expected)
+
+        ser = Series([0, np.nan, 2, 3, 4])
+        expected = ser.ffill()
+        result = ser.replace(np.nan)
+        assert_series_equal(result, expected)
+
     def test_replace_with_single_list(self):
         ser = Series([0, 1, 2, 3, 4])
         result = ser.replace([1,2,3])