diff --git a/packages/python/plotly/plotly/express/_core.py b/packages/python/plotly/plotly/express/_core.py index dd2d53be1ed..167c8e90f93 100644 --- a/packages/python/plotly/plotly/express/_core.py +++ b/packages/python/plotly/plotly/express/_core.py @@ -1303,7 +1303,20 @@ def build_dataframe(args, constructor): # Cast data_frame argument to DataFrame (it could be a numpy array, dict etc.) df_provided = args["data_frame"] is not None if df_provided and not isinstance(args["data_frame"], pd.DataFrame): - args["data_frame"] = pd.DataFrame(args["data_frame"]) + if hasattr(args["data_frame"], "__dataframe__"): + try: + import pandas.api.interchange + except ModuleNotFoundError: + raise NotImplementedError( + "The dataframe you provided supports the dataframe interchange" + "protocol, " + "but pandas 1.5.0 or greater is required to consume it." + ) + df_not_pandas = args["data_frame"] + df_pandas = pandas.api.interchange.from_dataframe(df_not_pandas) + args["data_frame"] = df_pandas + else: + args["data_frame"] = pd.DataFrame(args["data_frame"]) df_input = args["data_frame"] # now we handle special cases like wide-mode or x-xor-y specification diff --git a/packages/python/plotly/plotly/tests/test_optional/test_px/test_px_input.py b/packages/python/plotly/plotly/tests/test_optional/test_px/test_px_input.py index 477e7dbcb04..b4fae3a6a5c 100644 --- a/packages/python/plotly/plotly/tests/test_optional/test_px/test_px_input.py +++ b/packages/python/plotly/plotly/tests/test_optional/test_px/test_px_input.py @@ -233,6 +233,19 @@ def test_build_df_with_index(): assert_frame_equal(tips.reset_index()[out["data_frame"].columns], out["data_frame"]) +def test_build_df_protocol(): + import vaex + + # take out the 'species' columns since there are still some issues with strings + iris_pandas = px.data.iris()[["petal_width", "sepal_length"]] + iris_vaex = vaex.from_pandas(iris_pandas) + args = dict(data_frame=iris_vaex, x="petal_width", y="sepal_length") + out = build_dataframe(args, go.Scatter) + assert_frame_equal( + iris_pandas.reset_index()[out["data_frame"].columns], out["data_frame"] + ) + + def test_timezones(): df = pd.DataFrame({"date": ["2015-04-04 19:31:30+1:00"], "value": [3]}) df["date"] = pd.to_datetime(df["date"])