Repository: pandas-dev/pandas

Issue:
_ensure_numeric does not check for complex numbers
`_ensure_numeric` from `pandas.core.nanops` does not include a check for complex numbers, just converts them to floats.

Relevant StackOverflow question that found the bug: [python pandas complex number](http://stackoverflow.com/questions/18919699/python-pandas-complex-number/)



Discussion:

Resolving patch:
diff --git a/pandas/core/nanops.py b/pandas/core/nanops.py
--- a/pandas/core/nanops.py
+++ b/pandas/core/nanops.py
@@ -546,12 +546,14 @@ def _ensure_numeric(x):
     if isinstance(x, np.ndarray):
         if x.dtype == np.object_:
             x = x.astype(np.float64)
-    elif not (com.is_float(x) or com.is_integer(x)):
+    elif not (com.is_float(x) or com.is_integer(x) or com.is_complex(x)):
         try:
             x = float(x)
         except Exception:
-            raise TypeError('Could not convert %s to numeric' % str(x))
-
+            try:
+                x = complex(x)
+            except Exception:
+                raise TypeError('Could not convert %s to numeric' % str(x))
     return x
 
 # NA-friendly array comparisons


Test patch:
diff --git a/pandas/tests/test_common.py b/pandas/tests/test_common.py
--- a/pandas/tests/test_common.py
+++ b/pandas/tests/test_common.py
@@ -14,6 +14,7 @@
 import pandas.core.common as com
 import pandas.util.testing as tm
 import pandas.core.config as cf
+from pandas.core import nanops
 
 _multiprocess_can_split_ = True
 
@@ -311,6 +312,54 @@ def test_ensure_int32():
     assert(result.dtype == np.int32)
 
 
+class TestEnsureNumeric(unittest.TestCase):
+    def test_numeric_values(self):
+        # Test integer
+        self.assertEqual(nanops._ensure_numeric(1), 1, 'Failed for int')
+        # Test float
+        self.assertEqual(nanops._ensure_numeric(1.1), 1.1, 'Failed for float')
+        # Test complex
+        self.assertEqual(nanops._ensure_numeric(1 + 2j), 1 + 2j,
+                         'Failed for complex')
+
+    def test_ndarray(self):
+        # Test numeric ndarray
+        values = np.array([1, 2, 3])
+        self.assertTrue(np.allclose(nanops._ensure_numeric(values), values),
+                        'Failed for numeric ndarray')
+
+        # Test object ndarray
+        o_values = values.astype(object)
+        self.assertTrue(np.allclose(nanops._ensure_numeric(o_values), values),
+                        'Failed for object ndarray')
+
+        # Test convertible string ndarray
+        s_values = np.array(['1', '2', '3'], dtype=object)
+        self.assertTrue(np.allclose(nanops._ensure_numeric(s_values), values),
+                        'Failed for convertible string ndarray')
+
+        # Test non-convertible string ndarray
+        s_values = np.array(['foo', 'bar', 'baz'], dtype=object)
+        self.assertRaises(ValueError,
+                          lambda: nanops._ensure_numeric(s_values))
+
+    def test_convertable_values(self):
+        self.assertTrue(np.allclose(nanops._ensure_numeric('1'), 1.0),
+                        'Failed for convertible integer string')
+        self.assertTrue(np.allclose(nanops._ensure_numeric('1.1'), 1.1),
+                        'Failed for convertible float string')
+        self.assertTrue(np.allclose(nanops._ensure_numeric('1+1j'), 1 + 1j),
+                        'Failed for convertible complex string')
+
+    def test_non_convertable_values(self):
+        self.assertRaises(TypeError,
+                          lambda: nanops._ensure_numeric('foo'))
+        self.assertRaises(TypeError,
+                          lambda: nanops._ensure_numeric({}))
+        self.assertRaises(TypeError,
+                          lambda: nanops._ensure_numeric([]))
+
+
 def test_ensure_platform_int():
 
     # verify that when we create certain types of indices