diff --git a/doc/source/whatsnew/v1.6.0.rst b/doc/source/whatsnew/v1.6.0.rst index 848e87f0bc029..50f7104fb59c1 100644 --- a/doc/source/whatsnew/v1.6.0.rst +++ b/doc/source/whatsnew/v1.6.0.rst @@ -154,7 +154,7 @@ Indexing ^^^^^^^^ - Bug in :meth:`DataFrame.reindex` filling with wrong values when indexing columns and index for ``uint`` dtypes (:issue:`48184`) - Bug in :meth:`DataFrame.reindex` casting dtype to ``object`` when :class:`DataFrame` has single extension array column when re-indexing ``columns`` and ``index`` (:issue:`48190`) -- +- Bug in :func:`~DataFrame.describe` when formatting percentiles in the resulting index showed more decimals than needed (:issue:`46362`) Missing ^^^^^^^ diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 27094fff5f812..0250b2bdda709 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -1720,11 +1720,12 @@ def format_percentiles( raise ValueError("percentiles should all be in the interval [0,1]") percentiles = 100 * percentiles + percentiles_round_type = percentiles.round().astype(int) - int_idx = np.isclose(percentiles.astype(int), percentiles) + int_idx = np.isclose(percentiles_round_type, percentiles) if np.all(int_idx): - out = percentiles.astype(int).astype(str) + out = percentiles_round_type.astype(str) return [i + "%" for i in out] unique_pcts = np.unique(percentiles) @@ -1737,7 +1738,7 @@ def format_percentiles( ).astype(int) prec = max(1, prec) out = np.empty_like(percentiles, dtype=object) - out[int_idx] = percentiles[int_idx].astype(int).astype(str) + out[int_idx] = percentiles[int_idx].round().astype(int).astype(str) out[~int_idx] = percentiles[~int_idx].round(prec).astype(str) return [i + "%" for i in out] diff --git a/pandas/tests/io/formats/test_format.py b/pandas/tests/io/formats/test_format.py index a7c9c86b3d9a5..443aa743a6444 100644 --- a/pandas/tests/io/formats/test_format.py +++ b/pandas/tests/io/formats/test_format.py @@ -3306,24 +3306,34 @@ def test_nat_representations(self): assert f(NaT) == "NaT" -def test_format_percentiles(): - result = fmt.format_percentiles([0.01999, 0.02001, 0.5, 0.666666, 0.9999]) - expected = ["1.999%", "2.001%", "50%", "66.667%", "99.99%"] +@pytest.mark.parametrize( + "percentiles, expected", + [ + ( + [0.01999, 0.02001, 0.5, 0.666666, 0.9999], + ["1.999%", "2.001%", "50%", "66.667%", "99.99%"], + ), + ( + [0, 0.5, 0.02001, 0.5, 0.666666, 0.9999], + ["0%", "50%", "2.0%", "50%", "66.67%", "99.99%"], + ), + ([0.281, 0.29, 0.57, 0.58], ["28.1%", "29%", "57%", "58%"]), + ([0.28, 0.29, 0.57, 0.58], ["28%", "29%", "57%", "58%"]), + ], +) +def test_format_percentiles(percentiles, expected): + result = fmt.format_percentiles(percentiles) assert result == expected - result = fmt.format_percentiles([0, 0.5, 0.02001, 0.5, 0.666666, 0.9999]) - expected = ["0%", "50%", "2.0%", "50%", "66.67%", "99.99%"] - assert result == expected +@pytest.mark.parametrize( + "percentiles", + [([0.1, np.nan, 0.5]), ([-0.001, 0.1, 0.5]), ([2, 0.1, 0.5]), ([0.1, 0.5, "a"])], +) +def test_error_format_percentiles(percentiles): msg = r"percentiles should all be in the interval \[0,1\]" with pytest.raises(ValueError, match=msg): - fmt.format_percentiles([0.1, np.nan, 0.5]) - with pytest.raises(ValueError, match=msg): - fmt.format_percentiles([-0.001, 0.1, 0.5]) - with pytest.raises(ValueError, match=msg): - fmt.format_percentiles([2, 0.1, 0.5]) - with pytest.raises(ValueError, match=msg): - fmt.format_percentiles([0.1, 0.5, "a"]) + fmt.format_percentiles(percentiles) def test_format_percentiles_integer_idx():