Skip to content

Commit f2f2aff

Browse files
committed
DEPR: Warn about Series.to_csv signature alignment
Warns about aligning Series.to_csv's signature with that of DataFrame.to_csv's. In anticipation, we have moved DataFrame.to_csv to generic.py so that we can later delete the Series.to_csv implementation, and allow it to automatically adopt DataFrame's to_csv due to inheritance. Closes pandas-devgh-19745.
1 parent bdb6168 commit f2f2aff

File tree

6 files changed

+171
-127
lines changed

6 files changed

+171
-127
lines changed

doc/source/whatsnew/v0.24.0.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ Deprecations
245245
- :meth:`DataFrame.to_stata`, :meth:`read_stata`, :class:`StataReader` and :class:`StataWriter` have deprecated the ``encoding`` argument. The encoding of a Stata dta file is determined by the file type and cannot be changed (:issue:`21244`).
246246
- :meth:`MultiIndex.to_hierarchical` is deprecated and will be removed in a future version (:issue:`21613`)
247247
- :meth:`Series.ptp` is deprecated. Use ``numpy.ptp`` instead (:issue:`21614`)
248-
-
248+
- The signature in :meth:`Series.to_csv` has been deprecated. Please follow the signature in :meth:`DataFrame.to_csv` instead (:issue:`19745`)
249249

250250
.. _whatsnew_0240.prior_deprecations:
251251

pandas/core/frame.py

-97
Original file line numberDiff line numberDiff line change
@@ -1710,103 +1710,6 @@ def to_panel(self):
17101710

17111711
return self._constructor_expanddim(new_mgr)
17121712

1713-
def to_csv(self, path_or_buf=None, sep=",", na_rep='', float_format=None,
1714-
columns=None, header=True, index=True, index_label=None,
1715-
mode='w', encoding=None, compression=None, quoting=None,
1716-
quotechar='"', line_terminator='\n', chunksize=None,
1717-
tupleize_cols=None, date_format=None, doublequote=True,
1718-
escapechar=None, decimal='.'):
1719-
r"""Write DataFrame to a comma-separated values (csv) file
1720-
1721-
Parameters
1722-
----------
1723-
path_or_buf : string or file handle, default None
1724-
File path or object, if None is provided the result is returned as
1725-
a string.
1726-
sep : character, default ','
1727-
Field delimiter for the output file.
1728-
na_rep : string, default ''
1729-
Missing data representation
1730-
float_format : string, default None
1731-
Format string for floating point numbers
1732-
columns : sequence, optional
1733-
Columns to write
1734-
header : boolean or list of string, default True
1735-
Write out the column names. If a list of strings is given it is
1736-
assumed to be aliases for the column names
1737-
index : boolean, default True
1738-
Write row names (index)
1739-
index_label : string or sequence, or False, default None
1740-
Column label for index column(s) if desired. If None is given, and
1741-
`header` and `index` are True, then the index names are used. A
1742-
sequence should be given if the DataFrame uses MultiIndex. If
1743-
False do not print fields for index names. Use index_label=False
1744-
for easier importing in R
1745-
mode : str
1746-
Python write mode, default 'w'
1747-
encoding : string, optional
1748-
A string representing the encoding to use in the output file,
1749-
defaults to 'ascii' on Python 2 and 'utf-8' on Python 3.
1750-
compression : {'infer', 'gzip', 'bz2', 'xz', None}, default None
1751-
If 'infer' and `path_or_buf` is path-like, then detect compression
1752-
from the following extensions: '.gz', '.bz2' or '.xz'
1753-
(otherwise no compression).
1754-
line_terminator : string, default ``'\n'``
1755-
The newline character or character sequence to use in the output
1756-
file
1757-
quoting : optional constant from csv module
1758-
defaults to csv.QUOTE_MINIMAL. If you have set a `float_format`
1759-
then floats are converted to strings and thus csv.QUOTE_NONNUMERIC
1760-
will treat them as non-numeric
1761-
quotechar : string (length 1), default '\"'
1762-
character used to quote fields
1763-
doublequote : boolean, default True
1764-
Control quoting of `quotechar` inside a field
1765-
escapechar : string (length 1), default None
1766-
character used to escape `sep` and `quotechar` when appropriate
1767-
chunksize : int or None
1768-
rows to write at a time
1769-
tupleize_cols : boolean, default False
1770-
.. deprecated:: 0.21.0
1771-
This argument will be removed and will always write each row
1772-
of the multi-index as a separate row in the CSV file.
1773-
1774-
Write MultiIndex columns as a list of tuples (if True) or in
1775-
the new, expanded format, where each MultiIndex column is a row
1776-
in the CSV (if False).
1777-
date_format : string, default None
1778-
Format string for datetime objects
1779-
decimal: string, default '.'
1780-
Character recognized as decimal separator. E.g. use ',' for
1781-
European data
1782-
1783-
"""
1784-
1785-
if tupleize_cols is not None:
1786-
warnings.warn("The 'tupleize_cols' parameter is deprecated and "
1787-
"will be removed in a future version",
1788-
FutureWarning, stacklevel=2)
1789-
else:
1790-
tupleize_cols = False
1791-
1792-
from pandas.io.formats.csvs import CSVFormatter
1793-
formatter = CSVFormatter(self, path_or_buf,
1794-
line_terminator=line_terminator, sep=sep,
1795-
encoding=encoding,
1796-
compression=compression, quoting=quoting,
1797-
na_rep=na_rep, float_format=float_format,
1798-
cols=columns, header=header, index=index,
1799-
index_label=index_label, mode=mode,
1800-
chunksize=chunksize, quotechar=quotechar,
1801-
tupleize_cols=tupleize_cols,
1802-
date_format=date_format,
1803-
doublequote=doublequote,
1804-
escapechar=escapechar, decimal=decimal)
1805-
formatter.save()
1806-
1807-
if path_or_buf is None:
1808-
return formatter.path_or_buf.getvalue()
1809-
18101713
@Appender(_shared_docs['to_excel'] % _shared_doc_kwargs)
18111714
def to_excel(self, excel_writer, sheet_name='Sheet1', na_rep='',
18121715
float_format=None, columns=None, header=True, index=True,

pandas/core/generic.py

+101
Original file line numberDiff line numberDiff line change
@@ -9161,6 +9161,107 @@ def first_valid_index(self):
91619161
def last_valid_index(self):
91629162
return self._find_valid_index('last')
91639163

9164+
def to_csv(self, path_or_buf=None, sep=",", na_rep='', float_format=None,
9165+
columns=None, header=True, index=True, index_label=None,
9166+
mode='w', encoding=None, compression=None, quoting=None,
9167+
quotechar='"', line_terminator='\n', chunksize=None,
9168+
tupleize_cols=None, date_format=None, doublequote=True,
9169+
escapechar=None, decimal='.'):
9170+
r"""Export to a comma-separated values (CSV) file
9171+
9172+
Parameters
9173+
----------
9174+
path_or_buf : string or file handle, default None
9175+
File path or object, if None is provided the result is returned as
9176+
a string.
9177+
sep : character, default ','
9178+
Field delimiter for the output file.
9179+
na_rep : string, default ''
9180+
Missing data representation
9181+
float_format : string, default None
9182+
Format string for floating point numbers
9183+
columns : sequence, optional
9184+
Columns to write
9185+
header : boolean or list of string, default True
9186+
Write out the column names. If a list of strings is given it is
9187+
assumed to be aliases for the column names
9188+
index : boolean, default True
9189+
Write row names (index)
9190+
index_label : string or sequence, or False, default None
9191+
Column label for index column(s) if desired. If None is given, and
9192+
`header` and `index` are True, then the index names are used. A
9193+
sequence should be given if the DataFrame uses MultiIndex. If
9194+
False do not print fields for index names. Use index_label=False
9195+
for easier importing in R
9196+
mode : str
9197+
Python write mode, default 'w'
9198+
encoding : string, optional
9199+
A string representing the encoding to use in the output file,
9200+
defaults to 'ascii' on Python 2 and 'utf-8' on Python 3.
9201+
compression : {'infer', 'gzip', 'bz2', 'xz', None}, default None
9202+
If 'infer' and `path_or_buf` is path-like, then detect compression
9203+
from the following extensions: '.gz', '.bz2' or '.xz'
9204+
(otherwise no compression).
9205+
line_terminator : string, default ``'\n'``
9206+
The newline character or character sequence to use in the output
9207+
file
9208+
quoting : optional constant from csv module
9209+
defaults to csv.QUOTE_MINIMAL. If you have set a `float_format`
9210+
then floats are converted to strings and thus csv.QUOTE_NONNUMERIC
9211+
will treat them as non-numeric
9212+
quotechar : string (length 1), default '\"'
9213+
character used to quote fields
9214+
doublequote : boolean, default True
9215+
Control quoting of `quotechar` inside a field
9216+
escapechar : string (length 1), default None
9217+
character used to escape `sep` and `quotechar` when appropriate
9218+
chunksize : int or None
9219+
rows to write at a time
9220+
tupleize_cols : boolean, default False
9221+
.. deprecated:: 0.21.0
9222+
This argument will be removed and will always write each row
9223+
of the multi-index as a separate row in the CSV file.
9224+
9225+
Write MultiIndex columns as a list of tuples (if True) or in
9226+
the new, expanded format, where each MultiIndex column is a row
9227+
in the CSV (if False).
9228+
date_format : string, default None
9229+
Format string for datetime objects
9230+
decimal: string, default '.'
9231+
Character recognized as decimal separator. E.g. use ',' for
9232+
European data
9233+
9234+
"""
9235+
9236+
from pandas.core.frame import DataFrame
9237+
from pandas.io.formats.csvs import CSVFormatter
9238+
9239+
df = self if isinstance(self, DataFrame) else DataFrame(self)
9240+
9241+
if tupleize_cols is not None:
9242+
warnings.warn("The 'tupleize_cols' parameter is deprecated and "
9243+
"will be removed in a future version",
9244+
FutureWarning, stacklevel=2)
9245+
else:
9246+
tupleize_cols = False
9247+
9248+
formatter = CSVFormatter(df, path_or_buf,
9249+
line_terminator=line_terminator, sep=sep,
9250+
encoding=encoding,
9251+
compression=compression, quoting=quoting,
9252+
na_rep=na_rep, float_format=float_format,
9253+
cols=columns, header=header, index=index,
9254+
index_label=index_label, mode=mode,
9255+
chunksize=chunksize, quotechar=quotechar,
9256+
tupleize_cols=tupleize_cols,
9257+
date_format=date_format,
9258+
doublequote=doublequote,
9259+
escapechar=escapechar, decimal=decimal)
9260+
formatter.save()
9261+
9262+
if path_or_buf is None:
9263+
return formatter.path_or_buf.getvalue()
9264+
91649265

91659266
def _doc_parms(cls):
91669267
"""Return a tuple of the doc parms."""

pandas/core/series.py

+36-12
Original file line numberDiff line numberDiff line change
@@ -3761,11 +3761,17 @@ def from_csv(cls, path, sep=',', parse_dates=True, header=None,
37613761
return result
37623762

37633763
def to_csv(self, path=None, index=True, sep=",", na_rep='',
3764-
float_format=None, header=False, index_label=None,
3764+
float_format=None, header=None, index_label=None,
37653765
mode='w', encoding=None, compression=None, date_format=None,
3766-
decimal='.'):
3767-
"""
3768-
Write Series to a comma-separated values (csv) file
3766+
decimal='.', **kwargs):
3767+
"""Export to a comma-separated values (CSV) file
3768+
3769+
.. deprecated:: 0.24.0
3770+
The signature will aligned to that of :func:`DataFrame.to_csv`.
3771+
3772+
:func:`Series.to_csv` will align its signature with that of
3773+
`DataFrame.to_csv`. Please pass in keyword arguments in accordance
3774+
with that signature instead.
37693775
37703776
Parameters
37713777
----------
@@ -3776,8 +3782,8 @@ def to_csv(self, path=None, index=True, sep=",", na_rep='',
37763782
Missing data representation
37773783
float_format : string, default None
37783784
Format string for floating point numbers
3779-
header : boolean, default False
3780-
Write out series name
3785+
header : boolean, default None
3786+
Write out Series name. By default, the name will be omitted.
37813787
index : boolean, default True
37823788
Write row names (index)
37833789
index_label : string or sequence, default None
@@ -3800,14 +3806,32 @@ def to_csv(self, path=None, index=True, sep=",", na_rep='',
38003806
Character recognized as decimal separator. E.g. use ',' for
38013807
European data
38023808
"""
3809+
38033810
from pandas.core.frame import DataFrame
38043811
df = DataFrame(self)
3805-
# result is only a string if no path provided, otherwise None
3806-
result = df.to_csv(path, index=index, sep=sep, na_rep=na_rep,
3807-
float_format=float_format, header=header,
3808-
index_label=index_label, mode=mode,
3809-
encoding=encoding, compression=compression,
3810-
date_format=date_format, decimal=decimal)
3812+
3813+
if header is None:
3814+
warnings.warn("The signature of `Series.to_csv` will be "
3815+
"aligned to that of `DataFrame.to_csv` in the "
3816+
"future. Note that some of the default arguments "
3817+
"and argument names are different, so please refer "
3818+
"to the documentation for `DataFrame.to_csv` when "
3819+
"changing your function calls.",
3820+
FutureWarning, stacklevel=2)
3821+
header = False
3822+
3823+
to_csv_kwargs = dict(path_or_buf=path, index=index, sep=sep,
3824+
na_rep=na_rep, float_format=float_format,
3825+
header=header, index_label=index_label,
3826+
mode=mode, encoding=encoding,
3827+
compression=compression,
3828+
date_format=date_format,
3829+
decimal=decimal)
3830+
to_csv_kwargs.update(**kwargs)
3831+
3832+
# Result is only a string if no path provided, otherwise None.
3833+
result = df.to_csv(**to_csv_kwargs)
3834+
38113835
if path is None:
38123836
return result
38133837

pandas/tests/series/test_io.py

+23-12
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,28 @@ def read_csv(self, path, **kwargs):
3737
def test_from_csv_deprecation(self):
3838
# see gh-17812
3939
with ensure_clean() as path:
40-
self.ts.to_csv(path)
40+
self.ts.to_csv(path, header=False)
4141

4242
with tm.assert_produces_warning(FutureWarning,
4343
check_stacklevel=False):
4444
ts = self.read_csv(path)
4545
depr_ts = Series.from_csv(path)
4646
assert_series_equal(depr_ts, ts)
4747

48+
def test_to_csv_deprecation(self):
49+
# see gh-19745
50+
with ensure_clean() as path:
51+
with tm.assert_produces_warning(FutureWarning):
52+
self.ts.to_csv(path)
53+
54+
# Make sure roundtrip still works.
55+
ts = self.read_csv(path)
56+
assert_series_equal(self.ts, ts, check_names=False)
57+
4858
def test_from_csv(self):
4959

5060
with ensure_clean() as path:
51-
self.ts.to_csv(path)
61+
self.ts.to_csv(path, header=False)
5262
ts = self.read_csv(path)
5363
assert_series_equal(self.ts, ts, check_names=False)
5464

@@ -65,7 +75,7 @@ def test_from_csv(self):
6575
ts_h = self.read_csv(path, header=0)
6676
assert ts_h.name == "ts"
6777

68-
self.series.to_csv(path)
78+
self.series.to_csv(path, header=False)
6979
series = self.read_csv(path)
7080
assert_series_equal(self.series, series, check_names=False)
7181

@@ -92,21 +102,21 @@ def test_to_csv(self):
92102
import io
93103

94104
with ensure_clean() as path:
95-
self.ts.to_csv(path)
105+
self.ts.to_csv(path, header=False)
96106

97107
with io.open(path, newline=None) as f:
98108
lines = f.readlines()
99109
assert (lines[1] != '\n')
100110

101-
self.ts.to_csv(path, index=False)
111+
self.ts.to_csv(path, index=False, header=False)
102112
arr = np.loadtxt(path)
103113
assert_almost_equal(arr, self.ts.values)
104114

105115
def test_to_csv_unicode_index(self):
106116
buf = StringIO()
107117
s = Series([u("\u05d0"), "d2"], index=[u("\u05d0"), u("\u05d1")])
108118

109-
s.to_csv(buf, encoding="UTF-8")
119+
s.to_csv(buf, encoding="UTF-8", header=False)
110120
buf.seek(0)
111121

112122
s2 = self.read_csv(buf, index_col=0, encoding="UTF-8")
@@ -116,7 +126,7 @@ def test_to_csv_float_format(self):
116126

117127
with ensure_clean() as filename:
118128
ser = Series([0.123456, 0.234567, 0.567567])
119-
ser.to_csv(filename, float_format="%.2f")
129+
ser.to_csv(filename, float_format="%.2f", header=False)
120130

121131
rs = self.read_csv(filename)
122132
xp = Series([0.12, 0.23, 0.57])
@@ -128,14 +138,15 @@ def test_to_csv_list_entries(self):
128138
split = s.str.split(r'\s+and\s+')
129139

130140
buf = StringIO()
131-
split.to_csv(buf)
141+
split.to_csv(buf, header=False)
132142

133143
def test_to_csv_path_is_none(self):
134-
# GH 8215
144+
# see gh-8215
145+
#
135146
# Series.to_csv() was returning None, inconsistent with
136147
# DataFrame.to_csv() which returned string
137148
s = Series([1, 2, 3])
138-
csv_str = s.to_csv(path=None)
149+
csv_str = s.to_csv(None, header=False)
139150
assert isinstance(csv_str, str)
140151

141152
@pytest.mark.parametrize('s,encoding', [
@@ -150,8 +161,8 @@ def test_to_csv_compression(self, s, encoding, compression):
150161

151162
with ensure_clean() as filename:
152163

153-
s.to_csv(filename, compression=compression, encoding=encoding,
154-
header=True)
164+
s.to_csv(filename, compression=compression,
165+
encoding=encoding, header=True)
155166
# test the round trip - to_csv -> read_csv
156167
result = pd.read_csv(filename, compression=compression,
157168
encoding=encoding, index_col=0, squeeze=True)

0 commit comments

Comments
 (0)