From 1b6c47717c7b6a77c0cbc7cf7bd5955bbd7a9353 Mon Sep 17 00:00:00 2001 From: Christopher Pruijsen Date: Sun, 13 Sep 2026 09:51:54 +0000 Subject: [PATCH 1/2] Fix Plotly Express mutating lists passed to x or y in wide mode args['wide_variable'] aliased the user-supplied x/y list, and process_args_into_dataframe replaces its elements with column-name strings in place. Copy it into a fresh list (this also accepts tuples, which previously failed on item assignment). Fixes #4117 --- CHANGELOG.md | 1 + plotly/express/_core.py | 5 +++-- tests/test_optional/test_px/test_px_wide.py | 13 +++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2b2594c9e3..1bbee1a1fd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Fix the sphinx-gallery scraper so that it generates thumbnails for figures shown with `fig.show()` or displayed as the last expression of a code block [[#5701](https://github.com/plotly/plotly.py/pull/5701)], with thanks to @larsoner for the contribution! - The scaper now warns once (instead of failing the build) when static image export is unavailable - The sphinx-gallery scraper no longer scrapes files belonging to other examples during parallel builds [[#5701](https://github.com/plotly/plotly.py/pull/5701)], with thanks to @larsoner for the contribution! +- Fix Plotly Express mutating lists passed to the `x` or `y` arguments in wide mode [[#4117](https://github.com/plotly/plotly.py/issues/4117)] ### Updated - Update plotly.js from version 4.0.0 to version 4.1.1 [[#5722](https://github.com/plotly/plotly.py/pull/5722), [#5730](https://github.com/plotly/plotly.py/pull/5730)]. See the plotly.js release notes for [v4.1.0](https://github.com/plotly/plotly.js/releases/tag/v4.1.0) and [v4.1.1](https://github.com/plotly/plotly.js/releases/tag/v4.1.1) for details. Notable changes include: diff --git a/plotly/express/_core.py b/plotly/express/_core.py index f26d72390dc..fa480a27d3c 100644 --- a/plotly/express/_core.py +++ b/plotly/express/_core.py @@ -1687,8 +1687,9 @@ def build_dataframe(args, constructor): args["wide_variable"] = args["y"] if wide_y else args["x"] if df_provided and is_pd_like and args["wide_variable"] is columns: var_name = columns.name - if is_pd_like and isinstance(args["wide_variable"], native_namespace.Index): - args["wide_variable"] = list(args["wide_variable"]) + # copy into a new list so that the list provided by the user for + # x or y is not mutated when wide_variable's entries are replaced + args["wide_variable"] = list(args["wide_variable"]) if var_name in [None, "value", "index"] or ( df_provided and var_name in columns ): diff --git a/tests/test_optional/test_px/test_px_wide.py b/tests/test_optional/test_px/test_px_wide.py index 88e1fd0278b..2f8a5edcac9 100644 --- a/tests/test_optional/test_px/test_px_wide.py +++ b/tests/test_optional/test_px/test_px_wide.py @@ -890,3 +890,16 @@ def test_no_pd_perf_warning(): if issubclass(warn.category, pd.errors.PerformanceWarning) ] assert len(performance_warnings) == 0, "PerformanceWarning(s) raised!" + + +def test_wide_mode_does_not_mutate_x_or_y(): + # https://github.com/plotly/plotly.py/issues/4117 + df = pd.DataFrame(dict(a=[1, 2, 3], b=[4, 5, 6], c=[7, 8, 9])) + for arg in ["x", "y"]: + cols = ["a", "b"] + px.bar(df, **{arg: cols}) + assert cols == ["a", "b"] + cols = [0, 1] + df_int = pd.DataFrame([[1, 2], [3, 4]]) + px.histogram(df_int, x=cols) + assert cols == [0, 1] From 756156d3b4f3913d76863bcd73c5a69edd5309d6 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Thu, 8 Oct 2026 12:53:14 -0600 Subject: [PATCH 2/2] Update CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 480c43e8896..80125b56ad2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Fix `mpl_to_plotly` showing unwanted legends with placeholder or internal label entries (`_nolegend_`, `_childN`) by defaulting `layout.showlegend=False` unless the matplotlib figure explicitly defines a legend [[#5699](https://github.com/plotly/plotly.py/pull/5699)], with thanks to @robertoffmoura for the contribution! - Fix `mpl_to_plotly` crashing on figures with fully transparent or alpha-carrying colors, such as boxplot outlier markers (`facecolor="none"`), rgba line colors, and `color="none"` text and axis labels; all matplotlib colors are now exported through a single helper that maps `"none"` to transparent rgba and keeps or overrides alpha as matplotlib does [[#5700](https://github.com/plotly/plotly.py/pull/5700)], with thanks to @robertoffmoura for the contribution! - Export tick label colors to the plotly tickfont in `mpl_to_plotly` [[#5716](https://github.com/plotly/plotly.py/pull/5716)], with thanks to @robertoffmoura for the contribution! +- Fix Plotly Express mutating lists passed to the `x` or `y` arguments in wide mode [[#5727](https://github.com/plotly/plotly.py/pull/5727)], with thanks to @cpruijsen for the contribution! - Fix `px.sunburst`, `px.treemap` and `px.icicle` listing sectors in a different order on every run when `path` is used with a Polars DataFrame; sectors now follow their order of first appearance for all dataframe backends [[#5766](https://github.com/plotly/plotly.py/pull/5766)], with thanks to @Irahan2 for the contribution! - Fix `mpl_to_plotly` conversion of matplotlib contour lines and line collections: close contour rings ending with `Z` codes, support dash styles, convert date x-axes, separate disjoint subpaths with `None` separators, hide line collection traces from the legend by default, and group consecutive same-style lines into single traces [[#5770](https://github.com/plotly/plotly.py/pull/5770)], with thanks to @robertoffmoura for the contribution! @@ -28,7 +29,6 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Fix the sphinx-gallery scraper so that it generates thumbnails for figures shown with `fig.show()` or displayed as the last expression of a code block [[#5701](https://github.com/plotly/plotly.py/pull/5701)], with thanks to @larsoner for the contribution! - The scaper now warns once (instead of failing the build) when static image export is unavailable - The sphinx-gallery scraper no longer scrapes files belonging to other examples during parallel builds [[#5701](https://github.com/plotly/plotly.py/pull/5701)], with thanks to @larsoner for the contribution! -- Fix Plotly Express mutating lists passed to the `x` or `y` arguments in wide mode [[#4117](https://github.com/plotly/plotly.py/issues/4117)] ### Updated - Update plotly.js from version 4.0.0 to version 4.1.1 [[#5722](https://github.com/plotly/plotly.py/pull/5722), [#5730](https://github.com/plotly/plotly.py/pull/5730)]. See the plotly.js release notes for [v4.1.0](https://github.com/plotly/plotly.js/releases/tag/v4.1.0) and [v4.1.1](https://github.com/plotly/plotly.js/releases/tag/v4.1.1) for details. Notable changes include: