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.
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/.github/workflows/ci.yml +3 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/PKG-INFO +1 -1
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/examples/kwargs.ipynb +65 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/tests/test_accessor.py +125 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly/__init__.py +4 -3
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly/accessor.py +46 -1
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly/common.py +73 -4
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly/config.py +7 -7
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly/figures.py +7 -5
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly/plotting.py +73 -1
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly.egg-info/PKG-INFO +1 -1
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/.github/dependabot.yml +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/.github/workflows/dependabot-auto-merge.yml +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/.github/workflows/docs.yml +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/.github/workflows/release.yml +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/.gitignore +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/.pre-commit-config.yaml +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/CONTRIBUTING.md +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/LICENSE +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/README.md +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/api.md +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/examples/combining.ipynb +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/examples/datasets.ipynb +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/examples/dimensions.ipynb +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/examples/fast_bar.ipynb +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/examples/figure.ipynb +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/examples/manipulation.ipynb +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/examples/plot-types.ipynb +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/getting-started.ipynb +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/docs/index.md +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/mkdocs.yml +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/pyproject.toml +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/setup.cfg +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/tests/__init__.py +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/tests/test_common.py +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/tests/test_config.py +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/tests/test_figures.py +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly/py.typed +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly.egg-info/SOURCES.txt +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly.egg-info/dependency_links.txt +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly.egg-info/requires.txt +0 -0
- {xarray_plotly-0.0.11 → xarray_plotly-0.0.12}/xarray_plotly.egg-info/top_level.txt +0 -0
|
@@ -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
|
-
|
|
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(
|
|
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:
|
|
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",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|