diff --git a/numpy/lib/_function_base_impl.py b/numpy/lib/_function_base_impl.py index f5690a829fc6..6ebb0c2d6edf 100644 --- a/numpy/lib/_function_base_impl.py +++ b/numpy/lib/_function_base_impl.py @@ -4812,11 +4812,38 @@ def _quantile( """ # --- Setup arr = np.asanyarray(arr) - values_count = arr.shape[axis] # The dimensions of `q` are prepended to the output shape, so we need the # axis being sampled from `arr` to be last. if axis != 0: # But moveaxis is slow, so only call it if necessary. arr = np.moveaxis(arr, axis, destination=0) + values_count = arr.shape[0] # Get length along axis + # Handle empty array case immediately + if values_count == 0: + # Determine output shape: quantiles shape + other dimensions + shape = quantiles.shape + arr.shape[1:] + # Determine dtype for missing values + if out is not None: + dtype = out.dtype + else: + if arr.dtype.kind in 'mM': # datetime/timedelta + dtype = arr.dtype + elif arr.dtype.kind in 'iub': # integer, unsigned, boolean + dtype = np.float64 + else: # float, etc. + dtype = arr.dtype + # Choose appropriate missing value + if dtype.kind in 'mM': + missing_value = dtype.type('NaT') + else: + missing_value = np.nan + + # Create or fill output array + if out is None: + return np.full(shape, missing_value, dtype=dtype) + else: + out.fill(missing_value) + return out + supports_nans = ( np.issubdtype(arr.dtype, np.inexact) or arr.dtype.kind in 'Mm' ) @@ -4874,6 +4901,10 @@ def _quantile( # --- Get values from indexes previous = arr[previous_indexes] next = arr[next_indexes] + # Fix integer overflow by converting to float for interpolation + if arr.dtype.kind in 'iub': # integer, unsigned, boolean + previous = previous.astype(np.float64) + next = next.astype(np.float64) # --- Linear interpolation gamma = _get_gamma(virtual_indexes, previous_indexes, method_props) result_shape = virtual_indexes.shape + (1,) * (arr.ndim - 1) diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index eccf4bcfb019..999849f78246 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -4209,6 +4209,45 @@ def test_closest_observation(self): assert_equal(4, np.quantile(arr[0:9], q, method=m)) assert_equal(5, np.quantile(arr, q, method=m)) + def test_quantile_empty(self): + # Empty array + assert np.isnan(np.quantile([], 0.5)) + + # Multiple quantiles + result = np.quantile([], [0, 0.5, 1]) + assert result.shape == (3,) + assert np.all(np.isnan(result)) + + # Keepdims=True + a = np.array([[], [], []]).T # Shape (0, 3) + result = np.quantile(a, 0.5, axis=0, keepdims=True) + assert result.shape == (1, 3) + assert np.all(np.isnan(result)) + + # Axis=0 + a = np.array([]) + result = np.quantile(a, 0.5, axis=0) + assert np.isnan(result) + + # Multiple axes reduction + a = np.zeros((3, 0, 2)) # Shape (3, 0, 2) + result = np.quantile(a, 0.5, axis=(0, 1)) + assert result.shape == (2,) + assert np.all(np.isnan(result)) + + def test_quantile_int_overflow(self): + # Signed integer overflow + a = np.array([32767, -1], dtype=np.int16) + assert np.quantile(a, 0.5) == 16383.0 + + # Unsigned integer + b = np.array([0, 65535], dtype=np.uint16) + assert np.quantile(b, 0.5) == 32767.5 + + # Large integers + c = np.array([np.iinfo(np.int32).max, np.iinfo(np.int32).min], dtype=np.int32) + expected = (np.iinfo(np.int32).max + np.iinfo(np.int32).min) / 2.0 + assert np.quantile(c, 0.5) == expected class TestLerp: @hypothesis.given(t0=st.floats(allow_nan=False, allow_infinity=False, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy