From cea7a2b9ef6a6a4bca0ef5bce5a486445dbe02bd Mon Sep 17 00:00:00 2001 From: Yong Seung Lee Date: Sun, 16 Nov 2025 22:04:49 -0500 Subject: [PATCH 1/3] Descriptive error message on when the dataframe is empty and margin is set --- pandas/core/reshape/pivot.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pandas/core/reshape/pivot.py b/pandas/core/reshape/pivot.py index 04c584c226aed..c3f1f82c4885a 100644 --- a/pandas/core/reshape/pivot.py +++ b/pandas/core/reshape/pivot.py @@ -1165,6 +1165,9 @@ def _normalize( elif margins is True: # keep index and column of pivoted table + if table.empty: + raise ValueError("Can't get margins since the result dataframe is empty ") + table_index = table.index table_columns = table.columns last_ind_or_col = table.iloc[-1, :].name From 107071beaed31c82abae422c1c4ced7f865e2558 Mon Sep 17 00:00:00 2001 From: Yong Seung Lee Date: Sun, 16 Nov 2025 22:21:00 -0500 Subject: [PATCH 2/3] Typo --- pandas/core/reshape/pivot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/reshape/pivot.py b/pandas/core/reshape/pivot.py index c3f1f82c4885a..dd677a01a71e6 100644 --- a/pandas/core/reshape/pivot.py +++ b/pandas/core/reshape/pivot.py @@ -1166,7 +1166,7 @@ def _normalize( elif margins is True: # keep index and column of pivoted table if table.empty: - raise ValueError("Can't get margins since the result dataframe is empty ") + raise ValueError("Can't get margins since the result dataframe is empty") table_index = table.index table_columns = table.columns From 340129a6fec3405a0a712ae786df1562dd9f096d Mon Sep 17 00:00:00 2001 From: Yong Seung Lee Date: Sat, 22 Nov 2025 23:16:18 -0500 Subject: [PATCH 3/3] Resolve comments --- pandas/core/reshape/pivot.py | 9 ++++--- pandas/tests/reshape/test_crosstab.py | 35 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/pandas/core/reshape/pivot.py b/pandas/core/reshape/pivot.py index 63f59794d363a..433b4664fa037 100644 --- a/pandas/core/reshape/pivot.py +++ b/pandas/core/reshape/pivot.py @@ -1163,12 +1163,15 @@ def _normalize( elif margins is True: # keep index and column of pivoted table - if table.empty: - raise ValueError("Can't get margins since the result dataframe is empty") table_index = table.index table_columns = table.columns - last_ind_or_col = table.iloc[-1, :].name + try: + last_ind_or_col = table.iloc[-1, :].name + except IndexError as err: + raise IndexError( + "Can't get margins since the result dataframe is empty" + ) from err # check if margin name is not in (for MI cases) and not equal to last # index/column and save the column and index margin diff --git a/pandas/tests/reshape/test_crosstab.py b/pandas/tests/reshape/test_crosstab.py index 1482da8a074eb..fdf808ca49ea6 100644 --- a/pandas/tests/reshape/test_crosstab.py +++ b/pandas/tests/reshape/test_crosstab.py @@ -877,3 +877,38 @@ def test_categoricals(a_dtype, b_dtype): expected = expected.loc[[0, 2, "All"]] expected["All"] = expected["All"].astype("int64") tm.assert_frame_equal(result, expected) + + +def test_crosstab_empty_result_with_normalize(): + # https://github.com/pandas-dev/pandas/issues/60768 + index = ["index1", "index2", "index3"] + columns = ["col1", "col2", "col3"] + values = [1, 2, 3] + + with pytest.raises(IndexError, match="Can't get margins"): + # Raise error when margins=True + crosstab( + index, + columns, + values=values, + normalize=1, + margins=True, + dropna=True, + aggfunc="skew", + ) + + # No error when margins=False, just return empty DataFrame + result = crosstab( + index, + columns, + values=values, + normalize=1, + margins=False, + dropna=True, + aggfunc="skew", + ) + expected = DataFrame( + index=Index([], dtype="str", name="row_0"), + columns=Index([], dtype="str", name="col_0"), + ) + tm.assert_frame_equal(result, expected)