xarray-plotly 0.0.5__tar.gz → 0.0.6__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.5 → xarray_plotly-0.0.6}/.github/workflows/ci.yml +32 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/.github/workflows/docs.yml +2 -1
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/PKG-INFO +1 -1
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/tests/test_accessor.py +49 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/xarray_plotly/accessor.py +4 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/xarray_plotly/plotting.py +20 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/xarray_plotly.egg-info/PKG-INFO +1 -1
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/.github/dependabot.yml +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/.github/workflows/dependabot-auto-merge.yml +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/.github/workflows/release.yml +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/.gitignore +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/.pre-commit-config.yaml +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/CONTRIBUTING.md +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/LICENSE +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/README.md +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/docs/api.md +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/docs/examples/datasets.ipynb +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/docs/examples/dimensions.ipynb +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/docs/examples/figure.ipynb +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/docs/examples/kwargs.ipynb +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/docs/examples/plot-types.ipynb +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/docs/getting-started.ipynb +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/docs/index.md +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/mkdocs.yml +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/pyproject.toml +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/setup.cfg +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/tests/__init__.py +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/tests/test_common.py +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/tests/test_config.py +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/xarray_plotly/__init__.py +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/xarray_plotly/common.py +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/xarray_plotly/config.py +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/xarray_plotly/py.typed +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/xarray_plotly.egg-info/SOURCES.txt +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/xarray_plotly.egg-info/dependency_links.txt +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/xarray_plotly.egg-info/requires.txt +0 -0
- {xarray_plotly-0.0.5 → xarray_plotly-0.0.6}/xarray_plotly.egg-info/top_level.txt +0 -0
|
@@ -40,3 +40,35 @@ jobs:
|
|
|
40
40
|
with:
|
|
41
41
|
token: ${{ secrets.CODECOV_TOKEN }}
|
|
42
42
|
fail_ci_if_error: false
|
|
43
|
+
|
|
44
|
+
docs:
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v6
|
|
48
|
+
|
|
49
|
+
- name: Install uv
|
|
50
|
+
uses: astral-sh/setup-uv@v7
|
|
51
|
+
|
|
52
|
+
- name: Install dependencies
|
|
53
|
+
run: uv sync --extra docs
|
|
54
|
+
|
|
55
|
+
- name: Build docs
|
|
56
|
+
run: uv run mkdocs build
|
|
57
|
+
|
|
58
|
+
ci-success:
|
|
59
|
+
name: CI Success
|
|
60
|
+
needs: [test, docs]
|
|
61
|
+
if: always()
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
steps:
|
|
64
|
+
- name: Check all jobs passed
|
|
65
|
+
run: |
|
|
66
|
+
if [[ "${{ needs.test.result }}" != "success" ]]; then
|
|
67
|
+
echo "Test job failed or was cancelled"
|
|
68
|
+
exit 1
|
|
69
|
+
fi
|
|
70
|
+
if [[ "${{ needs.docs.result }}" != "success" ]]; then
|
|
71
|
+
echo "Docs job failed or was cancelled"
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
74
|
+
echo "All CI checks passed!"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
name: Docs
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
|
-
|
|
4
|
+
pull_request:
|
|
5
5
|
branches: [main]
|
|
6
6
|
release:
|
|
7
7
|
types: [published]
|
|
@@ -26,6 +26,7 @@ jobs:
|
|
|
26
26
|
run: uv run mkdocs build
|
|
27
27
|
|
|
28
28
|
- name: Deploy to GitHub Pages
|
|
29
|
+
if: github.event_name == 'release'
|
|
29
30
|
uses: peaceiris/actions-gh-pages@v4
|
|
30
31
|
with:
|
|
31
32
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -292,3 +292,52 @@ class TestDatasetPlotlyAccessor:
|
|
|
292
292
|
"""Test box plot with all variables."""
|
|
293
293
|
fig = self.ds.plotly.box()
|
|
294
294
|
assert isinstance(fig, go.Figure)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
class TestImshowBounds:
|
|
298
|
+
"""Tests for imshow global bounds and robust mode."""
|
|
299
|
+
|
|
300
|
+
def test_imshow_global_bounds(self) -> None:
|
|
301
|
+
"""Test that imshow uses global min/max by default."""
|
|
302
|
+
da = xr.DataArray(
|
|
303
|
+
np.array([[[1, 2], [3, 4]], [[5, 6], [7, 100]]]),
|
|
304
|
+
dims=["time", "y", "x"],
|
|
305
|
+
)
|
|
306
|
+
fig = da.plotly.imshow(animation_frame="time")
|
|
307
|
+
# Check coloraxis for zmin/zmax (plotly stores them there)
|
|
308
|
+
coloraxis = fig.layout.coloraxis
|
|
309
|
+
assert coloraxis.cmin == 1.0
|
|
310
|
+
assert coloraxis.cmax == 100.0
|
|
311
|
+
|
|
312
|
+
def test_imshow_robust_bounds(self) -> None:
|
|
313
|
+
"""Test that robust=True uses percentile-based bounds."""
|
|
314
|
+
# Create data with outlier
|
|
315
|
+
data = np.random.rand(10, 20) * 100
|
|
316
|
+
data[0, 0] = 10000 # extreme outlier
|
|
317
|
+
da = xr.DataArray(data, dims=["y", "x"])
|
|
318
|
+
|
|
319
|
+
fig = da.plotly.imshow(robust=True)
|
|
320
|
+
# With robust=True, cmax should be much less than the outlier
|
|
321
|
+
coloraxis = fig.layout.coloraxis
|
|
322
|
+
assert coloraxis.cmax < 10000
|
|
323
|
+
assert coloraxis.cmax < 200 # Should be around 98th percentile (~98)
|
|
324
|
+
|
|
325
|
+
def test_imshow_user_zmin_zmax_override(self) -> None:
|
|
326
|
+
"""Test that user-provided zmin/zmax overrides auto bounds."""
|
|
327
|
+
da = xr.DataArray(np.random.rand(10, 20) * 100, dims=["y", "x"])
|
|
328
|
+
fig = da.plotly.imshow(zmin=0, zmax=50)
|
|
329
|
+
coloraxis = fig.layout.coloraxis
|
|
330
|
+
assert coloraxis.cmin == 0
|
|
331
|
+
assert coloraxis.cmax == 50
|
|
332
|
+
|
|
333
|
+
def test_imshow_animation_consistent_bounds(self) -> None:
|
|
334
|
+
"""Test that animation frames have consistent color bounds."""
|
|
335
|
+
da = xr.DataArray(
|
|
336
|
+
np.array([[[0, 10], [20, 30]], [[40, 50], [60, 70]]]),
|
|
337
|
+
dims=["time", "y", "x"],
|
|
338
|
+
)
|
|
339
|
+
fig = da.plotly.imshow(animation_frame="time")
|
|
340
|
+
# All frames should use global min (0) and max (70)
|
|
341
|
+
coloraxis = fig.layout.coloraxis
|
|
342
|
+
assert coloraxis.cmin == 0.0
|
|
343
|
+
assert coloraxis.cmax == 70.0
|
|
@@ -250,6 +250,7 @@ class DataArrayPlotlyAccessor:
|
|
|
250
250
|
y: SlotValue = auto,
|
|
251
251
|
facet_col: SlotValue = auto,
|
|
252
252
|
animation_frame: SlotValue = auto,
|
|
253
|
+
robust: bool = False,
|
|
253
254
|
**px_kwargs: Any,
|
|
254
255
|
) -> go.Figure:
|
|
255
256
|
"""Create an interactive heatmap image.
|
|
@@ -261,7 +262,9 @@ class DataArrayPlotlyAccessor:
|
|
|
261
262
|
y: Dimension for y-axis (rows). Default: first dimension.
|
|
262
263
|
facet_col: Dimension for subplot columns. Default: third dimension.
|
|
263
264
|
animation_frame: Dimension for animation. Default: fourth dimension.
|
|
265
|
+
robust: If True, use 2nd/98th percentiles for color bounds (handles outliers).
|
|
264
266
|
**px_kwargs: Additional arguments passed to `plotly.express.imshow()`.
|
|
267
|
+
Use `zmin` and `zmax` to manually set color scale bounds.
|
|
265
268
|
|
|
266
269
|
Returns:
|
|
267
270
|
Interactive Plotly Figure.
|
|
@@ -272,6 +275,7 @@ class DataArrayPlotlyAccessor:
|
|
|
272
275
|
y=y,
|
|
273
276
|
facet_col=facet_col,
|
|
274
277
|
animation_frame=animation_frame,
|
|
278
|
+
robust=robust,
|
|
275
279
|
**px_kwargs,
|
|
276
280
|
)
|
|
277
281
|
|
|
@@ -6,6 +6,7 @@ from __future__ import annotations
|
|
|
6
6
|
|
|
7
7
|
from typing import TYPE_CHECKING, Any
|
|
8
8
|
|
|
9
|
+
import numpy as np
|
|
9
10
|
import plotly.express as px
|
|
10
11
|
|
|
11
12
|
from xarray_plotly.common import (
|
|
@@ -398,6 +399,7 @@ def imshow(
|
|
|
398
399
|
y: SlotValue = auto,
|
|
399
400
|
facet_col: SlotValue = auto,
|
|
400
401
|
animation_frame: SlotValue = auto,
|
|
402
|
+
robust: bool = False,
|
|
401
403
|
**px_kwargs: Any,
|
|
402
404
|
) -> go.Figure:
|
|
403
405
|
"""
|
|
@@ -418,8 +420,12 @@ def imshow(
|
|
|
418
420
|
Dimension for subplot columns. Default: third dimension.
|
|
419
421
|
animation_frame
|
|
420
422
|
Dimension for animation. Default: fourth dimension.
|
|
423
|
+
robust
|
|
424
|
+
If True, compute color bounds using 2nd and 98th percentiles
|
|
425
|
+
for robustness against outliers. Default: False.
|
|
421
426
|
**px_kwargs
|
|
422
427
|
Additional arguments passed to `plotly.express.imshow()`.
|
|
428
|
+
Use `zmin` and `zmax` to manually set color scale bounds.
|
|
423
429
|
|
|
424
430
|
Returns
|
|
425
431
|
-------
|
|
@@ -440,6 +446,20 @@ def imshow(
|
|
|
440
446
|
]
|
|
441
447
|
plot_data = darray.transpose(*transpose_order) if transpose_order else darray
|
|
442
448
|
|
|
449
|
+
# Compute global color bounds if not provided
|
|
450
|
+
if "zmin" not in px_kwargs or "zmax" not in px_kwargs:
|
|
451
|
+
values = plot_data.values
|
|
452
|
+
if robust:
|
|
453
|
+
# Use percentiles for outlier robustness
|
|
454
|
+
zmin = float(np.nanpercentile(values, 2))
|
|
455
|
+
zmax = float(np.nanpercentile(values, 98))
|
|
456
|
+
else:
|
|
457
|
+
# Use global min/max across all data
|
|
458
|
+
zmin = float(np.nanmin(values))
|
|
459
|
+
zmax = float(np.nanmax(values))
|
|
460
|
+
px_kwargs.setdefault("zmin", zmin)
|
|
461
|
+
px_kwargs.setdefault("zmax", zmax)
|
|
462
|
+
|
|
443
463
|
return px.imshow(
|
|
444
464
|
plot_data,
|
|
445
465
|
facet_col=slots.get("facet_col"),
|
|
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
|