Repository: pandas-dev/pandas

Issue:
BUG: formatters argument to DataFrame.to_latex() is broken
It appears that neither the `formatters` nor `float_format` arguments to `DataFrame.to_latex` work if changed from their default values. Both raise the same exception: `AttributeError: 'numpy.float64' object has no attribute 'decode'`.

Note: neither of these arguments has test coverage in `pandas/tests/test_format.py`

My test script:

``` python
In [1]: from pandas.util.print_versions import show_versions

In [2]: show_versions()

INSTALLED VERSIONS
------------------
Python: 2.7.5.final.0
OS: Darwin
Release: 13.0.0
Processor: i386
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8

pandas: 0.13.0
Cython: 0.19.2
Numpy: 1.8.0
Scipy: 0.13.1
statsmodels: Not installed
    patsy: 0.2.1
scikits.timeseries: Not installed
dateutil: 2.2
pytz: 2013.9
bottleneck: 0.7.0
PyTables: Not Installed
    numexpr: 2.1
matplotlib: 1.3.1
openpyxl: Not installed
xlrd: Not installed
xlwt: Not installed
xlsxwriter: Not installed
sqlalchemy: 0.7.5
lxml: Not installed
bs4: Not installed
html5lib: Not installed
bigquery: Not installed
apiclient: 1.0c1

In [3]: import pandas as pd

In [4]: df = pd.DataFrame({'a': [1.0, 2.0]})

In [5]: df.to_latex()
Out[5]: u'\\begin{tabular}{lr}\n\\toprule\n{} &  a \\\\\n\\midrule\n0 &  1 \\\\\n1 &  2 \\\\\n\\bottomrule\n\\end{tabular}\n'

In [6]: df.to_latex(float_format=lambda x: x)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-6-c932ce2a22b5> in <module>()
----> 1 df.to_latex(float_format=lambda x: x)

/Users/shoyer/dev/climatology-research/library/python/target/climatology/lib/python2.7/site-packages/pandas/core/frame.pyc in to_latex(self, buf, columns, col_space, colSpace, header, index, na_rep, formatters, float_format, sparsify, index_names, bold_rows, force_unicode)
   1381                                            sparsify=sparsify,
   1382                                            index_names=index_names)
-> 1383         formatter.to_latex()
   1384
   1385         if buf is None:

/Users/shoyer/dev/climatology-research/library/python/target/climatology/lib/python2.7/site-packages/pandas/core/format.pyc in to_latex(self, force_unicode, column_format)
    446             strcols = [[info_line]]
    447         else:
--> 448             strcols = self._to_str_columns()
    449
    450         if column_format is None:

/Users/shoyer/dev/climatology-research/library/python/target/climatology/lib/python2.7/site-packages/pandas/core/format.pyc in _to_str_columns(self)
    325             stringified = []
    326             for i, c in enumerate(cols_to_show):
--> 327                 fmt_values = self._format_col(i)
    328                 cheader = str_columns[i]
    329

/Users/shoyer/dev/climatology-research/library/python/target/climatology/lib/python2.7/site-packages/pandas/core/format.pyc in _format_col(self, i)
    490             (self.frame.iloc[:self.max_rows_displayed, i]).get_values(),
    491             formatter, float_format=self.float_format, na_rep=self.na_rep,
--> 492             space=self.col_space
    493         )
    494

/Users/shoyer/dev/climatology-research/library/python/target/climatology/lib/python2.7/site-packages/pandas/core/format.pyc in format_array(values, formatter, float_format, na_rep, digits, space, justify)
   1615                         justify=justify)
   1616
-> 1617     return fmt_obj.get_result()
   1618
   1619

/Users/shoyer/dev/climatology-research/library/python/target/climatology/lib/python2.7/site-packages/pandas/core/format.pyc in get_result(self)
   1733                 fmt_values = self._format_with(fmt_str)
   1734
-> 1735         return _make_fixed_width(fmt_values, self.justify)
   1736
   1737

/Users/shoyer/dev/climatology-research/library/python/target/climatology/lib/python2.7/site-packages/pandas/core/format.pyc in _make_fixed_width(strings, justify, minimum, truncated)
   1795     _strlen = _strlen_func()
   1796
-> 1797     max_len = np.max([_strlen(x) for x in strings])
   1798
   1799     if minimum is not None:

/Users/shoyer/dev/climatology-research/library/python/target/climatology/lib/python2.7/site-packages/pandas/core/format.pyc in _strlen(x)
    227         def _strlen(x):
    228             try:
--> 229                 return len(x.decode(encoding))
    230             except UnicodeError:
    231                 return len(x)

AttributeError: 'numpy.float64' object has no attribute 'decode'
```



Discussion:
let's call this a bug then!

feel free to do a PR for this!

Some investigation reveals that the issue is that `formatter` function must return a string. That seems like a reasonable choice, although it isn't what I guessed (I tried `float_format=round`). So this will actually just be a doc fix -- expect a PR for that shortly.

I think you can do: `float_format=lambda x: round(x)` or even better: `float_float='%.0f'`


Resolving patch:
diff --git a/pandas/core/format.py b/pandas/core/format.py
--- a/pandas/core/format.py
+++ b/pandas/core/format.py
@@ -43,11 +43,11 @@
         string representation of NAN to use, default 'NaN'
     formatters : list or dict of one-parameter functions, optional
         formatter functions to apply to columns' elements by position or name,
-        default None, if the result is a string , it must be a unicode
-        string. List must be of length equal to the number of columns.
+        default None. The result of each function must be a unicode string.
+        List must be of length equal to the number of columns.
     float_format : one-parameter function, optional
-        formatter function to apply to columns' elements if they are floats
-        default None
+        formatter function to apply to columns' elements if they are floats,
+        default None. The result of this function must be a unicode string.
     sparsify : bool, optional
         Set to False for a DataFrame with a hierarchical index to print every
         multiindex key at each row, default True


Test patch: