tsam-xarray 0.6.2__tar.gz → 0.6.3__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.
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/.github/workflows/ci.yaml +3 -3
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/.github/workflows/publish.yaml +1 -1
- tsam_xarray-0.6.3/.release-please-manifest.json +3 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/CHANGELOG.md +7 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/PKG-INFO +1 -1
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/src/tsam_xarray/_clustering.py +38 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/src/tsam_xarray/_version.py +2 -2
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/test/test_aggregate.py +59 -0
- tsam_xarray-0.6.2/.release-please-manifest.json +0 -3
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/.github/dependabot.yml +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/.github/workflows/dependabot-auto-merge.yaml +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/.github/workflows/pr-title.yaml +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/.github/workflows/release.yaml +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/.gitignore +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/.pre-commit-config.yaml +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/.readthedocs.yaml +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/.release-please-config.json +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/CLAUDE.md +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/LICENSE +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/README.md +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/codecov.yml +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/docs/assets/multi-dim-input.png +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/docs/assets/multi-dim-metrics.png +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/docs/changelog.md +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/docs/data-model.md +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/docs/examples/clustering-io.ipynb +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/docs/examples/getting-started.ipynb +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/docs/examples/multi-dim.ipynb +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/docs/examples/segmentation.ipynb +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/docs/examples/tuning.ipynb +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/docs/gen_ref_pages.py +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/docs/index.md +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/docs/stylesheets/extra.css +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/mkdocs.yml +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/pyproject.toml +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/scripts/generate_readme_images.py +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/src/tsam_xarray/__init__.py +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/src/tsam_xarray/_core.py +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/src/tsam_xarray/_result.py +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/src/tsam_xarray/_sample_data.py +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/src/tsam_xarray/_tuning.py +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/test/conftest.py +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/test/test_parametrized.py +0 -0
- {tsam_xarray-0.6.2 → tsam_xarray-0.6.3}/test/test_tuning.py +0 -0
|
@@ -30,7 +30,7 @@ jobs:
|
|
|
30
30
|
with:
|
|
31
31
|
enable-cache: true
|
|
32
32
|
|
|
33
|
-
- uses: actions/setup-python@
|
|
33
|
+
- uses: actions/setup-python@v7
|
|
34
34
|
with:
|
|
35
35
|
python-version: "3.12"
|
|
36
36
|
|
|
@@ -56,7 +56,7 @@ jobs:
|
|
|
56
56
|
with:
|
|
57
57
|
enable-cache: true
|
|
58
58
|
|
|
59
|
-
- uses: actions/setup-python@
|
|
59
|
+
- uses: actions/setup-python@v7
|
|
60
60
|
with:
|
|
61
61
|
python-version: ${{ matrix.python-version }}
|
|
62
62
|
|
|
@@ -83,7 +83,7 @@ jobs:
|
|
|
83
83
|
with:
|
|
84
84
|
enable-cache: true
|
|
85
85
|
|
|
86
|
-
- uses: actions/setup-python@
|
|
86
|
+
- uses: actions/setup-python@v7
|
|
87
87
|
with:
|
|
88
88
|
python-version: "3.12"
|
|
89
89
|
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.6.3](https://github.com/FBumann/tsam_xarray/compare/v0.6.2...v0.6.3) (2026-07-22)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* drop stored weights on apply() when columns are absent ([#100](https://github.com/FBumann/tsam_xarray/issues/100)) ([5616d1c](https://github.com/FBumann/tsam_xarray/commit/5616d1c14923c00e0bd795e7ea75193d457d6860))
|
|
9
|
+
|
|
3
10
|
## [0.6.2](https://github.com/FBumann/tsam_xarray/compare/v0.6.1...v0.6.2) (2026-07-21)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -560,6 +560,43 @@ def _validate_apply(
|
|
|
560
560
|
raise ValueError(msg)
|
|
561
561
|
|
|
562
562
|
|
|
563
|
+
def _drop_missing_weights(
|
|
564
|
+
cr: tsam.ClusteringResult, df: pd.DataFrame
|
|
565
|
+
) -> tsam.ClusteringResult:
|
|
566
|
+
"""Strip stored clustering weights when they reference absent columns.
|
|
567
|
+
|
|
568
|
+
tsam's ``ClusteringResult.apply()`` hard-fails when a stored weight column
|
|
569
|
+
is missing from the new data, which blocks transferring a clustering to a
|
|
570
|
+
differently-composed dataset. At apply time the cluster assignments and
|
|
571
|
+
centers are already fixed, so weights cannot change which periods are
|
|
572
|
+
selected — their only remaining effect is on the weighted accuracy
|
|
573
|
+
metrics. When a weighted column is absent we drop all weights and warn.
|
|
574
|
+
|
|
575
|
+
Tracking upstream fix: https://github.com/FZJ-IEK3-VSA/tsam/issues/396.
|
|
576
|
+
Once that lands, ``test_upstream_still_rejects_missing_weight_columns``
|
|
577
|
+
fails and this workaround can be removed.
|
|
578
|
+
"""
|
|
579
|
+
import dataclasses
|
|
580
|
+
import warnings
|
|
581
|
+
|
|
582
|
+
weights = getattr(cr, "weights", None)
|
|
583
|
+
if not weights:
|
|
584
|
+
return cr
|
|
585
|
+
missing = set(weights) - set(df.columns)
|
|
586
|
+
if not missing:
|
|
587
|
+
return cr
|
|
588
|
+
|
|
589
|
+
warnings.warn(
|
|
590
|
+
f"Stored clustering weights reference columns absent from the new "
|
|
591
|
+
f"data ({sorted(map(str, missing))}); dropping all weights. "
|
|
592
|
+
f"Representatives and reconstruction are unaffected — only the "
|
|
593
|
+
f"weighted accuracy metrics change.",
|
|
594
|
+
UserWarning,
|
|
595
|
+
stacklevel=3,
|
|
596
|
+
)
|
|
597
|
+
return dataclasses.replace(cr, weights=None)
|
|
598
|
+
|
|
599
|
+
|
|
563
600
|
def _apply_single(
|
|
564
601
|
da: xr.DataArray,
|
|
565
602
|
cr: tsam.ClusteringResult,
|
|
@@ -580,6 +617,7 @@ def _apply_single(
|
|
|
580
617
|
from tsam_xarray._result import AccuracyMetrics, AggregationResult
|
|
581
618
|
|
|
582
619
|
df = _to_dataframe(da, time_dim, col_dims)
|
|
620
|
+
cr = _drop_missing_weights(cr, df)
|
|
583
621
|
tsam_result = cr.apply(df, **tsam_kwargs)
|
|
584
622
|
|
|
585
623
|
typical = _representatives_to_da(tsam_result.cluster_representatives, col_dims)
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.6.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 6,
|
|
21
|
+
__version__ = version = '0.6.3'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 6, 3)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import warnings
|
|
6
|
+
|
|
5
7
|
import numpy as np
|
|
6
8
|
import pandas as pd
|
|
7
9
|
import pytest
|
|
@@ -1373,6 +1375,63 @@ class TestClusteringIO:
|
|
|
1373
1375
|
xr.testing.assert_allclose(dis, new_result.reconstructed)
|
|
1374
1376
|
|
|
1375
1377
|
|
|
1378
|
+
class TestApplyWeightTransfer:
|
|
1379
|
+
"""Transferring a weighted clustering to differently-composed data.
|
|
1380
|
+
|
|
1381
|
+
Works around tsam#396: ``ClusteringResult.apply()`` hard-fails when a
|
|
1382
|
+
stored weight column is missing from the new data. We drop the weights
|
|
1383
|
+
and warn instead, since at apply time weights cannot change selection.
|
|
1384
|
+
"""
|
|
1385
|
+
|
|
1386
|
+
def _weighted_clustering(self):
|
|
1387
|
+
da = _make_da()
|
|
1388
|
+
da_flat = da.isel(region=0).drop_vars("region")
|
|
1389
|
+
result = tsam_xarray.aggregate(
|
|
1390
|
+
da_flat,
|
|
1391
|
+
time_dim="time",
|
|
1392
|
+
cluster_dim="variable",
|
|
1393
|
+
n_clusters=4,
|
|
1394
|
+
weights={"solar": 5.0, "wind": 1.0},
|
|
1395
|
+
)
|
|
1396
|
+
return result.clustering, da_flat
|
|
1397
|
+
|
|
1398
|
+
def test_apply_warns_and_drops_missing_weight_columns(self):
|
|
1399
|
+
"""Applying to data without a weighted column warns, not raises."""
|
|
1400
|
+
clustering, da_flat = self._weighted_clustering()
|
|
1401
|
+
new_da = da_flat.sel(variable=["wind"]) # 'solar' (weighted) absent
|
|
1402
|
+
with pytest.warns(UserWarning, match="absent from the new data"):
|
|
1403
|
+
new_result = clustering.apply(new_da)
|
|
1404
|
+
assert "solar" not in new_result.cluster_representatives["variable"].values
|
|
1405
|
+
assert new_result.reconstructed.notnull().all()
|
|
1406
|
+
|
|
1407
|
+
def test_apply_no_warning_when_all_weight_columns_present(self):
|
|
1408
|
+
"""Same composition keeps weights and emits no warning."""
|
|
1409
|
+
clustering, da_flat = self._weighted_clustering()
|
|
1410
|
+
with warnings.catch_warnings():
|
|
1411
|
+
warnings.simplefilter("error")
|
|
1412
|
+
new_result = clustering.apply(da_flat)
|
|
1413
|
+
assert new_result.n_clusters == 4
|
|
1414
|
+
|
|
1415
|
+
def test_upstream_still_rejects_missing_weight_columns(self):
|
|
1416
|
+
"""Canary: when raw tsam stops raising, tsam#396 is fixed.
|
|
1417
|
+
|
|
1418
|
+
Remove ``_drop_missing_weights`` and this test once that lands.
|
|
1419
|
+
"""
|
|
1420
|
+
import tsam
|
|
1421
|
+
|
|
1422
|
+
idx = pd.date_range("2020-01-01", periods=24 * 20, freq="h")
|
|
1423
|
+
rng = np.random.default_rng(0)
|
|
1424
|
+
df = pd.DataFrame(
|
|
1425
|
+
{"a": rng.random(len(idx)), "b": rng.random(len(idx))},
|
|
1426
|
+
index=idx,
|
|
1427
|
+
)
|
|
1428
|
+
cr = tsam.aggregate(
|
|
1429
|
+
df, 4, period_duration=24, weights={"a": 5.0, "b": 0.1}
|
|
1430
|
+
).clustering
|
|
1431
|
+
with pytest.raises(ValueError, match="Weight columns not found"):
|
|
1432
|
+
cr.apply(df.drop(columns=["b"]))
|
|
1433
|
+
|
|
1434
|
+
|
|
1376
1435
|
class TestClusteringDisaggregate:
|
|
1377
1436
|
"""Tests for ClusteringInfo.disaggregate()."""
|
|
1378
1437
|
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|