Skip to content

Commit 5a6b867

Browse files
merge master
1 parent 634577e commit 5a6b867

File tree

5 files changed

+93
-31
lines changed

5 files changed

+93
-31
lines changed

doc/source/whatsnew/v0.25.0.rst

+2
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,7 @@ I/O
647647

648648
- Bug in :func:`DataFrame.to_html()` where values were truncated using display options instead of outputting the full content (:issue:`17004`)
649649
- Fixed bug in missing text when using :meth:`to_clipboard` if copying utf-16 characters in Python 3 on Windows (:issue:`25040`)
650+
- Bug in :meth:`Series.to_string` adding a leading space when ``index=False`` (:issue:`24980`)
650651
- Bug in :func:`read_json` for ``orient='table'`` when it tries to infer dtypes by default, which is not applicable as dtypes are already defined in the JSON schema (:issue:`21345`)
651652
- Bug in :func:`read_json` for ``orient='table'`` and float index, as it infers index dtype by default, which is not applicable because index dtype is already defined in the JSON schema (:issue:`25433`)
652653
- Bug in :func:`read_json` for ``orient='table'`` and string of float column names, as it makes a column name type conversion to :class:`Timestamp`, which is not applicable because column names are already defined in the JSON schema (:issue:`25435`)
@@ -671,6 +672,7 @@ I/O
671672
- :func:`read_excel` now raises a ``ValueError`` when input is of type :class:`pandas.io.excel.ExcelFile` and ``engine`` param is passed since :class:`pandas.io.excel.ExcelFile` has an engine defined (:issue:`26566`)
672673
- Bug while selecting from :class:`HDFStore` with ``where=''`` specified (:issue:`26610`).
673674

675+
674676
Plotting
675677
^^^^^^^^
676678

pandas/core/generic.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -2859,9 +2859,9 @@ def to_latex(self, buf=None, columns=None, col_space=None, header=True,
28592859
... 'mask': ['red', 'purple'],
28602860
... 'weapon': ['sai', 'bo staff']})
28612861
>>> df.to_latex(index=False) # doctest: +NORMALIZE_WHITESPACE
2862-
'\\begin{tabular}{lll}\n\\toprule\n name & mask & weapon
2863-
\\\\\n\\midrule\n Raphael & red & sai \\\\\n Donatello &
2864-
purple & bo staff \\\\\n\\bottomrule\n\\end{tabular}\n'
2862+
'\\begin{tabular}{lll}\n\\toprule\n name & mask & weapon
2863+
\\\\\n\\midrule\n Raphael & red & sai \\\\\nDonatello &
2864+
purple & bo staff \\\\\n\\bottomrule\n\\end{tabular}\n'
28652865
"""
28662866
# Get defaults from the pandas config
28672867
if self.ndim == 1:

pandas/io/formats/format.py

+35-9
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,15 @@ def _get_formatted_index(self):
247247

248248
def _get_formatted_values(self):
249249
values_to_format = self.tr_series._formatting_values()
250+
251+
if self.index:
252+
leading_space = 'compat'
253+
else:
254+
leading_space = False
250255
return format_array(values_to_format, None,
251-
float_format=self.float_format, na_rep=self.na_rep)
256+
float_format=self.float_format,
257+
na_rep=self.na_rep,
258+
leading_space=leading_space)
252259

253260
def to_string(self):
254261
series = self.tr_series
@@ -712,9 +719,15 @@ def _format_col(self, i):
712719
frame = self.tr_frame
713720
formatter = self._get_formatter(i)
714721
values_to_format = frame.iloc[:, i]._formatting_values()
722+
723+
if self.index:
724+
leading_space = 'compat'
725+
else:
726+
leading_space = False
715727
return format_array(values_to_format, formatter,
716728
float_format=self.float_format, na_rep=self.na_rep,
717-
space=self.col_space, decimal=self.decimal)
729+
space=self.col_space, decimal=self.decimal,
730+
leading_space=leading_space)
718731

719732
def to_html(self, classes=None, notebook=False, border=None):
720733
"""
@@ -851,7 +864,7 @@ def _get_column_name_list(self):
851864

852865
def format_array(values, formatter, float_format=None, na_rep='NaN',
853866
digits=None, space=None, justify='right', decimal='.',
854-
leading_space=None):
867+
leading_space='compat'):
855868
"""
856869
Format an array for printing.
857870
@@ -865,7 +878,7 @@ def format_array(values, formatter, float_format=None, na_rep='NaN',
865878
space
866879
justify
867880
decimal
868-
leading_space : bool, optional
881+
leading_space : bool, default is 'compat'
869882
Whether the array should be formatted with a leading space.
870883
When an array as a column of a Series or DataFrame, we do want
871884
the leading space to pad between columns.
@@ -915,7 +928,7 @@ class GenericArrayFormatter:
915928

916929
def __init__(self, values, digits=7, formatter=None, na_rep='NaN',
917930
space=12, float_format=None, justify='right', decimal='.',
918-
quoting=None, fixed_width=True, leading_space=None):
931+
quoting=None, fixed_width=True, leading_space='compat'):
919932
self.values = values
920933
self.digits = digits
921934
self.na_rep = na_rep
@@ -973,7 +986,7 @@ def _format(x):
973986

974987
is_float_type = lib.map_infer(vals, is_float) & notna(vals)
975988
leading_space = self.leading_space
976-
if leading_space is None:
989+
if leading_space == 'compat':
977990
leading_space = is_float_type.any()
978991

979992
fmt_values = []
@@ -1101,7 +1114,11 @@ def format_values_with(float_format):
11011114
# The default is otherwise to use str instead of a formatting string
11021115
if self.float_format is None:
11031116
if self.fixed_width:
1104-
float_format = partial('{value: .{digits:d}f}'.format,
1117+
if self.leading_space is not False:
1118+
fmt_str = '{value: .{digits:d}f}'
1119+
else:
1120+
fmt_str = '{value:.{digits:d}f}'
1121+
float_format = partial(fmt_str.format,
11051122
digits=self.digits)
11061123
else:
11071124
float_format = self.float_format
@@ -1133,7 +1150,11 @@ def format_values_with(float_format):
11331150
(abs_vals > 0)).any()
11341151

11351152
if has_small_values or (too_long and has_large_values):
1136-
float_format = partial('{value: .{digits:d}e}'.format,
1153+
if self.leading_space is not False:
1154+
fmt_str = '{value: .{digits:d}e}'
1155+
else:
1156+
fmt_str = '{value:.{digits:d}e}'
1157+
float_format = partial(fmt_str.format,
11371158
digits=self.digits)
11381159
formatted_values = format_values_with(float_format)
11391160

@@ -1150,7 +1171,12 @@ def _format_strings(self):
11501171
class IntArrayFormatter(GenericArrayFormatter):
11511172

11521173
def _format_strings(self):
1153-
formatter = self.formatter or (lambda x: '{x: d}'.format(x=x))
1174+
if self.leading_space is False:
1175+
fmt_str = '{x:d}'
1176+
else:
1177+
fmt_str = '{x: d}'
1178+
formatter = self.formatter or (lambda x: fmt_str.format(x=x))
1179+
# formatter = self.formatter or (lambda x: '{x: d}'.format(x=x))
11541180
fmt_values = [formatter(x) for x in self.values]
11551181
return fmt_values
11561182

pandas/tests/io/formats/test_format.py

+42-8
Original file line numberDiff line numberDiff line change
@@ -1232,15 +1232,15 @@ def test_to_string_no_index(self):
12321232

12331233
df_s = df.to_string(index=False)
12341234
# Leading space is expected for positive numbers.
1235-
expected = (" x y z\n"
1236-
" 11 33 AAA\n"
1237-
" 22 -44 ")
1235+
expected = (" x y z\n"
1236+
"11 33 AAA\n"
1237+
"22 -44 ")
12381238
assert df_s == expected
12391239

12401240
df_s = df[['y', 'x', 'z']].to_string(index=False)
1241-
expected = (" y x z\n"
1242-
" 33 11 AAA\n"
1243-
"-44 22 ")
1241+
expected = (" y x z\n"
1242+
" 33 11 AAA\n"
1243+
"-44 22 ")
12441244
assert df_s == expected
12451245

12461246
def test_to_string_line_width_no_index(self):
@@ -1255,7 +1255,7 @@ def test_to_string_line_width_no_index(self):
12551255
df = DataFrame({'x': [11, 22, 33], 'y': [4, 5, 6]})
12561256

12571257
df_s = df.to_string(line_width=1, index=False)
1258-
expected = " x \\\n 11 \n 22 \n 33 \n\n y \n 4 \n 5 \n 6 "
1258+
expected = " x \\\n11 \n22 \n33 \n\n y \n 4 \n 5 \n 6 "
12591259

12601260
assert df_s == expected
12611261

@@ -1844,7 +1844,7 @@ def test_to_string_without_index(self):
18441844
# GH 11729 Test index=False option
18451845
s = Series([1, 2, 3, 4])
18461846
result = s.to_string(index=False)
1847-
expected = (' 1\n' + ' 2\n' + ' 3\n' + ' 4')
1847+
expected = ('1\n' + '2\n' + '3\n' + '4')
18481848
assert result == expected
18491849

18501850
def test_unicode_name_in_footer(self):
@@ -2332,6 +2332,15 @@ def test_to_string_header(self):
23322332
exp = '0 0\n ..\n9 9'
23332333
assert res == exp
23342334

2335+
@pytest.mark.parametrize("inputs, expected", [
2336+
([' a', ' b'], ' a\n b'),
2337+
(['.1', '1'], '.1\n 1'),
2338+
(['10', '-10'], ' 10\n-10')
2339+
])
2340+
def test_to_string_index_false_corner_case(self, inputs, expected):
2341+
s = pd.Series(inputs).to_string(index=False)
2342+
assert s == expected
2343+
23352344
def test_to_string_multindex_header(self):
23362345
# GH 16718
23372346
df = (pd.DataFrame({'a': [0], 'b': [1], 'c': [2], 'd': [3]})
@@ -2740,6 +2749,31 @@ def test_format_percentiles():
27402749
fmt.format_percentiles([0.1, 0.5, 'a'])
27412750

27422751

2752+
@pytest.mark.parametrize("input_array, expected", [
2753+
("a", "a"),
2754+
(["a", "b"], "a\nb"),
2755+
([1, "a"], "1\na"),
2756+
(1, "1"),
2757+
([0, -1], " 0\n-1"),
2758+
(1.0, '1.0')
2759+
])
2760+
def test_format_remove_leading_space_series(input_array, expected):
2761+
# GH: 24980
2762+
s = pd.Series(input_array).to_string(index=False)
2763+
assert s == expected
2764+
2765+
2766+
@pytest.mark.parametrize("input_array, expected", [
2767+
({"A": ["a"]}, "A\na"),
2768+
({"A": ["a", "b"], "B": ["c", "dd"]}, "A B\na c\nb dd"),
2769+
({"A": ["a", 1], "B": ["aa", 1]}, "A B\na aa\n1 1")
2770+
])
2771+
def test_format_remove_leading_space_dataframe(input_array, expected):
2772+
# GH: 24980
2773+
df = pd.DataFrame(input_array).to_string(index=False)
2774+
assert df == expected
2775+
2776+
27432777
def test_format_percentiles_integer_idx():
27442778
# Issue #26660
27452779
result = fmt.format_percentiles(np.linspace(0, 1, 10 + 1))

pandas/tests/io/formats/test_to_latex.py

+11-11
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ def test_to_latex(self, float_frame):
5151
withoutindex_result = df.to_latex(index=False)
5252
withoutindex_expected = r"""\begin{tabular}{rl}
5353
\toprule
54-
a & b \\
54+
a & b \\
5555
\midrule
56-
1 & b1 \\
57-
2 & b2 \\
56+
1 & b1 \\
57+
2 & b2 \\
5858
\bottomrule
5959
\end{tabular}
6060
"""
@@ -410,7 +410,7 @@ def test_to_latex_longtable(self, float_frame):
410410
withoutindex_result = df.to_latex(index=False, longtable=True)
411411
withoutindex_expected = r"""\begin{longtable}{rl}
412412
\toprule
413-
a & b \\
413+
a & b \\
414414
\midrule
415415
\endhead
416416
\midrule
@@ -420,8 +420,8 @@ def test_to_latex_longtable(self, float_frame):
420420
421421
\bottomrule
422422
\endlastfoot
423-
1 & b1 \\
424-
2 & b2 \\
423+
1 & b1 \\
424+
2 & b2 \\
425425
\end{longtable}
426426
"""
427427

@@ -477,8 +477,8 @@ def test_to_latex_no_header(self):
477477
withoutindex_result = df.to_latex(index=False, header=False)
478478
withoutindex_expected = r"""\begin{tabular}{rl}
479479
\toprule
480-
1 & b1 \\
481-
2 & b2 \\
480+
1 & b1 \\
481+
2 & b2 \\
482482
\bottomrule
483483
\end{tabular}
484484
"""
@@ -504,10 +504,10 @@ def test_to_latex_specified_header(self):
504504
withoutindex_result = df.to_latex(header=['AA', 'BB'], index=False)
505505
withoutindex_expected = r"""\begin{tabular}{rl}
506506
\toprule
507-
AA & BB \\
507+
AA & BB \\
508508
\midrule
509-
1 & b1 \\
510-
2 & b2 \\
509+
1 & b1 \\
510+
2 & b2 \\
511511
\bottomrule
512512
\end{tabular}
513513
"""

0 commit comments

Comments
 (0)