From df95bfd73f61bd1b33cad3102e13580b91633985 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Jun 2026 09:39:01 +0000 Subject: [PATCH 1/2] Initial plan From ea4d4a104494c5428421de155119577febba15d9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Jun 2026 09:53:01 +0000 Subject: [PATCH 2/2] Fix Comparer.sel() to handle date strings with no temporal overlap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When calling Comparer.sel(time='2022-01-03') (non-slice string): - If no data exists for that date, xarray raises KeyError → now returns empty comparer - If exact daily timestamp exists, xarray returns 0D dataset causing 'IndexVariable must be 1-dimensional' → now expanded back to 1D with expand_dims Also replaces the TODO: FAILS commented-out test with two correct failing tests that now pass after the fix. --- src/modelskill/comparison/_comparison.py | 18 ++++++++++++----- tests/test_comparercollection.py | 25 ++++++++++++++---------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/modelskill/comparison/_comparison.py b/src/modelskill/comparison/_comparison.py index aed4cc27e..72a664bf4 100644 --- a/src/modelskill/comparison/_comparison.py +++ b/src/modelskill/comparison/_comparison.py @@ -907,17 +907,25 @@ def sel( k: v.sel(time=slice(start, end)) for k, v in raw_mod_data.items() } # type: ignore if time is not None: - d = d.sel(time=time) + try: + d = d.sel(time=time) + except KeyError: + d = d.isel(time=slice(0, 0)) # No data for this time, return empty + else: + if "time" not in d.dims: + # Exact timestamp match returns 0-dimensional dataset; expand back to 1D + d = d.expand_dims("time") raw_mod = {} # Nearest time selection for raw_mod_data, # as it may not have the same time points as the matched data if isinstance(time, slice): raw_mod_data = {k: v.sel(time=time) for k, v in raw_mod_data.items()} # type: ignore else: - for k, v in raw_mod_data.items(): - time_vals = v.time.values - idx = np.abs(time_vals - np.datetime64(time)).argmin() - raw_mod[k] = v.sel(time=time_vals[idx]) + if len(d.time) > 0: + for k, v in raw_mod_data.items(): + time_vals = v.time.values + idx = np.abs(time_vals - np.datetime64(time)).argmin() + raw_mod[k] = v.sel(time=time_vals[idx]) raw_mod_data = raw_mod if area is not None: if _area_is_bbox(area): diff --git a/tests/test_comparercollection.py b/tests/test_comparercollection.py index f884ff7f4..6365d3195 100644 --- a/tests/test_comparercollection.py +++ b/tests/test_comparercollection.py @@ -140,16 +140,21 @@ def test_cc_sel_model_last(cc): assert cc2.mod_names == ["m3"] -# TODO: FAILS -# def test_cc_sel_time_single(cc): -# cc1 = cc.sel(time="2019-01-03") -# assert cc1.n_comparers == 2 -# assert cc1.n_models == 3 -# assert cc1.n_points == 6 -# assert cc1.start == pd.Timestamp("2019-01-03") -# assert cc1.end == pd.Timestamp("2019-01-05") -# assert cc1.obs_names == ["fake point obs", "fake track obs"] -# assert cc1.mod_names == ["m1", "m2", "m3"] +def test_cc_sel_time_string(cc): + """Selecting a date string where both comparers have data should include both.""" + # pc has data from 2019-01-01, tc starts from 2019-01-03 - both have data on Jan 3 + cc1 = cc.sel(time="2019-01-03") + assert len(cc1) == 2 + assert cc1.obs_names == ["fake point obs", "fake track obs"] + + +def test_cc_sel_time_string_no_overlap(cc): + """Selecting a date string where one comparer has no data should exclude that comparer.""" + # pc has data from 2019-01-01, tc starts from 2019-01-03 + # Selecting 2019-01-01 should return only pc + cc1 = cc.sel(time="2019-01-01") + assert len(cc1) == 1 + assert cc1.obs_names == ["fake point obs"] def test_cc_sel_time(cc):