Repository: pandas-dev/pandas

Issue:
API: round() method to ignore non-numerical columns?
If you call the new `round` method on a mixed dataframe (not only numerical columns), you get an error:

```
In [1]: df = pd.DataFrame({'floats':[0.123, 0.156, 5.687], 'strings': ['a', 'b', 'c']})

In [2]: df.round(2)
TypeError: can't multiply sequence by non-int of type 'float'

In [3]: df.round()
AttributeError: 'str' object has no attribute 'rint'
```

Would it be better to ignore these non-numerical columns instead of raising an error? 
Another option would be to drop these columns (like numerical aggregations like `df.mean()` do).



Discussion:
I think it would make sense to ignore non-numerical columns.

What about time columns? Didn't we add support for round with times recently, or is that still in the works?

There is indeed new support for rounding datetime64 series, but the problem is that this goes through the accessor method `.dt.round()` and not `.round()`. So it is not really obvious how to integrate that.

```
In [1]: df = pd.DataFrame({'floats':[0.123, 0.156, 5.687], 'dates':pd.date_range('2012-01-01', periods=3)})

In [3]: df.round(2)
TypeError: ufunc multiply cannot use operands with types dtype('<M8[ns]') and dtype('float64')

In [4]: df['dates'].round()
TypeError: ufunc 'rint' not supported for the input types, and the inputs could
not be safely coerced to any supported types according to the casting rule ''safe''
```

That's a bit in general a problem with specific dtypes that have specialized versions of standard methods requiring other keywords. 
Because we could also give access to datetime rounding through `Series.round()` instead of `Series.dt.round()`?

In any case, that is maybe another discussion, so for now I would say to also ignore datetime and period columns.

`.round` should only apply to `float/int` columns normally. I would simply skip all others (but include them in the return). I think we could pass thru the `freq` kw to `DataFrame.round` to handle datetimelike rounding.

further `Series.round` _could_ defer to `.dt.` for `.round` behavior of datetimelikes (and with the `freq` arg non-optional in that case). we can make that another issue I think.


Resolving patch:
diff --git a/pandas/core/frame.py b/pandas/core/frame.py
--- a/pandas/core/frame.py
+++ b/pandas/core/frame.py
@@ -4416,10 +4416,15 @@ def round(self, decimals=0, out=None):
         def _dict_round(df, decimals):
             for col, vals in df.iteritems():
                 try:
-                    yield vals.round(decimals[col])
+                    yield _series_round(vals, decimals[col])
                 except KeyError:
                     yield vals
 
+        def _series_round(s, decimals):
+            if com.is_integer_dtype(s) or com.is_float_dtype(s):
+                return s.round(decimals)
+            return s
+
         if isinstance(decimals, (dict, Series)):
             if isinstance(decimals, Series):
                 if not decimals.index.is_unique:
@@ -4427,7 +4432,7 @@ def _dict_round(df, decimals):
             new_cols = [col for col in _dict_round(self, decimals)]
         elif com.is_integer(decimals):
             # Dispatch to Series.round
-            new_cols = [v.round(decimals) for _, v in self.iteritems()]
+            new_cols = [_series_round(v, decimals) for _, v in self.iteritems()]
         else:
             raise TypeError("decimals must be an integer, a dict-like or a Series")
 


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
@@ -13523,6 +13523,21 @@ def test_round(self):
         # Make sure this doesn't break existing Series.round
         tm.assert_series_equal(df['col1'].round(1), expected_rounded['col1'])
 
+
+    def test_round_mixed_type(self):
+        # GH11885
+        df = DataFrame({'col1': [1.1, 2.2, 3.3, 4.4], 'col2': ['1', 'a', 'c', 'f'],
+                        'col3': date_range('20111111', periods=4)})
+        round_0 = DataFrame({'col1': [1., 2., 3., 4.], 'col2': ['1', 'a', 'c' ,'f'],
+                              'col3': date_range('20111111', periods=4)})
+        tm.assert_frame_equal(df.round(), round_0)
+        tm.assert_frame_equal(df.round(1), df)
+        tm.assert_frame_equal(df.round({'col1':1}), df)
+        tm.assert_frame_equal(df.round({'col1':0}), round_0)
+        tm.assert_frame_equal(df.round({'col1':0, 'col2':1}), round_0)
+        tm.assert_frame_equal(df.round({'col3':1}), df)
+
+
     def test_round_issue(self):
         # GH11611