diff --git a/doc/source/release.rst b/doc/source/release.rst index 2576982d6976f..d1c3041d6bd6b 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -153,6 +153,9 @@ API Changes - all offset operations now return ``Timestamp`` types (rather than datetime), Business/Week frequencies were incorrect (:issue:`4069`) - ``Series.iteritems()`` is now lazy (returns an iterator rather than a list). This was the documented behavior prior to 0.14. (:issue:`6760`) - ``Panel.shift`` now uses ``NDFrame.shift``. It no longer drops the ``nan`` data and retains its original shape. (:issue:`4867`) +- ``to_excel`` now converts ``np.inf`` into a string representation, + customizable by the ``inf_rep`` keyword argument (Excel has no native inf + representation) (:issue:`6782`) Deprecations ~~~~~~~~~~~~ diff --git a/pandas/core/format.py b/pandas/core/format.py index a2a68b23c2018..636b3f452a20c 100644 --- a/pandas/core/format.py +++ b/pandas/core/format.py @@ -1369,10 +1369,14 @@ class ExcelFormatter(object): sequence should be given if the DataFrame uses MultiIndex. merge_cells : boolean, default False Format MultiIndex and Hierarchical Rows as merged cells. + inf_rep : string, default `'inf'` + representation for np.inf values (which aren't representable in Excel) + A `'-'` sign will be added in front of -inf. """ def __init__(self, df, na_rep='', float_format=None, cols=None, - header=True, index=True, index_label=None, merge_cells=False): + header=True, index=True, index_label=None, merge_cells=False, + inf_rep='inf'): self.df = df self.rowcounter = 0 self.na_rep = na_rep @@ -1384,12 +1388,18 @@ def __init__(self, df, na_rep='', float_format=None, cols=None, self.index_label = index_label self.header = header self.merge_cells = merge_cells + self.inf_rep = inf_rep def _format_value(self, val): if lib.checknull(val): val = self.na_rep - if self.float_format is not None and com.is_float(val): - val = float(self.float_format % val) + elif com.is_float(val): + if np.isposinf(val): + val = '-%s' % self.inf_rep + elif np.isneginf(val): + val = self.inf_rep + elif self.float_format is not None: + val = float(self.float_format % val) return val def _format_header_mi(self): diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 8a30b74575ec2..8875d2fdfb39a 100755 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -1155,7 +1155,7 @@ def to_csv(self, path_or_buf=None, sep=",", na_rep='', float_format=None, def to_excel(self, excel_writer, sheet_name='Sheet1', na_rep='', float_format=None, columns=None, header=True, index=True, index_label=None, startrow=0, startcol=0, engine=None, - merge_cells=True, encoding=None): + merge_cells=True, encoding=None, inf_rep='inf'): """ Write DataFrame to a excel sheet @@ -1194,6 +1194,9 @@ def to_excel(self, excel_writer, sheet_name='Sheet1', na_rep='', encoding of the resulting excel file. Only necessary for xlwt, other writers support unicode natively. cols : kwarg only alias of columns [deprecated] + inf_rep : string, default 'inf' + Representation for infinity (there is no native representation for + infinity in Excel) Notes ----- @@ -1207,7 +1210,7 @@ def to_excel(self, excel_writer, sheet_name='Sheet1', na_rep='', >>> writer.save() """ from pandas.io.excel import ExcelWriter - + need_save = False if encoding == None: encoding = 'ascii' @@ -1223,7 +1226,8 @@ def to_excel(self, excel_writer, sheet_name='Sheet1', na_rep='', float_format=float_format, index=index, index_label=index_label, - merge_cells=merge_cells) + merge_cells=merge_cells, + inf_rep=inf_rep) formatted_cells = formatter.get_formatted_cells() excel_writer.write_cells(formatted_cells, sheet_name, startrow=startrow, startcol=startcol) diff --git a/pandas/io/tests/test_excel.py b/pandas/io/tests/test_excel.py index 8ba2a5dfc3d9c..fde5764993e76 100644 --- a/pandas/io/tests/test_excel.py +++ b/pandas/io/tests/test_excel.py @@ -538,6 +538,16 @@ def test_bool_types(self): recons = reader.parse('test1').astype(np_type) tm.assert_frame_equal(frame, recons) + def test_inf_roundtrip(self): + _skip_if_no_xlrd() + + frame = DataFrame([(1, np.inf), (2, 3), (5, -np.inf)]) + with ensure_clean(self.ext) as path: + frame.to_excel(path, 'test1') + reader = ExcelFile(path) + recons = reader.parse('test1') + tm.assert_frame_equal(frame, recons) + def test_sheets(self): _skip_if_no_xlrd()