Repository: pandas-dev/pandas

Issue:
CLN: Consistency of arguments in to_html and to_string
The methods `to_html` and `to_string` share most of the parameters, together with `DataFrameFormatter`, but there are some differences in the order, and in some parameters that probably should be the same.

Those are the common parameters:
```
buf=None,
columns=None,
col_space=None,
header=True,
index=True,
na_rep='NaN',
formatters=None,
float_format=None,
sparsify=None,
index_names=True,
justify=None,
max_rows=None,
max_cols=None,
show_dimensions=False
```

But after `formatters` they are in different order, which adds complexity (for example in reusing docstrings, or in switching from one function to another when using positional arguments).

Then, those are the parameters that are not shared among the 3:
```
DataFrameFormatter
--
frame,  # (positional, at the beginning)
line_width=None,
decimal='.',
table_id=None,

to_string
--
line_width=None,

to_html
--
decimal='.',
table_id=None
bold_rows=True,
classes=None,
escape=True,
notebook=False,
border=None,
```

While that looks mostly ok, it feels like probably `to_string` should also have a `decimal` argument (and not sure what `DataFrameFormatter` should have.

What I propose is:
- Reorder the arguments so the common ones are located first, and in the same order across all methods.
- Add `decimal` to `to_string`, and check if any other addition (or deletion) makes sense.
- Add a note in whatsnew, but don't do any deprecation (which would make things quite tricky in our code). Hopefully no users are using positional arguments for the last arguments in a method with around 15.
- Make sure the docstrings have the parameters in the right order.
- Looks like in `pandas/io/formats/format.py` there is a related `docstring_to_string` variable that is not being used, so we should remove it.

@jreback happy to make these changes?

CC @thoo 


Discussion:
as i said ok with changing to be consistent
ideally don’t move the first few args (which are the only realistic positonally passed ones)
otherwise ok - 
Thanks. I think the first change would be in the 8th argument.

@thoo, will you work on this?
Sure.


Resolving patch:
diff --git a/pandas/core/frame.py b/pandas/core/frame.py
--- a/pandas/core/frame.py
+++ b/pandas/core/frame.py
@@ -2035,24 +2035,21 @@ def to_parquet(self, fname, engine='auto', compression='snappy',
     def to_string(self, buf=None, columns=None, col_space=None, header=True,
                   index=True, na_rep='NaN', formatters=None, float_format=None,
                   sparsify=None, index_names=True, justify=None,
-                  line_width=None, max_rows=None, max_cols=None,
-                  show_dimensions=False):
+                  max_rows=None, max_cols=None, show_dimensions=False,
+                  decimal='.', line_width=None):
         """
         Render a DataFrame to a console-friendly tabular output.
-
         %(shared_params)s
         line_width : int, optional
             Width to wrap a line in characters.
-
         %(returns)s
-
         See Also
         --------
         to_html : Convert DataFrame to HTML.
 
         Examples
         --------
-        >>> d = {'col1' : [1, 2, 3], 'col2' : [4, 5, 6]}
+        >>> d = {'col1': [1, 2, 3], 'col2': [4, 5, 6]}
         >>> df = pd.DataFrame(d)
         >>> print(df.to_string())
            col1  col2
@@ -2068,42 +2065,37 @@ def to_string(self, buf=None, columns=None, col_space=None, header=True,
                                            sparsify=sparsify, justify=justify,
                                            index_names=index_names,
                                            header=header, index=index,
-                                           line_width=line_width,
                                            max_rows=max_rows,
                                            max_cols=max_cols,
-                                           show_dimensions=show_dimensions)
+                                           show_dimensions=show_dimensions,
+                                           decimal=decimal,
+                                           line_width=line_width)
         formatter.to_string()
 
         if buf is None:
             result = formatter.buf.getvalue()
             return result
 
-    @Substitution(header='whether to print column labels, default True')
+    @Substitution(header='Whether to print column labels, default True')
     @Substitution(shared_params=fmt.common_docstring,
                   returns=fmt.return_docstring)
     def to_html(self, buf=None, columns=None, col_space=None, header=True,
                 index=True, na_rep='NaN', formatters=None, float_format=None,
-                sparsify=None, index_names=True, justify=None, bold_rows=True,
-                classes=None, escape=True, max_rows=None, max_cols=None,
-                show_dimensions=False, notebook=False, decimal='.',
-                border=None, table_id=None):
+                sparsify=None, index_names=True, justify=None, max_rows=None,
+                max_cols=None, show_dimensions=False, decimal='.',
+                bold_rows=True, classes=None, escape=True,
+                notebook=False, border=None, table_id=None):
         """
         Render a DataFrame as an HTML table.
-
         %(shared_params)s
-        bold_rows : boolean, default True
-            Make the row labels bold in the output
+        bold_rows : bool, default True
+            Make the row labels bold in the output.
         classes : str or list or tuple, default None
-            CSS class(es) to apply to the resulting html table
-        escape : boolean, default True
+            CSS class(es) to apply to the resulting html table.
+        escape : bool, default True
             Convert the characters <, >, and & to HTML-safe sequences.
         notebook : {True, False}, default False
             Whether the generated HTML is for IPython Notebook.
-        decimal : string, default '.'
-            Character recognized as decimal separator, e.g. ',' in Europe
-
-            .. versionadded:: 0.18.0
-
         border : int
             A ``border=border`` attribute is included in the opening
             `<table>` tag. Default ``pd.options.html.border``.
@@ -2114,9 +2106,7 @@ def to_html(self, buf=None, columns=None, col_space=None, header=True,
             A css id is included in the opening `<table>` tag if specified.
 
             .. versionadded:: 0.23.0
-
         %(returns)s
-
         See Also
         --------
         to_string : Convert DataFrame to a string.
diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py
--- a/pandas/io/formats/format.py
+++ b/pandas/io/formats/format.py
@@ -88,6 +88,10 @@
             Maximum number of columns to display in the console.
         show_dimensions : bool, default False
             Display DataFrame dimensions (number of rows by number of columns).
+        decimal : str, default '.'
+            Character recognized as decimal separator, e.g. ',' in Europe.
+
+            .. versionadded:: 0.18.0
     """
 
 _VALID_JUSTIFY_PARAMETERS = ("left", "right", "center", "justify",
@@ -101,8 +105,6 @@
             String representation of the dataframe.
     """
 
-docstring_to_string = common_docstring + return_docstring
-
 
 class CategoricalFormatter(object):
 


Test patch:
diff --git a/pandas/tests/io/formats/test_format.py b/pandas/tests/io/formats/test_format.py
--- a/pandas/tests/io/formats/test_format.py
+++ b/pandas/tests/io/formats/test_format.py
@@ -1458,6 +1458,12 @@ def test_to_string_format_na(self):
                     '4  4.0     bar')
         assert result == expected
 
+    def test_to_string_decimal(self):
+        # Issue #23614
+        df = DataFrame({'A': [6.0, 3.1, 2.2]})
+        expected = '     A\n0  6,0\n1  3,1\n2  2,2'
+        assert df.to_string(decimal=',') == expected
+
     def test_to_string_line_width(self):
         df = DataFrame(123, lrange(10, 15), lrange(30))
         s = df.to_string(line_width=80)