@@ -2017,7 +2017,7 @@ def disp(mesg, device=None, linefeed=True):
2017
2017
"(deprecated in NumPy 2.0)" ,
2018
2018
DeprecationWarning ,
2019
2019
stacklevel = 2
2020
- )
2020
+ )
2021
2021
2022
2022
if device is None :
2023
2023
device = sys .stdout
@@ -3984,22 +3984,22 @@ def _median(a, axis=None, out=None, overwrite_input=False):
3984
3984
return rout
3985
3985
3986
3986
3987
- def _percentile_dispatcher (a , q , axis = None , weights = None , out = None ,
3987
+ def _percentile_dispatcher (a , q , axis = None , out = None ,
3988
3988
overwrite_input = None , method = None , keepdims = None , * ,
3989
- interpolation = None ):
3989
+ weights = None , interpolation = None ):
3990
3990
return (a , q , out )
3991
3991
3992
3992
3993
3993
@array_function_dispatch (_percentile_dispatcher )
3994
3994
def percentile (a ,
3995
3995
q ,
3996
3996
axis = None ,
3997
- weights = None ,
3998
3997
out = None ,
3999
3998
overwrite_input = False ,
4000
3999
method = "linear" ,
4001
4000
keepdims = False ,
4002
4001
* ,
4002
+ weights = None ,
4003
4003
interpolation = None ):
4004
4004
"""
4005
4005
Compute the q-th percentile of the data along the specified axis.
@@ -4319,22 +4319,22 @@ def percentile(a,
4319
4319
a , q , axis , weights , out , overwrite_input , method , keepdims )
4320
4320
4321
4321
4322
- def _quantile_dispatcher (a , q , axis = None , weights = None , out = None ,
4322
+ def _quantile_dispatcher (a , q , axis = None , out = None ,
4323
4323
overwrite_input = None , method = None , keepdims = None , * ,
4324
- interpolation = None ):
4324
+ weights = None , interpolation = None ):
4325
4325
return (a , q , out )
4326
4326
4327
4327
4328
4328
@array_function_dispatch (_quantile_dispatcher )
4329
4329
def quantile (a ,
4330
4330
q ,
4331
4331
axis = None ,
4332
- weights = None ,
4333
4332
out = None ,
4334
4333
overwrite_input = False ,
4335
4334
method = "linear" ,
4336
4335
keepdims = False ,
4337
4336
* ,
4337
+ weights = None ,
4338
4338
interpolation = None ):
4339
4339
"""
4340
4340
Compute the q-th quantile of the data along the specified axis.
@@ -4647,11 +4647,10 @@ def _validate_and_ureduce_weights(a, axis, wgts):
4647
4647
4648
4648
Weights cannot:
4649
4649
* be negative
4650
+ * be (0, 1)
4650
4651
* sum to 0
4651
4652
However, they can be
4652
4653
* 0, as long as they do not sum to 0
4653
- * less than 1. In this case, all weights are re-normalized by
4654
- the lowest non-zero weight prior to computation.
4655
4654
4656
4655
Weights will be broadcasted to the shape of a, then reduced as done
4657
4656
via _ureduce().
@@ -4678,6 +4677,9 @@ def _validate_and_ureduce_weights(a, axis, wgts):
4678
4677
if (wgts < 0 ).any ():
4679
4678
raise ValueError ("Negative weight not allowed." )
4680
4679
4680
+ if ((0 < wgts ) & (wgts < 1 )).any ():
4681
+ raise ValueError ("Partial weight (0, 1) not allowed." )
4682
+
4681
4683
# dims to reshape to, before broadcast
4682
4684
if axis is None :
4683
4685
dims = tuple (range (a .ndim )) # all axes
@@ -4714,22 +4716,6 @@ def _validate_and_ureduce_weights(a, axis, wgts):
4714
4716
# Obtain a weights array of the same shape as ureduced a
4715
4717
wgts = _ureduce (wgts , func = lambda x , ** kwargs : x , axis = dims )
4716
4718
4717
- # Now check/renormalize weights if any is (0, 1)
4718
- def _normalize (v ):
4719
- inds = v > 0
4720
- if (v [inds ] < 1 ).any ():
4721
- vec = v .copy ()
4722
- vec [inds ] = vec [inds ] / vec [inds ].min () # renormalization
4723
- return vec
4724
- else :
4725
- return v
4726
-
4727
- # perform normalization along reduced axis
4728
- if len (dims ) > 1 :
4729
- wgts = np .apply_along_axis (_normalize , - 1 , wgts )
4730
- else :
4731
- wgts = np .apply_along_axis (_normalize , dims [0 ], wgts )
4732
-
4733
4719
return wgts
4734
4720
4735
4721
@@ -5022,11 +5008,14 @@ def _get_weighted_quantile_values(arr1d, wgts1d):
5022
5008
5023
5009
# each weight occupies a range in weight space w/ left/right bounds
5024
5010
left_weight_bound = np .roll (wgts1d_cumsum , 1 )
5025
- left_weight_bound [0 ] = 0 # left-most weight bound fixed at 0
5026
- right_weight_bound = wgts1d_cumsum - 1
5011
+ # value i left weight index bound = sum(weights before i) + 1 - 1,
5012
+ # the +1 due to neighboring values having an index distance of 1,
5013
+ # the -1 due to 0-indexing in Python
5014
+ left_weight_bound [0 ] = 0 # left-most weight bound defined to be 0
5015
+ right_weight_bound = wgts1d_cumsum - 1 # -1 due to 0-indexing
5027
5016
5028
5017
# now construct a mapping from weight bounds to real indexes
5029
- # for example, arr1d=[1, 2 ] & wgts1d=[2, 3] ->
5018
+ # arr1d=[7, 8 ] & wgts1d=[2, 3] == [7, 7, 8, 8, 8]
5030
5019
# -> real_indexes=[0, 0, 1, 1] & w_index_bounds=[0, 1, 2, 4]
5031
5020
indexes = np .arange (arr1d .size )
5032
5021
real_indexes = np .zeros (2 * indexes .size )
@@ -5039,29 +5028,35 @@ def _get_weighted_quantile_values(arr1d, wgts1d):
5039
5028
# first define previous_w_indexes/next_w_indexes as the indexes
5040
5029
# within w_index_bounds whose values sandwich weight_space_indexes.
5041
5030
# so if w_index_bounds=[0, 1, 2, 4] and weight_space_index=3.5,
5042
- # then previous_w_indexes = 2 and next_w_indexes = 3
5031
+ # then previous_w_indexes = 2 and next_w_indexes = 3,
5032
+ # meaning weight_space_indexed is sandwiched by w_index_bounds[2]
5033
+ # and w_index_bounds[3].
5043
5034
previous_w_indexes = np .searchsorted (w_index_bounds ,
5044
5035
weight_space_indexes ,
5045
5036
side = "right" ) - 1
5046
5037
# leverage _get_index() to deal with out-of-bound indices
5047
5038
previous_w_indexes , next_w_indexes = \
5048
5039
_get_indexes (w_index_bounds , previous_w_indexes ,
5049
5040
len (w_index_bounds ))
5050
- # now redefine previous_w_indexes/next_w_indexes as the weight
5051
- # space indexes that neighbor weight_space_indexes.
5041
+ # following earlier example, we now know weight_space_indexed is
5042
+ # sandwiched by w_index_bounds[2] and w_index_bounds[3], which are
5043
+ # 2 and 4. We want the 2 and 4.
5044
+ # so redefine previous_w_indexes/next_w_indexes as the
5045
+ # w_index_bounds that neighbor weight_space_indexes.
5052
5046
previous_w_indexes = w_index_bounds [previous_w_indexes ]
5053
5047
next_w_indexes = w_index_bounds [next_w_indexes ]
5054
5048
5055
- # map all weight space indexes to real indexes, then compute gamma
5049
+ # method-dependent gammas determine interpolation scheme between
5050
+ # neighboring values, and are computed in weight space.
5051
+ gamma = \
5052
+ _get_gamma (weight_space_indexes , previous_w_indexes , method )
5053
+
5054
+ # map all weight space indexes to real indexes
5056
5055
previous_indexes = \
5057
5056
np .interp (previous_w_indexes , w_index_bounds , real_indexes )
5058
5057
next_indexes = \
5059
5058
np .interp (next_w_indexes , w_index_bounds , real_indexes )
5060
5059
5061
- # method-dependent gammas determine interpolation scheme between
5062
- # neighboring values, and are computed in weight space.
5063
- gamma = \
5064
- _get_gamma (weight_space_indexes , previous_w_indexes , method )
5065
5060
previous = take (arr1d , previous_indexes .astype (int ))
5066
5061
next = take (arr1d , next_indexes .astype (int ))
5067
5062
return _lerp (previous , next , gamma , out = out )
@@ -5075,7 +5070,8 @@ def _get_weighted_quantile_values(arr1d, wgts1d):
5075
5070
result = get_weighted_quantile_values (arr , weights )
5076
5071
5077
5072
# now move data to DATA_AXIS to be consistent with no-weights case
5078
- result = np .moveaxis (result , - 1 , destination = 0 )
5073
+ if axis != - 1 and quantiles .ndim :
5074
+ result = np .moveaxis (result , - 1 , destination = 0 )
5079
5075
5080
5076
else :
5081
5077
values_count = arr .shape [axis ]
0 commit comments