@@ -76,6 +76,7 @@ def average(
76
76
keep_weights : bool = False ,
77
77
lat_bounds : Optional [RegionAxisBounds ] = None ,
78
78
lon_bounds : Optional [RegionAxisBounds ] = None ,
79
+ required_weight : Optional [float ] = 0.0 ,
79
80
) -> xr .Dataset :
80
81
"""
81
82
Calculates the spatial average for a rectilinear grid over an optionally
@@ -125,6 +126,9 @@ def average(
125
126
ignored if ``weights`` are supplied. The lower bound can be larger
126
127
than the upper bound (e.g., across the prime meridian, dateline), by
127
128
default None.
129
+ required_weight : optional, float
130
+ Fraction of data coverage (i..e, weight) needed to return a
131
+ spatial average value. Value must range from 0 to 1.
128
132
129
133
Returns
130
134
-------
@@ -196,7 +200,7 @@ def average(
196
200
self ._weights = weights
197
201
198
202
self ._validate_weights (dv , axis )
199
- ds [dv .name ] = self ._averager (dv , axis )
203
+ ds [dv .name ] = self ._averager (dv , axis , required_weight = required_weight )
200
204
201
205
if keep_weights :
202
206
ds [self ._weights .name ] = self ._weights
@@ -702,7 +706,10 @@ def _validate_weights(
702
706
)
703
707
704
708
def _averager (
705
- self , data_var : xr .DataArray , axis : List [SpatialAxis ] | Tuple [SpatialAxis , ...]
709
+ self ,
710
+ data_var : xr .DataArray ,
711
+ axis : List [SpatialAxis ] | Tuple [SpatialAxis , ...],
712
+ required_weight : Optional [float ] = 0.0 ,
706
713
):
707
714
"""Perform a weighted average of a data variable.
708
715
@@ -721,6 +728,9 @@ def _averager(
721
728
Data variable inside a Dataset.
722
729
axis : List[SpatialAxis] | Tuple[SpatialAxis, ...]
723
730
List of axis dimensions to average over.
731
+ required_weight : optional, float
732
+ Fraction of data coverage (i..e, weight) needed to return a
733
+ spatial average value. Value must range from 0 to 1.
724
734
725
735
Returns
726
736
-------
@@ -734,11 +744,48 @@ def _averager(
734
744
"""
735
745
weights = self ._weights .fillna (0 )
736
746
747
+ # ensure required weight is between 0 and 1
748
+ if required_weight is None :
749
+ required_weight = 0.0
750
+
751
+ if required_weight < 0.0 :
752
+ raise ValueError (
753
+ "required_weight argment is less than zero. "
754
+ "required_weight must be between 0 and 1."
755
+ )
756
+
757
+ if required_weight > 1.0 :
758
+ raise ValueError (
759
+ "required_weight argment is greater than zero. "
760
+ "required_weight must be between 0 and 1."
761
+ )
762
+
763
+ # need weights to match data_var dimensionality
764
+ if required_weight > 0.0 :
765
+ weights , data_var = xr .broadcast (weights , data_var )
766
+
767
+ # get averaging dimensions
737
768
dim = []
738
769
for key in axis :
739
770
dim .append (get_dim_keys (data_var , key ))
740
771
772
+ # compute weighed mean
741
773
with xr .set_options (keep_attrs = True ):
742
774
weighted_mean = data_var .cf .weighted (weights ).mean (dim = dim )
743
775
776
+ # if weight thresholds applied, calculate fraction of data availability
777
+ # replace values that do not meet minimum weight with nan
778
+ if required_weight > 0.0 :
779
+ # sum all weights (assuming no missing values exist)
780
+ print (dim )
781
+ weight_sum_all = weights .sum (dim = dim ) # type: ignore
782
+ # zero out cells with missing values in data_var
783
+ weights = xr .where (~ np .isnan (data_var ), weights , 0 )
784
+ # sum all weights (including zero for missing values)
785
+ weight_sum_masked = weights .sum (dim = dim ) # type: ignore
786
+ # get fraction of weight available
787
+ frac = weight_sum_masked / weight_sum_all
788
+ # nan out values that don't meet specified weight threshold
789
+ weighted_mean = xr .where (frac >= required_weight , weighted_mean , np .nan )
790
+
744
791
return weighted_mean
0 commit comments