From d926117b560f549a65a13b9b2330a6f986e9d88f Mon Sep 17 00:00:00 2001 From: Matthew Roeschke <10647082+mroeschke@users.noreply.github.com> Date: Tue, 9 May 2023 16:07:28 -0700 Subject: [PATCH 1/2] BUG: from_dataframe with empty objects --- doc/source/whatsnew/v2.1.0.rst | 1 + pandas/core/interchange/from_dataframe.py | 4 +++- pandas/tests/interchange/test_impl.py | 10 ++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v2.1.0.rst b/doc/source/whatsnew/v2.1.0.rst index 281db2759b9f0..03aec47e94729 100644 --- a/doc/source/whatsnew/v2.1.0.rst +++ b/doc/source/whatsnew/v2.1.0.rst @@ -447,6 +447,7 @@ Metadata Other ^^^^^ - Bug in :class:`FloatingArray.__contains__` with ``NaN`` item incorrectly returning ``False`` when ``NaN`` values are present (:issue:`52840`) +- Bug in :func:`api.interchange.from_dataframe` when converting an empty DataFrame object (:issue:`53155`) - Bug in :func:`assert_almost_equal` now throwing assertion error for two unequal sets (:issue:`51727`) - Bug in :func:`assert_frame_equal` checks category dtypes even when asked not to check index type (:issue:`52126`) - Bug in :meth:`DataFrame.reindex` with a ``fill_value`` that should be inferred with a :class:`ExtensionDtype` incorrectly inferring ``object`` dtype (:issue:`52586`) diff --git a/pandas/core/interchange/from_dataframe.py b/pandas/core/interchange/from_dataframe.py index 78e530f915117..d4210344f6b40 100644 --- a/pandas/core/interchange/from_dataframe.py +++ b/pandas/core/interchange/from_dataframe.py @@ -79,7 +79,9 @@ def _from_dataframe(df: DataFrameXchg, allow_copy: bool = True): raise RuntimeError( "To join chunks a copy is required which is forbidden by allow_copy=False" ) - if len(pandas_dfs) == 1: + if not pandas_dfs: + pandas_df = pd.DataFrame({col: [] for col in df.column_names()}) + elif len(pandas_dfs) == 1: pandas_df = pandas_dfs[0] else: pandas_df = pd.concat(pandas_dfs, axis=0, ignore_index=True, copy=False) diff --git a/pandas/tests/interchange/test_impl.py b/pandas/tests/interchange/test_impl.py index 606f1374a61ce..dc8610f02e9e9 100644 --- a/pandas/tests/interchange/test_impl.py +++ b/pandas/tests/interchange/test_impl.py @@ -272,3 +272,13 @@ def test_categorical_to_numpy_dlpack(): result = np.from_dlpack(col.get_buffers()["data"][0]) expected = np.array([0, 1, 0], dtype="int8") tm.assert_numpy_array_equal(result, expected) + + +@pytest.mark.parametrize("data", [{}, {"a": []}]) +def test_empty_pyarrow(data): + # GH 53155 + pa_interchange = pytest.importorskip("pyarrow.interchange", "11.0.0") + expected = pd.DataFrame(data) + arrow_df = pa_interchange.from_dataframe(expected) + result = from_dataframe(arrow_df) + tm.assert_frame_equal(result, expected) From 0449adae761e2dbf275164b385be4bf631edeb3c Mon Sep 17 00:00:00 2001 From: Matthew Roeschke <10647082+mroeschke@users.noreply.github.com> Date: Wed, 10 May 2023 09:37:34 -0700 Subject: [PATCH 2/2] Use protocol_df_chunk_to_pandas --- pandas/core/interchange/from_dataframe.py | 2 +- pandas/tests/interchange/test_impl.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pandas/core/interchange/from_dataframe.py b/pandas/core/interchange/from_dataframe.py index d4210344f6b40..4b67659aa40fd 100644 --- a/pandas/core/interchange/from_dataframe.py +++ b/pandas/core/interchange/from_dataframe.py @@ -80,7 +80,7 @@ def _from_dataframe(df: DataFrameXchg, allow_copy: bool = True): "To join chunks a copy is required which is forbidden by allow_copy=False" ) if not pandas_dfs: - pandas_df = pd.DataFrame({col: [] for col in df.column_names()}) + pandas_df = protocol_df_chunk_to_pandas(df) elif len(pandas_dfs) == 1: pandas_df = pandas_dfs[0] else: diff --git a/pandas/tests/interchange/test_impl.py b/pandas/tests/interchange/test_impl.py index dc8610f02e9e9..49873768ca952 100644 --- a/pandas/tests/interchange/test_impl.py +++ b/pandas/tests/interchange/test_impl.py @@ -277,8 +277,10 @@ def test_categorical_to_numpy_dlpack(): @pytest.mark.parametrize("data", [{}, {"a": []}]) def test_empty_pyarrow(data): # GH 53155 - pa_interchange = pytest.importorskip("pyarrow.interchange", "11.0.0") + pytest.importorskip("pyarrow", "11.0.0") + from pyarrow.interchange import from_dataframe as pa_from_dataframe + expected = pd.DataFrame(data) - arrow_df = pa_interchange.from_dataframe(expected) + arrow_df = pa_from_dataframe(expected) result = from_dataframe(arrow_df) tm.assert_frame_equal(result, expected)