Skip to content

Commit 2093a6d

Browse files
authored
Merge pull request numpy#26769 from aherbert/fix-closest-observation
BUG: Quantile closest_observation to round to nearest even order
2 parents fe52670 + 796b718 commit 2093a6d

File tree

3 files changed

+28
-5
lines changed

3 files changed

+28
-5
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
`np.quantile` with method ``closest_observation`` chooses nearest even order statistic
2+
--------------------------------------------------------------------------------------
3+
This changes the definition of nearest for border cases from the nearest odd
4+
order statistic to nearest even order statistic. The numpy implementation now
5+
matches other reference implementations.

numpy/lib/_function_base_impl.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4355,12 +4355,14 @@ def quantile(a,
43554355
The table above includes only the estimators from H&F that are continuous
43564356
functions of probability `q` (estimators 4-9). NumPy also provides the
43574357
three discontinuous estimators from H&F (estimators 1-3), where ``j`` is
4358-
defined as above and ``m`` and ``g`` are defined as follows.
4358+
defined as above, ``m`` is defined as follows, and ``g`` is a function
4359+
of the real-valued ``index = q*n + m - 1`` and ``j``.
43594360
4360-
1. ``inverted_cdf``: ``m = 0`` and ``g = int(q*n > 0)``
4361-
2. ``averaged_inverted_cdf``: ``m = 0`` and ``g = (1 + int(q*n > 0)) / 2``
4361+
1. ``inverted_cdf``: ``m = 0`` and ``g = int(index - j > 0)``
4362+
2. ``averaged_inverted_cdf``: ``m = 0`` and
4363+
``g = (1 + int(index - j > 0)) / 2``
43624364
3. ``closest_observation``: ``m = -1/2`` and
4363-
``1 - int((g == 0) & (j%2 == 0))``
4365+
``g = 1 - int((index == j) & (j%2 == 1))``
43644366
43654367
For backward compatibility with previous versions of NumPy, `quantile`
43664368
provides four additional discontinuous estimators. Like
@@ -4608,7 +4610,9 @@ def _discret_interpolation_to_boundaries(index, gamma_condition_fun):
46084610

46094611

46104612
def _closest_observation(n, quantiles):
4611-
gamma_fun = lambda gamma, index: (gamma == 0) & (np.floor(index) % 2 == 0)
4613+
# "choose the nearest even order statistic at g=0" (H&F (1996) pp. 362).
4614+
# Order is 1-based so for zero-based indexing round to nearest odd index.
4615+
gamma_fun = lambda gamma, index: (gamma == 0) & (np.floor(index) % 2 == 1)
46124616
return _discret_interpolation_to_boundaries((n * quantiles) - 1 - 0.5,
46134617
gamma_fun)
46144618

numpy/lib/tests/test_function_base.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4029,6 +4029,20 @@ def test_weibull_fraction(self):
40294029
quantile = np.quantile(arr, [Fraction(1, 2)], method='weibull')
40304030
assert_equal(quantile, np.array(Fraction(1, 20)))
40314031

4032+
def test_closest_observation(self):
4033+
# Round ties to nearest even order statistic (see #26656)
4034+
m = 'closest_observation'
4035+
q = 0.5
4036+
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
4037+
assert_equal(2, np.quantile(arr[0:3], q, method=m))
4038+
assert_equal(2, np.quantile(arr[0:4], q, method=m))
4039+
assert_equal(2, np.quantile(arr[0:5], q, method=m))
4040+
assert_equal(3, np.quantile(arr[0:6], q, method=m))
4041+
assert_equal(4, np.quantile(arr[0:7], q, method=m))
4042+
assert_equal(4, np.quantile(arr[0:8], q, method=m))
4043+
assert_equal(4, np.quantile(arr[0:9], q, method=m))
4044+
assert_equal(5, np.quantile(arr, q, method=m))
4045+
40324046

40334047
class TestLerp:
40344048
@hypothesis.given(t0=st.floats(allow_nan=False, allow_infinity=False,

0 commit comments

Comments
 (0)