@@ -151,6 +151,37 @@ def test_by_time_unit_from_seconds_binning(self):
151151 mask ['prediction_timedelta_sec_hour' ], np .arange (0 , 6 )
152152 )
153153
154+ @parameterized .parameters (
155+ ('second' , None , np .arange (0 , 60 )),
156+ ('second' , [0 , 15 , 30 , 45 ], [0 , 15 , 30 , 45 ]),
157+ ('minute' , None , np .arange (0 , 60 )),
158+ ('minute' , [0 , 30 ], [0 , 30 ]),
159+ ('hour' , None , np .arange (0 , 24 )),
160+ ('hour' , [0 , 6 , 12 , 18 ], [0 , 6 , 12 , 18 ]),
161+ )
162+ def test_by_time_unit_from_seconds_binning_with_units (
163+ self , unit , bins , expected_bins
164+ ):
165+ statistic_values = test_utils .mock_prediction_data (
166+ time_start = '2020-01-01T00' ,
167+ time_stop = '2020-01-01T01' ,
168+ time_resolution = '1 hr' ,
169+ lead_resolution = '1 second' ,
170+ lead_stop = '24 hour' ,
171+ )['2m_temperature' ]
172+ statistic_values = statistic_values .assign_coords ({
173+ 'prediction_timedelta_sec' : (
174+ statistic_values .prediction_timedelta .dt .total_seconds ()
175+ )
176+ })
177+ binning_obj = binning .ByTimeUnitFromSeconds (
178+ unit , 'prediction_timedelta_sec' , bins = bins
179+ )
180+ mask = binning_obj .create_bin_mask (statistic_values )
181+ np .testing .assert_array_equal (
182+ mask [f'prediction_timedelta_sec_{ unit } ' ].values , expected_bins
183+ )
184+
154185 def test_by_coord_bins (self ):
155186 target_path = resources .files ('weatherbenchX' ).joinpath (
156187 'test_data/metar-timeNominal-by-month'
@@ -222,6 +253,67 @@ def test_by_sets(self):
222253 self .assertEqual (mask .sum ('index' ).sel (station_subset = 'wrong_set' ), 0 )
223254 self .assertLen (statistic , mask .sum ('index' ).sel (station_subset = 'global' ))
224255
256+ @parameterized .parameters (
257+ (10 , (- 90 , 90 ), 18 ),
258+ (30 , (- 90 , 90 ), 6 ),
259+ (20 , (0 , 60 ), 3 ),
260+ )
261+ def test_latitude_bins (self , degrees , lat_range , expected_bins ):
262+ statistic_values = test_utils .mock_prediction_data (
263+ time_start = '2020-01-01T00' , time_stop = '2020-01-01T01'
264+ )['2m_temperature' ]
265+ binning_obj = binning .LatitudeBins (degrees , lat_range )
266+ mask = binning_obj .create_bin_mask (statistic_values )
267+ self .assertEqual (mask .latitude_bins .shape [0 ], expected_bins )
268+ self .assertTrue (np .all (mask .latitude_bins .values >= lat_range [0 ]))
269+ self .assertTrue (np .all (mask .latitude_bins .values < lat_range [1 ]))
270+ self .assertEqual (mask .shape , (expected_bins ,) + statistic_values .shape )
271+ # Check that a point is in the correct bin
272+ # Find the latitude closest to 25
273+ lat_val = 25
274+ if not (lat_range [0 ] <= lat_val < lat_range [1 ]):
275+ # If 25 is not in range, pick a value that is.
276+ lat_val = (lat_range [0 ] + lat_range [1 ]) / 2
277+ lat_idx = np .argmin (np .abs (statistic_values .latitude .values - lat_val ))
278+ lon_idx = np .argmin (np .abs (statistic_values .longitude .values - 0 ))
279+
280+ # Find the bin that contains the selected latitude
281+ expected_bin_idx = (statistic_values .latitude .values [lat_idx ] - lat_range [0 ]) // degrees
282+ self .assertTrue (
283+ mask .isel (latitude_bins = int (expected_bin_idx ), latitude = lat_idx , longitude = lon_idx ).values .all ()
284+ )
285+
286+ @parameterized .parameters (
287+ (10 , (0 , 360 ), 36 , 10 ),
288+ (30 , (0 , 360 ), 12 , 150 ),
289+ (60 , (- 180 , 180 ), 6 , 0 ),
290+ (90 , (270 , 360 ), 1 , 300 ),
291+ )
292+ def test_longitude_bins (self , degrees , lon_range , expected_bins , test_lon ):
293+ statistic_values = test_utils .mock_prediction_data (
294+ time_start = '2020-01-01T00' , time_stop = '2020-01-01T01'
295+ )['2m_temperature' ]
296+ binning_obj = binning .LongitudeBins (degrees , lon_range )
297+ mask = binning_obj .create_bin_mask (statistic_values )
298+ self .assertEqual (mask .longitude_bins .shape [0 ], expected_bins )
299+ self .assertEqual (mask .shape , (expected_bins ,) + statistic_values .shape )
300+ # Check wrapping
301+ if lon_range == (- 180 , 180 ):
302+ self .assertTrue (0 in mask .longitude_bins .values )
303+
304+ # Find the longitude closest to test_lon
305+ lon_idx = np .argmin (np .abs (statistic_values .longitude .values - test_lon ))
306+ lat_idx = np .argmin (np .abs (statistic_values .latitude .values - 0 ))
307+ lon_val = statistic_values .longitude .values [lon_idx ]
308+
309+ # Calculate expected bin index: (lon_val - lon_range[0]) // degrees
310+ # This works even with wrapping ranges because lon_bins is constructed correctly.
311+ expected_bin_idx = (lon_val - lon_range [0 ]) // degrees
312+
313+ self .assertTrue (
314+ mask .isel (longitude_bins = int (expected_bin_idx ), latitude = lat_idx , longitude = lon_idx ).values .all ()
315+ )
316+
225317
226318if __name__ == '__main__' :
227319 absltest .main ()
0 commit comments