From 3c2d1bf3f1e3079c908dc7e06f565d08d4df71ee Mon Sep 17 00:00:00 2001 From: "Maarten A. Breddels" Date: Thu, 16 Sep 2021 13:04:08 +0200 Subject: [PATCH 01/15] support dataframe protocol (tested with Vaex) This allows plotly express to take in any dataframe that supports the dataframe protocol, see: https://data-apis.org/blog/dataframe_protocol_rfc/ https://data-apis.org/dataframe-protocol/latest/index.html Test includes an example with vaex, which should work with https://github.com/vaexio/vaex/pull/1509/ (not yet released) --- packages/python/plotly/plotly/express/_core.py | 12 +++++++++++- .../tests/test_optional/test_px/test_px_input.py | 13 +++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/python/plotly/plotly/express/_core.py b/packages/python/plotly/plotly/express/_core.py index c9f03580faf..b28a0134a59 100644 --- a/packages/python/plotly/plotly/express/_core.py +++ b/packages/python/plotly/plotly/express/_core.py @@ -1307,7 +1307,17 @@ 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__"): + # Pandas does not implement a `from_dataframe` yet + # $ wget https://raw.githubusercontent.com/data-apis/dataframe-api/main/protocol/pandas_implementation.py + # $ export PYTHONPATH=`pwd` + import pandas_implementation + + args["data_frame"] = pandas_implementation.from_dataframe( + args["data_frame"] + ) + 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..1dbfc95dff9 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 the vaex implementation does not cover strings yet + 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"]) From b75324b231bdbef61eb11c1d005bed948065db7e Mon Sep 17 00:00:00 2001 From: "Maarten A. Breddels" Date: Fri, 30 Sep 2022 11:53:22 +0200 Subject: [PATCH 02/15] use pandas 1.5.0 to consume other dataframes --- .../python/plotly/plotly/express/_core.py | 19 +++++++++++-------- .../test_optional/test_px/test_px_input.py | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/python/plotly/plotly/express/_core.py b/packages/python/plotly/plotly/express/_core.py index b28a0134a59..f866dcc4ffb 100644 --- a/packages/python/plotly/plotly/express/_core.py +++ b/packages/python/plotly/plotly/express/_core.py @@ -1308,14 +1308,17 @@ def build_dataframe(args, constructor): df_provided = args["data_frame"] is not None if df_provided and not isinstance(args["data_frame"], pd.DataFrame): if hasattr(args["data_frame"], "__dataframe__"): - # Pandas does not implement a `from_dataframe` yet - # $ wget https://raw.githubusercontent.com/data-apis/dataframe-api/main/protocol/pandas_implementation.py - # $ export PYTHONPATH=`pwd` - import pandas_implementation - - args["data_frame"] = pandas_implementation.from_dataframe( - args["data_frame"] - ) + 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"] 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 1dbfc95dff9..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 @@ -236,7 +236,7 @@ def test_build_df_with_index(): def test_build_df_protocol(): import vaex - # take out the 'species' columns since the vaex implementation does not cover strings yet + # 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") From 2fec0b2497f6ff89defaa090ca19ceece250c3ea Mon Sep 17 00:00:00 2001 From: Anatoly Myachev Date: Mon, 12 Jun 2023 18:48:07 +0200 Subject: [PATCH 03/15] update test Signed-off-by: Anatoly Myachev --- .../test_optional/test_px/test_px_input.py | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) 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 b4fae3a6a5c..eb0dfd63f92 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 @@ -3,6 +3,7 @@ import numpy as np import pandas as pd import pytest +import unittest.mock as mock from plotly.express._core import build_dataframe from pandas.testing import assert_frame_equal @@ -233,17 +234,22 @@ 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 +def test_build_df_using_interchange_protocol_mock(): + class CustomDataFrame: + def __dataframe__(self): + pass - # 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"] - ) + input_dataframe = CustomDataFrame() + args = dict(data_frame=input_dataframe, x="petal_width", y="sepal_length") + + iris_pandas = px.data.iris() + + with mock.patch("pandas.__version__", "2.0.2"): + with mock.patch( + "pandas.api.interchange.from_dataframe", return_value=iris_pandas + ) as mock_from_dataframe: + build_dataframe(args, go.Scatter) + mock_from_dataframe.assert_called_once_with(input_dataframe) def test_timezones(): From 9b2154ee58d2ad07a16624ae9e95f71120d4d7c5 Mon Sep 17 00:00:00 2001 From: Anatoly Myachev Date: Mon, 12 Jun 2023 20:44:31 +0200 Subject: [PATCH 04/15] add fixture Signed-off-by: Anatoly Myachev --- .../tests/test_optional/test_px/test_px_input.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) 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 eb0dfd63f92..db8f733c519 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 @@ -7,6 +7,18 @@ from plotly.express._core import build_dataframe from pandas.testing import assert_frame_equal +# Fixtures +# -------- +@pytest.fixture +def add_interchange_module_for_old_pandas(): + if not hasattr(pd.api, "interchange"): + pd.api.interchange = mock.MagicMock() + # to make the following import work: `import pandas.api.interchange` + with mock.patch.dict("sys.modules", {"pandas.api.interchange": pd.api.interchange}): + yield + else: + yield + def test_numpy(): fig = px.scatter(x=[1, 2, 3], y=[2, 3, 4], color=[1, 3, 9]) @@ -234,7 +246,7 @@ def test_build_df_with_index(): assert_frame_equal(tips.reset_index()[out["data_frame"].columns], out["data_frame"]) -def test_build_df_using_interchange_protocol_mock(): +def test_build_df_using_interchange_protocol_mock(add_interchange_module_for_old_pandas): class CustomDataFrame: def __dataframe__(self): pass From 41fcb0c11081acdcaca8983dda30330d1c0629fe Mon Sep 17 00:00:00 2001 From: Anatoly Myachev Date: Mon, 12 Jun 2023 20:51:51 +0200 Subject: [PATCH 05/15] refactor using black Signed-off-by: Anatoly Myachev --- .../plotly/tests/test_optional/test_px/test_px_input.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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 db8f733c519..4f728fae049 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 @@ -14,7 +14,9 @@ def add_interchange_module_for_old_pandas(): if not hasattr(pd.api, "interchange"): pd.api.interchange = mock.MagicMock() # to make the following import work: `import pandas.api.interchange` - with mock.patch.dict("sys.modules", {"pandas.api.interchange": pd.api.interchange}): + with mock.patch.dict( + "sys.modules", {"pandas.api.interchange": pd.api.interchange} + ): yield else: yield @@ -246,7 +248,9 @@ def test_build_df_with_index(): assert_frame_equal(tips.reset_index()[out["data_frame"].columns], out["data_frame"]) -def test_build_df_using_interchange_protocol_mock(add_interchange_module_for_old_pandas): +def test_build_df_using_interchange_protocol_mock( + add_interchange_module_for_old_pandas, +): class CustomDataFrame: def __dataframe__(self): pass From 23f51272c0cb40e399b4f619d1e151b01897c095 Mon Sep 17 00:00:00 2001 From: Anatoly Myachev Date: Mon, 26 Jun 2023 15:20:14 +0200 Subject: [PATCH 06/15] return vaex test Signed-off-by: Anatoly Myachev --- .../tests/test_optional/test_px/test_px_input.py | 13 +++++++++++++ .../test_requirements/requirements_36_optional.txt | 1 + .../test_requirements/requirements_37_optional.txt | 1 + .../test_requirements/requirements_38_optional.txt | 1 + .../test_requirements/requirements_39_optional.txt | 1 + 5 files changed, 17 insertions(+) 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 4f728fae049..0de76733076 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 @@ -268,6 +268,19 @@ def __dataframe__(self): mock_from_dataframe.assert_called_once_with(input_dataframe) +def test_build_df_from_vaex(): + import vaex + + # take out the 'species' columns since the vaex implementation does not cover strings yet + 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"]) diff --git a/packages/python/plotly/test_requirements/requirements_36_optional.txt b/packages/python/plotly/test_requirements/requirements_36_optional.txt index 3fb4185e9c9..ef58a2a23dd 100644 --- a/packages/python/plotly/test_requirements/requirements_36_optional.txt +++ b/packages/python/plotly/test_requirements/requirements_36_optional.txt @@ -19,3 +19,4 @@ matplotlib==2.2.3 scikit-image==0.14.4 psutil==5.7.0 kaleido +vaex diff --git a/packages/python/plotly/test_requirements/requirements_37_optional.txt b/packages/python/plotly/test_requirements/requirements_37_optional.txt index 224fbf7db8b..dbd1f7fbdc0 100644 --- a/packages/python/plotly/test_requirements/requirements_37_optional.txt +++ b/packages/python/plotly/test_requirements/requirements_37_optional.txt @@ -20,3 +20,4 @@ scikit-image==0.14.4 psutil==5.7.0 kaleido orjson==3.8.12 +vaex diff --git a/packages/python/plotly/test_requirements/requirements_38_optional.txt b/packages/python/plotly/test_requirements/requirements_38_optional.txt index 34b686ad024..a2e58b6ea88 100644 --- a/packages/python/plotly/test_requirements/requirements_38_optional.txt +++ b/packages/python/plotly/test_requirements/requirements_38_optional.txt @@ -19,3 +19,4 @@ matplotlib==2.2.3 scikit-image==0.18.1 psutil==5.7.0 kaleido +vaex diff --git a/packages/python/plotly/test_requirements/requirements_39_optional.txt b/packages/python/plotly/test_requirements/requirements_39_optional.txt index eae8cd6d2ec..9c27a56def7 100644 --- a/packages/python/plotly/test_requirements/requirements_39_optional.txt +++ b/packages/python/plotly/test_requirements/requirements_39_optional.txt @@ -20,3 +20,4 @@ scikit-image==0.18.1 psutil==5.7.0 kaleido orjson==3.8.12 +vaex From 2b2d2e6e88c0f88ecb46dd84f12e98a7a4e02731 Mon Sep 17 00:00:00 2001 From: Anatoly Myachev Date: Mon, 26 Jun 2023 15:29:10 +0200 Subject: [PATCH 07/15] add changelog entry Signed-off-by: Anatoly Myachev --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67cdd158bb2..cd88cbe2251 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Add `legend.xref` and `legend.yref` to enable container-referenced positioning of legends [[#6589](https://github.com/plotly/plotly.js/pull/6589)], with thanks to [Gamma Technologies](https://www.gtisoft.com/) for sponsoring the related development. - Add `colorbar.xref` and `colorbar.yref` to enable container-referenced positioning of colorbars [[#6593](https://github.com/plotly/plotly.js/pull/6593)], with thanks to [Gamma Technologies](https://www.gtisoft.com/) for sponsoring the related development. - `px` methods now accept data-frame-like objects that support a `to_pandas()` method, such as polars, cudf, vaex etc + - `px` methods now accept data-frame-like objects that support a [dataframe interchange protocol](https://data-apis.org/dataframe-protocol/latest/index.html), such as polars, vaex, modin etc ### Fixed - Fixed another compatibility issue with Pandas 2.0, just affecting `px.*(line_close=True)` [[#4190](https://github.com/plotly/plotly.py/pull/4190)] From 926dcac7c7c82e7fbaa3c59da5b436ca11e4e008 Mon Sep 17 00:00:00 2001 From: Anatoly Myachev Date: Mon, 26 Jun 2023 15:33:51 +0200 Subject: [PATCH 08/15] move changelog entry Signed-off-by: Anatoly Myachev --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 210974ae723..8ddb756645a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Updated - Updated Plotly.js from version 2.24.1 to version 2.24.2. See the [plotly.js CHANGELOG](https://github.com/plotly/plotly.js/blob/master/CHANGELOG.md#2242----2023-06-09) for more information. These changes are reflected in the auto-generated `plotly.graph_objects` module. +- `px` methods now accept data-frame-like objects that support a [dataframe interchange protocol](https://data-apis.org/dataframe-protocol/latest/index.html), such as polars, vaex, modin etc ## [5.15.0] - 2023-06-08 @@ -23,7 +24,6 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Add `legend.xref` and `legend.yref` to enable container-referenced positioning of legends [[#6589](https://github.com/plotly/plotly.js/pull/6589)], with thanks to [Gamma Technologies](https://www.gtisoft.com/) for sponsoring the related development. - Add `colorbar.xref` and `colorbar.yref` to enable container-referenced positioning of colorbars [[#6593](https://github.com/plotly/plotly.js/pull/6593)], with thanks to [Gamma Technologies](https://www.gtisoft.com/) for sponsoring the related development. - `px` methods now accept data-frame-like objects that support a `to_pandas()` method, such as polars, cudf, vaex etc - - `px` methods now accept data-frame-like objects that support a [dataframe interchange protocol](https://data-apis.org/dataframe-protocol/latest/index.html), such as polars, vaex, modin etc ### Fixed - Fixed another compatibility issue with Pandas 2.0, just affecting `px.*(line_close=True)` [[#4190](https://github.com/plotly/plotly.py/pull/4190)] From e01de2bccaf8e7786cf9a906c73800e1edd657e5 Mon Sep 17 00:00:00 2001 From: Anatoly Myachev Date: Mon, 26 Jun 2023 15:58:54 +0200 Subject: [PATCH 09/15] add vaex into 'requirements_39_pandas_2_optional.txt' Signed-off-by: Anatoly Myachev --- .../test_requirements/requirements_39_pandas_2_optional.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/python/plotly/test_requirements/requirements_39_pandas_2_optional.txt b/packages/python/plotly/test_requirements/requirements_39_pandas_2_optional.txt index acb9752ac16..6ba8d851ee4 100644 --- a/packages/python/plotly/test_requirements/requirements_39_pandas_2_optional.txt +++ b/packages/python/plotly/test_requirements/requirements_39_pandas_2_optional.txt @@ -19,3 +19,4 @@ matplotlib==2.2.3 scikit-image==0.18.1 psutil==5.7.0 kaleido +vaex From d8096d40b69e3138188268a61f3d2d8c6921b941 Mon Sep 17 00:00:00 2001 From: Anatoly Myachev Date: Mon, 26 Jun 2023 17:43:32 +0200 Subject: [PATCH 10/15] upgrade pandas from 2.0.1 to 2.0.2 Signed-off-by: Anatoly Myachev --- .../plotly/tests/test_optional/test_px/test_px_input.py | 5 +++++ .../test_requirements/requirements_39_pandas_2_optional.txt | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) 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 0de76733076..637a7e08135 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 @@ -3,6 +3,7 @@ import numpy as np import pandas as pd import pytest +from packaging import version import unittest.mock as mock from plotly.express._core import build_dataframe from pandas.testing import assert_frame_equal @@ -268,6 +269,10 @@ def __dataframe__(self): mock_from_dataframe.assert_called_once_with(input_dataframe) +@pytest.mark.skipif( + version.parse(pd.__version__) < version.parse("2.0.2"), + reason="plotly doesn't use a dataframe interchange protocol for pandas < 2.0.2", +) def test_build_df_from_vaex(): import vaex diff --git a/packages/python/plotly/test_requirements/requirements_39_pandas_2_optional.txt b/packages/python/plotly/test_requirements/requirements_39_pandas_2_optional.txt index 6ba8d851ee4..61a3e968b7a 100644 --- a/packages/python/plotly/test_requirements/requirements_39_pandas_2_optional.txt +++ b/packages/python/plotly/test_requirements/requirements_39_pandas_2_optional.txt @@ -1,6 +1,6 @@ requests==2.25.1 tenacity==6.2.0 -pandas==2.0.1 +pandas==2.0.2 numpy==1.20.3 xarray==0.17.0 statsmodels From 0e1dc839174ed6ed484ffbae665dee5cd6f0b164 Mon Sep 17 00:00:00 2001 From: Anatoly Myachev Date: Mon, 26 Jun 2023 17:59:04 +0200 Subject: [PATCH 11/15] update changelog entry Signed-off-by: Anatoly Myachev --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ddb756645a..9ba4bde693d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Updated - Updated Plotly.js from version 2.24.1 to version 2.24.2. See the [plotly.js CHANGELOG](https://github.com/plotly/plotly.js/blob/master/CHANGELOG.md#2242----2023-06-09) for more information. These changes are reflected in the auto-generated `plotly.graph_objects` module. -- `px` methods now accept data-frame-like objects that support a [dataframe interchange protocol](https://data-apis.org/dataframe-protocol/latest/index.html), such as polars, vaex, modin etc +- `px` methods now accept data-frame-like objects that support a [dataframe interchange protocol](https://data-apis.org/dataframe-protocol/latest/index.html), such as polars, vaex, modin etc. This protocol has priority on `to_pandas` call, but will only be used if pandas>=2.0.2 is installed in the environment. ## [5.15.0] - 2023-06-08 From 91fd0de6bd4f61fc7ae417a37884df161ecaf89c Mon Sep 17 00:00:00 2001 From: Anatoly Myachev Date: Mon, 26 Jun 2023 18:02:00 +0200 Subject: [PATCH 12/15] remove vaex dependency on environments that don't run 'test_build_df_from_vaex' test Signed-off-by: Anatoly Myachev --- .../python/plotly/test_requirements/requirements_36_optional.txt | 1 - .../python/plotly/test_requirements/requirements_37_optional.txt | 1 - .../python/plotly/test_requirements/requirements_38_optional.txt | 1 - .../python/plotly/test_requirements/requirements_39_optional.txt | 1 - 4 files changed, 4 deletions(-) diff --git a/packages/python/plotly/test_requirements/requirements_36_optional.txt b/packages/python/plotly/test_requirements/requirements_36_optional.txt index ef58a2a23dd..3fb4185e9c9 100644 --- a/packages/python/plotly/test_requirements/requirements_36_optional.txt +++ b/packages/python/plotly/test_requirements/requirements_36_optional.txt @@ -19,4 +19,3 @@ matplotlib==2.2.3 scikit-image==0.14.4 psutil==5.7.0 kaleido -vaex diff --git a/packages/python/plotly/test_requirements/requirements_37_optional.txt b/packages/python/plotly/test_requirements/requirements_37_optional.txt index dbd1f7fbdc0..224fbf7db8b 100644 --- a/packages/python/plotly/test_requirements/requirements_37_optional.txt +++ b/packages/python/plotly/test_requirements/requirements_37_optional.txt @@ -20,4 +20,3 @@ scikit-image==0.14.4 psutil==5.7.0 kaleido orjson==3.8.12 -vaex diff --git a/packages/python/plotly/test_requirements/requirements_38_optional.txt b/packages/python/plotly/test_requirements/requirements_38_optional.txt index a2e58b6ea88..34b686ad024 100644 --- a/packages/python/plotly/test_requirements/requirements_38_optional.txt +++ b/packages/python/plotly/test_requirements/requirements_38_optional.txt @@ -19,4 +19,3 @@ matplotlib==2.2.3 scikit-image==0.18.1 psutil==5.7.0 kaleido -vaex diff --git a/packages/python/plotly/test_requirements/requirements_39_optional.txt b/packages/python/plotly/test_requirements/requirements_39_optional.txt index 9c27a56def7..eae8cd6d2ec 100644 --- a/packages/python/plotly/test_requirements/requirements_39_optional.txt +++ b/packages/python/plotly/test_requirements/requirements_39_optional.txt @@ -20,4 +20,3 @@ scikit-image==0.18.1 psutil==5.7.0 kaleido orjson==3.8.12 -vaex From 5f7bb3428c8a13a7c3793df4290f5cf79f90534d Mon Sep 17 00:00:00 2001 From: Anatoly Myachev Date: Mon, 26 Jun 2023 19:18:43 +0200 Subject: [PATCH 13/15] test polars Signed-off-by: Anatoly Myachev --- .../tests/test_optional/test_px/test_px_input.py | 10 +++++++--- .../requirements_39_pandas_2_optional.txt | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) 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 637a7e08135..1acbf3f1e64 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 @@ -273,12 +273,16 @@ def __dataframe__(self): version.parse(pd.__version__) < version.parse("2.0.2"), reason="plotly doesn't use a dataframe interchange protocol for pandas < 2.0.2", ) -def test_build_df_from_vaex(): - import vaex +@pytest.mark.parametrize("test_lib", ["vaex", "polars"]) +def test_build_df_from_vaex_and_polars(test_lib): + if test_lib == "vaex": + import vaex as lib + else: + import polars as lib # take out the 'species' columns since the vaex implementation does not cover strings yet iris_pandas = px.data.iris()[["petal_width", "sepal_length"]] - iris_vaex = vaex.from_pandas(iris_pandas) + iris_vaex = lib.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( diff --git a/packages/python/plotly/test_requirements/requirements_39_pandas_2_optional.txt b/packages/python/plotly/test_requirements/requirements_39_pandas_2_optional.txt index 61a3e968b7a..e7f16aeecc8 100644 --- a/packages/python/plotly/test_requirements/requirements_39_pandas_2_optional.txt +++ b/packages/python/plotly/test_requirements/requirements_39_pandas_2_optional.txt @@ -20,3 +20,4 @@ scikit-image==0.18.1 psutil==5.7.0 kaleido vaex +polars From e3019990bdc54b1700ba8ea1330fc28fb699c99e Mon Sep 17 00:00:00 2001 From: Anatoly Myachev Date: Thu, 29 Jun 2023 22:14:36 +0200 Subject: [PATCH 14/15] catch exceptions and try another way Signed-off-by: Anatoly Myachev --- packages/python/plotly/plotly/express/_core.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/python/plotly/plotly/express/_core.py b/packages/python/plotly/plotly/express/_core.py index 075a2561778..3dc7b9ccf3e 100644 --- a/packages/python/plotly/plotly/express/_core.py +++ b/packages/python/plotly/plotly/express/_core.py @@ -1314,7 +1314,17 @@ def build_dataframe(args, constructor): import pandas.api.interchange df_not_pandas = args["data_frame"] - df_pandas = pandas.api.interchange.from_dataframe(df_not_pandas) + try: + df_pandas = pandas.api.interchange.from_dataframe(df_not_pandas) + except (ImportError, NotImplementedError) as exc: + # temporary workaround; developers of third-party libraries themselves + # should try a different implementation, if available. For example: + # def __dataframe__(self, ...): + # if not some_condition: + # self.to_pandas(...) + if not hasattr(df_not_pandas, "to_pandas"): + raise exc + df_not_pandas.to_pandas() args["data_frame"] = df_pandas elif hasattr(args["data_frame"], "to_pandas"): args["data_frame"] = args["data_frame"].to_pandas() From c6deacda91515f7ddde8331583dd330aeb543027 Mon Sep 17 00:00:00 2001 From: Anatoly Myachev Date: Thu, 29 Jun 2023 23:07:57 +0200 Subject: [PATCH 15/15] Update packages/python/plotly/plotly/express/_core.py Co-authored-by: Alex Johnson --- packages/python/plotly/plotly/express/_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/python/plotly/plotly/express/_core.py b/packages/python/plotly/plotly/express/_core.py index 3dc7b9ccf3e..452c0a7ff79 100644 --- a/packages/python/plotly/plotly/express/_core.py +++ b/packages/python/plotly/plotly/express/_core.py @@ -1324,7 +1324,7 @@ def build_dataframe(args, constructor): # self.to_pandas(...) if not hasattr(df_not_pandas, "to_pandas"): raise exc - df_not_pandas.to_pandas() + df_pandas = df_not_pandas.to_pandas() args["data_frame"] = df_pandas elif hasattr(args["data_frame"], "to_pandas"): args["data_frame"] = args["data_frame"].to_pandas()