@@ -301,15 +301,11 @@ def get_weights(
301301 """Calculate suitable weights for given coordinates."""
302302 npx = get_array_module (cube .core_data ())
303303 weights = npx .ones_like (cube .core_data ())
304+ coords = [c .name () if hasattr (c , "name" ) else c for c in coords ]
304305
305306 # Time weights: lengths of time interval
306307 if "time" in coords :
307- weights = weights * broadcast_to_shape (
308- npx .array (get_time_weights (cube )),
309- cube .shape ,
310- cube .coord_dims ("time" ),
311- chunks = cube .lazy_data ().chunks if cube .has_lazy_data () else None ,
312- )
308+ weights = weights * get_coord_weights (cube , "time" , broadcast = True )
313309
314310 # Latitude weights: cell areas
315311 if "latitude" in coords :
@@ -319,9 +315,8 @@ def get_weights(
319315 ):
320316 raise CoordinateNotFoundError (
321317 f"Cube { cube .summary (shorten = True )} needs a `longitude` "
322- f"coordinate to calculate cell area weights for weighted "
323- f"distance metric over coordinates { coords } (alternatively, "
324- f"a `cell_area` can be given to the cube as supplementary "
318+ f"coordinate to calculate cell area weights (alternatively, a "
319+ f"`cell_area` can be given to the cube as supplementary "
325320 f"variable)"
326321 )
327322 try_adding_calculated_cell_area (cube )
@@ -341,43 +336,74 @@ def get_weights(
341336 return weights
342337
343338
344- def get_time_weights (cube : Cube ) -> np .ndarray | da .core .Array :
345- """Compute the weighting of the time axis.
339+ def get_coord_weights (
340+ cube : Cube ,
341+ coord : str | Coord ,
342+ broadcast : bool = False ,
343+ ) -> np .ndarray | da .core .Array :
344+ """Compute weighting for an arbitrary coordinate.
345+
346+ Weights are calculated as the difference between the upper and lower
347+ bounds.
346348
347349 Parameters
348350 ----------
349351 cube:
350352 Input cube.
353+ coord:
354+ Coordinate which is used to calculate the weights. Must have bounds
355+ array with 2 bounds per point.
356+ broadcast:
357+ If ``False``, weights have the shape of ``coord``. If ``True``,
358+ broadcast weights to shape of cube.
351359
352360 Returns
353361 -------
354362 np.ndarray or da.Array
355- Array of time weights for averaging. Returns a
356- :class:`dask.array.Array` if the input cube has lazy data; a
357- :class:`numpy.ndarray` otherwise.
363+ Array of axis weights. Returns a :class:`dask.array.Array` if the input
364+ cube has lazy data; a :class:`numpy.ndarray` otherwise.
358365
359366 """
360- time = cube .coord ("time" )
361- coord_dims = cube .coord_dims ("time" )
367+ coord = cube .coord (coord )
368+ coord_dims = cube .coord_dims (coord )
362369
363- # Multidimensional time coordinates are not supported: In this case,
364- # weights cannot be simply calculated as difference between the bounds
365- if len (coord_dims ) > 1 :
370+ # Coordinate needs bounds of size 2
371+ if not coord .has_bounds ():
372+ raise ValueError (
373+ f"Cannot calculate weights for coordinate '{ coord .name ()} ' "
374+ f"without bounds"
375+ )
376+ if coord .core_bounds ().shape [- 1 ] != 2 :
366377 raise ValueError (
367- f"Weighted statistical operations are not supported for "
368- f"{ len (coord_dims ):d} D time coordinates, expected 0D or 1D"
378+ f"Cannot calculate weights for coordinate '{ coord .name ()} ' "
379+ f"with { coord .core_bounds ().shape [- 1 ]} bounds per point, expected "
380+ f"2 bounds per point"
369381 )
370382
371- # Extract 1D time weights (= lengths of time intervals)
372- time_weights = time .lazy_bounds ()[:, 1 ] - time .lazy_bounds ()[:, 0 ]
373- if cube .has_lazy_data ():
374- # Align the weight chunks with the data chunks to avoid excessively
375- # large chunks as a result of broadcasting.
376- time_chunks = cube .lazy_data ().chunks [coord_dims [0 ]]
377- time_weights = time_weights .rechunk (time_chunks )
378- else :
379- time_weights = time_weights .compute ()
380- return time_weights
383+ # Calculate weights of same shape as coordinate and make sure to use
384+ # identical chunks as parent cube for non-scalar lazy data
385+ weights = np .abs (coord .lazy_bounds ()[:, 1 ] - coord .lazy_bounds ()[:, 0 ])
386+ if cube .has_lazy_data () and coord_dims :
387+ coord_chunks = tuple (cube .lazy_data ().chunks [d ] for d in coord_dims )
388+ weights = weights .rechunk (coord_chunks )
389+ if not cube .has_lazy_data ():
390+ weights = weights .compute ()
391+
392+ # Broadcast to cube shape if desired; scalar arrays needs special treatment
393+ # since iris.broadcast_to_shape cannot handle this
394+ if broadcast :
395+ chunks = cube .lazy_data ().chunks if cube .has_lazy_data () else None
396+ if coord_dims :
397+ weights = broadcast_to_shape (
398+ weights , cube .shape , coord_dims , chunks = chunks
399+ )
400+ else :
401+ if cube .has_lazy_data ():
402+ weights = da .broadcast_to (weights , cube .shape , chunks = chunks )
403+ else :
404+ weights = np .broadcast_to (weights , cube .shape )
405+
406+ return weights
381407
382408
383409def try_adding_calculated_cell_area (cube : Cube ) -> None :
0 commit comments