xarray-plotly 0.0.11__tar.gz → 0.0.12__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/.github/workflows/ci.yml +3 -0
  2. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/PKG-INFO +1 -1
  3. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/examples/kwargs.ipynb +65 -0
  4. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/tests/test_accessor.py +125 -0
  5. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly/__init__.py +4 -3
  6. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly/accessor.py +46 -1
  7. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly/common.py +73 -4
  8. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly/config.py +7 -7
  9. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly/figures.py +7 -5
  10. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly/plotting.py +73 -1
  11. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly.egg-info/PKG-INFO +1 -1
  12. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/.github/dependabot.yml +0 -0
  13. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/.github/workflows/dependabot-auto-merge.yml +0 -0
  14. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/.github/workflows/docs.yml +0 -0
  15. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/.github/workflows/release.yml +0 -0
  16. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/.gitignore +0 -0
  17. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/.pre-commit-config.yaml +0 -0
  18. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/CONTRIBUTING.md +0 -0
  19. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/LICENSE +0 -0
  20. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/README.md +0 -0
  21. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/api.md +0 -0
  22. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/examples/combining.ipynb +0 -0
  23. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/examples/datasets.ipynb +0 -0
  24. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/examples/dimensions.ipynb +0 -0
  25. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/examples/fast_bar.ipynb +0 -0
  26. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/examples/figure.ipynb +0 -0
  27. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/examples/manipulation.ipynb +0 -0
  28. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/examples/plot-types.ipynb +0 -0
  29. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/getting-started.ipynb +0 -0
  30. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/index.md +0 -0
  31. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/mkdocs.yml +0 -0
  32. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/pyproject.toml +0 -0
  33. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/setup.cfg +0 -0
  34. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/tests/__init__.py +0 -0
  35. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/tests/test_common.py +0 -0
  36. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/tests/test_config.py +0 -0
  37. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/tests/test_figures.py +0 -0
  38. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly/py.typed +0 -0
  39. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly.egg-info/SOURCES.txt +0 -0
  40. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly.egg-info/dependency_links.txt +0 -0
  41. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly.egg-info/requires.txt +0 -0
  42. {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly.egg-info/top_level.txt +0 -0
@@ -31,6 +31,9 @@ jobs:
31
31
  - name: Format check
32
32
  run: uv run ruff format --check .
33
33
 
34
+ - name: Type check
35
+ run: uv run mypy xarray_plotly
36
+
34
37
  - name: Test
35
38
  run: uv run pytest --cov=xarray_plotly --cov-report=xml
36
39
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xarray_plotly
3
- Version: 0.0.11
3
+ Version: 0.0.12
4
4
  Summary: Interactive Plotly Express plotting accessor for xarray
5
5
  Author: Felix
6
6
  License: MIT
@@ -159,6 +159,71 @@
159
159
  "xpx(change).imshow(color_continuous_scale=\"RdBu_r\", color_continuous_midpoint=0)"
160
160
  ]
161
161
  },
162
+ {
163
+ "cell_type": "markdown",
164
+ "metadata": {},
165
+ "source": [
166
+ "## colors (unified parameter)\n",
167
+ "\n",
168
+ "The `colors` parameter provides a simpler way to set colors without remembering the exact Plotly parameter name. It automatically maps to the correct parameter based on the input type:\n",
169
+ "\n",
170
+ "| Input | Maps To |\n",
171
+ "|-------|---------|\n",
172
+ "| `\"Viridis\"` (continuous scale name) | `color_continuous_scale` |\n",
173
+ "| `\"D3\"` (qualitative palette name) | `color_discrete_sequence` |\n",
174
+ "| `[\"red\", \"blue\"]` (list) | `color_discrete_sequence` |\n",
175
+ "| `{\"A\": \"red\"}` (dict) | `color_discrete_map` |"
176
+ ]
177
+ },
178
+ {
179
+ "cell_type": "code",
180
+ "execution_count": null,
181
+ "metadata": {},
182
+ "outputs": [],
183
+ "source": [
184
+ "# Named qualitative palette\n",
185
+ "xpx(stocks).line(colors=\"D3\")"
186
+ ]
187
+ },
188
+ {
189
+ "cell_type": "code",
190
+ "execution_count": null,
191
+ "metadata": {},
192
+ "outputs": [],
193
+ "source": [
194
+ "# List of custom colors\n",
195
+ "xpx(stocks).line(colors=[\"#E63946\", \"#457B9D\", \"#2A9D8F\", \"#E9C46A\", \"#F4A261\"])"
196
+ ]
197
+ },
198
+ {
199
+ "cell_type": "code",
200
+ "execution_count": null,
201
+ "metadata": {},
202
+ "outputs": [],
203
+ "source": [
204
+ "# Dict for explicit mapping\n",
205
+ "xpx(stocks).line(\n",
206
+ " colors={\n",
207
+ " \"GOOG\": \"red\",\n",
208
+ " \"AAPL\": \"blue\",\n",
209
+ " \"AMZN\": \"green\",\n",
210
+ " \"FB\": \"purple\",\n",
211
+ " \"NFLX\": \"orange\",\n",
212
+ " \"MSFT\": \"brown\",\n",
213
+ " }\n",
214
+ ")"
215
+ ]
216
+ },
217
+ {
218
+ "cell_type": "code",
219
+ "execution_count": null,
220
+ "metadata": {},
221
+ "outputs": [],
222
+ "source": [
223
+ "# Continuous scale for heatmaps\n",
224
+ "xpx(stocks).imshow(colors=\"Plasma\")"
225
+ ]
226
+ },
162
227
  {
163
228
  "cell_type": "markdown",
164
229
  "metadata": {},
@@ -401,3 +401,128 @@ class TestImshowBounds:
401
401
  coloraxis = fig.layout.coloraxis
402
402
  assert coloraxis.cmin == 0.0
403
403
  assert coloraxis.cmax == 70.0
404
+
405
+
406
+ class TestColorsParameter:
407
+ """Tests for the unified colors parameter."""
408
+
409
+ @pytest.fixture(autouse=True)
410
+ def setup(self) -> None:
411
+ """Create test DataArrays."""
412
+ self.da = xr.DataArray(
413
+ np.random.rand(10, 3),
414
+ dims=["time", "city"],
415
+ coords={"city": ["A", "B", "C"]},
416
+ )
417
+
418
+ def test_colors_list_sets_discrete_sequence(self) -> None:
419
+ """Test that a list of colors sets color_discrete_sequence."""
420
+ fig = self.da.plotly.line(colors=["red", "blue", "green"])
421
+ # Check that traces have the expected colors
422
+ assert len(fig.data) == 3
423
+ assert fig.data[0].line.color == "red"
424
+ assert fig.data[1].line.color == "blue"
425
+ assert fig.data[2].line.color == "green"
426
+
427
+ def test_colors_dict_sets_discrete_map(self) -> None:
428
+ """Test that a dict sets color_discrete_map."""
429
+ fig = self.da.plotly.line(colors={"A": "red", "B": "blue", "C": "green"})
430
+ # Traces should be colored according to the mapping
431
+ assert len(fig.data) == 3
432
+ # Find traces by name and check their color
433
+ colors_by_name = {trace.name: trace.line.color for trace in fig.data}
434
+ assert colors_by_name["A"] == "red"
435
+ assert colors_by_name["B"] == "blue"
436
+ assert colors_by_name["C"] == "green"
437
+
438
+ def test_colors_continuous_scale_string(self) -> None:
439
+ """Test that a continuous scale name sets color_continuous_scale."""
440
+ da = xr.DataArray(
441
+ np.random.rand(50, 2),
442
+ dims=["point", "coord"],
443
+ coords={"coord": ["x", "y"]},
444
+ )
445
+ fig = da.plotly.scatter(y="coord", x="point", color="value", colors="Viridis")
446
+ # Plotly Express uses coloraxis in the layout for continuous scales
447
+ # Check that the colorscale was applied to the coloraxis
448
+ assert fig.layout.coloraxis.colorscale is not None
449
+ colorscale = fig.layout.coloraxis.colorscale
450
+ # Viridis should be in the colorscale definition
451
+ assert any("viridis" in str(c).lower() for c in colorscale) or len(colorscale) > 0
452
+
453
+ def test_colors_qualitative_palette_string(self) -> None:
454
+ """Test that a qualitative palette name sets color_discrete_sequence."""
455
+ import plotly.express as px
456
+
457
+ fig = self.da.plotly.line(colors="D3")
458
+ # D3 palette should be applied - check first trace color is from D3
459
+ d3_colors = px.colors.qualitative.D3
460
+ assert fig.data[0].line.color in d3_colors
461
+
462
+ def test_colors_ignored_with_warning_when_px_kwargs_present(self) -> None:
463
+ """Test that colors is ignored with warning when color_* kwargs are present."""
464
+ import warnings
465
+
466
+ with warnings.catch_warnings(record=True) as w:
467
+ warnings.simplefilter("always")
468
+ fig = self.da.plotly.line(
469
+ colors="D3", color_discrete_sequence=["orange", "purple", "cyan"]
470
+ )
471
+ # Should have raised a warning about colors being ignored
472
+ assert any(
473
+ "colors" in str(m.message).lower() and "ignored" in str(m.message).lower()
474
+ for m in w
475
+ ), "Expected warning about 'colors' being 'ignored' not found"
476
+ # The explicit px_kwargs should take precedence
477
+ assert fig.data[0].line.color == "orange"
478
+
479
+ def test_colors_none_uses_defaults(self) -> None:
480
+ """Test that colors=None uses Plotly defaults."""
481
+ fig1 = self.da.plotly.line(colors=None)
482
+ fig2 = self.da.plotly.line()
483
+ # Both should produce the same result
484
+ assert fig1.data[0].line.color == fig2.data[0].line.color
485
+
486
+ def test_colors_works_with_bar(self) -> None:
487
+ """Test colors parameter with bar chart."""
488
+ fig = self.da.plotly.bar(colors=["#e41a1c", "#377eb8", "#4daf4a"])
489
+ assert fig.data[0].marker.color == "#e41a1c"
490
+
491
+ def test_colors_works_with_area(self) -> None:
492
+ """Test colors parameter with area chart."""
493
+ fig = self.da.plotly.area(colors=["red", "green", "blue"])
494
+ assert len(fig.data) == 3
495
+
496
+ def test_colors_works_with_scatter(self) -> None:
497
+ """Test colors parameter with scatter plot."""
498
+ fig = self.da.plotly.scatter(colors=["red", "green", "blue"])
499
+ assert len(fig.data) == 3
500
+
501
+ def test_colors_works_with_imshow(self) -> None:
502
+ """Test colors parameter with imshow (continuous scale)."""
503
+ da = xr.DataArray(np.random.rand(10, 10), dims=["y", "x"])
504
+ fig = da.plotly.imshow(colors="RdBu")
505
+ # Plotly Express uses coloraxis in the layout for continuous scales
506
+ assert fig.layout.coloraxis.colorscale is not None
507
+ colorscale = fig.layout.coloraxis.colorscale
508
+ # RdBu should be in the colorscale definition
509
+ assert any("rdbu" in str(c).lower() for c in colorscale) or len(colorscale) > 0
510
+
511
+ def test_colors_works_with_pie(self) -> None:
512
+ """Test colors parameter with pie chart."""
513
+ da = xr.DataArray([30, 40, 30], dims=["category"], coords={"category": ["A", "B", "C"]})
514
+ fig = da.plotly.pie(colors={"A": "red", "B": "blue", "C": "green"})
515
+ assert isinstance(fig, go.Figure)
516
+
517
+ def test_colors_works_with_dataset(self) -> None:
518
+ """Test colors parameter works with Dataset accessor."""
519
+ ds = xr.Dataset(
520
+ {
521
+ "temp": (["time"], np.random.rand(10)),
522
+ "precip": (["time"], np.random.rand(10)),
523
+ }
524
+ )
525
+ fig = ds.plotly.line(colors=["red", "blue"])
526
+ assert len(fig.data) == 2
527
+ assert fig.data[0].line.color == "red"
528
+ assert fig.data[1].line.color == "blue"
@@ -52,7 +52,7 @@ from xarray import DataArray, Dataset, register_dataarray_accessor, register_dat
52
52
 
53
53
  from xarray_plotly import config
54
54
  from xarray_plotly.accessor import DataArrayPlotlyAccessor, DatasetPlotlyAccessor
55
- from xarray_plotly.common import SLOT_ORDERS, auto
55
+ from xarray_plotly.common import SLOT_ORDERS, Colors, auto
56
56
  from xarray_plotly.figures import (
57
57
  add_secondary_y,
58
58
  overlay,
@@ -61,6 +61,7 @@ from xarray_plotly.figures import (
61
61
 
62
62
  __all__ = [
63
63
  "SLOT_ORDERS",
64
+ "Colors",
64
65
  "add_secondary_y",
65
66
  "auto",
66
67
  "config",
@@ -110,5 +111,5 @@ def xpx(data: DataArray | Dataset) -> DataArrayPlotlyAccessor | DatasetPlotlyAcc
110
111
  __version__ = version("xarray_plotly")
111
112
 
112
113
  # Register the accessors
113
- register_dataarray_accessor("plotly")(DataArrayPlotlyAccessor)
114
- register_dataset_accessor("plotly")(DatasetPlotlyAccessor)
114
+ register_dataarray_accessor("plotly")(DataArrayPlotlyAccessor) # type: ignore[no-untyped-call]
115
+ register_dataset_accessor("plotly")(DatasetPlotlyAccessor) # type: ignore[no-untyped-call]
@@ -6,7 +6,7 @@ import plotly.graph_objects as go
6
6
  from xarray import DataArray, Dataset
7
7
 
8
8
  from xarray_plotly import plotting
9
- from xarray_plotly.common import SlotValue, auto
9
+ from xarray_plotly.common import Colors, SlotValue, auto
10
10
  from xarray_plotly.config import _options
11
11
 
12
12
 
@@ -53,6 +53,7 @@ class DataArrayPlotlyAccessor:
53
53
  facet_col: SlotValue = auto,
54
54
  facet_row: SlotValue = auto,
55
55
  animation_frame: SlotValue = auto,
56
+ colors: Colors = None,
56
57
  **px_kwargs: Any,
57
58
  ) -> go.Figure:
58
59
  """Create an interactive line plot.
@@ -67,6 +68,7 @@ class DataArrayPlotlyAccessor:
67
68
  facet_col: Dimension for subplot columns. Default: fifth dimension.
68
69
  facet_row: Dimension for subplot rows. Default: sixth dimension.
69
70
  animation_frame: Dimension for animation. Default: seventh dimension.
71
+ colors: Color specification (scale name, list, or dict). See module docs.
70
72
  **px_kwargs: Additional arguments passed to `plotly.express.line()`.
71
73
 
72
74
  Returns:
@@ -81,6 +83,7 @@ class DataArrayPlotlyAccessor:
81
83
  facet_col=facet_col,
82
84
  facet_row=facet_row,
83
85
  animation_frame=animation_frame,
86
+ colors=colors,
84
87
  **px_kwargs,
85
88
  )
86
89
 
@@ -93,6 +96,7 @@ class DataArrayPlotlyAccessor:
93
96
  facet_col: SlotValue = auto,
94
97
  facet_row: SlotValue = auto,
95
98
  animation_frame: SlotValue = auto,
99
+ colors: Colors = None,
96
100
  **px_kwargs: Any,
97
101
  ) -> go.Figure:
98
102
  """Create an interactive bar chart.
@@ -106,6 +110,7 @@ class DataArrayPlotlyAccessor:
106
110
  facet_col: Dimension for subplot columns. Default: fourth dimension.
107
111
  facet_row: Dimension for subplot rows. Default: fifth dimension.
108
112
  animation_frame: Dimension for animation. Default: sixth dimension.
113
+ colors: Color specification (scale name, list, or dict). See module docs.
109
114
  **px_kwargs: Additional arguments passed to `plotly.express.bar()`.
110
115
 
111
116
  Returns:
@@ -119,6 +124,7 @@ class DataArrayPlotlyAccessor:
119
124
  facet_col=facet_col,
120
125
  facet_row=facet_row,
121
126
  animation_frame=animation_frame,
127
+ colors=colors,
122
128
  **px_kwargs,
123
129
  )
124
130
 
@@ -131,6 +137,7 @@ class DataArrayPlotlyAccessor:
131
137
  facet_col: SlotValue = auto,
132
138
  facet_row: SlotValue = auto,
133
139
  animation_frame: SlotValue = auto,
140
+ colors: Colors = None,
134
141
  **px_kwargs: Any,
135
142
  ) -> go.Figure:
136
143
  """Create an interactive stacked area chart.
@@ -144,6 +151,7 @@ class DataArrayPlotlyAccessor:
144
151
  facet_col: Dimension for subplot columns. Default: fourth dimension.
145
152
  facet_row: Dimension for subplot rows. Default: fifth dimension.
146
153
  animation_frame: Dimension for animation. Default: sixth dimension.
154
+ colors: Color specification (scale name, list, or dict). See module docs.
147
155
  **px_kwargs: Additional arguments passed to `plotly.express.area()`.
148
156
 
149
157
  Returns:
@@ -157,6 +165,7 @@ class DataArrayPlotlyAccessor:
157
165
  facet_col=facet_col,
158
166
  facet_row=facet_row,
159
167
  animation_frame=animation_frame,
168
+ colors=colors,
160
169
  **px_kwargs,
161
170
  )
162
171
 
@@ -168,6 +177,7 @@ class DataArrayPlotlyAccessor:
168
177
  facet_col: SlotValue = auto,
169
178
  facet_row: SlotValue = auto,
170
179
  animation_frame: SlotValue = auto,
180
+ colors: Colors = None,
171
181
  **px_kwargs: Any,
172
182
  ) -> go.Figure:
173
183
  """Create a bar-like chart using stacked areas for better performance.
@@ -180,6 +190,7 @@ class DataArrayPlotlyAccessor:
180
190
  facet_col: Dimension for subplot columns. Default: third dimension.
181
191
  facet_row: Dimension for subplot rows. Default: fourth dimension.
182
192
  animation_frame: Dimension for animation. Default: fifth dimension.
193
+ colors: Color specification (scale name, list, or dict). See module docs.
183
194
  **px_kwargs: Additional arguments passed to `plotly.express.area()`.
184
195
 
185
196
  Returns:
@@ -192,6 +203,7 @@ class DataArrayPlotlyAccessor:
192
203
  facet_col=facet_col,
193
204
  facet_row=facet_row,
194
205
  animation_frame=animation_frame,
206
+ colors=colors,
195
207
  **px_kwargs,
196
208
  )
197
209
 
@@ -205,6 +217,7 @@ class DataArrayPlotlyAccessor:
205
217
  facet_col: SlotValue = auto,
206
218
  facet_row: SlotValue = auto,
207
219
  animation_frame: SlotValue = auto,
220
+ colors: Colors = None,
208
221
  **px_kwargs: Any,
209
222
  ) -> go.Figure:
210
223
  """Create an interactive scatter plot.
@@ -223,6 +236,7 @@ class DataArrayPlotlyAccessor:
223
236
  facet_col: Dimension for subplot columns. Default: fourth dimension.
224
237
  facet_row: Dimension for subplot rows. Default: fifth dimension.
225
238
  animation_frame: Dimension for animation. Default: sixth dimension.
239
+ colors: Color specification (scale name, list, or dict). See module docs.
226
240
  **px_kwargs: Additional arguments passed to `plotly.express.scatter()`.
227
241
 
228
242
  Returns:
@@ -237,6 +251,7 @@ class DataArrayPlotlyAccessor:
237
251
  facet_col=facet_col,
238
252
  facet_row=facet_row,
239
253
  animation_frame=animation_frame,
254
+ colors=colors,
240
255
  **px_kwargs,
241
256
  )
242
257
 
@@ -248,6 +263,7 @@ class DataArrayPlotlyAccessor:
248
263
  facet_col: SlotValue = None,
249
264
  facet_row: SlotValue = None,
250
265
  animation_frame: SlotValue = None,
266
+ colors: Colors = None,
251
267
  **px_kwargs: Any,
252
268
  ) -> go.Figure:
253
269
  """Create an interactive box plot.
@@ -263,6 +279,7 @@ class DataArrayPlotlyAccessor:
263
279
  facet_col: Dimension for subplot columns. Default: None (aggregated).
264
280
  facet_row: Dimension for subplot rows. Default: None (aggregated).
265
281
  animation_frame: Dimension for animation. Default: None (aggregated).
282
+ colors: Color specification (scale name, list, or dict). See module docs.
266
283
  **px_kwargs: Additional arguments passed to `plotly.express.box()`.
267
284
 
268
285
  Returns:
@@ -275,6 +292,7 @@ class DataArrayPlotlyAccessor:
275
292
  facet_col=facet_col,
276
293
  facet_row=facet_row,
277
294
  animation_frame=animation_frame,
295
+ colors=colors,
278
296
  **px_kwargs,
279
297
  )
280
298
 
@@ -286,6 +304,7 @@ class DataArrayPlotlyAccessor:
286
304
  facet_col: SlotValue = auto,
287
305
  animation_frame: SlotValue = auto,
288
306
  robust: bool = False,
307
+ colors: Colors = None,
289
308
  **px_kwargs: Any,
290
309
  ) -> go.Figure:
291
310
  """Create an interactive heatmap image.
@@ -303,6 +322,7 @@ class DataArrayPlotlyAccessor:
303
322
  facet_col: Dimension for subplot columns. Default: third dimension.
304
323
  animation_frame: Dimension for animation. Default: fourth dimension.
305
324
  robust: If True, use 2nd/98th percentiles for color bounds (handles outliers).
325
+ colors: Color scale name (e.g., "Viridis", "RdBu"). See module docs.
306
326
  **px_kwargs: Additional arguments passed to `plotly.express.imshow()`.
307
327
  Use `zmin` and `zmax` to manually set color scale bounds.
308
328
 
@@ -316,6 +336,7 @@ class DataArrayPlotlyAccessor:
316
336
  facet_col=facet_col,
317
337
  animation_frame=animation_frame,
318
338
  robust=robust,
339
+ colors=colors,
319
340
  **px_kwargs,
320
341
  )
321
342
 
@@ -326,6 +347,7 @@ class DataArrayPlotlyAccessor:
326
347
  color: SlotValue = None,
327
348
  facet_col: SlotValue = auto,
328
349
  facet_row: SlotValue = auto,
350
+ colors: Colors = None,
329
351
  **px_kwargs: Any,
330
352
  ) -> go.Figure:
331
353
  """Create an interactive pie chart.
@@ -337,6 +359,7 @@ class DataArrayPlotlyAccessor:
337
359
  color: Dimension for color grouping. Default: None (uses names).
338
360
  facet_col: Dimension for subplot columns. Default: second dimension.
339
361
  facet_row: Dimension for subplot rows. Default: third dimension.
362
+ colors: Color specification (scale name, list, or dict). See module docs.
340
363
  **px_kwargs: Additional arguments passed to `plotly.express.pie()`.
341
364
 
342
365
  Returns:
@@ -348,6 +371,7 @@ class DataArrayPlotlyAccessor:
348
371
  color=color,
349
372
  facet_col=facet_col,
350
373
  facet_row=facet_row,
374
+ colors=colors,
351
375
  **px_kwargs,
352
376
  )
353
377
 
@@ -427,6 +451,7 @@ class DatasetPlotlyAccessor:
427
451
  facet_col: SlotValue = auto,
428
452
  facet_row: SlotValue = auto,
429
453
  animation_frame: SlotValue = auto,
454
+ colors: Colors = None,
430
455
  **px_kwargs: Any,
431
456
  ) -> go.Figure:
432
457
  """Create an interactive line plot.
@@ -440,6 +465,7 @@ class DatasetPlotlyAccessor:
440
465
  facet_col: Dimension for subplot columns.
441
466
  facet_row: Dimension for subplot rows.
442
467
  animation_frame: Dimension for animation.
468
+ colors: Color specification (scale name, list, or dict). See module docs.
443
469
  **px_kwargs: Additional arguments passed to `plotly.express.line()`.
444
470
 
445
471
  Returns:
@@ -455,6 +481,7 @@ class DatasetPlotlyAccessor:
455
481
  facet_col=facet_col,
456
482
  facet_row=facet_row,
457
483
  animation_frame=animation_frame,
484
+ colors=colors,
458
485
  **px_kwargs,
459
486
  )
460
487
 
@@ -468,6 +495,7 @@ class DatasetPlotlyAccessor:
468
495
  facet_col: SlotValue = auto,
469
496
  facet_row: SlotValue = auto,
470
497
  animation_frame: SlotValue = auto,
498
+ colors: Colors = None,
471
499
  **px_kwargs: Any,
472
500
  ) -> go.Figure:
473
501
  """Create an interactive bar chart.
@@ -480,6 +508,7 @@ class DatasetPlotlyAccessor:
480
508
  facet_col: Dimension for subplot columns.
481
509
  facet_row: Dimension for subplot rows.
482
510
  animation_frame: Dimension for animation.
511
+ colors: Color specification (scale name, list, or dict). See module docs.
483
512
  **px_kwargs: Additional arguments passed to `plotly.express.bar()`.
484
513
 
485
514
  Returns:
@@ -494,6 +523,7 @@ class DatasetPlotlyAccessor:
494
523
  facet_col=facet_col,
495
524
  facet_row=facet_row,
496
525
  animation_frame=animation_frame,
526
+ colors=colors,
497
527
  **px_kwargs,
498
528
  )
499
529
 
@@ -507,6 +537,7 @@ class DatasetPlotlyAccessor:
507
537
  facet_col: SlotValue = auto,
508
538
  facet_row: SlotValue = auto,
509
539
  animation_frame: SlotValue = auto,
540
+ colors: Colors = None,
510
541
  **px_kwargs: Any,
511
542
  ) -> go.Figure:
512
543
  """Create an interactive stacked area chart.
@@ -519,6 +550,7 @@ class DatasetPlotlyAccessor:
519
550
  facet_col: Dimension for subplot columns.
520
551
  facet_row: Dimension for subplot rows.
521
552
  animation_frame: Dimension for animation.
553
+ colors: Color specification (scale name, list, or dict). See module docs.
522
554
  **px_kwargs: Additional arguments passed to `plotly.express.area()`.
523
555
 
524
556
  Returns:
@@ -533,6 +565,7 @@ class DatasetPlotlyAccessor:
533
565
  facet_col=facet_col,
534
566
  facet_row=facet_row,
535
567
  animation_frame=animation_frame,
568
+ colors=colors,
536
569
  **px_kwargs,
537
570
  )
538
571
 
@@ -545,6 +578,7 @@ class DatasetPlotlyAccessor:
545
578
  facet_col: SlotValue = auto,
546
579
  facet_row: SlotValue = auto,
547
580
  animation_frame: SlotValue = auto,
581
+ colors: Colors = None,
548
582
  **px_kwargs: Any,
549
583
  ) -> go.Figure:
550
584
  """Create a bar-like chart using stacked areas for better performance.
@@ -556,6 +590,7 @@ class DatasetPlotlyAccessor:
556
590
  facet_col: Dimension for subplot columns.
557
591
  facet_row: Dimension for subplot rows.
558
592
  animation_frame: Dimension for animation.
593
+ colors: Color specification (scale name, list, or dict). See module docs.
559
594
  **px_kwargs: Additional arguments passed to `plotly.express.area()`.
560
595
 
561
596
  Returns:
@@ -569,6 +604,7 @@ class DatasetPlotlyAccessor:
569
604
  facet_col=facet_col,
570
605
  facet_row=facet_row,
571
606
  animation_frame=animation_frame,
607
+ colors=colors,
572
608
  **px_kwargs,
573
609
  )
574
610
 
@@ -583,6 +619,7 @@ class DatasetPlotlyAccessor:
583
619
  facet_col: SlotValue = auto,
584
620
  facet_row: SlotValue = auto,
585
621
  animation_frame: SlotValue = auto,
622
+ colors: Colors = None,
586
623
  **px_kwargs: Any,
587
624
  ) -> go.Figure:
588
625
  """Create an interactive scatter plot.
@@ -596,6 +633,7 @@ class DatasetPlotlyAccessor:
596
633
  facet_col: Dimension for subplot columns.
597
634
  facet_row: Dimension for subplot rows.
598
635
  animation_frame: Dimension for animation.
636
+ colors: Color specification (scale name, list, or dict). See module docs.
599
637
  **px_kwargs: Additional arguments passed to `plotly.express.scatter()`.
600
638
 
601
639
  Returns:
@@ -611,6 +649,7 @@ class DatasetPlotlyAccessor:
611
649
  facet_col=facet_col,
612
650
  facet_row=facet_row,
613
651
  animation_frame=animation_frame,
652
+ colors=colors,
614
653
  **px_kwargs,
615
654
  )
616
655
 
@@ -623,6 +662,7 @@ class DatasetPlotlyAccessor:
623
662
  facet_col: SlotValue = None,
624
663
  facet_row: SlotValue = None,
625
664
  animation_frame: SlotValue = None,
665
+ colors: Colors = None,
626
666
  **px_kwargs: Any,
627
667
  ) -> go.Figure:
628
668
  """Create an interactive box plot.
@@ -634,6 +674,7 @@ class DatasetPlotlyAccessor:
634
674
  facet_col: Dimension for subplot columns.
635
675
  facet_row: Dimension for subplot rows.
636
676
  animation_frame: Dimension for animation.
677
+ colors: Color specification (scale name, list, or dict). See module docs.
637
678
  **px_kwargs: Additional arguments passed to `plotly.express.box()`.
638
679
 
639
680
  Returns:
@@ -647,6 +688,7 @@ class DatasetPlotlyAccessor:
647
688
  facet_col=facet_col,
648
689
  facet_row=facet_row,
649
690
  animation_frame=animation_frame,
691
+ colors=colors,
650
692
  **px_kwargs,
651
693
  )
652
694
 
@@ -658,6 +700,7 @@ class DatasetPlotlyAccessor:
658
700
  color: SlotValue = None,
659
701
  facet_col: SlotValue = auto,
660
702
  facet_row: SlotValue = auto,
703
+ colors: Colors = None,
661
704
  **px_kwargs: Any,
662
705
  ) -> go.Figure:
663
706
  """Create an interactive pie chart.
@@ -668,6 +711,7 @@ class DatasetPlotlyAccessor:
668
711
  color: Dimension for color grouping.
669
712
  facet_col: Dimension for subplot columns.
670
713
  facet_row: Dimension for subplot rows.
714
+ colors: Color specification (scale name, list, or dict). See module docs.
671
715
  **px_kwargs: Additional arguments passed to `plotly.express.pie()`.
672
716
 
673
717
  Returns:
@@ -680,5 +724,6 @@ class DatasetPlotlyAccessor:
680
724
  color=color,
681
725
  facet_col=facet_col,
682
726
  facet_row=facet_row,
727
+ colors=colors,
683
728
  **px_kwargs,
684
729
  )
@@ -2,13 +2,16 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import TYPE_CHECKING
5
+ import functools
6
+ import warnings
7
+ from collections.abc import Hashable, Mapping, Sequence
8
+ from typing import TYPE_CHECKING, Any
9
+
10
+ import plotly.express as px
6
11
 
7
12
  from xarray_plotly.config import DEFAULT_SLOT_ORDERS, _options
8
13
 
9
14
  if TYPE_CHECKING:
10
- from collections.abc import Hashable, Sequence
11
-
12
15
  import pandas as pd
13
16
  from xarray import DataArray
14
17
 
@@ -27,6 +30,15 @@ auto = _AUTO()
27
30
  SlotValue = _AUTO | str | None
28
31
  """Type alias for slot values: auto, explicit dimension name, or None (skip)."""
29
32
 
33
+ Colors = str | Sequence[str] | Mapping[str, str] | None
34
+ """Type alias for unified colors parameter.
35
+
36
+ - str: Named color scale (e.g., "Viridis" for continuous, "D3" for discrete)
37
+ - Sequence[str]: List of colors for discrete sequence (e.g., ["red", "blue"])
38
+ - Mapping[str, str]: Explicit mapping of values to colors (e.g., {"A": "red"})
39
+ - None: Use Plotly defaults
40
+ """
41
+
30
42
  # Re-export for backward compatibility
31
43
  SLOT_ORDERS = DEFAULT_SLOT_ORDERS
32
44
  """Slot orders per plot type.
@@ -147,7 +159,7 @@ def to_dataframe(darray: DataArray) -> pd.DataFrame:
147
159
  return df
148
160
 
149
161
 
150
- def _get_label_from_attrs(attrs: dict, fallback: str) -> str:
162
+ def _get_label_from_attrs(attrs: dict[str, object], fallback: str) -> str:
151
163
  """Extract a label from xarray attributes based on current config.
152
164
 
153
165
  Args:
@@ -227,3 +239,60 @@ def build_labels(
227
239
  labels[value_col] = get_label(darray, "value")
228
240
 
229
241
  return labels
242
+
243
+
244
+ @functools.cache
245
+ def _get_qualitative_scale_names() -> frozenset[str]:
246
+ """Get all named qualitative (discrete) color scales from Plotly."""
247
+ return frozenset(
248
+ name
249
+ for name in dir(px.colors.qualitative)
250
+ if not name.startswith("_") and name[0].isupper()
251
+ )
252
+
253
+
254
+ def resolve_colors(colors: Colors, px_kwargs: dict[str, Any]) -> dict[str, Any]:
255
+ """Map unified `colors` parameter to appropriate Plotly px_kwargs.
256
+
257
+ Direct color_* kwargs take precedence and trigger a warning if
258
+ colors was also specified.
259
+
260
+ Args:
261
+ colors: Unified color specification (str, list, dict, or None).
262
+ px_kwargs: Existing kwargs to pass to Plotly Express.
263
+
264
+ Returns:
265
+ Updated px_kwargs with color parameters injected.
266
+ """
267
+ if colors is None:
268
+ return px_kwargs
269
+
270
+ # Check if any color_* kwarg is present - these take precedence
271
+ color_kwargs = [k for k in px_kwargs if k.startswith("color_")]
272
+ if color_kwargs:
273
+ warnings.warn(
274
+ f"`colors` parameter ignored because {color_kwargs[0]!r} "
275
+ f"was explicitly provided in px_kwargs.",
276
+ UserWarning,
277
+ stacklevel=3,
278
+ )
279
+ return px_kwargs
280
+
281
+ px_kwargs = px_kwargs.copy()
282
+
283
+ if isinstance(colors, str):
284
+ # Check if it's a qualitative (discrete) palette name
285
+ if colors in _get_qualitative_scale_names():
286
+ px_kwargs["color_discrete_sequence"] = getattr(px.colors.qualitative, colors)
287
+ else:
288
+ # Assume continuous scale
289
+ px_kwargs["color_continuous_scale"] = colors
290
+ elif isinstance(colors, Mapping):
291
+ px_kwargs["color_discrete_map"] = dict(colors)
292
+ elif isinstance(colors, Sequence):
293
+ px_kwargs["color_discrete_sequence"] = list(colors)
294
+ else:
295
+ msg = f"`colors` must be str, list, dict, or None, got {type(colors).__name__}"
296
+ raise TypeError(msg)
297
+
298
+ return px_kwargs
@@ -8,7 +8,7 @@ from __future__ import annotations
8
8
 
9
9
  from contextlib import contextmanager
10
10
  from dataclasses import dataclass, field
11
- from typing import TYPE_CHECKING, Any
11
+ from typing import TYPE_CHECKING, Any, cast
12
12
 
13
13
  if TYPE_CHECKING:
14
14
  from collections.abc import Generator
@@ -166,12 +166,12 @@ def set_options(
166
166
  yield
167
167
  finally:
168
168
  # Restore old values (modify in place)
169
- _options.label_use_long_name = old_values["label_use_long_name"]
170
- _options.label_use_standard_name = old_values["label_use_standard_name"]
171
- _options.label_include_units = old_values["label_include_units"]
172
- _options.label_unit_format = old_values["label_unit_format"]
173
- _options.slot_orders = old_values["slot_orders"]
174
- _options.dataset_variable_position = old_values["dataset_variable_position"]
169
+ _options.label_use_long_name = cast("bool", old_values["label_use_long_name"])
170
+ _options.label_use_standard_name = cast("bool", old_values["label_use_standard_name"])
171
+ _options.label_include_units = cast("bool", old_values["label_include_units"])
172
+ _options.label_unit_format = cast("str", old_values["label_unit_format"])
173
+ _options.slot_orders = cast("dict[str, tuple[str, ...]]", old_values["slot_orders"])
174
+ _options.dataset_variable_position = cast("int", old_values["dataset_variable_position"])
175
175
 
176
176
 
177
177
  def notebook(renderer: str = "notebook") -> None:
@@ -5,7 +5,7 @@ Helper functions for combining and manipulating Plotly figures.
5
5
  from __future__ import annotations
6
6
 
7
7
  import copy
8
- from typing import TYPE_CHECKING
8
+ from typing import TYPE_CHECKING, Any
9
9
 
10
10
  if TYPE_CHECKING:
11
11
  from collections.abc import Iterator
@@ -13,7 +13,7 @@ if TYPE_CHECKING:
13
13
  import plotly.graph_objects as go
14
14
 
15
15
 
16
- def _iter_all_traces(fig: go.Figure) -> Iterator:
16
+ def _iter_all_traces(fig: go.Figure) -> Iterator[Any]:
17
17
  """Iterate over all traces in a figure, including animation frames.
18
18
 
19
19
  Yields traces from fig.data first, then from each frame in fig.frames.
@@ -107,7 +107,7 @@ def _merge_frames(
107
107
  overlays: list[go.Figure],
108
108
  base_trace_count: int,
109
109
  overlay_trace_counts: list[int],
110
- ) -> list:
110
+ ) -> list[go.Frame]:
111
111
  """Merge animation frames from base and overlay figures.
112
112
 
113
113
  Args:
@@ -360,7 +360,7 @@ def _merge_secondary_y_frames(
360
360
  base: go.Figure,
361
361
  secondary: go.Figure,
362
362
  y_mapping: dict[str, str],
363
- ) -> list:
363
+ ) -> list[go.Frame]:
364
364
  """Merge animation frames for secondary y-axis combination.
365
365
 
366
366
  Args:
@@ -411,7 +411,9 @@ def _merge_secondary_y_frames(
411
411
  return merged_frames
412
412
 
413
413
 
414
- def update_traces(fig: go.Figure, selector: dict | None = None, **kwargs) -> go.Figure:
414
+ def update_traces(
415
+ fig: go.Figure, selector: dict[str, Any] | None = None, **kwargs: Any
416
+ ) -> go.Figure:
415
417
  """Update traces in both base figure and all animation frames.
416
418
 
417
419
  Plotly's `update_traces()` only updates the base figure, not animation frames.
@@ -8,15 +8,18 @@ import warnings
8
8
  from typing import TYPE_CHECKING, Any
9
9
 
10
10
  import numpy as np
11
+ import numpy.typing as npt
11
12
  import plotly.express as px
12
13
 
13
14
  from xarray_plotly.common import (
15
+ Colors,
14
16
  SlotValue,
15
17
  assign_slots,
16
18
  auto,
17
19
  build_labels,
18
20
  get_label,
19
21
  get_value_col,
22
+ resolve_colors,
20
23
  to_dataframe,
21
24
  )
22
25
  from xarray_plotly.figures import (
@@ -38,6 +41,7 @@ def line(
38
41
  facet_col: SlotValue = auto,
39
42
  facet_row: SlotValue = auto,
40
43
  animation_frame: SlotValue = auto,
44
+ colors: Colors = None,
41
45
  **px_kwargs: Any,
42
46
  ) -> go.Figure:
43
47
  """
@@ -64,6 +68,13 @@ def line(
64
68
  Dimension for subplot rows. Default: sixth dimension.
65
69
  animation_frame
66
70
  Dimension for animation. Default: seventh dimension.
71
+ colors
72
+ Unified color specification. Can be:
73
+ - A named continuous scale (e.g., "Viridis")
74
+ - A named discrete palette (e.g., "D3", "Plotly")
75
+ - A list of colors (e.g., ["red", "blue", "green"])
76
+ - A dict mapping values to colors (e.g., {"A": "red", "B": "blue"})
77
+ Explicit color_* kwargs in px_kwargs take precedence.
67
78
  **px_kwargs
68
79
  Additional arguments passed to `plotly.express.line()`.
69
80
 
@@ -71,6 +82,7 @@ def line(
71
82
  -------
72
83
  plotly.graph_objects.Figure
73
84
  """
85
+ px_kwargs = resolve_colors(colors, px_kwargs)
74
86
  slots = assign_slots(
75
87
  list(darray.dims),
76
88
  "line",
@@ -111,6 +123,7 @@ def bar(
111
123
  facet_col: SlotValue = auto,
112
124
  facet_row: SlotValue = auto,
113
125
  animation_frame: SlotValue = auto,
126
+ colors: Colors = None,
114
127
  **px_kwargs: Any,
115
128
  ) -> go.Figure:
116
129
  """
@@ -135,6 +148,13 @@ def bar(
135
148
  Dimension for subplot rows. Default: fifth dimension.
136
149
  animation_frame
137
150
  Dimension for animation. Default: sixth dimension.
151
+ colors
152
+ Unified color specification. Can be:
153
+ - A named continuous scale (e.g., "Viridis")
154
+ - A named discrete palette (e.g., "D3", "Plotly")
155
+ - A list of colors (e.g., ["red", "blue", "green"])
156
+ - A dict mapping values to colors (e.g., {"A": "red", "B": "blue"})
157
+ Explicit color_* kwargs in px_kwargs take precedence.
138
158
  **px_kwargs
139
159
  Additional arguments passed to `plotly.express.bar()`.
140
160
 
@@ -142,6 +162,7 @@ def bar(
142
162
  -------
143
163
  plotly.graph_objects.Figure
144
164
  """
165
+ px_kwargs = resolve_colors(colors, px_kwargs)
145
166
  slots = assign_slots(
146
167
  list(darray.dims),
147
168
  "bar",
@@ -171,7 +192,7 @@ def bar(
171
192
  )
172
193
 
173
194
 
174
- def _classify_trace_sign(y_values: np.ndarray) -> str:
195
+ def _classify_trace_sign(y_values: npt.ArrayLike) -> str:
175
196
  """Classify a trace as 'positive', 'negative', or 'mixed' based on its values."""
176
197
  y_arr = np.asarray(y_values)
177
198
  y_clean = y_arr[np.isfinite(y_arr) & (np.abs(y_arr) > 1e-9)]
@@ -263,6 +284,7 @@ def fast_bar(
263
284
  facet_col: SlotValue = auto,
264
285
  facet_row: SlotValue = auto,
265
286
  animation_frame: SlotValue = auto,
287
+ colors: Colors = None,
266
288
  **px_kwargs: Any,
267
289
  ) -> go.Figure:
268
290
  """
@@ -293,6 +315,13 @@ def fast_bar(
293
315
  Dimension for subplot rows. Default: fourth dimension.
294
316
  animation_frame
295
317
  Dimension for animation. Default: fifth dimension.
318
+ colors
319
+ Unified color specification. Can be:
320
+ - A named continuous scale (e.g., "Viridis")
321
+ - A named discrete palette (e.g., "D3", "Plotly")
322
+ - A list of colors (e.g., ["red", "blue", "green"])
323
+ - A dict mapping values to colors (e.g., {"A": "red", "B": "blue"})
324
+ Explicit color_* kwargs in px_kwargs take precedence.
296
325
  **px_kwargs
297
326
  Additional arguments passed to `plotly.express.area()`.
298
327
 
@@ -300,6 +329,7 @@ def fast_bar(
300
329
  -------
301
330
  plotly.graph_objects.Figure
302
331
  """
332
+ px_kwargs = resolve_colors(colors, px_kwargs)
303
333
  slots = assign_slots(
304
334
  list(darray.dims),
305
335
  "fast_bar",
@@ -341,6 +371,7 @@ def area(
341
371
  facet_col: SlotValue = auto,
342
372
  facet_row: SlotValue = auto,
343
373
  animation_frame: SlotValue = auto,
374
+ colors: Colors = None,
344
375
  **px_kwargs: Any,
345
376
  ) -> go.Figure:
346
377
  """
@@ -365,6 +396,13 @@ def area(
365
396
  Dimension for subplot rows. Default: fifth dimension.
366
397
  animation_frame
367
398
  Dimension for animation. Default: sixth dimension.
399
+ colors
400
+ Unified color specification. Can be:
401
+ - A named continuous scale (e.g., "Viridis")
402
+ - A named discrete palette (e.g., "D3", "Plotly")
403
+ - A list of colors (e.g., ["red", "blue", "green"])
404
+ - A dict mapping values to colors (e.g., {"A": "red", "B": "blue"})
405
+ Explicit color_* kwargs in px_kwargs take precedence.
368
406
  **px_kwargs
369
407
  Additional arguments passed to `plotly.express.area()`.
370
408
 
@@ -372,6 +410,7 @@ def area(
372
410
  -------
373
411
  plotly.graph_objects.Figure
374
412
  """
413
+ px_kwargs = resolve_colors(colors, px_kwargs)
375
414
  slots = assign_slots(
376
415
  list(darray.dims),
377
416
  "area",
@@ -409,6 +448,7 @@ def box(
409
448
  facet_col: SlotValue = None,
410
449
  facet_row: SlotValue = None,
411
450
  animation_frame: SlotValue = None,
451
+ colors: Colors = None,
412
452
  **px_kwargs: Any,
413
453
  ) -> go.Figure:
414
454
  """
@@ -433,6 +473,13 @@ def box(
433
473
  Dimension for subplot rows. Default: None (aggregated).
434
474
  animation_frame
435
475
  Dimension for animation. Default: None (aggregated).
476
+ colors
477
+ Unified color specification. Can be:
478
+ - A named continuous scale (e.g., "Viridis")
479
+ - A named discrete palette (e.g., "D3", "Plotly")
480
+ - A list of colors (e.g., ["red", "blue", "green"])
481
+ - A dict mapping values to colors (e.g., {"A": "red", "B": "blue"})
482
+ Explicit color_* kwargs in px_kwargs take precedence.
436
483
  **px_kwargs
437
484
  Additional arguments passed to `plotly.express.box()`.
438
485
 
@@ -440,6 +487,7 @@ def box(
440
487
  -------
441
488
  plotly.graph_objects.Figure
442
489
  """
490
+ px_kwargs = resolve_colors(colors, px_kwargs)
443
491
  slots = assign_slots(
444
492
  list(darray.dims),
445
493
  "box",
@@ -478,6 +526,7 @@ def scatter(
478
526
  facet_col: SlotValue = auto,
479
527
  facet_row: SlotValue = auto,
480
528
  animation_frame: SlotValue = auto,
529
+ colors: Colors = None,
481
530
  **px_kwargs: Any,
482
531
  ) -> go.Figure:
483
532
  """
@@ -509,6 +558,13 @@ def scatter(
509
558
  Dimension for subplot rows. Default: fifth dimension.
510
559
  animation_frame
511
560
  Dimension for animation. Default: sixth dimension.
561
+ colors
562
+ Unified color specification. Can be:
563
+ - A named continuous scale (e.g., "Viridis")
564
+ - A named discrete palette (e.g., "D3", "Plotly")
565
+ - A list of colors (e.g., ["red", "blue", "green"])
566
+ - A dict mapping values to colors (e.g., {"A": "red", "B": "blue"})
567
+ Explicit color_* kwargs in px_kwargs take precedence.
512
568
  **px_kwargs
513
569
  Additional arguments passed to `plotly.express.scatter()`.
514
570
 
@@ -516,6 +572,7 @@ def scatter(
516
572
  -------
517
573
  plotly.graph_objects.Figure
518
574
  """
575
+ px_kwargs = resolve_colors(colors, px_kwargs)
519
576
  # If y is a dimension, exclude it from slot assignment
520
577
  y_is_dim = y != "value" and y in darray.dims
521
578
  dims_for_slots = [d for d in darray.dims if d != y] if y_is_dim else list(darray.dims)
@@ -565,6 +622,7 @@ def imshow(
565
622
  facet_col: SlotValue = auto,
566
623
  animation_frame: SlotValue = auto,
567
624
  robust: bool = False,
625
+ colors: Colors = None,
568
626
  **px_kwargs: Any,
569
627
  ) -> go.Figure:
570
628
  """
@@ -596,6 +654,11 @@ def imshow(
596
654
  robust
597
655
  If True, compute color bounds using 2nd and 98th percentiles
598
656
  for robustness against outliers. Default: False (uses min/max).
657
+ colors
658
+ Unified color specification. For imshow, typically a named
659
+ continuous scale (e.g., "Viridis", "RdBu"). Lists and dicts
660
+ are not applicable for heatmaps.
661
+ Explicit color_continuous_scale in px_kwargs takes precedence.
599
662
  **px_kwargs
600
663
  Additional arguments passed to `plotly.express.imshow()`.
601
664
  Use `zmin` and `zmax` to manually set color scale bounds.
@@ -604,6 +667,7 @@ def imshow(
604
667
  -------
605
668
  plotly.graph_objects.Figure
606
669
  """
670
+ px_kwargs = resolve_colors(colors, px_kwargs)
607
671
  slots = assign_slots(
608
672
  list(darray.dims),
609
673
  "imshow",
@@ -648,6 +712,7 @@ def pie(
648
712
  color: SlotValue = None,
649
713
  facet_col: SlotValue = auto,
650
714
  facet_row: SlotValue = auto,
715
+ colors: Colors = None,
651
716
  **px_kwargs: Any,
652
717
  ) -> go.Figure:
653
718
  """
@@ -668,6 +733,12 @@ def pie(
668
733
  Dimension for subplot columns. Default: second dimension.
669
734
  facet_row
670
735
  Dimension for subplot rows. Default: third dimension.
736
+ colors
737
+ Unified color specification. Can be:
738
+ - A named discrete palette (e.g., "D3", "Plotly")
739
+ - A list of colors (e.g., ["red", "blue", "green"])
740
+ - A dict mapping values to colors (e.g., {"A": "red", "B": "blue"})
741
+ Explicit color_* kwargs in px_kwargs take precedence.
671
742
  **px_kwargs
672
743
  Additional arguments passed to `plotly.express.pie()`.
673
744
 
@@ -675,6 +746,7 @@ def pie(
675
746
  -------
676
747
  plotly.graph_objects.Figure
677
748
  """
749
+ px_kwargs = resolve_colors(colors, px_kwargs)
678
750
  slots = assign_slots(
679
751
  list(darray.dims),
680
752
  "pie",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xarray_plotly
3
- Version: 0.0.11
3
+ Version: 0.0.12
4
4
  Summary: Interactive Plotly Express plotting accessor for xarray
5
5
  Author: Felix
6
6
  License: MIT
File without changes
File without changes
File without changes