scipy 1.15.3__cp312-cp312-musllinux_1_2_aarch64.whl → 1.16.0rc2__cp312-cp312-musllinux_1_2_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scipy/__config__.py +10 -10
- scipy/__init__.py +3 -6
- scipy/_cyutility.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_array_api.py +486 -161
- scipy/_lib/_array_api_compat_vendor.py +9 -0
- scipy/_lib/_bunch.py +4 -0
- scipy/_lib/_ccallback_c.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_docscrape.py +1 -1
- scipy/_lib/_elementwise_iterative_method.py +15 -26
- scipy/_lib/_fpumode.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_sparse.py +41 -0
- scipy/_lib/_test_ccallback.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_test_deprecation_call.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_test_deprecation_def.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_testutils.py +6 -2
- scipy/_lib/_uarray/_uarray.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_util.py +222 -125
- scipy/_lib/array_api_compat/__init__.py +4 -4
- scipy/_lib/array_api_compat/_internal.py +19 -6
- scipy/_lib/array_api_compat/common/__init__.py +1 -1
- scipy/_lib/array_api_compat/common/_aliases.py +365 -193
- scipy/_lib/array_api_compat/common/_fft.py +94 -64
- scipy/_lib/array_api_compat/common/_helpers.py +413 -180
- scipy/_lib/array_api_compat/common/_linalg.py +116 -40
- scipy/_lib/array_api_compat/common/_typing.py +179 -10
- scipy/_lib/array_api_compat/cupy/__init__.py +1 -4
- scipy/_lib/array_api_compat/cupy/_aliases.py +61 -41
- scipy/_lib/array_api_compat/cupy/_info.py +16 -6
- scipy/_lib/array_api_compat/cupy/_typing.py +24 -39
- scipy/_lib/array_api_compat/dask/array/__init__.py +6 -3
- scipy/_lib/array_api_compat/dask/array/_aliases.py +267 -108
- scipy/_lib/array_api_compat/dask/array/_info.py +105 -34
- scipy/_lib/array_api_compat/dask/array/fft.py +5 -8
- scipy/_lib/array_api_compat/dask/array/linalg.py +21 -22
- scipy/_lib/array_api_compat/numpy/__init__.py +13 -15
- scipy/_lib/array_api_compat/numpy/_aliases.py +98 -49
- scipy/_lib/array_api_compat/numpy/_info.py +36 -16
- scipy/_lib/array_api_compat/numpy/_typing.py +27 -43
- scipy/_lib/array_api_compat/numpy/fft.py +11 -5
- scipy/_lib/array_api_compat/numpy/linalg.py +75 -22
- scipy/_lib/array_api_compat/torch/__init__.py +3 -5
- scipy/_lib/array_api_compat/torch/_aliases.py +262 -159
- scipy/_lib/array_api_compat/torch/_info.py +27 -16
- scipy/_lib/array_api_compat/torch/_typing.py +3 -0
- scipy/_lib/array_api_compat/torch/fft.py +17 -18
- scipy/_lib/array_api_compat/torch/linalg.py +16 -16
- scipy/_lib/array_api_extra/__init__.py +26 -3
- scipy/_lib/array_api_extra/_delegation.py +171 -0
- scipy/_lib/array_api_extra/_lib/__init__.py +1 -0
- scipy/_lib/array_api_extra/_lib/_at.py +463 -0
- scipy/_lib/array_api_extra/_lib/_backends.py +46 -0
- scipy/_lib/array_api_extra/_lib/_funcs.py +937 -0
- scipy/_lib/array_api_extra/_lib/_lazy.py +357 -0
- scipy/_lib/array_api_extra/_lib/_testing.py +278 -0
- scipy/_lib/array_api_extra/_lib/_utils/__init__.py +1 -0
- scipy/_lib/array_api_extra/_lib/_utils/_compat.py +74 -0
- scipy/_lib/array_api_extra/_lib/_utils/_compat.pyi +45 -0
- scipy/_lib/array_api_extra/_lib/_utils/_helpers.py +559 -0
- scipy/_lib/array_api_extra/_lib/_utils/_typing.py +10 -0
- scipy/_lib/array_api_extra/_lib/_utils/_typing.pyi +105 -0
- scipy/_lib/array_api_extra/testing.py +359 -0
- scipy/_lib/decorator.py +2 -2
- scipy/_lib/doccer.py +1 -7
- scipy/_lib/messagestream.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/pyprima/__init__.py +212 -0
- scipy/_lib/pyprima/cobyla/__init__.py +0 -0
- scipy/_lib/pyprima/cobyla/cobyla.py +559 -0
- scipy/_lib/pyprima/cobyla/cobylb.py +714 -0
- scipy/_lib/pyprima/cobyla/geometry.py +226 -0
- scipy/_lib/pyprima/cobyla/initialize.py +215 -0
- scipy/_lib/pyprima/cobyla/trustregion.py +492 -0
- scipy/_lib/pyprima/cobyla/update.py +289 -0
- scipy/_lib/pyprima/common/__init__.py +0 -0
- scipy/_lib/pyprima/common/_bounds.py +34 -0
- scipy/_lib/pyprima/common/_linear_constraints.py +46 -0
- scipy/_lib/pyprima/common/_nonlinear_constraints.py +54 -0
- scipy/_lib/pyprima/common/_project.py +173 -0
- scipy/_lib/pyprima/common/checkbreak.py +93 -0
- scipy/_lib/pyprima/common/consts.py +47 -0
- scipy/_lib/pyprima/common/evaluate.py +99 -0
- scipy/_lib/pyprima/common/history.py +38 -0
- scipy/_lib/pyprima/common/infos.py +30 -0
- scipy/_lib/pyprima/common/linalg.py +435 -0
- scipy/_lib/pyprima/common/message.py +290 -0
- scipy/_lib/pyprima/common/powalg.py +131 -0
- scipy/_lib/pyprima/common/preproc.py +277 -0
- scipy/_lib/pyprima/common/present.py +5 -0
- scipy/_lib/pyprima/common/ratio.py +54 -0
- scipy/_lib/pyprima/common/redrho.py +47 -0
- scipy/_lib/pyprima/common/selectx.py +296 -0
- scipy/_lib/tests/test__util.py +105 -121
- scipy/_lib/tests/test_array_api.py +166 -35
- scipy/_lib/tests/test_bunch.py +7 -0
- scipy/_lib/tests/test_ccallback.py +2 -10
- scipy/_lib/tests/test_public_api.py +13 -0
- scipy/cluster/_hierarchy.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/cluster/_optimal_leaf_ordering.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/cluster/_vq.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/cluster/hierarchy.py +393 -223
- scipy/cluster/tests/test_hierarchy.py +273 -335
- scipy/cluster/tests/test_vq.py +45 -61
- scipy/cluster/vq.py +39 -35
- scipy/conftest.py +263 -157
- scipy/constants/_constants.py +4 -1
- scipy/constants/tests/test_codata.py +2 -2
- scipy/constants/tests/test_constants.py +11 -18
- scipy/datasets/_download_all.py +15 -1
- scipy/datasets/_fetchers.py +7 -1
- scipy/datasets/_utils.py +1 -1
- scipy/differentiate/_differentiate.py +25 -25
- scipy/differentiate/tests/test_differentiate.py +24 -25
- scipy/fft/_basic.py +20 -0
- scipy/fft/_helper.py +3 -34
- scipy/fft/_pocketfft/helper.py +29 -1
- scipy/fft/_pocketfft/pypocketfft.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/fft/_pocketfft/tests/test_basic.py +2 -4
- scipy/fft/_pocketfft/tests/test_real_transforms.py +4 -4
- scipy/fft/_realtransforms.py +13 -0
- scipy/fft/tests/test_basic.py +27 -25
- scipy/fft/tests/test_fftlog.py +16 -7
- scipy/fft/tests/test_helper.py +18 -34
- scipy/fft/tests/test_real_transforms.py +8 -10
- scipy/fftpack/convolve.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/fftpack/tests/test_basic.py +2 -4
- scipy/fftpack/tests/test_real_transforms.py +8 -9
- scipy/integrate/_bvp.py +9 -3
- scipy/integrate/_cubature.py +3 -2
- scipy/integrate/_dop.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/_lsoda.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/_ode.py +9 -2
- scipy/integrate/_odepack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/_quad_vec.py +21 -29
- scipy/integrate/_quadpack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/_quadpack_py.py +11 -7
- scipy/integrate/_quadrature.py +3 -3
- scipy/integrate/_rules/_base.py +2 -2
- scipy/integrate/_tanhsinh.py +48 -47
- scipy/integrate/_test_multivariate.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/_test_odeint_banded.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/_vode.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/tests/test__quad_vec.py +0 -6
- scipy/integrate/tests/test_banded_ode_solvers.py +85 -0
- scipy/integrate/tests/test_cubature.py +21 -35
- scipy/integrate/tests/test_quadrature.py +6 -8
- scipy/integrate/tests/test_tanhsinh.py +56 -48
- scipy/interpolate/__init__.py +70 -58
- scipy/interpolate/_bary_rational.py +22 -22
- scipy/interpolate/_bsplines.py +119 -66
- scipy/interpolate/_cubic.py +65 -50
- scipy/interpolate/_dfitpack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_dierckx.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_fitpack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_fitpack2.py +9 -6
- scipy/interpolate/_fitpack_impl.py +32 -26
- scipy/interpolate/_fitpack_repro.py +23 -19
- scipy/interpolate/_interpnd.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_interpolate.py +30 -12
- scipy/interpolate/_ndbspline.py +13 -18
- scipy/interpolate/_ndgriddata.py +5 -8
- scipy/interpolate/_polyint.py +95 -31
- scipy/interpolate/_ppoly.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_rbf.py +2 -2
- scipy/interpolate/_rbfinterp.py +1 -1
- scipy/interpolate/_rbfinterp_pythran.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_rgi.py +31 -26
- scipy/interpolate/_rgi_cython.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/dfitpack.py +0 -20
- scipy/interpolate/interpnd.py +1 -2
- scipy/interpolate/tests/test_bary_rational.py +2 -2
- scipy/interpolate/tests/test_bsplines.py +97 -1
- scipy/interpolate/tests/test_fitpack2.py +39 -1
- scipy/interpolate/tests/test_interpnd.py +32 -20
- scipy/interpolate/tests/test_interpolate.py +48 -4
- scipy/interpolate/tests/test_rgi.py +2 -1
- scipy/io/_fast_matrix_market/__init__.py +2 -0
- scipy/io/_fast_matrix_market/_fmm_core.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/io/_harwell_boeing/_fortran_format_parser.py +19 -16
- scipy/io/_harwell_boeing/hb.py +7 -11
- scipy/io/_idl.py +5 -7
- scipy/io/_netcdf.py +15 -5
- scipy/io/_test_fortran.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/io/arff/tests/test_arffread.py +3 -3
- scipy/io/matlab/__init__.py +5 -3
- scipy/io/matlab/_mio.py +4 -1
- scipy/io/matlab/_mio5.py +19 -13
- scipy/io/matlab/_mio5_utils.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/io/matlab/_mio_utils.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/io/matlab/_miobase.py +4 -1
- scipy/io/matlab/_streams.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/io/matlab/tests/test_mio.py +46 -18
- scipy/io/matlab/tests/test_mio_funcs.py +1 -1
- scipy/io/tests/test_mmio.py +7 -1
- scipy/io/tests/test_wavfile.py +41 -0
- scipy/io/wavfile.py +57 -10
- scipy/linalg/_basic.py +113 -86
- scipy/linalg/_cythonized_array_utils.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_decomp.py +22 -9
- scipy/linalg/_decomp_cholesky.py +28 -13
- scipy/linalg/_decomp_cossin.py +45 -30
- scipy/linalg/_decomp_interpolative.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_decomp_ldl.py +4 -1
- scipy/linalg/_decomp_lu.py +18 -6
- scipy/linalg/_decomp_lu_cython.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_decomp_polar.py +2 -0
- scipy/linalg/_decomp_qr.py +6 -2
- scipy/linalg/_decomp_qz.py +3 -0
- scipy/linalg/_decomp_schur.py +3 -1
- scipy/linalg/_decomp_svd.py +13 -2
- scipy/linalg/_decomp_update.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_expm_frechet.py +4 -0
- scipy/linalg/_fblas.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_flapack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_linalg_pythran.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_matfuncs.py +187 -4
- scipy/linalg/_matfuncs_expm.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_matfuncs_schur_sqrtm.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_matfuncs_sqrtm.py +1 -99
- scipy/linalg/_matfuncs_sqrtm_triu.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_procrustes.py +2 -0
- scipy/linalg/_sketches.py +17 -6
- scipy/linalg/_solve_toeplitz.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_solvers.py +7 -2
- scipy/linalg/_special_matrices.py +26 -36
- scipy/linalg/cython_blas.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/cython_lapack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/lapack.py +22 -2
- scipy/linalg/tests/_cython_examples/meson.build +7 -0
- scipy/linalg/tests/test_basic.py +31 -16
- scipy/linalg/tests/test_batch.py +588 -0
- scipy/linalg/tests/test_cythonized_array_utils.py +0 -2
- scipy/linalg/tests/test_decomp.py +40 -3
- scipy/linalg/tests/test_decomp_cossin.py +14 -0
- scipy/linalg/tests/test_decomp_ldl.py +1 -1
- scipy/linalg/tests/test_lapack.py +115 -7
- scipy/linalg/tests/test_matfuncs.py +157 -102
- scipy/linalg/tests/test_procrustes.py +0 -7
- scipy/linalg/tests/test_solve_toeplitz.py +1 -1
- scipy/linalg/tests/test_special_matrices.py +1 -5
- scipy/ndimage/__init__.py +1 -0
- scipy/ndimage/_ctest.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_cytest.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_delegators.py +8 -2
- scipy/ndimage/_filters.py +453 -5
- scipy/ndimage/_interpolation.py +36 -6
- scipy/ndimage/_measurements.py +4 -2
- scipy/ndimage/_morphology.py +5 -0
- scipy/ndimage/_nd_image.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_ni_docstrings.py +5 -1
- scipy/ndimage/_ni_label.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_ni_support.py +1 -5
- scipy/ndimage/_rank_filter_1d.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_support_alternative_backends.py +18 -6
- scipy/ndimage/tests/test_filters.py +370 -259
- scipy/ndimage/tests/test_fourier.py +7 -9
- scipy/ndimage/tests/test_interpolation.py +68 -61
- scipy/ndimage/tests/test_measurements.py +18 -35
- scipy/ndimage/tests/test_morphology.py +143 -131
- scipy/ndimage/tests/test_splines.py +1 -3
- scipy/odr/__odrpack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_basinhopping.py +13 -7
- scipy/optimize/_bglu_dense.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_bracket.py +17 -24
- scipy/optimize/_chandrupatla.py +9 -10
- scipy/optimize/_cobyla_py.py +104 -123
- scipy/optimize/_constraints.py +14 -10
- scipy/optimize/_differentiable_functions.py +371 -230
- scipy/optimize/_differentialevolution.py +4 -3
- scipy/optimize/_direct.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_dual_annealing.py +1 -1
- scipy/optimize/_elementwise.py +1 -4
- scipy/optimize/_group_columns.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_highspy/_core.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_highspy/_highs_options.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_lbfgsb.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_lbfgsb_py.py +57 -16
- scipy/optimize/_linprog_doc.py +2 -2
- scipy/optimize/_linprog_highs.py +2 -2
- scipy/optimize/_linprog_ip.py +25 -10
- scipy/optimize/_linprog_util.py +14 -16
- scipy/optimize/_lsap.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_lsq/common.py +3 -3
- scipy/optimize/_lsq/dogbox.py +16 -2
- scipy/optimize/_lsq/givens_elimination.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_lsq/least_squares.py +198 -126
- scipy/optimize/_lsq/lsq_linear.py +6 -6
- scipy/optimize/_lsq/trf.py +35 -8
- scipy/optimize/_milp.py +3 -1
- scipy/optimize/_minimize.py +105 -36
- scipy/optimize/_minpack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_minpack_py.py +21 -14
- scipy/optimize/_moduleTNC.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_nnls.py +20 -21
- scipy/optimize/_nonlin.py +34 -3
- scipy/optimize/_numdiff.py +288 -110
- scipy/optimize/_optimize.py +86 -48
- scipy/optimize/_pava_pybind.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_remove_redundancy.py +5 -5
- scipy/optimize/_root_scalar.py +1 -1
- scipy/optimize/_shgo.py +6 -0
- scipy/optimize/_shgo_lib/_complex.py +1 -1
- scipy/optimize/_slsqp_py.py +216 -124
- scipy/optimize/_slsqplib.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_spectral.py +1 -1
- scipy/optimize/_tnc.py +8 -1
- scipy/optimize/_trlib/_trlib.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_trustregion.py +20 -6
- scipy/optimize/_trustregion_constr/canonical_constraint.py +7 -7
- scipy/optimize/_trustregion_constr/equality_constrained_sqp.py +1 -1
- scipy/optimize/_trustregion_constr/minimize_trustregion_constr.py +11 -3
- scipy/optimize/_trustregion_constr/projections.py +12 -8
- scipy/optimize/_trustregion_constr/qp_subproblem.py +9 -9
- scipy/optimize/_trustregion_constr/tests/test_projections.py +7 -7
- scipy/optimize/_trustregion_constr/tests/test_qp_subproblem.py +77 -77
- scipy/optimize/_trustregion_constr/tr_interior_point.py +5 -5
- scipy/optimize/_trustregion_exact.py +0 -1
- scipy/optimize/_zeros.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_zeros_py.py +97 -17
- scipy/optimize/cython_optimize/_zeros.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/slsqp.py +0 -1
- scipy/optimize/tests/test__basinhopping.py +1 -1
- scipy/optimize/tests/test__differential_evolution.py +4 -4
- scipy/optimize/tests/test__linprog_clean_inputs.py +5 -3
- scipy/optimize/tests/test__numdiff.py +66 -22
- scipy/optimize/tests/test__remove_redundancy.py +2 -2
- scipy/optimize/tests/test__shgo.py +9 -1
- scipy/optimize/tests/test_bracket.py +36 -46
- scipy/optimize/tests/test_chandrupatla.py +133 -135
- scipy/optimize/tests/test_cobyla.py +74 -45
- scipy/optimize/tests/test_constraints.py +1 -1
- scipy/optimize/tests/test_differentiable_functions.py +226 -6
- scipy/optimize/tests/test_lbfgsb_hessinv.py +22 -0
- scipy/optimize/tests/test_least_squares.py +125 -13
- scipy/optimize/tests/test_linear_assignment.py +3 -3
- scipy/optimize/tests/test_linprog.py +3 -3
- scipy/optimize/tests/test_lsq_linear.py +6 -6
- scipy/optimize/tests/test_minimize_constrained.py +2 -2
- scipy/optimize/tests/test_minpack.py +4 -4
- scipy/optimize/tests/test_nnls.py +43 -3
- scipy/optimize/tests/test_nonlin.py +36 -0
- scipy/optimize/tests/test_optimize.py +95 -17
- scipy/optimize/tests/test_slsqp.py +36 -4
- scipy/optimize/tests/test_zeros.py +34 -1
- scipy/signal/__init__.py +12 -23
- scipy/signal/_delegators.py +568 -0
- scipy/signal/_filter_design.py +459 -241
- scipy/signal/_fir_filter_design.py +262 -90
- scipy/signal/_lti_conversion.py +3 -2
- scipy/signal/_ltisys.py +118 -91
- scipy/signal/_max_len_seq_inner.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/signal/_peak_finding_utils.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/signal/_polyutils.py +172 -0
- scipy/signal/_short_time_fft.py +519 -70
- scipy/signal/_signal_api.py +30 -0
- scipy/signal/_signaltools.py +719 -399
- scipy/signal/_sigtools.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/signal/_sosfilt.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/signal/_spectral_py.py +230 -50
- scipy/signal/_spline.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/signal/_spline_filters.py +108 -68
- scipy/signal/_support_alternative_backends.py +73 -0
- scipy/signal/_upfirdn.py +4 -1
- scipy/signal/_upfirdn_apply.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/signal/_waveforms.py +2 -11
- scipy/signal/_wavelets.py +1 -1
- scipy/signal/fir_filter_design.py +1 -0
- scipy/signal/spline.py +4 -11
- scipy/signal/tests/_scipy_spectral_test_shim.py +2 -171
- scipy/signal/tests/test_bsplines.py +114 -79
- scipy/signal/tests/test_cont2discrete.py +9 -2
- scipy/signal/tests/test_filter_design.py +721 -481
- scipy/signal/tests/test_fir_filter_design.py +332 -140
- scipy/signal/tests/test_savitzky_golay.py +4 -3
- scipy/signal/tests/test_short_time_fft.py +221 -3
- scipy/signal/tests/test_signaltools.py +2144 -1348
- scipy/signal/tests/test_spectral.py +50 -6
- scipy/signal/tests/test_splines.py +161 -96
- scipy/signal/tests/test_upfirdn.py +84 -50
- scipy/signal/tests/test_waveforms.py +20 -0
- scipy/signal/tests/test_windows.py +607 -466
- scipy/signal/windows/_windows.py +287 -148
- scipy/sparse/__init__.py +23 -4
- scipy/sparse/_base.py +270 -108
- scipy/sparse/_bsr.py +7 -4
- scipy/sparse/_compressed.py +59 -231
- scipy/sparse/_construct.py +90 -38
- scipy/sparse/_coo.py +115 -181
- scipy/sparse/_csc.py +4 -4
- scipy/sparse/_csparsetools.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/_csr.py +2 -2
- scipy/sparse/_data.py +48 -48
- scipy/sparse/_dia.py +105 -18
- scipy/sparse/_dok.py +0 -23
- scipy/sparse/_index.py +4 -4
- scipy/sparse/_matrix.py +23 -0
- scipy/sparse/_sparsetools.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/_sputils.py +37 -22
- scipy/sparse/base.py +0 -9
- scipy/sparse/bsr.py +0 -14
- scipy/sparse/compressed.py +0 -23
- scipy/sparse/construct.py +0 -6
- scipy/sparse/coo.py +0 -14
- scipy/sparse/csc.py +0 -3
- scipy/sparse/csgraph/_flow.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_matching.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_min_spanning_tree.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_reordering.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_shortest_path.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_tools.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_traversal.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/tests/test_matching.py +14 -2
- scipy/sparse/csgraph/tests/test_pydata_sparse.py +4 -1
- scipy/sparse/csgraph/tests/test_shortest_path.py +83 -27
- scipy/sparse/csr.py +0 -5
- scipy/sparse/data.py +1 -6
- scipy/sparse/dia.py +0 -7
- scipy/sparse/dok.py +0 -10
- scipy/sparse/linalg/_dsolve/_superlu.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_dsolve/linsolve.py +9 -0
- scipy/sparse/linalg/_dsolve/tests/test_linsolve.py +35 -28
- scipy/sparse/linalg/_eigen/arpack/_arpack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_eigen/arpack/arpack.py +23 -17
- scipy/sparse/linalg/_eigen/lobpcg/lobpcg.py +6 -6
- scipy/sparse/linalg/_interface.py +17 -18
- scipy/sparse/linalg/_isolve/_gcrotmk.py +4 -4
- scipy/sparse/linalg/_isolve/iterative.py +51 -45
- scipy/sparse/linalg/_isolve/lgmres.py +6 -6
- scipy/sparse/linalg/_isolve/minres.py +5 -5
- scipy/sparse/linalg/_isolve/tfqmr.py +7 -7
- scipy/sparse/linalg/_isolve/utils.py +2 -8
- scipy/sparse/linalg/_matfuncs.py +1 -1
- scipy/sparse/linalg/_norm.py +1 -1
- scipy/sparse/linalg/_propack/_cpropack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_propack/_dpropack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_propack/_spropack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_propack/_zpropack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_special_sparse_arrays.py +39 -38
- scipy/sparse/linalg/tests/test_pydata_sparse.py +14 -0
- scipy/sparse/tests/test_arithmetic1d.py +5 -2
- scipy/sparse/tests/test_base.py +214 -42
- scipy/sparse/tests/test_common1d.py +7 -7
- scipy/sparse/tests/test_construct.py +1 -1
- scipy/sparse/tests/test_coo.py +272 -4
- scipy/sparse/tests/test_sparsetools.py +5 -0
- scipy/sparse/tests/test_sputils.py +36 -7
- scipy/spatial/_ckdtree.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/_distance_pybind.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/_distance_wrap.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/_hausdorff.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/_qhull.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/_voronoi.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/distance.py +49 -42
- scipy/spatial/tests/test_distance.py +15 -1
- scipy/spatial/tests/test_kdtree.py +1 -0
- scipy/spatial/tests/test_qhull.py +7 -2
- scipy/spatial/transform/__init__.py +5 -3
- scipy/spatial/transform/_rigid_transform.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/transform/_rotation.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/transform/tests/test_rigid_transform.py +1221 -0
- scipy/spatial/transform/tests/test_rotation.py +1213 -832
- scipy/spatial/transform/tests/test_rotation_groups.py +3 -3
- scipy/spatial/transform/tests/test_rotation_spline.py +29 -8
- scipy/special/__init__.py +1 -47
- scipy/special/_add_newdocs.py +34 -772
- scipy/special/_basic.py +22 -25
- scipy/special/_comb.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_ellip_harm_2.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_gufuncs.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_logsumexp.py +67 -58
- scipy/special/_orthogonal.pyi +1 -1
- scipy/special/_specfun.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_special_ufuncs.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_spherical_bessel.py +4 -4
- scipy/special/_support_alternative_backends.py +212 -119
- scipy/special/_test_internal.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_testutils.py +4 -4
- scipy/special/_ufuncs.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_ufuncs.pyi +1 -0
- scipy/special/_ufuncs.pyx +215 -1400
- scipy/special/_ufuncs_cxx.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_ufuncs_cxx.pxd +2 -15
- scipy/special/_ufuncs_cxx.pyx +5 -44
- scipy/special/_ufuncs_cxx_defs.h +2 -16
- scipy/special/_ufuncs_defs.h +0 -8
- scipy/special/cython_special.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/cython_special.pxd +1 -1
- scipy/special/tests/_cython_examples/meson.build +10 -1
- scipy/special/tests/test_basic.py +153 -20
- scipy/special/tests/test_boost_ufuncs.py +3 -0
- scipy/special/tests/test_cdflib.py +35 -11
- scipy/special/tests/test_gammainc.py +16 -0
- scipy/special/tests/test_hyp2f1.py +2 -2
- scipy/special/tests/test_log1mexp.py +85 -0
- scipy/special/tests/test_logsumexp.py +206 -64
- scipy/special/tests/test_mpmath.py +1 -0
- scipy/special/tests/test_nan_inputs.py +1 -1
- scipy/special/tests/test_orthogonal.py +17 -18
- scipy/special/tests/test_sf_error.py +3 -2
- scipy/special/tests/test_sph_harm.py +6 -7
- scipy/special/tests/test_support_alternative_backends.py +211 -76
- scipy/stats/__init__.py +4 -1
- scipy/stats/_ansari_swilk_statistics.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_axis_nan_policy.py +5 -12
- scipy/stats/_biasedurn.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_continued_fraction.py +387 -0
- scipy/stats/_continuous_distns.py +277 -310
- scipy/stats/_correlation.py +1 -1
- scipy/stats/_covariance.py +6 -3
- scipy/stats/_discrete_distns.py +39 -32
- scipy/stats/_distn_infrastructure.py +39 -12
- scipy/stats/_distribution_infrastructure.py +900 -238
- scipy/stats/_entropy.py +9 -10
- scipy/{_lib → stats}/_finite_differences.py +1 -1
- scipy/stats/_hypotests.py +83 -50
- scipy/stats/_kde.py +53 -49
- scipy/stats/_ksstats.py +1 -1
- scipy/stats/_levy_stable/__init__.py +7 -15
- scipy/stats/_levy_stable/levyst.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_morestats.py +118 -73
- scipy/stats/_mstats_basic.py +13 -17
- scipy/stats/_mstats_extras.py +8 -8
- scipy/stats/_multivariate.py +89 -113
- scipy/stats/_new_distributions.py +97 -20
- scipy/stats/_page_trend_test.py +12 -5
- scipy/stats/_probability_distribution.py +265 -43
- scipy/stats/_qmc.py +14 -9
- scipy/stats/_qmc_cy.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_qmvnt.py +16 -95
- scipy/stats/_qmvnt_cy.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_quantile.py +335 -0
- scipy/stats/_rcont/rcont.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_resampling.py +4 -29
- scipy/stats/_sampling.py +1 -1
- scipy/stats/_sobol.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_stats.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_stats_mstats_common.py +21 -2
- scipy/stats/_stats_py.py +550 -476
- scipy/stats/_stats_pythran.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_unuran/unuran_wrapper.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_unuran/unuran_wrapper.pyi +2 -1
- scipy/stats/_variation.py +6 -8
- scipy/stats/_wilcoxon.py +13 -7
- scipy/stats/tests/common_tests.py +6 -4
- scipy/stats/tests/test_axis_nan_policy.py +62 -24
- scipy/stats/tests/test_continued_fraction.py +173 -0
- scipy/stats/tests/test_continuous.py +379 -60
- scipy/stats/tests/test_continuous_basic.py +18 -12
- scipy/stats/tests/test_discrete_basic.py +14 -8
- scipy/stats/tests/test_discrete_distns.py +16 -16
- scipy/stats/tests/test_distributions.py +95 -75
- scipy/stats/tests/test_entropy.py +40 -48
- scipy/stats/tests/test_fit.py +4 -3
- scipy/stats/tests/test_hypotests.py +153 -24
- scipy/stats/tests/test_kdeoth.py +109 -41
- scipy/stats/tests/test_marray.py +289 -0
- scipy/stats/tests/test_morestats.py +79 -47
- scipy/stats/tests/test_mstats_basic.py +3 -3
- scipy/stats/tests/test_multivariate.py +434 -83
- scipy/stats/tests/test_qmc.py +13 -10
- scipy/stats/tests/test_quantile.py +199 -0
- scipy/stats/tests/test_rank.py +119 -112
- scipy/stats/tests/test_resampling.py +47 -56
- scipy/stats/tests/test_sampling.py +9 -4
- scipy/stats/tests/test_stats.py +799 -939
- scipy/stats/tests/test_variation.py +8 -6
- scipy/version.py +2 -2
- {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/LICENSE.txt +4 -4
- {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/METADATA +11 -11
- {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/RECORD +1262 -1269
- scipy.libs/libgcc_s-69c45f16.so.1 +0 -0
- scipy.libs/libgfortran-db0b6589.so.5.0.0 +0 -0
- scipy.libs/{libstdc++-1b614e01.so.6.0.32 → libstdc++-1f1a71be.so.6.0.33} +0 -0
- scipy/_lib/array_api_extra/_funcs.py +0 -484
- scipy/_lib/array_api_extra/_typing.py +0 -8
- scipy/interpolate/_bspl.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_cobyla.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_cython_nnls.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_slsqp.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/qhull_src/COPYING.txt +0 -38
- scipy/special/libsf_error_state.so +0 -0
- scipy/special/tests/test_log_softmax.py +0 -109
- scipy/special/tests/test_xsf_cuda.py +0 -114
- scipy/special/xsf/binom.h +0 -89
- scipy/special/xsf/cdflib.h +0 -100
- scipy/special/xsf/cephes/airy.h +0 -307
- scipy/special/xsf/cephes/besselpoly.h +0 -51
- scipy/special/xsf/cephes/beta.h +0 -257
- scipy/special/xsf/cephes/cbrt.h +0 -131
- scipy/special/xsf/cephes/chbevl.h +0 -85
- scipy/special/xsf/cephes/chdtr.h +0 -193
- scipy/special/xsf/cephes/const.h +0 -87
- scipy/special/xsf/cephes/ellie.h +0 -293
- scipy/special/xsf/cephes/ellik.h +0 -251
- scipy/special/xsf/cephes/ellpe.h +0 -107
- scipy/special/xsf/cephes/ellpk.h +0 -117
- scipy/special/xsf/cephes/expn.h +0 -260
- scipy/special/xsf/cephes/gamma.h +0 -398
- scipy/special/xsf/cephes/hyp2f1.h +0 -596
- scipy/special/xsf/cephes/hyperg.h +0 -361
- scipy/special/xsf/cephes/i0.h +0 -149
- scipy/special/xsf/cephes/i1.h +0 -158
- scipy/special/xsf/cephes/igam.h +0 -421
- scipy/special/xsf/cephes/igam_asymp_coeff.h +0 -195
- scipy/special/xsf/cephes/igami.h +0 -313
- scipy/special/xsf/cephes/j0.h +0 -225
- scipy/special/xsf/cephes/j1.h +0 -198
- scipy/special/xsf/cephes/jv.h +0 -715
- scipy/special/xsf/cephes/k0.h +0 -164
- scipy/special/xsf/cephes/k1.h +0 -163
- scipy/special/xsf/cephes/kn.h +0 -243
- scipy/special/xsf/cephes/lanczos.h +0 -112
- scipy/special/xsf/cephes/ndtr.h +0 -275
- scipy/special/xsf/cephes/poch.h +0 -85
- scipy/special/xsf/cephes/polevl.h +0 -167
- scipy/special/xsf/cephes/psi.h +0 -194
- scipy/special/xsf/cephes/rgamma.h +0 -111
- scipy/special/xsf/cephes/scipy_iv.h +0 -811
- scipy/special/xsf/cephes/shichi.h +0 -248
- scipy/special/xsf/cephes/sici.h +0 -224
- scipy/special/xsf/cephes/sindg.h +0 -221
- scipy/special/xsf/cephes/tandg.h +0 -139
- scipy/special/xsf/cephes/trig.h +0 -58
- scipy/special/xsf/cephes/unity.h +0 -186
- scipy/special/xsf/cephes/zeta.h +0 -172
- scipy/special/xsf/config.h +0 -304
- scipy/special/xsf/digamma.h +0 -205
- scipy/special/xsf/error.h +0 -57
- scipy/special/xsf/evalpoly.h +0 -47
- scipy/special/xsf/expint.h +0 -266
- scipy/special/xsf/hyp2f1.h +0 -694
- scipy/special/xsf/iv_ratio.h +0 -173
- scipy/special/xsf/lambertw.h +0 -150
- scipy/special/xsf/loggamma.h +0 -163
- scipy/special/xsf/sici.h +0 -200
- scipy/special/xsf/tools.h +0 -427
- scipy/special/xsf/trig.h +0 -164
- scipy/special/xsf/wright_bessel.h +0 -843
- scipy/special/xsf/zlog1.h +0 -35
- scipy/stats/_mvn.cpython-312-aarch64-linux-musl.so +0 -0
- scipy.libs/libgcc_s-7393e603.so.1 +0 -0
- scipy.libs/libgfortran-eb933d8e.so.5.0.0 +0 -0
- {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/WHEEL +0 -0
scipy/stats/tests/test_stats.py
CHANGED
@@ -22,7 +22,6 @@ from numpy.testing import (assert_, assert_equal,
|
|
22
22
|
assert_array_less)
|
23
23
|
import pytest
|
24
24
|
from pytest import raises as assert_raises
|
25
|
-
import numpy.ma.testutils as mat
|
26
25
|
from numpy import array, arange, float32, power
|
27
26
|
import numpy as np
|
28
27
|
|
@@ -40,11 +39,14 @@ from scipy.stats._axis_nan_policy import (_broadcast_concatenate, SmallSampleWar
|
|
40
39
|
from scipy.stats._stats_py import (_permutation_distribution_t, _chk_asarray, _moment,
|
41
40
|
LinregressResult, _xp_mean, _xp_var, _SimpleChi2)
|
42
41
|
from scipy._lib._util import AxisError
|
43
|
-
from scipy.conftest import
|
44
|
-
from scipy._lib._array_api import (array_namespace,
|
45
|
-
is_torch, xp_size,
|
42
|
+
from scipy.conftest import skip_xp_invalid_arg
|
43
|
+
from scipy._lib._array_api import (array_namespace, eager_warns, is_lazy_array,
|
44
|
+
is_numpy, is_torch, xp_default_dtype, xp_size,
|
45
|
+
SCIPY_ARRAY_API, make_xp_test_case)
|
46
46
|
from scipy._lib._array_api_no_0d import xp_assert_close, xp_assert_equal
|
47
|
+
import scipy._lib.array_api_extra as xpx
|
47
48
|
|
49
|
+
lazy_xp_modules = [stats]
|
48
50
|
skip_xp_backends = pytest.mark.skip_xp_backends
|
49
51
|
|
50
52
|
|
@@ -73,19 +75,14 @@ TINY = array([1e-12,2e-12,3e-12,4e-12,5e-12,6e-12,7e-12,8e-12,9e-12], float)
|
|
73
75
|
ROUND = array([0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5], float)
|
74
76
|
|
75
77
|
|
76
|
-
@array_api_compatible
|
77
|
-
@skip_xp_backends('jax.numpy', reason="JAX doesn't allow item assignment.")
|
78
|
-
@skip_xp_backends('array_api_strict',
|
79
|
-
reason=("`array_api_strict.where` `fillvalue` doesn't "
|
80
|
-
"accept Python floats. See data-apis/array-api#807.")
|
81
|
-
)
|
82
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
83
78
|
class TestTrimmedStats:
|
84
79
|
# TODO: write these tests to handle missing values properly
|
85
80
|
dprec = np.finfo(np.float64).precision
|
86
81
|
|
82
|
+
@make_xp_test_case(stats.tmean)
|
87
83
|
def test_tmean(self, xp):
|
88
|
-
|
84
|
+
default_dtype = xp_default_dtype(xp)
|
85
|
+
x = xp.asarray(X, dtype=default_dtype)
|
89
86
|
|
90
87
|
y = stats.tmean(x, (2, 8), (True, True))
|
91
88
|
xp_assert_close(y, xp.asarray(5.0))
|
@@ -115,8 +112,7 @@ class TestTrimmedStats:
|
|
115
112
|
y_true = [10.5, 11.5, 9, 10, 11, 12, 13]
|
116
113
|
xp_assert_close(y, xp.asarray(y_true))
|
117
114
|
|
118
|
-
x_2d_with_nan =
|
119
|
-
x_2d_with_nan[-1, -3:] = xp.nan
|
115
|
+
x_2d_with_nan = xpx.at(x_2d)[-1, -3:].set(xp.nan, copy=True)
|
120
116
|
y = stats.tmean(x_2d_with_nan, limits=(1, 13), axis=0)
|
121
117
|
y_true = [7, 4.5, 5.5, 6.5, xp.nan, xp.nan, xp.nan]
|
122
118
|
xp_assert_close(y, xp.asarray(y_true))
|
@@ -130,21 +126,22 @@ class TestTrimmedStats:
|
|
130
126
|
y_true = [4.5, 10, 17, 21, xp.nan, xp.nan, xp.nan, xp.nan, xp.nan]
|
131
127
|
xp_assert_close(y, xp.asarray(y_true))
|
132
128
|
|
133
|
-
@
|
134
|
-
|
129
|
+
@make_xp_test_case(stats.tvar)
|
130
|
+
@pytest.mark.filterwarnings(
|
131
|
+
"ignore:invalid value encountered in divide:RuntimeWarning:dask"
|
132
|
+
)
|
135
133
|
def test_tvar(self, xp):
|
136
134
|
x = xp.asarray(X.tolist()) # use default dtype of xp
|
137
|
-
xp_test = array_namespace(x) # need array-api-compat var for `correction`
|
138
135
|
|
139
136
|
y = stats.tvar(x, limits=(2, 8), inclusive=(True, True))
|
140
137
|
xp_assert_close(y, xp.asarray(4.6666666666666661))
|
141
138
|
|
142
139
|
y = stats.tvar(x, limits=None)
|
143
|
-
xp_assert_close(y,
|
140
|
+
xp_assert_close(y, xp.var(x, correction=1))
|
144
141
|
|
145
142
|
x_2d = xp.reshape(xp.arange(63.), (9, 7))
|
146
143
|
y = stats.tvar(x_2d, axis=None)
|
147
|
-
xp_assert_close(y,
|
144
|
+
xp_assert_close(y, xp.var(x_2d, correction=1))
|
148
145
|
|
149
146
|
y = stats.tvar(x_2d, axis=0)
|
150
147
|
xp_assert_close(y, xp.full((7,), 367.5))
|
@@ -161,35 +158,35 @@ class TestTrimmedStats:
|
|
161
158
|
xp_assert_close(y[0], xp.asarray(4.666666666666667))
|
162
159
|
xp_assert_equal(y[1], xp.asarray(xp.nan))
|
163
160
|
|
161
|
+
@make_xp_test_case(stats.tstd)
|
164
162
|
def test_tstd(self, xp):
|
165
163
|
x = xp.asarray(X.tolist()) # use default dtype of xp
|
166
|
-
xp_test = array_namespace(x) # need array-api-compat std for `correction`
|
167
164
|
|
168
165
|
y = stats.tstd(x, (2, 8), (True, True))
|
169
166
|
xp_assert_close(y, xp.asarray(2.1602468994692865))
|
170
167
|
|
171
168
|
y = stats.tstd(x, limits=None)
|
172
|
-
xp_assert_close(y,
|
169
|
+
xp_assert_close(y, xp.std(x, correction=1))
|
173
170
|
|
171
|
+
@make_xp_test_case(stats.tmin)
|
174
172
|
def test_tmin(self, xp):
|
175
|
-
x = xp.arange(10)
|
176
|
-
xp_assert_equal(stats.tmin(x), xp.asarray(0))
|
177
|
-
xp_assert_equal(stats.tmin(x, lowerlimit=0), xp.asarray(0))
|
178
|
-
xp_assert_equal(stats.tmin(x, lowerlimit=0, inclusive=False), xp.asarray(1))
|
173
|
+
x = xp.arange(10.)
|
174
|
+
xp_assert_equal(stats.tmin(x), xp.asarray(0.))
|
175
|
+
xp_assert_equal(stats.tmin(x, lowerlimit=0), xp.asarray(0.))
|
176
|
+
xp_assert_equal(stats.tmin(x, lowerlimit=0, inclusive=False), xp.asarray(1.))
|
179
177
|
|
180
178
|
x = xp.reshape(x, (5, 2))
|
181
179
|
xp_assert_equal(stats.tmin(x, lowerlimit=0, inclusive=False),
|
182
|
-
xp.asarray([2
|
183
|
-
xp_assert_equal(stats.tmin(x, axis=1), xp.asarray([0
|
184
|
-
xp_assert_equal(stats.tmin(x, axis=None), xp.asarray(0))
|
180
|
+
xp.asarray([2., 1.]))
|
181
|
+
xp_assert_equal(stats.tmin(x, axis=1), xp.asarray([0., 2., 4., 6., 8.]))
|
182
|
+
xp_assert_equal(stats.tmin(x, axis=None), xp.asarray(0.))
|
185
183
|
|
186
|
-
x = xp.arange(10.)
|
187
|
-
x[9] = xp.nan
|
184
|
+
x = xpx.at(xp.arange(10.), 9).set(xp.nan)
|
188
185
|
xp_assert_equal(stats.tmin(x), xp.asarray(xp.nan))
|
189
186
|
|
190
187
|
# check that if a full slice is masked, the output returns a
|
191
188
|
# nan instead of a garbage value.
|
192
|
-
x = xp.arange(16)
|
189
|
+
x = xp.reshape(xp.arange(16), (4, 4))
|
193
190
|
res = stats.tmin(x, lowerlimit=4, axis=1)
|
194
191
|
xp_assert_equal(res, xp.asarray([np.nan, 4, 8, 12]))
|
195
192
|
|
@@ -210,20 +207,20 @@ class TestTrimmedStats:
|
|
210
207
|
with assert_raises(ValueError, match=msg):
|
211
208
|
stats.tmin(x, nan_policy='foobar')
|
212
209
|
|
210
|
+
@make_xp_test_case(stats.tmax)
|
213
211
|
def test_tmax(self, xp):
|
214
|
-
x = xp.arange(10)
|
215
|
-
xp_assert_equal(stats.tmax(x), xp.asarray(9))
|
216
|
-
xp_assert_equal(stats.tmax(x, upperlimit=9), xp.asarray(9))
|
217
|
-
xp_assert_equal(stats.tmax(x, upperlimit=9, inclusive=False), xp.asarray(8))
|
212
|
+
x = xp.arange(10.)
|
213
|
+
xp_assert_equal(stats.tmax(x), xp.asarray(9.))
|
214
|
+
xp_assert_equal(stats.tmax(x, upperlimit=9), xp.asarray(9.))
|
215
|
+
xp_assert_equal(stats.tmax(x, upperlimit=9, inclusive=False), xp.asarray(8.))
|
218
216
|
|
219
217
|
x = xp.reshape(x, (5, 2))
|
220
218
|
xp_assert_equal(stats.tmax(x, upperlimit=9, inclusive=False),
|
221
|
-
xp.asarray([8
|
222
|
-
xp_assert_equal(stats.tmax(x, axis=1), xp.asarray([1
|
223
|
-
xp_assert_equal(stats.tmax(x, axis=None), xp.asarray(9))
|
219
|
+
xp.asarray([8., 7.]))
|
220
|
+
xp_assert_equal(stats.tmax(x, axis=1), xp.asarray([1., 3., 5., 7., 9.]))
|
221
|
+
xp_assert_equal(stats.tmax(x, axis=None), xp.asarray(9.))
|
224
222
|
|
225
|
-
x = xp.arange(10.)
|
226
|
-
x[9] = xp.nan
|
223
|
+
x = xpx.at(xp.arange(10.), 9).set(xp.nan)
|
227
224
|
xp_assert_equal(stats.tmax(x), xp.asarray(xp.nan))
|
228
225
|
|
229
226
|
# check that if a full slice is masked, the output returns a
|
@@ -236,7 +233,7 @@ class TestTrimmedStats:
|
|
236
233
|
|
237
234
|
@skip_xp_backends(np_only=True,
|
238
235
|
reason="Only NumPy arrays support scalar input/`nan_policy`.")
|
239
|
-
def
|
236
|
+
def test_tmax_scalar_and_nanpolicy(self, xp):
|
240
237
|
assert_equal(stats.tmax(4), 4)
|
241
238
|
|
242
239
|
x = np.arange(10.)
|
@@ -251,13 +248,36 @@ class TestTrimmedStats:
|
|
251
248
|
with assert_raises(ValueError, match=msg):
|
252
249
|
stats.tmax(x, nan_policy='foobar')
|
253
250
|
|
251
|
+
@make_xp_test_case(stats.tmin, stats.tmax)
|
252
|
+
def test_tmin_tmax_int_dtype(self, xp):
|
253
|
+
x = xp.reshape(xp.arange(10, dtype=xp.int16), (2, 5)).T
|
254
|
+
|
255
|
+
# When tmin/tmax don't need to inject any NaNs,
|
256
|
+
# retain the input dtype. Dask/JAX can't inspect
|
257
|
+
# the data so they always return float.
|
258
|
+
expect_dtype = xp_default_dtype(xp) if is_lazy_array(x) else x.dtype
|
259
|
+
xp_assert_equal(stats.tmin(x), xp.asarray([0, 5], dtype=expect_dtype))
|
260
|
+
xp_assert_equal(stats.tmax(x), xp.asarray([4, 9], dtype=expect_dtype))
|
261
|
+
|
262
|
+
# When they do inject NaNs, all backends behave the same.
|
263
|
+
xp_assert_equal(stats.tmin(x, lowerlimit=6), xp.asarray([xp.nan, 6.]))
|
264
|
+
xp_assert_equal(stats.tmax(x, upperlimit=3), xp.asarray([3., xp.nan]))
|
265
|
+
|
266
|
+
@skip_xp_backends(eager_only=True, reason="Only with data-dependent output dtype")
|
267
|
+
@make_xp_test_case(stats.tmin, stats.tmax)
|
268
|
+
def test_gh_22626(self, xp):
|
269
|
+
# Test that `tmin`/`tmax` returns exact result with outrageously large integers
|
270
|
+
x = xp.arange(2**62, 2**62+10)
|
271
|
+
xp_assert_equal(stats.tmin(x[None, :]), x)
|
272
|
+
xp_assert_equal(stats.tmax(x[None, :]), x)
|
273
|
+
|
274
|
+
@make_xp_test_case(stats.tsem)
|
254
275
|
def test_tsem(self, xp):
|
255
276
|
x = xp.asarray(X.tolist()) # use default dtype of xp
|
256
|
-
xp_test = array_namespace(x) # need array-api-compat std for `correction`
|
257
277
|
|
258
278
|
y = stats.tsem(x, limits=(3, 8), inclusive=(False, True))
|
259
279
|
y_ref = xp.asarray([4., 5., 6., 7., 8.])
|
260
|
-
xp_assert_close(y,
|
280
|
+
xp_assert_close(y, xp.std(y_ref, correction=1) / xp_size(y_ref)**0.5)
|
261
281
|
xp_assert_close(stats.tsem(x, limits=[-1, 10]), stats.tsem(x, limits=None))
|
262
282
|
|
263
283
|
|
@@ -376,19 +396,14 @@ class TestPearsonrWilkinson:
|
|
376
396
|
assert_approx_equal(r,1.0)
|
377
397
|
|
378
398
|
|
379
|
-
@
|
380
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
381
|
-
@skip_xp_backends(cpu_only=True)
|
399
|
+
@make_xp_test_case(stats.pearsonr)
|
382
400
|
class TestPearsonr:
|
383
|
-
@skip_xp_backends(np_only=True)
|
384
401
|
def test_pearsonr_result_attributes(self):
|
385
402
|
res = stats.pearsonr(X, X)
|
386
403
|
attributes = ('correlation', 'pvalue')
|
387
404
|
check_named_results(res, attributes)
|
388
405
|
assert_equal(res.correlation, res.statistic)
|
389
406
|
|
390
|
-
@skip_xp_backends('jax.numpy',
|
391
|
-
reason='JAX arrays do not support item assignment')
|
392
407
|
def test_r_almost_exactly_pos1(self, xp):
|
393
408
|
a = xp.arange(3.0)
|
394
409
|
r, prob = stats.pearsonr(a, a)
|
@@ -398,8 +413,6 @@ class TestPearsonr:
|
|
398
413
|
# square root of the error in r.
|
399
414
|
xp_assert_close(prob, xp.asarray(0.0), atol=np.sqrt(2*np.spacing(1.0)))
|
400
415
|
|
401
|
-
@skip_xp_backends('jax.numpy',
|
402
|
-
reason='JAX arrays do not support item assignment')
|
403
416
|
def test_r_almost_exactly_neg1(self, xp):
|
404
417
|
a = xp.arange(3.0)
|
405
418
|
r, prob = stats.pearsonr(a, -a)
|
@@ -409,8 +422,6 @@ class TestPearsonr:
|
|
409
422
|
# square root of the error in r.
|
410
423
|
xp_assert_close(prob, xp.asarray(0.0), atol=np.sqrt(2*np.spacing(1.0)))
|
411
424
|
|
412
|
-
@skip_xp_backends('jax.numpy',
|
413
|
-
reason='JAX arrays do not support item assignment')
|
414
425
|
def test_basic(self, xp):
|
415
426
|
# A basic test, with a correlation coefficient
|
416
427
|
# that is not 1 or -1.
|
@@ -420,9 +431,6 @@ class TestPearsonr:
|
|
420
431
|
xp_assert_close(r, xp.asarray(3**0.5/2))
|
421
432
|
xp_assert_close(prob, xp.asarray(1/3))
|
422
433
|
|
423
|
-
@skip_xp_backends('jax.numpy',
|
424
|
-
reason='JAX arrays do not support item assignment',
|
425
|
-
)
|
426
434
|
def test_constant_input(self, xp):
|
427
435
|
# Zero variance input
|
428
436
|
# See https://github.com/scipy/scipy/issues/3728
|
@@ -434,9 +442,6 @@ class TestPearsonr:
|
|
434
442
|
xp_assert_close(r, xp.asarray(xp.nan))
|
435
443
|
xp_assert_close(p, xp.asarray(xp.nan))
|
436
444
|
|
437
|
-
@skip_xp_backends('jax.numpy',
|
438
|
-
reason='JAX arrays do not support item assignment',
|
439
|
-
)
|
440
445
|
@pytest.mark.parametrize('dtype', ['float32', 'float64'])
|
441
446
|
def test_near_constant_input(self, xp, dtype):
|
442
447
|
npdtype = getattr(np, dtype)
|
@@ -450,9 +455,6 @@ class TestPearsonr:
|
|
450
455
|
# (The exact value of r would be 1.)
|
451
456
|
stats.pearsonr(x, y)
|
452
457
|
|
453
|
-
@skip_xp_backends('jax.numpy',
|
454
|
-
reason='JAX arrays do not support item assignment',
|
455
|
-
)
|
456
458
|
def test_very_small_input_values(self, xp):
|
457
459
|
# Very small values in an input. A naive implementation will
|
458
460
|
# suffer from underflow.
|
@@ -468,9 +470,6 @@ class TestPearsonr:
|
|
468
470
|
xp_assert_close(r, xp.asarray(0.7272930540750450, dtype=xp.float64))
|
469
471
|
xp_assert_close(p, xp.asarray(0.1637805429533202, dtype=xp.float64))
|
470
472
|
|
471
|
-
@skip_xp_backends('jax.numpy',
|
472
|
-
reason='JAX arrays do not support item assignment',
|
473
|
-
)
|
474
473
|
def test_very_large_input_values(self, xp):
|
475
474
|
# Very large values in an input. A naive implementation will
|
476
475
|
# suffer from overflow.
|
@@ -485,8 +484,6 @@ class TestPearsonr:
|
|
485
484
|
xp_assert_close(r, xp.asarray(0.8660254037844386, dtype=xp.float64))
|
486
485
|
xp_assert_close(p, xp.asarray(0.011724811003954638, dtype=xp.float64))
|
487
486
|
|
488
|
-
@skip_xp_backends('jax.numpy',
|
489
|
-
reason='JAX arrays do not support item assignment')
|
490
487
|
def test_extremely_large_input_values(self, xp):
|
491
488
|
# Extremely large values in x and y. These values would cause the
|
492
489
|
# product sigma_x * sigma_y to overflow if the two factors were
|
@@ -500,7 +497,6 @@ class TestPearsonr:
|
|
500
497
|
xp_assert_close(r, xp.asarray(0.351312332103289, dtype=xp.float64))
|
501
498
|
xp_assert_close(p, xp.asarray(0.648687667896711, dtype=xp.float64))
|
502
499
|
|
503
|
-
@skip_xp_backends('jax.numpy', reason="JAX doesn't allow item assignment.")
|
504
500
|
def test_length_two_pos1(self, xp):
|
505
501
|
# Inputs with length 2.
|
506
502
|
# See https://github.com/scipy/scipy/issues/7730
|
@@ -515,7 +511,6 @@ class TestPearsonr:
|
|
515
511
|
xp_assert_equal(low, -one)
|
516
512
|
xp_assert_equal(high, one)
|
517
513
|
|
518
|
-
@skip_xp_backends('jax.numpy', reason="JAX doesn't allow item assignment.")
|
519
514
|
def test_length_two_neg1(self, xp):
|
520
515
|
# Inputs with length 2.
|
521
516
|
# See https://github.com/scipy/scipy/issues/7730
|
@@ -530,7 +525,7 @@ class TestPearsonr:
|
|
530
525
|
xp_assert_equal(low, -one)
|
531
526
|
xp_assert_equal(high, one)
|
532
527
|
|
533
|
-
@
|
528
|
+
@pytest.mark.filterwarnings("ignore:invalid value encountered in divide")
|
534
529
|
def test_length_two_constant_input(self, xp):
|
535
530
|
# Zero variance input
|
536
531
|
# See https://github.com/scipy/scipy/issues/3728
|
@@ -550,7 +545,7 @@ class TestPearsonr:
|
|
550
545
|
# cor.test(x, y, method = "pearson", alternative = "g")
|
551
546
|
# correlation coefficient and p-value for alternative='two-sided'
|
552
547
|
# calculated with mpmath agree to 16 digits.
|
553
|
-
@
|
548
|
+
@skip_xp_backends(np_only=True)
|
554
549
|
@pytest.mark.parametrize('alternative, pval, rlow, rhigh, sign',
|
555
550
|
[('two-sided', 0.325800137536, -0.814938968841, 0.99230697523, 1),
|
556
551
|
('less', 0.8370999312316, -1, 0.985600937290653, 1),
|
@@ -558,7 +553,7 @@ class TestPearsonr:
|
|
558
553
|
('two-sided', 0.325800137536, -0.992306975236, 0.81493896884, -1),
|
559
554
|
('less', 0.1629000687684, -1.0, 0.6785654158217636, -1),
|
560
555
|
('greater', 0.8370999312316, -0.985600937290653, 1.0, -1)])
|
561
|
-
def test_basic_example(self, alternative, pval, rlow, rhigh, sign):
|
556
|
+
def test_basic_example(self, alternative, pval, rlow, rhigh, sign, xp):
|
562
557
|
x = [1, 2, 3, 4]
|
563
558
|
y = np.array([0, 1, 0.5, 1]) * sign
|
564
559
|
result = stats.pearsonr(x, y, alternative=alternative)
|
@@ -567,8 +562,6 @@ class TestPearsonr:
|
|
567
562
|
ci = result.confidence_interval()
|
568
563
|
assert_allclose(ci, (rlow, rhigh), rtol=1e-6)
|
569
564
|
|
570
|
-
@skip_xp_backends('jax.numpy',
|
571
|
-
reason='JAX arrays do not support item assignment')
|
572
565
|
def test_negative_correlation_pvalue_gh17795(self, xp):
|
573
566
|
x = xp.arange(10.)
|
574
567
|
y = -x
|
@@ -577,8 +570,6 @@ class TestPearsonr:
|
|
577
570
|
xp_assert_close(test_greater.pvalue, xp.asarray(1.))
|
578
571
|
xp_assert_close(test_less.pvalue, xp.asarray(0.), atol=1e-20)
|
579
572
|
|
580
|
-
@skip_xp_backends('jax.numpy',
|
581
|
-
reason='JAX arrays do not support item assignment')
|
582
573
|
def test_length3_r_exactly_negative_one(self, xp):
|
583
574
|
x = xp.asarray([1., 2., 3.])
|
584
575
|
y = xp.asarray([5., -4., -13.])
|
@@ -593,14 +584,20 @@ class TestPearsonr:
|
|
593
584
|
xp_assert_equal(low, -one)
|
594
585
|
xp_assert_equal(high, one)
|
595
586
|
|
596
|
-
@pytest.mark.skip_xp_backends(np_only=True)
|
597
587
|
def test_input_validation(self):
|
588
|
+
# Arraylike is np only
|
598
589
|
x = [1, 2, 3]
|
599
|
-
y = [4
|
590
|
+
y = [4]
|
600
591
|
message = '`x` and `y` must have the same length along `axis`.'
|
601
592
|
with pytest.raises(ValueError, match=message):
|
602
593
|
stats.pearsonr(x, y)
|
603
594
|
|
595
|
+
x = [1, 2, 3]
|
596
|
+
y = [4, 5]
|
597
|
+
message = '`x` and `y` must be broadcastable.'
|
598
|
+
with pytest.raises(ValueError, match=message):
|
599
|
+
stats.pearsonr(x, y)
|
600
|
+
|
604
601
|
x = [1]
|
605
602
|
y = [2]
|
606
603
|
message = '`x` and `y` must have length at least 2.'
|
@@ -622,7 +619,6 @@ class TestPearsonr:
|
|
622
619
|
res.confidence_interval(method="exact")
|
623
620
|
|
624
621
|
@pytest.mark.fail_slow(10)
|
625
|
-
@pytest.mark.skip_xp_backends(np_only=True)
|
626
622
|
@pytest.mark.xfail_on_32bit("Monte Carlo method needs > a few kB of memory")
|
627
623
|
@pytest.mark.parametrize('alternative', ('less', 'greater', 'two-sided'))
|
628
624
|
@pytest.mark.parametrize('method_name',
|
@@ -647,7 +643,7 @@ class TestPearsonr:
|
|
647
643
|
assert_equal(res2.statistic, res.statistic)
|
648
644
|
assert_equal(res2.pvalue, res.pvalue)
|
649
645
|
|
650
|
-
@pytest.mark.
|
646
|
+
@pytest.mark.slow
|
651
647
|
@pytest.mark.parametrize('alternative', ('less', 'greater', 'two-sided'))
|
652
648
|
def test_bootstrap_ci(self, alternative):
|
653
649
|
rng = np.random.default_rng(2462935790378923)
|
@@ -668,9 +664,8 @@ class TestPearsonr:
|
|
668
664
|
res_ci2 = res.confidence_interval(method=method)
|
669
665
|
assert_allclose(res_ci2, res_ci)
|
670
666
|
|
671
|
-
@pytest.mark.skip_xp_backends(np_only=True)
|
672
667
|
@pytest.mark.parametrize('axis', [0, 1])
|
673
|
-
def test_axis01(self, axis
|
668
|
+
def test_axis01(self, axis):
|
674
669
|
rng = np.random.default_rng(38572345825)
|
675
670
|
shape = (9, 10)
|
676
671
|
x, y = rng.normal(size=(2,) + shape)
|
@@ -686,8 +681,7 @@ class TestPearsonr:
|
|
686
681
|
assert_allclose(ci.low[i], ci_i.low)
|
687
682
|
assert_allclose(ci.high[i], ci_i.high)
|
688
683
|
|
689
|
-
|
690
|
-
def test_axis_None(self, xp):
|
684
|
+
def test_axis_None(self):
|
691
685
|
rng = np.random.default_rng(38572345825)
|
692
686
|
shape = (9, 10)
|
693
687
|
x, y = rng.normal(size=(2,) + shape)
|
@@ -723,33 +717,32 @@ class TestPearsonr:
|
|
723
717
|
with pytest.raises(ValueError, match=message):
|
724
718
|
stats.pearsonr(x, x, method=stats.PermutationMethod())
|
725
719
|
|
726
|
-
@
|
727
|
-
reason='JAX arrays do not support item assignment')
|
720
|
+
@pytest.mark.filterwarnings("ignore:invalid value encountered in divide")
|
728
721
|
def test_nd_special_cases(self, xp):
|
729
722
|
rng = np.random.default_rng(34989235492245)
|
730
|
-
x0 =
|
731
|
-
y0 = xp.asarray(rng.random((3, 5)))
|
723
|
+
x0, y0 = rng.random((4, 5)), rng.random((4, 5))
|
732
724
|
|
733
725
|
message = 'An input array is constant'
|
734
726
|
with pytest.warns(stats.ConstantInputWarning, match=message):
|
735
|
-
|
736
|
-
x
|
737
|
-
res = stats.pearsonr(x,
|
727
|
+
x0[0, ...], y0[1, ...] = 1, 2
|
728
|
+
x, y = xp.asarray(x0), xp.asarray(y0)
|
729
|
+
res = stats.pearsonr(x, y, axis=1)
|
738
730
|
ci = res.confidence_interval()
|
739
|
-
|
740
|
-
xp_assert_equal(res.statistic[0],
|
741
|
-
xp_assert_equal(res.pvalue[0],
|
742
|
-
xp_assert_equal(ci.low[0],
|
743
|
-
xp_assert_equal(ci.high[0],
|
744
|
-
assert
|
745
|
-
assert
|
746
|
-
assert
|
747
|
-
assert
|
731
|
+
nans = xp.asarray([xp.nan, xp.nan], dtype=xp.float64)
|
732
|
+
xp_assert_equal(res.statistic[0:2], nans)
|
733
|
+
xp_assert_equal(res.pvalue[0:2], nans)
|
734
|
+
xp_assert_equal(ci.low[0:2], nans)
|
735
|
+
xp_assert_equal(ci.high[0:2], nans)
|
736
|
+
assert xp.all(xp.isfinite(res.statistic[2:]))
|
737
|
+
assert xp.all(xp.isfinite(res.pvalue[2:]))
|
738
|
+
assert xp.all(xp.isfinite(ci.low[2:]))
|
739
|
+
assert xp.all(xp.isfinite(ci.high[2:]))
|
748
740
|
|
749
741
|
message = 'An input array is nearly constant'
|
750
742
|
with pytest.warns(stats.NearConstantInputWarning, match=message):
|
751
|
-
|
752
|
-
|
743
|
+
x0[0, 0], y0[1, 1] = 1 + 1e-15, 2 + 1e-15
|
744
|
+
x, y = xp.asarray(x0), xp.asarray(y0)
|
745
|
+
stats.pearsonr(x, y, axis=1)
|
753
746
|
|
754
747
|
# length 2 along axis
|
755
748
|
x = xp.asarray([[1, 2], [1, 2], [2, 1], [2, 1.]])
|
@@ -762,8 +755,24 @@ class TestPearsonr:
|
|
762
755
|
xp_assert_close(ci.low, -ones)
|
763
756
|
xp_assert_close(ci.high, ones)
|
764
757
|
|
765
|
-
|
766
|
-
|
758
|
+
def test_different_dimensionality(self, xp):
|
759
|
+
# For better or for worse, there is one difference between the broadcasting
|
760
|
+
# behavior of most stats functions and NumPy gufuncs / NEP 5: gufuncs `axis`
|
761
|
+
# refers to the core dimension *before* prepending `1`s to the array shapes
|
762
|
+
# to match dimensionality; SciPy's prepends `1`s first. For instance, in
|
763
|
+
# SciPy, `vecdot` would work just like `xp.sum(x * y, axis=axis)`, but this
|
764
|
+
# is NOT true of NumPy. The discrepancy only arises when there are multiple
|
765
|
+
# arguments with different dimensionality and positive indices are used,
|
766
|
+
# which is probably why it hasn't been a problem. There are pros and cons of
|
767
|
+
# each convention, and we might want to consider changing our behavior in
|
768
|
+
# SciPy 2.0. For now, preserve consistency / backward compatibility.
|
769
|
+
rng = np.random.default_rng(45834598265019344)
|
770
|
+
x = rng.random((3, 10))
|
771
|
+
y = rng.random(10)
|
772
|
+
res = stats.pearsonr(x, y, axis=1)
|
773
|
+
ref = stats.pearsonr(x, y, axis=-1)
|
774
|
+
assert_equal(res.statistic, ref.statistic)
|
775
|
+
|
767
776
|
@pytest.mark.parametrize('axis', [0, 1, None])
|
768
777
|
@pytest.mark.parametrize('alternative', ['less', 'greater', 'two-sided'])
|
769
778
|
def test_array_api(self, xp, axis, alternative):
|
@@ -1276,9 +1285,9 @@ class TestCorrSpearmanr:
|
|
1276
1285
|
def test_gh_8111(self):
|
1277
1286
|
# Regression test for gh-8111 (different result for float/int/bool).
|
1278
1287
|
n = 100
|
1279
|
-
np.random.
|
1280
|
-
x =
|
1281
|
-
m =
|
1288
|
+
rng = np.random.RandomState(234568)
|
1289
|
+
x = rng.rand(n)
|
1290
|
+
m = rng.rand(n) > 0.7
|
1282
1291
|
|
1283
1292
|
# bool against float, no nans
|
1284
1293
|
a = (x > .5)
|
@@ -1314,9 +1323,9 @@ class TestCorrSpearmanr2:
|
|
1314
1323
|
assert_equal(stats.spearmanr([], []), (np.nan, np.nan))
|
1315
1324
|
|
1316
1325
|
def test_normal_draws(self):
|
1317
|
-
np.random.
|
1318
|
-
x = np.array([
|
1319
|
-
|
1326
|
+
rng = np.random.RandomState(7546)
|
1327
|
+
x = np.array([rng.normal(loc=1, scale=1, size=500),
|
1328
|
+
rng.normal(loc=1, scale=1, size=500)])
|
1320
1329
|
corr = [[1.0, 0.3],
|
1321
1330
|
[0.3, 1.0]]
|
1322
1331
|
x = np.dot(np.linalg.cholesky(corr), x)
|
@@ -1666,12 +1675,13 @@ def test_kendalltau():
|
|
1666
1675
|
(np.nan, np.nan))
|
1667
1676
|
|
1668
1677
|
# empty arrays provided as input
|
1669
|
-
|
1678
|
+
with pytest.warns(SmallSampleWarning, match="One or more sample..."):
|
1679
|
+
assert_equal(stats.kendalltau([], []), (np.nan, np.nan))
|
1670
1680
|
|
1671
1681
|
# check with larger arrays
|
1672
|
-
np.random.
|
1673
|
-
x = np.array([
|
1674
|
-
|
1682
|
+
rng = np.random.RandomState(7546)
|
1683
|
+
x = np.array([rng.normal(loc=1, scale=1, size=500),
|
1684
|
+
rng.normal(loc=1, scale=1, size=500)])
|
1675
1685
|
corr = [[1.0, 0.3],
|
1676
1686
|
[0.3, 1.0]]
|
1677
1687
|
x = np.dot(np.linalg.cholesky(corr), x)
|
@@ -1703,10 +1713,8 @@ def test_kendalltau():
|
|
1703
1713
|
assert_raises(ValueError, stats.kendalltau, x, y)
|
1704
1714
|
|
1705
1715
|
# test all ties
|
1706
|
-
|
1707
|
-
|
1708
|
-
assert_equal(np.nan, p_value)
|
1709
|
-
tau, p_value = stats.kendalltau([0], [0])
|
1716
|
+
with pytest.warns(SmallSampleWarning, match="One or more sample..."):
|
1717
|
+
tau, p_value = stats.kendalltau([0], [0])
|
1710
1718
|
assert_equal(np.nan, tau)
|
1711
1719
|
assert_equal(np.nan, p_value)
|
1712
1720
|
|
@@ -1715,19 +1723,19 @@ def test_kendalltau():
|
|
1715
1723
|
x = np.ma.masked_greater(x, 1995)
|
1716
1724
|
y = np.arange(2000, dtype=float)
|
1717
1725
|
y = np.concatenate((y[1000:], y[:1000]))
|
1718
|
-
assert_(np.isfinite(stats.kendalltau(x,y)[1]))
|
1726
|
+
assert_(np.isfinite(stats.mstats.kendalltau(x,y)[1]))
|
1719
1727
|
|
1720
1728
|
|
1721
1729
|
def test_kendalltau_vs_mstats_basic():
|
1722
|
-
np.random.
|
1723
|
-
for s in range(
|
1730
|
+
rng = np.random.RandomState(42)
|
1731
|
+
for s in range(3, 10):
|
1724
1732
|
a = []
|
1725
1733
|
# Generate rankings with ties
|
1726
1734
|
for i in range(s):
|
1727
1735
|
a += [i]*i
|
1728
1736
|
b = list(a)
|
1729
|
-
|
1730
|
-
|
1737
|
+
rng.shuffle(a)
|
1738
|
+
rng.shuffle(b)
|
1731
1739
|
expected = mstats_basic.kendalltau(a, b)
|
1732
1740
|
actual = stats.kendalltau(a, b)
|
1733
1741
|
assert_approx_equal(actual[0], expected[0])
|
@@ -1744,6 +1752,7 @@ def test_kendalltau_nan_2nd_arg():
|
|
1744
1752
|
assert_allclose(r1.statistic, r2.statistic, atol=1e-15)
|
1745
1753
|
|
1746
1754
|
|
1755
|
+
@pytest.mark.thread_unsafe
|
1747
1756
|
def test_kendalltau_gh18139_overflow():
|
1748
1757
|
# gh-18139 reported an overflow in `kendalltau` that appeared after
|
1749
1758
|
# SciPy 0.15.1. Check that this particular overflow does not occur.
|
@@ -1841,7 +1850,8 @@ class TestKendallTauAlternative:
|
|
1841
1850
|
def test_against_R_n1(self, alternative, p_expected, rev):
|
1842
1851
|
x, y = [1], [2]
|
1843
1852
|
stat_expected = np.nan
|
1844
|
-
|
1853
|
+
with pytest.warns(SmallSampleWarning, match="One or more sample..."):
|
1854
|
+
self.exact_test(x, y, alternative, rev, stat_expected, p_expected)
|
1845
1855
|
|
1846
1856
|
case_R_n2 = (list(zip(alternatives, p_n2, [False]*3))
|
1847
1857
|
+ list(zip(alternatives, reversed(p_n2), [True]*3)))
|
@@ -1908,9 +1918,9 @@ class TestKendallTauAlternative:
|
|
1908
1918
|
|
1909
1919
|
@pytest.mark.parametrize("alternative, p_expected, rev", case_R_lt_171b)
|
1910
1920
|
def test_against_R_lt_171b(self, alternative, p_expected, rev):
|
1911
|
-
np.random.
|
1912
|
-
x =
|
1913
|
-
y =
|
1921
|
+
rng = np.random.RandomState(0)
|
1922
|
+
x = rng.rand(100)
|
1923
|
+
y = rng.rand(100)
|
1914
1924
|
stat_expected = -0.04686868686868687
|
1915
1925
|
self.exact_test(x, y, alternative, rev, stat_expected, p_expected)
|
1916
1926
|
|
@@ -1920,9 +1930,9 @@ class TestKendallTauAlternative:
|
|
1920
1930
|
|
1921
1931
|
@pytest.mark.parametrize("alternative, p_expected, rev", case_R_lt_171c)
|
1922
1932
|
def test_against_R_lt_171c(self, alternative, p_expected, rev):
|
1923
|
-
np.random.
|
1924
|
-
x =
|
1925
|
-
y =
|
1933
|
+
rng = np.random.RandomState(0)
|
1934
|
+
x = rng.rand(170)
|
1935
|
+
y = rng.rand(170)
|
1926
1936
|
stat_expected = 0.1115906717716673
|
1927
1937
|
self.exact_test(x, y, alternative, rev, stat_expected, p_expected)
|
1928
1938
|
|
@@ -1931,9 +1941,9 @@ class TestKendallTauAlternative:
|
|
1931
1941
|
|
1932
1942
|
@pytest.mark.parametrize("alternative, rev", case_gt_171)
|
1933
1943
|
def test_gt_171(self, alternative, rev):
|
1934
|
-
np.random.
|
1935
|
-
x =
|
1936
|
-
y =
|
1944
|
+
rng = np.random.RandomState(0)
|
1945
|
+
x = rng.rand(400)
|
1946
|
+
y = rng.rand(400)
|
1937
1947
|
res0 = stats.kendalltau(x, y, method='exact',
|
1938
1948
|
alternative=alternative)
|
1939
1949
|
res1 = stats.kendalltau(x, y, method='asymptotic',
|
@@ -2028,15 +2038,17 @@ def test_weightedtau():
|
|
2028
2038
|
np.asarray(y, dtype=np.float64))
|
2029
2039
|
assert_approx_equal(tau, -0.56694968153682723)
|
2030
2040
|
# All ties
|
2031
|
-
|
2041
|
+
with pytest.warns(SmallSampleWarning, match="One or more sample..."):
|
2042
|
+
tau, p_value = stats.weightedtau([], [])
|
2032
2043
|
assert_equal(np.nan, tau)
|
2033
2044
|
assert_equal(np.nan, p_value)
|
2034
|
-
|
2045
|
+
with pytest.warns(SmallSampleWarning, match="One or more sample..."):
|
2046
|
+
tau, p_value = stats.weightedtau([0], [0])
|
2035
2047
|
assert_equal(np.nan, tau)
|
2036
2048
|
assert_equal(np.nan, p_value)
|
2037
2049
|
# Size mismatches
|
2038
2050
|
assert_raises(ValueError, stats.weightedtau, [0, 1], [0, 1, 2])
|
2039
|
-
assert_raises(ValueError, stats.weightedtau, [0, 1], [0, 1], [0])
|
2051
|
+
assert_raises(ValueError, stats.weightedtau, [0, 1], [0, 1], [0, 1, 2])
|
2040
2052
|
# NaNs
|
2041
2053
|
x = [12, 2, 1, 12, 2]
|
2042
2054
|
y = [1, 4, 7, 1, np.nan]
|
@@ -2070,10 +2082,12 @@ def test_segfault_issue_9710():
|
|
2070
2082
|
# https://github.com/scipy/scipy/issues/9710
|
2071
2083
|
# This test was created to check segfault
|
2072
2084
|
# In issue SEGFAULT only repros in optimized builds after calling the function twice
|
2073
|
-
|
2074
|
-
|
2075
|
-
|
2076
|
-
|
2085
|
+
message = "One or more sample arguments is too small"
|
2086
|
+
with pytest.warns(SmallSampleWarning, match=message):
|
2087
|
+
stats.weightedtau([1], [1.0])
|
2088
|
+
stats.weightedtau([1], [1.0])
|
2089
|
+
# The code below also caused SEGFAULT
|
2090
|
+
stats.weightedtau([np.nan], [52])
|
2077
2091
|
|
2078
2092
|
|
2079
2093
|
def test_kendall_tau_large():
|
@@ -2107,15 +2121,15 @@ def test_weightedtau_vs_quadratic():
|
|
2107
2121
|
def weigher(x):
|
2108
2122
|
return 1. / (x + 1)
|
2109
2123
|
|
2110
|
-
np.random.
|
2124
|
+
rng = np.random.default_rng(42)
|
2111
2125
|
for s in range(3,10):
|
2112
2126
|
a = []
|
2113
2127
|
# Generate rankings with ties
|
2114
2128
|
for i in range(s):
|
2115
2129
|
a += [i]*i
|
2116
2130
|
b = list(a)
|
2117
|
-
|
2118
|
-
|
2131
|
+
rng.shuffle(a)
|
2132
|
+
rng.shuffle(b)
|
2119
2133
|
# First pass: use element indices as ranks
|
2120
2134
|
rank = np.arange(len(a), dtype=np.intp)
|
2121
2135
|
for _ in range(2):
|
@@ -2124,7 +2138,7 @@ def test_weightedtau_vs_quadratic():
|
|
2124
2138
|
actual = stats.weightedtau(a, b, rank, weigher, add).statistic
|
2125
2139
|
assert_approx_equal(expected, actual)
|
2126
2140
|
# Second pass: use a random rank
|
2127
|
-
|
2141
|
+
rng.shuffle(rank)
|
2128
2142
|
|
2129
2143
|
|
2130
2144
|
class TestFindRepeats:
|
@@ -2148,14 +2162,6 @@ class TestFindRepeats:
|
|
2148
2162
|
|
2149
2163
|
|
2150
2164
|
class TestRegression:
|
2151
|
-
|
2152
|
-
def test_one_arg_deprecation(self):
|
2153
|
-
x = np.arange(20).reshape((2, 10))
|
2154
|
-
message = "Inference of the two sets..."
|
2155
|
-
with pytest.deprecated_call(match=message):
|
2156
|
-
stats.linregress(x)
|
2157
|
-
stats.linregress(x[0], x[1])
|
2158
|
-
|
2159
2165
|
def test_linregressBIGX(self):
|
2160
2166
|
# W.II.F. Regress BIG on X.
|
2161
2167
|
result = stats.linregress(X, BIG)
|
@@ -2194,7 +2200,9 @@ class TestRegression:
|
|
2194
2200
|
# total sum of squares of exactly 0.
|
2195
2201
|
result = stats.linregress(X, ZERO)
|
2196
2202
|
assert_almost_equal(result.intercept, 0.0)
|
2197
|
-
|
2203
|
+
with pytest.warns(stats.ConstantInputWarning, match="An input array..."):
|
2204
|
+
ref_rvalue = stats.pearsonr(X, ZERO).statistic
|
2205
|
+
assert_almost_equal(result.rvalue, ref_rvalue)
|
2198
2206
|
|
2199
2207
|
def test_regress_simple(self):
|
2200
2208
|
# Regress a line with sinusoidal noise.
|
@@ -2247,42 +2255,6 @@ class TestRegression:
|
|
2247
2255
|
assert_allclose(res.stderr, 0.0519051424731)
|
2248
2256
|
assert_allclose(res.intercept_stderr, 8.0490133029927)
|
2249
2257
|
|
2250
|
-
# TODO: remove this test once single-arg support is dropped;
|
2251
|
-
# deprecation warning tested in `test_one_arg_deprecation`
|
2252
|
-
@pytest.mark.filterwarnings('ignore::DeprecationWarning')
|
2253
|
-
def test_regress_simple_onearg_rows(self):
|
2254
|
-
# Regress a line w sinusoidal noise,
|
2255
|
-
# with a single input of shape (2, N)
|
2256
|
-
x = np.linspace(0, 100, 100)
|
2257
|
-
y = 0.2 * np.linspace(0, 100, 100) + 10
|
2258
|
-
y += np.sin(np.linspace(0, 20, 100))
|
2259
|
-
rows = np.vstack((x, y))
|
2260
|
-
|
2261
|
-
result = stats.linregress(rows)
|
2262
|
-
assert_almost_equal(result.stderr, 2.3957814497838803e-3)
|
2263
|
-
assert_almost_equal(result.intercept_stderr, 1.3866936078570702e-1)
|
2264
|
-
|
2265
|
-
# TODO: remove this test once single-arg support is dropped;
|
2266
|
-
# deprecation warning tested in `test_one_arg_deprecation`
|
2267
|
-
@pytest.mark.filterwarnings('ignore::DeprecationWarning')
|
2268
|
-
def test_regress_simple_onearg_cols(self):
|
2269
|
-
x = np.linspace(0, 100, 100)
|
2270
|
-
y = 0.2 * np.linspace(0, 100, 100) + 10
|
2271
|
-
y += np.sin(np.linspace(0, 20, 100))
|
2272
|
-
columns = np.hstack((np.expand_dims(x, 1), np.expand_dims(y, 1)))
|
2273
|
-
|
2274
|
-
result = stats.linregress(columns)
|
2275
|
-
assert_almost_equal(result.stderr, 2.3957814497838803e-3)
|
2276
|
-
assert_almost_equal(result.intercept_stderr, 1.3866936078570702e-1)
|
2277
|
-
|
2278
|
-
# TODO: remove this test once single-arg support is dropped;
|
2279
|
-
# deprecation warning tested in `test_one_arg_deprecation`
|
2280
|
-
@pytest.mark.filterwarnings('ignore::DeprecationWarning')
|
2281
|
-
def test_regress_shape_error(self):
|
2282
|
-
# Check that a single input argument to linregress with wrong shape
|
2283
|
-
# results in a ValueError.
|
2284
|
-
assert_raises(ValueError, stats.linregress, np.ones((3, 3)))
|
2285
|
-
|
2286
2258
|
def test_linregress(self):
|
2287
2259
|
# compared with multivariate ols with pinv
|
2288
2260
|
x = np.arange(11)
|
@@ -2368,7 +2340,7 @@ class TestRegression:
|
|
2368
2340
|
def test_nist_norris(self):
|
2369
2341
|
# If this causes a lint failure in the future, please note the history of
|
2370
2342
|
# requests to allow extra whitespace in table formatting (e.g. gh-12367).
|
2371
|
-
# Also see https://github.com/scipy/scipy/wiki/Why-do-we-not-use-an-auto%E2%80%90formatter%3F # noqa: E501
|
2343
|
+
# Also see https://github.com/scipy/scipy/wiki/Why-do-we-not-use-an-auto%E2%80%90formatter%3F # noqa: E501
|
2372
2344
|
x = [ 0.2, 337.4, 118.2, 884.6, 10.1, 226.5,
|
2373
2345
|
666.3, 996.3, 448.6, 777.0, 558.2, 0.4,
|
2374
2346
|
0.6, 775.5, 666.9, 338.0, 447.5, 11.6,
|
@@ -2405,7 +2377,9 @@ class TestRegression:
|
|
2405
2377
|
assert_almost_equal(result.intercept, poly[1])
|
2406
2378
|
|
2407
2379
|
def test_empty_input(self):
|
2408
|
-
|
2380
|
+
with pytest.warns(SmallSampleWarning, match="One or more sample..."):
|
2381
|
+
res = stats.linregress([], [])
|
2382
|
+
assert np.all(np.isnan(res))
|
2409
2383
|
|
2410
2384
|
def test_nan_input(self):
|
2411
2385
|
x = np.arange(10.)
|
@@ -2782,7 +2756,7 @@ class TestMode:
|
|
2782
2756
|
# was deprecated, so check for the appropriate error.
|
2783
2757
|
my_dtype = np.dtype([('asdf', np.uint8), ('qwer', np.float64, (3,))])
|
2784
2758
|
test = np.zeros(10, dtype=my_dtype)
|
2785
|
-
message = "Argument `a` is not....|An argument has dtype..."
|
2759
|
+
message = "Argument `a` is not....|An argument has dtype...|The DType..."
|
2786
2760
|
with pytest.raises(TypeError, match=message):
|
2787
2761
|
stats.mode(test, nan_policy=nan_policy)
|
2788
2762
|
|
@@ -2843,12 +2817,13 @@ class TestMode:
|
|
2843
2817
|
stats.mode(np.arange(3, dtype=object))
|
2844
2818
|
|
2845
2819
|
|
2846
|
-
@
|
2820
|
+
@make_xp_test_case(stats.sem)
|
2847
2821
|
class TestSEM:
|
2848
2822
|
|
2849
2823
|
testcase = [1., 2., 3., 4.]
|
2850
2824
|
scalar_testcase = 4.
|
2851
2825
|
|
2826
|
+
@pytest.mark.filterwarnings("ignore:invalid value encountered in divide")
|
2852
2827
|
def test_sem_scalar(self, xp):
|
2853
2828
|
# This is not in R, so used:
|
2854
2829
|
# sqrt(var(testcase)*3/4)/sqrt(3)
|
@@ -2876,7 +2851,7 @@ class TestSEM:
|
|
2876
2851
|
stats.sem(testcase, ddof=2))
|
2877
2852
|
|
2878
2853
|
x = xp.arange(10.)
|
2879
|
-
x = xp.where(x == 9, xp.
|
2854
|
+
x = xp.where(x == 9, xp.nan, x)
|
2880
2855
|
xp_assert_equal(stats.sem(x), xp.asarray(xp.nan))
|
2881
2856
|
|
2882
2857
|
@skip_xp_backends(np_only=True,
|
@@ -2889,10 +2864,8 @@ class TestSEM:
|
|
2889
2864
|
assert_raises(ValueError, stats.sem, x, nan_policy='foobar')
|
2890
2865
|
|
2891
2866
|
|
2892
|
-
@
|
2893
|
-
|
2894
|
-
@skip_xp_backends('jax.numpy', reason="JAX can't do item assignment")
|
2895
|
-
class TestZmapZscore:
|
2867
|
+
@make_xp_test_case(stats.zmap)
|
2868
|
+
class TestZmap:
|
2896
2869
|
|
2897
2870
|
@pytest.mark.parametrize(
|
2898
2871
|
'x, y',
|
@@ -2903,16 +2876,15 @@ class TestZmapZscore:
|
|
2903
2876
|
# For these simple cases, calculate the expected result directly
|
2904
2877
|
# by using the formula for the z-score.
|
2905
2878
|
x, y = xp.asarray(x), xp.asarray(y)
|
2906
|
-
|
2907
|
-
expected = (x - xp.mean(y)) / xp_test.std(y, correction=0)
|
2879
|
+
expected = (x - xp.mean(y)) / xp.std(y, correction=0)
|
2908
2880
|
z = stats.zmap(x, y)
|
2909
2881
|
xp_assert_close(z, expected)
|
2910
2882
|
|
2911
2883
|
def test_zmap_axis(self, xp):
|
2912
2884
|
# Test use of 'axis' keyword in zmap.
|
2913
|
-
x =
|
2914
|
-
|
2915
|
-
|
2885
|
+
x = xp.asarray([[0.0, 0.0, 1.0, 1.0],
|
2886
|
+
[1.0, 1.0, 1.0, 2.0],
|
2887
|
+
[2.0, 0.0, 2.0, 0.0]])
|
2916
2888
|
|
2917
2889
|
t1 = 1.0/(2.0/3)**0.5
|
2918
2890
|
t2 = 3.**0.5/3
|
@@ -2927,6 +2899,8 @@ class TestZmapZscore:
|
|
2927
2899
|
z1_expected = [[-1.0, -1.0, 1.0, 1.0],
|
2928
2900
|
[-t2, -t2, -t2, 3.**0.5],
|
2929
2901
|
[1.0, -1.0, 1.0, -1.0]]
|
2902
|
+
z0_expected = xp.asarray(z0_expected)
|
2903
|
+
z1_expected = xp.asarray(z1_expected)
|
2930
2904
|
|
2931
2905
|
xp_assert_close(z0, z0_expected)
|
2932
2906
|
xp_assert_close(z1, z1_expected)
|
@@ -2950,30 +2924,58 @@ class TestZmapZscore:
|
|
2950
2924
|
scores = xp.asarray([-3, -1, 2, np.nan])
|
2951
2925
|
compare = xp.asarray([-8, -3, 2, 7, 12, np.nan])
|
2952
2926
|
z = stats.zmap(scores, compare, ddof=ddof, nan_policy='omit')
|
2953
|
-
|
2927
|
+
# exclude nans from compare, don't use isnan + mask since that messes up
|
2928
|
+
# dask
|
2929
|
+
ref = stats.zmap(scores, compare[:5], ddof=ddof)
|
2954
2930
|
xp_assert_close(z, ref)
|
2955
2931
|
|
2956
2932
|
@pytest.mark.parametrize('ddof', [0, 2])
|
2957
2933
|
def test_zmap_nan_policy_omit_with_axis(self, ddof, xp):
|
2958
2934
|
scores = xp.reshape(xp.arange(-5.0, 9.0), (2, -1))
|
2959
|
-
compare =
|
2960
|
-
compare[0, 4] =
|
2961
|
-
compare[0, 6] =
|
2962
|
-
compare[1, 1] =
|
2935
|
+
compare = np.reshape(np.linspace(-8, 6, 24), (2, -1))
|
2936
|
+
compare[0, 4] = np.nan
|
2937
|
+
compare[0, 6] = np.nan
|
2938
|
+
compare[1, 1] = np.nan
|
2939
|
+
# convert from numpy since some libraries like dask
|
2940
|
+
# can't handle the data-dependent shapes from the isnan masking
|
2941
|
+
compare_0_notna = xp.asarray(compare[0, :][~np.isnan(compare[0, :])])
|
2942
|
+
compare_1_notna = xp.asarray(compare[1, :][~np.isnan(compare[1, :])])
|
2943
|
+
compare = xp.asarray(compare)
|
2944
|
+
|
2963
2945
|
z = stats.zmap(scores, compare, nan_policy='omit', axis=1, ddof=ddof)
|
2964
|
-
res0 = stats.zmap(scores[0, :],
|
2946
|
+
res0 = stats.zmap(scores[0, :], compare_0_notna,
|
2965
2947
|
ddof=ddof)
|
2966
|
-
res1 = stats.zmap(scores[1, :],
|
2948
|
+
res1 = stats.zmap(scores[1, :], compare_1_notna,
|
2967
2949
|
ddof=ddof)
|
2968
2950
|
expected = xp.stack((res0, res1))
|
2969
2951
|
xp_assert_close(z, expected)
|
2970
2952
|
|
2953
|
+
@skip_xp_backends(eager_only=True, reason="lazy arrays don't do 'raise'.")
|
2971
2954
|
def test_zmap_nan_policy_raise(self, xp):
|
2972
2955
|
scores = xp.asarray([1, 2, 3])
|
2973
2956
|
compare = xp.asarray([-8, -3, 2, 7, 12, xp.nan])
|
2974
2957
|
with pytest.raises(ValueError, match='input contains nan'):
|
2975
2958
|
stats.zmap(scores, compare, nan_policy='raise')
|
2976
2959
|
|
2960
|
+
@pytest.mark.filterwarnings("ignore:divide by zero encountered:RuntimeWarning:dask")
|
2961
|
+
@pytest.mark.filterwarnings("ignore:invalid value encountered:RuntimeWarning:dask")
|
2962
|
+
def test_degenerate_input(self, xp):
|
2963
|
+
scores = xp.arange(3)
|
2964
|
+
compare = xp.ones(3)
|
2965
|
+
ref = xp.asarray([-xp.inf, xp.nan, xp.inf])
|
2966
|
+
with eager_warns(scores, RuntimeWarning, match="Precision loss occurred..."):
|
2967
|
+
res = stats.zmap(scores, compare)
|
2968
|
+
xp_assert_equal(res, ref)
|
2969
|
+
|
2970
|
+
@pytest.mark.filterwarnings("ignore:divide by zero encountered:RuntimeWarning:dask")
|
2971
|
+
def test_complex_gh22404(self, xp):
|
2972
|
+
res = stats.zmap(xp.asarray([1, 2, 3, 4]), xp.asarray([1, 1j, -1, -1j]))
|
2973
|
+
ref = xp.asarray([1.+0.j, 2.+0.j, 3.+0.j, 4.+0.j])
|
2974
|
+
xp_assert_close(res, ref)
|
2975
|
+
|
2976
|
+
|
2977
|
+
@make_xp_test_case(stats.zscore)
|
2978
|
+
class TestZscore:
|
2977
2979
|
def test_zscore(self, xp):
|
2978
2980
|
# not in R, so tested by using:
|
2979
2981
|
# (testcase[i] - mean(testcase, axis=0)) / sqrt(var(testcase) * 3/4)
|
@@ -3017,7 +3019,6 @@ class TestZmapZscore:
|
|
3017
3019
|
xp_assert_close(z[0, :], z0_expected)
|
3018
3020
|
xp_assert_close(z[1, :], z1_expected)
|
3019
3021
|
|
3020
|
-
@skip_xp_backends('cupy', reason="cupy/cupy#8391")
|
3021
3022
|
def test_zscore_nan_propagate(self, xp):
|
3022
3023
|
x = xp.asarray([1, 2, np.nan, 4, 5])
|
3023
3024
|
z = stats.zscore(x, nan_policy='propagate')
|
@@ -3038,32 +3039,32 @@ class TestZmapZscore:
|
|
3038
3039
|
|
3039
3040
|
def test_zscore_nan_omit_with_ddof(self, xp):
|
3040
3041
|
x = xp.asarray([xp.nan, 1.0, 3.0, 5.0, 7.0, 9.0])
|
3041
|
-
xp_test = array_namespace(x) # numpy needs concat
|
3042
3042
|
z = stats.zscore(x, ddof=1, nan_policy='omit')
|
3043
|
-
expected =
|
3043
|
+
expected = xp.concat([xp.asarray([xp.nan]), stats.zscore(x[1:], ddof=1)])
|
3044
3044
|
xp_assert_close(z, expected)
|
3045
3045
|
|
3046
|
+
@skip_xp_backends(eager_only=True, reason="lazy arrays don't do 'raise'.")
|
3046
3047
|
def test_zscore_nan_raise(self, xp):
|
3047
3048
|
x = xp.asarray([1, 2, xp.nan, 4, 5])
|
3048
3049
|
with pytest.raises(ValueError, match="The input contains nan..."):
|
3049
3050
|
stats.zscore(x, nan_policy='raise')
|
3050
3051
|
|
3051
|
-
@skip_xp_backends('cupy', reason="cupy/cupy#8391")
|
3052
3052
|
def test_zscore_constant_input_1d(self, xp):
|
3053
3053
|
x = xp.asarray([-0.087] * 3)
|
3054
|
-
with
|
3054
|
+
with eager_warns(x, RuntimeWarning, match="Precision loss occurred..."):
|
3055
3055
|
z = stats.zscore(x)
|
3056
3056
|
xp_assert_equal(z, xp.full(x.shape, xp.nan))
|
3057
3057
|
|
3058
|
+
@pytest.mark.filterwarnings("ignore:invalid value encountered:RuntimeWarning:dask")
|
3058
3059
|
def test_zscore_constant_input_2d(self, xp):
|
3059
3060
|
x = xp.asarray([[10.0, 10.0, 10.0, 10.0],
|
3060
3061
|
[10.0, 11.0, 12.0, 13.0]])
|
3061
|
-
with
|
3062
|
+
with eager_warns(x, RuntimeWarning, match="Precision loss occurred..."):
|
3062
3063
|
z0 = stats.zscore(x, axis=0)
|
3063
3064
|
xp_assert_close(z0, xp.asarray([[xp.nan, -1.0, -1.0, -1.0],
|
3064
3065
|
[xp.nan, 1.0, 1.0, 1.0]]))
|
3065
3066
|
|
3066
|
-
with
|
3067
|
+
with eager_warns(x, RuntimeWarning, match="Precision loss occurred..."):
|
3067
3068
|
z1 = stats.zscore(x, axis=1)
|
3068
3069
|
xp_assert_equal(z1, xp.stack([xp.asarray([xp.nan, xp.nan, xp.nan, xp.nan]),
|
3069
3070
|
stats.zscore(x[1, :])]))
|
@@ -3072,10 +3073,11 @@ class TestZmapZscore:
|
|
3072
3073
|
xp_assert_equal(z, xp.reshape(stats.zscore(xp.reshape(x, (-1,))), x.shape))
|
3073
3074
|
|
3074
3075
|
y = xp.ones((3, 6))
|
3075
|
-
with
|
3076
|
+
with eager_warns(y, RuntimeWarning, match="Precision loss occurred..."):
|
3076
3077
|
z = stats.zscore(y, axis=None)
|
3077
|
-
xp_assert_equal(z, xp.full(y.shape, xp.
|
3078
|
+
xp_assert_equal(z, xp.full(y.shape, xp.nan))
|
3078
3079
|
|
3080
|
+
@pytest.mark.filterwarnings("ignore:invalid value encountered:RuntimeWarning:dask")
|
3079
3081
|
def test_zscore_constant_input_2d_nan_policy_omit(self, xp):
|
3080
3082
|
x = xp.asarray([[10.0, 10.0, 10.0, 10.0],
|
3081
3083
|
[10.0, 11.0, 12.0, xp.nan],
|
@@ -3083,18 +3085,19 @@ class TestZmapZscore:
|
|
3083
3085
|
s = (3/2)**0.5
|
3084
3086
|
s2 = 2**0.5
|
3085
3087
|
|
3086
|
-
with
|
3088
|
+
with eager_warns(x, RuntimeWarning, match="Precision loss occurred..."):
|
3087
3089
|
z0 = stats.zscore(x, nan_policy='omit', axis=0)
|
3088
3090
|
xp_assert_close(z0, xp.asarray([[xp.nan, -s, -1.0, xp.nan],
|
3089
3091
|
[xp.nan, 0, 1.0, xp.nan],
|
3090
3092
|
[xp.nan, s, xp.nan, xp.nan]]))
|
3091
3093
|
|
3092
|
-
with
|
3094
|
+
with eager_warns(x, RuntimeWarning, match="Precision loss occurred..."):
|
3093
3095
|
z1 = stats.zscore(x, nan_policy='omit', axis=1)
|
3094
3096
|
xp_assert_close(z1, xp.asarray([[xp.nan, xp.nan, xp.nan, xp.nan],
|
3095
3097
|
[-s, 0, s, xp.nan],
|
3096
3098
|
[-s2/2, s2, xp.nan, -s2/2]]))
|
3097
3099
|
|
3100
|
+
@pytest.mark.filterwarnings("ignore:invalid value encountered:RuntimeWarning:dask")
|
3098
3101
|
def test_zscore_2d_all_nan_row(self, xp):
|
3099
3102
|
# A row is all nan, and we use axis=1.
|
3100
3103
|
x = xp.asarray([[np.nan, np.nan, np.nan, np.nan],
|
@@ -3103,7 +3106,7 @@ class TestZmapZscore:
|
|
3103
3106
|
xp_assert_close(z, xp.asarray([[np.nan, np.nan, np.nan, np.nan],
|
3104
3107
|
[-1.0, -1.0, 1.0, 1.0]]))
|
3105
3108
|
|
3106
|
-
@
|
3109
|
+
@pytest.mark.filterwarnings("ignore:invalid value encountered:RuntimeWarning:dask")
|
3107
3110
|
def test_zscore_2d_all_nan(self, xp):
|
3108
3111
|
# The entire 2d array is nan, and we use axis=None.
|
3109
3112
|
y = xp.full((2, 3), xp.nan)
|
@@ -3116,25 +3119,6 @@ class TestZmapZscore:
|
|
3116
3119
|
z = stats.zscore(x)
|
3117
3120
|
xp_assert_equal(z, x)
|
3118
3121
|
|
3119
|
-
def test_gzscore_normal_array(self, xp):
|
3120
|
-
x = np.asarray([1, 2, 3, 4])
|
3121
|
-
z = stats.gzscore(xp.asarray(x))
|
3122
|
-
desired = np.log(x / stats.gmean(x)) / np.log(stats.gstd(x, ddof=0))
|
3123
|
-
xp_assert_close(z, xp.asarray(desired, dtype=xp.asarray(1.).dtype))
|
3124
|
-
|
3125
|
-
@skip_xp_invalid_arg
|
3126
|
-
def test_gzscore_masked_array(self, xp):
|
3127
|
-
x = np.array([1, 2, -1, 3, 4])
|
3128
|
-
mask = [0, 0, 1, 0, 0]
|
3129
|
-
mx = np.ma.masked_array(x, mask=mask)
|
3130
|
-
z = stats.gzscore(mx)
|
3131
|
-
desired = ([-1.526072095151, -0.194700599824, np.inf, 0.584101799472,
|
3132
|
-
1.136670895503])
|
3133
|
-
desired = np.ma.masked_array(desired, mask=mask)
|
3134
|
-
assert_allclose(z.compressed(), desired.compressed())
|
3135
|
-
assert_allclose(z.mask, desired.mask)
|
3136
|
-
assert isinstance(z, np.ma.MaskedArray)
|
3137
|
-
|
3138
3122
|
@skip_xp_invalid_arg
|
3139
3123
|
def test_zscore_masked_element_0_gh19039(self, xp):
|
3140
3124
|
# zscore returned all NaNs when 0th element was masked. See gh-19039.
|
@@ -3159,19 +3143,27 @@ class TestZmapZscore:
|
|
3159
3143
|
res = stats.zscore(y, axis=None)
|
3160
3144
|
assert_equal(res[1:], np.nan)
|
3161
3145
|
|
3162
|
-
def test_degenerate_input(self, xp):
|
3163
|
-
scores = xp.arange(3)
|
3164
|
-
compare = xp.ones(3)
|
3165
|
-
ref = xp.asarray([-xp.inf, xp.nan, xp.inf])
|
3166
|
-
with pytest.warns(RuntimeWarning, match="Precision loss occurred..."):
|
3167
|
-
res = stats.zmap(scores, compare)
|
3168
|
-
xp_assert_equal(res, ref)
|
3169
3146
|
|
3170
|
-
|
3171
|
-
|
3172
|
-
|
3173
|
-
|
3174
|
-
|
3147
|
+
@make_xp_test_case(stats.gzscore)
|
3148
|
+
class TestGZscore:
|
3149
|
+
def test_gzscore_normal_array(self, xp):
|
3150
|
+
x = np.asarray([1, 2, 3, 4])
|
3151
|
+
z = stats.gzscore(xp.asarray(x))
|
3152
|
+
desired = np.log(x / stats.gmean(x)) / np.log(stats.gstd(x, ddof=0))
|
3153
|
+
xp_assert_close(z, xp.asarray(desired, dtype=xp.asarray(1.).dtype))
|
3154
|
+
|
3155
|
+
@skip_xp_invalid_arg
|
3156
|
+
def test_gzscore_masked_array(self):
|
3157
|
+
x = np.array([1, 2, -1, 3, 4])
|
3158
|
+
mask = [0, 0, 1, 0, 0]
|
3159
|
+
mx = np.ma.masked_array(x, mask=mask)
|
3160
|
+
z = stats.gzscore(mx)
|
3161
|
+
desired = ([-1.526072095151, -0.194700599824, np.inf, 0.584101799472,
|
3162
|
+
1.136670895503])
|
3163
|
+
desired = np.ma.masked_array(desired, mask=mask)
|
3164
|
+
assert_allclose(z.compressed(), desired.compressed())
|
3165
|
+
assert_allclose(z.mask, desired.mask)
|
3166
|
+
assert isinstance(z, np.ma.MaskedArray)
|
3175
3167
|
|
3176
3168
|
|
3177
3169
|
class TestMedianAbsDeviation:
|
@@ -3481,6 +3473,7 @@ class TestIQR:
|
|
3481
3473
|
assert_raises(ValueError, stats.iqr, x, scale='foobar')
|
3482
3474
|
|
3483
3475
|
|
3476
|
+
@make_xp_test_case(stats.moment)
|
3484
3477
|
class TestMoments:
|
3485
3478
|
"""
|
3486
3479
|
Comparison numbers are found using R v.1.5.1
|
@@ -3496,18 +3489,11 @@ class TestMoments:
|
|
3496
3489
|
np.random.seed(1234)
|
3497
3490
|
testcase_moment_accuracy = np.random.rand(42)
|
3498
3491
|
|
3499
|
-
def _assert_equal(self, actual, expect, *, shape=None, dtype=None):
|
3500
|
-
expect = np.asarray(expect)
|
3501
|
-
if shape is not None:
|
3502
|
-
expect = np.broadcast_to(expect, shape)
|
3503
|
-
assert_array_equal(actual, expect)
|
3504
|
-
if dtype is None:
|
3505
|
-
dtype = expect.dtype
|
3506
|
-
assert actual.dtype == dtype
|
3507
|
-
|
3508
|
-
@array_api_compatible
|
3509
3492
|
@pytest.mark.parametrize('size', [10, (10, 2)])
|
3510
3493
|
@pytest.mark.parametrize('m, c', product((0, 1, 2, 3), (None, 0, 1)))
|
3494
|
+
@pytest.mark.filterwarnings(
|
3495
|
+
"ignore:divide by zero encountered in divide:RuntimeWarning:dask"
|
3496
|
+
)
|
3511
3497
|
def test_moment_center_scalar_moment(self, size, m, c, xp):
|
3512
3498
|
rng = np.random.default_rng(6581432544381372042)
|
3513
3499
|
x = xp.asarray(rng.random(size=size))
|
@@ -3516,20 +3502,19 @@ class TestMoments:
|
|
3516
3502
|
ref = xp.sum((x - c)**m, axis=0)/x.shape[0]
|
3517
3503
|
xp_assert_close(res, ref, atol=1e-16)
|
3518
3504
|
|
3519
|
-
@array_api_compatible
|
3520
3505
|
@pytest.mark.parametrize('size', [10, (10, 2)])
|
3521
3506
|
@pytest.mark.parametrize('c', (None, 0, 1))
|
3507
|
+
@pytest.mark.filterwarnings(
|
3508
|
+
"ignore:divide by zero encountered in divide:RuntimeWarning:dask"
|
3509
|
+
)
|
3522
3510
|
def test_moment_center_array_moment(self, size, c, xp):
|
3523
3511
|
rng = np.random.default_rng(1706828300224046506)
|
3524
3512
|
x = xp.asarray(rng.random(size=size))
|
3525
3513
|
m = [0, 1, 2, 3]
|
3526
3514
|
res = stats.moment(x, m, center=c)
|
3527
|
-
|
3528
|
-
ref = xp_test.concat([stats.moment(x, i, center=c)[xp_test.newaxis, ...]
|
3529
|
-
for i in m])
|
3515
|
+
ref = xp.concat([stats.moment(x, i, center=c)[xp.newaxis, ...] for i in m])
|
3530
3516
|
xp_assert_equal(res, ref)
|
3531
3517
|
|
3532
|
-
@array_api_compatible
|
3533
3518
|
def test_moment(self, xp):
|
3534
3519
|
# mean((testcase-mean(testcase))**power,axis=0),axis=0))**power))
|
3535
3520
|
testcase = xp.asarray(self.testcase)
|
@@ -3591,9 +3576,6 @@ class TestMoments:
|
|
3591
3576
|
assert_raises(ValueError, stats.moment, x, nan_policy='raise')
|
3592
3577
|
assert_raises(ValueError, stats.moment, x, nan_policy='foobar')
|
3593
3578
|
|
3594
|
-
@skip_xp_backends('cupy', reason='cupy/cupy#8391')
|
3595
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
3596
|
-
@array_api_compatible
|
3597
3579
|
@pytest.mark.parametrize('dtype', ['float32', 'float64', 'complex128'])
|
3598
3580
|
@pytest.mark.parametrize('expect, order', [(0, 1), (1, 0)])
|
3599
3581
|
def test_constant_moments(self, dtype, expect, order, xp):
|
@@ -3615,26 +3597,21 @@ class TestMoments:
|
|
3615
3597
|
order=order)
|
3616
3598
|
xp_assert_equal(y, xp.full((), expect, dtype=dtype))
|
3617
3599
|
|
3618
|
-
@skip_xp_backends('jax.numpy',
|
3619
|
-
reason="JAX arrays do not support item assignment")
|
3620
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
3621
|
-
@array_api_compatible
|
3622
3600
|
def test_moment_propagate_nan(self, xp):
|
3623
3601
|
# Check that the shape of the result is the same for inputs
|
3624
3602
|
# with and without nans, cf gh-5817
|
3625
3603
|
a = xp.reshape(xp.arange(8.), (2, -1))
|
3626
|
-
a[1, 0]
|
3627
|
-
|
3628
|
-
|
3604
|
+
a = xpx.at(a)[1, 0].set(xp.nan)
|
3605
|
+
|
3606
|
+
mm = stats.moment(xp.asarray(a), 2, axis=1)
|
3607
|
+
xp_assert_close(mm, xp.asarray([1.25, xp.nan]), atol=1e-15)
|
3629
3608
|
|
3630
|
-
@array_api_compatible
|
3631
3609
|
def test_moment_empty_order(self, xp):
|
3632
3610
|
# tests moment with empty `order` list
|
3633
3611
|
with pytest.raises(ValueError, match=r"`order` must be a scalar or a"
|
3634
3612
|
r" non-empty 1D array."):
|
3635
3613
|
stats.moment(xp.asarray([1, 2, 3, 4]), order=[])
|
3636
3614
|
|
3637
|
-
@array_api_compatible
|
3638
3615
|
def test_rename_moment_order(self, xp):
|
3639
3616
|
# Parameter 'order' was formerly known as 'moment'. The old name
|
3640
3617
|
# has not been deprecated, so it must continue to work.
|
@@ -3651,10 +3628,12 @@ class TestMoments:
|
|
3651
3628
|
assert_allclose(np.power(tc_no_mean, 42).mean(),
|
3652
3629
|
stats.moment(self.testcase_moment_accuracy, 42))
|
3653
3630
|
|
3654
|
-
@array_api_compatible
|
3655
3631
|
@pytest.mark.parametrize('order', [0, 1, 2, 3])
|
3656
3632
|
@pytest.mark.parametrize('axis', [-1, 0, 1])
|
3657
3633
|
@pytest.mark.parametrize('center', [None, 0])
|
3634
|
+
@pytest.mark.filterwarnings(
|
3635
|
+
"ignore:divide by zero encountered in divide:RuntimeWarning:dask"
|
3636
|
+
)
|
3658
3637
|
def test_moment_array_api(self, xp, order, axis, center):
|
3659
3638
|
rng = np.random.default_rng(34823589259425)
|
3660
3639
|
x = rng.random(size=(5, 6, 7))
|
@@ -3668,27 +3647,28 @@ class SkewKurtosisTest:
|
|
3668
3647
|
testcase = [1., 2., 3., 4.]
|
3669
3648
|
testmathworks = [1.165, 0.6268, 0.0751, 0.3516, -0.6965]
|
3670
3649
|
|
3671
|
-
|
3672
|
-
class TestSkew(SkewKurtosisTest):
|
3673
|
-
@array_api_compatible
|
3674
|
-
@pytest.mark.parametrize('stat_fun', [stats.skew, stats.kurtosis])
|
3675
|
-
def test_empty_1d(self, stat_fun, xp):
|
3650
|
+
def test_empty_1d(self, xp):
|
3676
3651
|
x = xp.asarray([])
|
3677
3652
|
if is_numpy(xp):
|
3678
3653
|
with pytest.warns(SmallSampleWarning, match=too_small_1d_not_omit):
|
3679
|
-
res = stat_fun(x)
|
3654
|
+
res = self.stat_fun(x)
|
3680
3655
|
else:
|
3681
3656
|
with np.testing.suppress_warnings() as sup:
|
3682
3657
|
# array_api_strict produces these
|
3683
3658
|
sup.filter(RuntimeWarning, "Mean of empty slice")
|
3684
3659
|
sup.filter(RuntimeWarning, "invalid value encountered")
|
3685
|
-
res = stat_fun(x)
|
3660
|
+
res = self.stat_fun(x)
|
3686
3661
|
xp_assert_equal(res, xp.asarray(xp.nan))
|
3687
3662
|
|
3688
|
-
|
3689
|
-
|
3690
|
-
|
3691
|
-
|
3663
|
+
|
3664
|
+
@make_xp_test_case(stats.skew)
|
3665
|
+
class TestSkew(SkewKurtosisTest):
|
3666
|
+
def stat_fun(self, x):
|
3667
|
+
return stats.skew(x)
|
3668
|
+
|
3669
|
+
@pytest.mark.filterwarnings(
|
3670
|
+
"ignore:invalid value encountered in scalar divide:RuntimeWarning:dask"
|
3671
|
+
)
|
3692
3672
|
def test_skewness(self, xp):
|
3693
3673
|
# Scalar test case
|
3694
3674
|
y = stats.skew(xp.asarray(self.scalar_testcase))
|
@@ -3716,25 +3696,21 @@ class TestSkew(SkewKurtosisTest):
|
|
3716
3696
|
# `skew` must return a scalar for 1-dim input (only for NumPy arrays)
|
3717
3697
|
assert_equal(stats.skew(arange(10)), 0.0)
|
3718
3698
|
|
3719
|
-
@skip_xp_backends('jax.numpy',
|
3720
|
-
reason="JAX arrays do not support item assignment")
|
3721
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
3722
|
-
@array_api_compatible
|
3723
3699
|
def test_skew_propagate_nan(self, xp):
|
3724
3700
|
# Check that the shape of the result is the same for inputs
|
3725
3701
|
# with and without nans, cf gh-5817
|
3726
3702
|
a = xp.arange(8.)
|
3727
3703
|
a = xp.reshape(a, (2, -1))
|
3728
|
-
a[1, 0]
|
3704
|
+
a = xpx.at(a)[1, 0].set(xp.nan)
|
3729
3705
|
with np.errstate(invalid='ignore'):
|
3730
|
-
s = stats.skew(a, axis=1)
|
3706
|
+
s = stats.skew(xp.asarray(a), axis=1)
|
3731
3707
|
xp_assert_equal(s, xp.asarray([0, xp.nan]))
|
3732
3708
|
|
3733
|
-
@array_api_compatible
|
3734
3709
|
def test_skew_constant_value(self, xp):
|
3735
3710
|
# Skewness of a constant input should be NaN (gh-16061)
|
3736
|
-
|
3737
|
-
|
3711
|
+
a = xp.repeat(xp.asarray([-0.27829495]), 10)
|
3712
|
+
|
3713
|
+
with eager_warns(a, RuntimeWarning, match="Precision loss occurred"):
|
3738
3714
|
xp_assert_equal(stats.skew(a), xp.asarray(xp.nan))
|
3739
3715
|
xp_assert_equal(stats.skew(a*2.**50), xp.asarray(xp.nan))
|
3740
3716
|
xp_assert_equal(stats.skew(a/2.**50), xp.asarray(xp.nan))
|
@@ -3746,24 +3722,17 @@ class TestSkew(SkewKurtosisTest):
|
|
3746
3722
|
a = 1. + xp.arange(-3., 4)*1e-16
|
3747
3723
|
xp_assert_equal(stats.skew(a), xp.asarray(xp.nan))
|
3748
3724
|
|
3749
|
-
@skip_xp_backends(
|
3750
|
-
reason="JAX arrays do not support item assignment")
|
3751
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
3752
|
-
@array_api_compatible
|
3725
|
+
@skip_xp_backends(eager_only=True)
|
3753
3726
|
def test_precision_loss_gh15554(self, xp):
|
3754
3727
|
# gh-15554 was one of several issues that have reported problems with
|
3755
3728
|
# constant or near-constant input. We can't always fix these, but
|
3756
3729
|
# make sure there's a warning.
|
3757
3730
|
with pytest.warns(RuntimeWarning, match="Precision loss occurred"):
|
3758
3731
|
rng = np.random.default_rng(34095309370)
|
3759
|
-
a =
|
3732
|
+
a = rng.random(size=(100, 10))
|
3760
3733
|
a[:, 0] = 1.01
|
3761
|
-
stats.skew(a)
|
3734
|
+
stats.skew(xp.asarray(a))
|
3762
3735
|
|
3763
|
-
@skip_xp_backends('jax.numpy',
|
3764
|
-
reason="JAX arrays do not support item assignment")
|
3765
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
3766
|
-
@array_api_compatible
|
3767
3736
|
@pytest.mark.parametrize('axis', [-1, 0, 2, None])
|
3768
3737
|
@pytest.mark.parametrize('bias', [False, True])
|
3769
3738
|
def test_vectorization(self, xp, axis, bias):
|
@@ -3777,10 +3746,9 @@ class TestSkew(SkewKurtosisTest):
|
|
3777
3746
|
if axis is None:
|
3778
3747
|
a = xp.reshape(a, (-1,))
|
3779
3748
|
axis = 0
|
3780
|
-
|
3781
|
-
|
3782
|
-
|
3783
|
-
std = xp_test.std(a, axis=axis)
|
3749
|
+
mean = xp.mean(a, axis=axis, keepdims=True)
|
3750
|
+
mu3 = xp.mean((a - mean)**3, axis=axis)
|
3751
|
+
std = xp.std(a, axis=axis)
|
3784
3752
|
res = mu3 / std ** 3
|
3785
3753
|
if not bias:
|
3786
3754
|
n = a.shape[axis]
|
@@ -3792,12 +3760,12 @@ class TestSkew(SkewKurtosisTest):
|
|
3792
3760
|
xp_assert_close(res, ref)
|
3793
3761
|
|
3794
3762
|
|
3763
|
+
@make_xp_test_case(stats.kurtosis)
|
3795
3764
|
class TestKurtosis(SkewKurtosisTest):
|
3765
|
+
def stat_fun(self, x):
|
3766
|
+
return stats.kurtosis(x)
|
3796
3767
|
|
3797
|
-
@
|
3798
|
-
reason='JAX arrays do not support item assignment')
|
3799
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
3800
|
-
@array_api_compatible
|
3768
|
+
@pytest.mark.filterwarnings("ignore:invalid value encountered in scalar divide")
|
3801
3769
|
def test_kurtosis(self, xp):
|
3802
3770
|
# Scalar test case
|
3803
3771
|
y = stats.kurtosis(xp.asarray(self.scalar_testcase))
|
@@ -3826,7 +3794,7 @@ class TestKurtosis(SkewKurtosisTest):
|
|
3826
3794
|
xp_assert_close(y, xp.asarray(1.64))
|
3827
3795
|
|
3828
3796
|
x = xp.arange(10.)
|
3829
|
-
x = xp.where(x == 8, xp.
|
3797
|
+
x = xp.where(x == 8, xp.nan, x)
|
3830
3798
|
xp_assert_equal(stats.kurtosis(x), xp.asarray(xp.nan))
|
3831
3799
|
|
3832
3800
|
def test_kurtosis_nan_policy(self):
|
@@ -3850,20 +3818,15 @@ class TestKurtosis(SkewKurtosisTest):
|
|
3850
3818
|
k = stats.kurtosis(a, axis=1, nan_policy="propagate")
|
3851
3819
|
np.testing.assert_allclose(k, [-1.36, np.nan], atol=1e-15)
|
3852
3820
|
|
3853
|
-
@array_api_compatible
|
3854
3821
|
def test_kurtosis_constant_value(self, xp):
|
3855
3822
|
# Kurtosis of a constant input should be NaN (gh-16061)
|
3856
3823
|
a = xp.asarray([-0.27829495]*10)
|
3857
|
-
with
|
3824
|
+
with eager_warns(a, RuntimeWarning, match="Precision loss occurred"):
|
3858
3825
|
assert xp.isnan(stats.kurtosis(a, fisher=False))
|
3859
3826
|
assert xp.isnan(stats.kurtosis(a * float(2**50), fisher=False))
|
3860
3827
|
assert xp.isnan(stats.kurtosis(a / float(2**50), fisher=False))
|
3861
3828
|
assert xp.isnan(stats.kurtosis(a, fisher=False, bias=False))
|
3862
3829
|
|
3863
|
-
@skip_xp_backends('jax.numpy',
|
3864
|
-
reason='JAX arrays do not support item assignment')
|
3865
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
3866
|
-
@array_api_compatible
|
3867
3830
|
@pytest.mark.parametrize('axis', [-1, 0, 2, None])
|
3868
3831
|
@pytest.mark.parametrize('bias', [False, True])
|
3869
3832
|
@pytest.mark.parametrize('fisher', [False, True])
|
@@ -3878,10 +3841,9 @@ class TestKurtosis(SkewKurtosisTest):
|
|
3878
3841
|
if axis is None:
|
3879
3842
|
a = xp.reshape(a, (-1,))
|
3880
3843
|
axis = 0
|
3881
|
-
|
3882
|
-
|
3883
|
-
|
3884
|
-
mu2 = xp_test.var(a, axis=axis, correction=0)
|
3844
|
+
mean = xp.mean(a, axis=axis, keepdims=True)
|
3845
|
+
mu4 = xp.mean((a - mean)**4, axis=axis)
|
3846
|
+
mu2 = xp.var(a, axis=axis, correction=0)
|
3885
3847
|
if bias:
|
3886
3848
|
res = mu4 / mu2**2 - 3
|
3887
3849
|
else:
|
@@ -3929,10 +3891,7 @@ def ttest_data_axis_strategy(draw):
|
|
3929
3891
|
return data, axis
|
3930
3892
|
|
3931
3893
|
|
3932
|
-
@
|
3933
|
-
reason='Uses NumPy for pvalue, CI')
|
3934
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
3935
|
-
@array_api_compatible
|
3894
|
+
@make_xp_test_case(stats.ttest_1samp)
|
3936
3895
|
class TestStudentTest:
|
3937
3896
|
# Preserving original test cases.
|
3938
3897
|
# Recomputed statistics and p-values with R t.test, e.g.
|
@@ -3951,6 +3910,8 @@ class TestStudentTest:
|
|
3951
3910
|
P1_1_l = P1_1 / 2
|
3952
3911
|
P1_1_g = 1 - (P1_1 / 2)
|
3953
3912
|
|
3913
|
+
@pytest.mark.filterwarnings("ignore:divide by zero encountered:RuntimeWarning:dask")
|
3914
|
+
@pytest.mark.filterwarnings("ignore:invalid value encountered:RuntimeWarning:dask")
|
3954
3915
|
def test_onesample(self, xp):
|
3955
3916
|
with suppress_warnings() as sup, \
|
3956
3917
|
np.errstate(invalid="ignore", divide="ignore"):
|
@@ -4003,6 +3964,7 @@ class TestStudentTest:
|
|
4003
3964
|
assert_raises(ValueError, stats.ttest_1samp, x, 5.0,
|
4004
3965
|
nan_policy='foobar')
|
4005
3966
|
|
3967
|
+
@pytest.mark.filterwarnings("ignore:divide by zero encountered in divide")
|
4006
3968
|
def test_1samp_alternative(self, xp):
|
4007
3969
|
message = "`alternative` must be 'less', 'greater', or 'two-sided'."
|
4008
3970
|
with pytest.raises(ValueError, match=message):
|
@@ -4016,6 +3978,7 @@ class TestStudentTest:
|
|
4016
3978
|
xp_assert_close(p, xp.asarray(self.P1_1_g))
|
4017
3979
|
xp_assert_close(t, xp.asarray(self.T1_1))
|
4018
3980
|
|
3981
|
+
@skip_xp_backends('jax.numpy', reason='Generic stdtrit mutates array.')
|
4019
3982
|
@pytest.mark.parametrize("alternative", ['two-sided', 'less', 'greater'])
|
4020
3983
|
def test_1samp_ci_1d(self, xp, alternative):
|
4021
3984
|
# test confidence interval method against reference values
|
@@ -4028,7 +3991,7 @@ class TestStudentTest:
|
|
4028
3991
|
# x = c(2.75532884, 0.93892217, 0.94835861, 1.49489446, -0.62396595,
|
4029
3992
|
# -1.88019867, -1.55684465, 4.88777104, 5.15310979, 4.34656348)
|
4030
3993
|
# t.test(x, conf.level=0.85, alternative='l')
|
4031
|
-
dtype = xp.
|
3994
|
+
dtype = xp.asarray(1.0).dtype
|
4032
3995
|
x = xp.asarray(x, dtype=dtype)
|
4033
3996
|
popmean = xp.asarray(popmean, dtype=dtype)
|
4034
3997
|
|
@@ -4048,6 +4011,7 @@ class TestStudentTest:
|
|
4048
4011
|
with pytest.raises(ValueError, match=message):
|
4049
4012
|
res.confidence_interval(confidence_level=10)
|
4050
4013
|
|
4014
|
+
@skip_xp_backends(np_only=True, reason='Too slow.')
|
4051
4015
|
@pytest.mark.xslow
|
4052
4016
|
@hypothesis.given(alpha=hypothesis.strategies.floats(1e-15, 1-1e-15),
|
4053
4017
|
data_axis=ttest_data_axis_strategy())
|
@@ -4060,8 +4024,7 @@ class TestStudentTest:
|
|
4060
4024
|
alternative=alternative, axis=axis)
|
4061
4025
|
l, u = res.confidence_interval(confidence_level=alpha)
|
4062
4026
|
popmean = l if alternative == 'greater' else u
|
4063
|
-
|
4064
|
-
popmean = xp_test.expand_dims(popmean, axis=axis)
|
4027
|
+
popmean = xp.expand_dims(popmean, axis=axis)
|
4065
4028
|
res = stats.ttest_1samp(data, popmean, alternative=alternative, axis=axis)
|
4066
4029
|
shape = list(data.shape)
|
4067
4030
|
shape.pop(axis)
|
@@ -4248,7 +4211,7 @@ power_div_empty_cases = [
|
|
4248
4211
|
]
|
4249
4212
|
|
4250
4213
|
|
4251
|
-
@
|
4214
|
+
@make_xp_test_case(stats.power_divergence)
|
4252
4215
|
class TestPowerDivergence:
|
4253
4216
|
|
4254
4217
|
def check_power_divergence(self, f_obs, f_exp, ddof, axis, lambda_,
|
@@ -4261,21 +4224,20 @@ class TestPowerDivergence:
|
|
4261
4224
|
if axis is None:
|
4262
4225
|
num_obs = xp_size(f_obs)
|
4263
4226
|
else:
|
4264
|
-
|
4265
|
-
arrays = (xp_test.broadcast_arrays(f_obs, f_exp) if f_exp is not None
|
4227
|
+
arrays = (xp.broadcast_arrays(f_obs, f_exp) if f_exp is not None
|
4266
4228
|
else (f_obs,))
|
4267
4229
|
num_obs = arrays[0].shape[axis]
|
4268
4230
|
|
4269
4231
|
with suppress_warnings() as sup:
|
4270
4232
|
sup.filter(RuntimeWarning, "Mean of empty slice")
|
4271
4233
|
stat, p = stats.power_divergence(
|
4272
|
-
f_obs
|
4234
|
+
f_obs, f_exp=f_exp, ddof=ddof,
|
4273
4235
|
axis=axis, lambda_=lambda_)
|
4274
4236
|
xp_assert_close(stat, xp.asarray(expected_stat, dtype=dtype))
|
4275
4237
|
|
4276
4238
|
if lambda_ == 1 or lambda_ == "pearson":
|
4277
4239
|
# Also test stats.chisquare.
|
4278
|
-
stat, p = stats.chisquare(f_obs
|
4240
|
+
stat, p = stats.chisquare(f_obs, f_exp=f_exp, ddof=ddof,
|
4279
4241
|
axis=axis)
|
4280
4242
|
xp_assert_close(stat, xp.asarray(expected_stat, dtype=dtype))
|
4281
4243
|
|
@@ -4352,13 +4314,10 @@ class TestPowerDivergence:
|
|
4352
4314
|
xp_assert_close(stat, expected_chi2)
|
4353
4315
|
|
4354
4316
|
# Compute the p values separately, passing in scalars for ddof.
|
4355
|
-
|
4356
|
-
|
4317
|
+
_, p0 = stats.power_divergence(f_obs, f_exp, ddof=ddof[0, 0])
|
4318
|
+
_, p1 = stats.power_divergence(f_obs, f_exp, ddof=ddof[1, 0])
|
4357
4319
|
|
4358
|
-
|
4359
|
-
expected_p = xp_test.concat((p0[xp_test.newaxis, :],
|
4360
|
-
p1[xp_test.newaxis, :]),
|
4361
|
-
axis=0)
|
4320
|
+
expected_p = xp.concat((p0[xp.newaxis, :], p1[xp.newaxis, :]), axis=0)
|
4362
4321
|
xp_assert_close(p, expected_p)
|
4363
4322
|
|
4364
4323
|
@pytest.mark.parametrize('case', power_div_empty_cases)
|
@@ -4383,7 +4342,7 @@ class TestPowerDivergence:
|
|
4383
4342
|
f_obs = xp.asarray(f_obs, dtype=dtype)
|
4384
4343
|
# f_exp is None
|
4385
4344
|
|
4386
|
-
res = stats.power_divergence(f_obs
|
4345
|
+
res = stats.power_divergence(f_obs, f_exp=f_exp, ddof=ddof,
|
4387
4346
|
axis=axis, lambda_="pearson")
|
4388
4347
|
attributes = ('statistic', 'pvalue')
|
4389
4348
|
check_named_results(res, attributes, xp=xp)
|
@@ -4394,10 +4353,10 @@ class TestPowerDivergence:
|
|
4394
4353
|
f_exp = xp.asarray([[5., 15.], [35., 25.]])
|
4395
4354
|
message = 'For each axis slice...'
|
4396
4355
|
with pytest.raises(ValueError, match=message):
|
4397
|
-
stats.power_divergence(f_obs
|
4356
|
+
stats.power_divergence(f_obs, f_exp=xp.asarray([30., 60.]))
|
4398
4357
|
with pytest.raises(ValueError, match=message):
|
4399
|
-
stats.power_divergence(f_obs
|
4400
|
-
stat, pval = stats.power_divergence(f_obs
|
4358
|
+
stats.power_divergence(f_obs, f_exp=f_exp, axis=1)
|
4359
|
+
stat, pval = stats.power_divergence(f_obs, f_exp=f_exp)
|
4401
4360
|
xp_assert_close(stat, xp.asarray([5.71428571, 2.66666667]))
|
4402
4361
|
xp_assert_close(pval, xp.asarray([0.01682741, 0.10247043]))
|
4403
4362
|
|
@@ -4418,9 +4377,8 @@ class TestPowerDivergence:
|
|
4418
4377
|
expected_counts = xp.exp(alpha + beta*i)
|
4419
4378
|
|
4420
4379
|
# `table4` holds just the second and third columns from Table 4.
|
4421
|
-
|
4422
|
-
|
4423
|
-
expected_counts[xp_test.newaxis, :])).T
|
4380
|
+
table4 = xp.concat((obs[xp.newaxis, :],
|
4381
|
+
expected_counts[xp.newaxis, :])).T
|
4424
4382
|
|
4425
4383
|
table5 = xp.asarray([
|
4426
4384
|
# lambda, statistic
|
@@ -4450,7 +4408,7 @@ class TestPowerDivergence:
|
|
4450
4408
|
xp_assert_close(stat, expected_stat, rtol=5e-3)
|
4451
4409
|
|
4452
4410
|
|
4453
|
-
@
|
4411
|
+
@make_xp_test_case(stats.chisquare)
|
4454
4412
|
class TestChisquare:
|
4455
4413
|
def test_chisquare_12282a(self, xp):
|
4456
4414
|
# Currently `chisquare` is implemented via power_divergence
|
@@ -4459,7 +4417,7 @@ class TestChisquare:
|
|
4459
4417
|
with assert_raises(ValueError, match='For each axis slice...'):
|
4460
4418
|
f_obs = xp.asarray([10., 20.])
|
4461
4419
|
f_exp = xp.asarray([30., 60.])
|
4462
|
-
stats.chisquare(f_obs
|
4420
|
+
stats.chisquare(f_obs, f_exp=f_exp)
|
4463
4421
|
|
4464
4422
|
def test_chisquare_12282b(self, xp):
|
4465
4423
|
# Check that users can now disable the sum check tested in
|
@@ -4471,7 +4429,7 @@ class TestChisquare:
|
|
4471
4429
|
x = rng.poisson(lam)
|
4472
4430
|
lam = xp.asarray(lam)
|
4473
4431
|
x = xp.asarray(x, dtype=lam.dtype)
|
4474
|
-
res = stats.chisquare(
|
4432
|
+
res = stats.chisquare(x, f_exp=lam, ddof=-1, sum_check=False)
|
4475
4433
|
# Poisson is approximately normal with mean and variance lam
|
4476
4434
|
z = (x - lam) / xp.sqrt(lam)
|
4477
4435
|
statistic = xp.sum(z**2)
|
@@ -4494,93 +4452,6 @@ class TestChisquare:
|
|
4494
4452
|
xp_assert_equal(res.pvalue, p)
|
4495
4453
|
|
4496
4454
|
|
4497
|
-
@skip_xp_invalid_arg
|
4498
|
-
class TestChisquareMA:
|
4499
|
-
@pytest.mark.filterwarnings('ignore::DeprecationWarning')
|
4500
|
-
def test_chisquare_masked_arrays(self):
|
4501
|
-
# Test masked arrays.
|
4502
|
-
obs = np.array([[8, 8, 16, 32, -1], [-1, -1, 3, 4, 5]]).T
|
4503
|
-
mask = np.array([[0, 0, 0, 0, 1], [1, 1, 0, 0, 0]]).T
|
4504
|
-
mobs = np.ma.masked_array(obs, mask)
|
4505
|
-
expected_chisq = np.array([24.0, 0.5])
|
4506
|
-
expected_g = np.array([2*(2*8*np.log(0.5) + 32*np.log(2.0)),
|
4507
|
-
2*(3*np.log(0.75) + 5*np.log(1.25))])
|
4508
|
-
|
4509
|
-
chi2 = stats.distributions.chi2
|
4510
|
-
|
4511
|
-
chisq, p = stats.chisquare(mobs)
|
4512
|
-
mat.assert_array_equal(chisq, expected_chisq)
|
4513
|
-
mat.assert_array_almost_equal(p, chi2.sf(expected_chisq,
|
4514
|
-
mobs.count(axis=0) - 1))
|
4515
|
-
|
4516
|
-
g, p = stats.power_divergence(mobs, lambda_='log-likelihood')
|
4517
|
-
mat.assert_array_almost_equal(g, expected_g, decimal=15)
|
4518
|
-
mat.assert_array_almost_equal(p, chi2.sf(expected_g,
|
4519
|
-
mobs.count(axis=0) - 1))
|
4520
|
-
|
4521
|
-
chisq, p = stats.chisquare(mobs.T, axis=1)
|
4522
|
-
mat.assert_array_equal(chisq, expected_chisq)
|
4523
|
-
mat.assert_array_almost_equal(p, chi2.sf(expected_chisq,
|
4524
|
-
mobs.T.count(axis=1) - 1))
|
4525
|
-
g, p = stats.power_divergence(mobs.T, axis=1, lambda_="log-likelihood")
|
4526
|
-
mat.assert_array_almost_equal(g, expected_g, decimal=15)
|
4527
|
-
mat.assert_array_almost_equal(p, chi2.sf(expected_g,
|
4528
|
-
mobs.count(axis=0) - 1))
|
4529
|
-
|
4530
|
-
obs1 = np.ma.array([3, 5, 6, 99, 10], mask=[0, 0, 0, 1, 0])
|
4531
|
-
exp1 = np.ma.array([2, 4, 8, 10, 99], mask=[0, 0, 0, 0, 1])
|
4532
|
-
chi2, p = stats.chisquare(obs1, f_exp=exp1)
|
4533
|
-
# Because of the mask at index 3 of obs1 and at index 4 of exp1,
|
4534
|
-
# only the first three elements are included in the calculation
|
4535
|
-
# of the statistic.
|
4536
|
-
mat.assert_array_equal(chi2, 1/2 + 1/4 + 4/8)
|
4537
|
-
|
4538
|
-
# When axis=None, the two values should have type np.float64.
|
4539
|
-
chisq, p = stats.chisquare(np.ma.array([1,2,3]), axis=None)
|
4540
|
-
assert_(isinstance(chisq, np.float64))
|
4541
|
-
assert_(isinstance(p, np.float64))
|
4542
|
-
assert_equal(chisq, 1.0)
|
4543
|
-
assert_almost_equal(p, stats.distributions.chi2.sf(1.0, 2))
|
4544
|
-
|
4545
|
-
# Empty arrays:
|
4546
|
-
# A data set with length 0 returns a masked scalar.
|
4547
|
-
with np.errstate(invalid='ignore'):
|
4548
|
-
with suppress_warnings() as sup:
|
4549
|
-
sup.filter(RuntimeWarning, "Mean of empty slice")
|
4550
|
-
chisq, p = stats.chisquare(np.ma.array([]))
|
4551
|
-
assert_(isinstance(chisq, np.ma.MaskedArray))
|
4552
|
-
assert_equal(chisq.shape, ())
|
4553
|
-
assert_(chisq.mask)
|
4554
|
-
|
4555
|
-
empty3 = np.ma.array([[],[],[]])
|
4556
|
-
|
4557
|
-
# empty3 is a collection of 0 data sets (whose lengths would be 3, if
|
4558
|
-
# there were any), so the return value is an array with length 0.
|
4559
|
-
chisq, p = stats.chisquare(empty3)
|
4560
|
-
assert_(isinstance(chisq, np.ma.MaskedArray))
|
4561
|
-
mat.assert_array_equal(chisq, [])
|
4562
|
-
|
4563
|
-
# empty3.T is an array containing 3 data sets, each with length 0,
|
4564
|
-
# so an array of size (3,) is returned, with all values masked.
|
4565
|
-
with np.errstate(invalid='ignore'):
|
4566
|
-
with suppress_warnings() as sup:
|
4567
|
-
sup.filter(RuntimeWarning, "Mean of empty slice")
|
4568
|
-
chisq, p = stats.chisquare(empty3.T)
|
4569
|
-
|
4570
|
-
assert_(isinstance(chisq, np.ma.MaskedArray))
|
4571
|
-
assert_equal(chisq.shape, (3,))
|
4572
|
-
assert_(np.all(chisq.mask))
|
4573
|
-
|
4574
|
-
def test_deprecation_warning(self):
|
4575
|
-
a = np.asarray([1., 2., 3.])
|
4576
|
-
ma = np.ma.masked_array(a)
|
4577
|
-
message = "`power_divergence` and `chisquare` support for masked..."
|
4578
|
-
with pytest.warns(DeprecationWarning, match=message):
|
4579
|
-
stats.chisquare(ma)
|
4580
|
-
with pytest.warns(DeprecationWarning, match=message):
|
4581
|
-
stats.chisquare(a, ma)
|
4582
|
-
|
4583
|
-
|
4584
4455
|
def test_friedmanchisquare():
|
4585
4456
|
# see ticket:113
|
4586
4457
|
# verified with matlab and R
|
@@ -4939,9 +4810,9 @@ class TestKSTwoSamples:
|
|
4939
4810
|
|
4940
4811
|
def test_gh11184(self):
|
4941
4812
|
# 3000, 3001, exact two-sided
|
4942
|
-
np.random.
|
4943
|
-
x =
|
4944
|
-
y =
|
4813
|
+
rng = np.random.RandomState(123456)
|
4814
|
+
x = rng.normal(size=3000)
|
4815
|
+
y = rng.normal(size=3001) * 1.5
|
4945
4816
|
self._testOne(x, y, 'two-sided', 0.11292880151060758, 2.7755575615628914e-15,
|
4946
4817
|
mode='asymp')
|
4947
4818
|
self._testOne(x, y, 'two-sided', 0.11292880151060758, 2.7755575615628914e-15,
|
@@ -4950,9 +4821,9 @@ class TestKSTwoSamples:
|
|
4950
4821
|
@pytest.mark.xslow
|
4951
4822
|
def test_gh11184_bigger(self):
|
4952
4823
|
# 10000, 10001, exact two-sided
|
4953
|
-
np.random.
|
4954
|
-
x =
|
4955
|
-
y =
|
4824
|
+
rng = np.random.RandomState(123456)
|
4825
|
+
x = rng.normal(size=10000)
|
4826
|
+
y = rng.normal(size=10001) * 1.5
|
4956
4827
|
self._testOne(x, y, 'two-sided', 0.10597913208679133, 3.3149311398483503e-49,
|
4957
4828
|
mode='asymp')
|
4958
4829
|
self._testOne(x, y, 'two-sided', 0.10597913208679133, 2.7755575615628914e-15,
|
@@ -4964,10 +4835,10 @@ class TestKSTwoSamples:
|
|
4964
4835
|
|
4965
4836
|
@pytest.mark.xslow
|
4966
4837
|
def test_gh12999(self):
|
4967
|
-
np.random.
|
4838
|
+
rng = np.random.RandomState(123456)
|
4968
4839
|
for x in range(1000, 12000, 1000):
|
4969
|
-
vals1 =
|
4970
|
-
vals2 =
|
4840
|
+
vals1 = rng.normal(size=(x))
|
4841
|
+
vals2 = rng.normal(size=(x + 10), loc=0.5)
|
4971
4842
|
exact = stats.ks_2samp(vals1, vals2, mode='exact').pvalue
|
4972
4843
|
asymp = stats.ks_2samp(vals1, vals2, mode='asymp').pvalue
|
4973
4844
|
# these two p-values should be in line with each other
|
@@ -5299,16 +5170,13 @@ def _desc_stats(x1, x2, axis=0, *, xp=None):
|
|
5299
5170
|
return _stats(x1, axis) + _stats(x2, axis)
|
5300
5171
|
|
5301
5172
|
|
5302
|
-
@
|
5303
|
-
@pytest.mark.skip_xp_backends(cpu_only=True,
|
5304
|
-
reason='Uses NumPy for pvalue, CI')
|
5305
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
5173
|
+
@make_xp_test_case(stats.ttest_ind, stats.ttest_ind_from_stats)
|
5306
5174
|
def test_ttest_ind(xp):
|
5307
5175
|
# regression test
|
5308
5176
|
tr = xp.asarray(1.0912746897927283)
|
5309
5177
|
pr = xp.asarray(0.27647818616351882)
|
5310
|
-
tr_2D = xp.
|
5311
|
-
pr_2D = xp.
|
5178
|
+
tr_2D = xp.stack([tr, -tr])
|
5179
|
+
pr_2D = xp.stack([pr, pr])
|
5312
5180
|
|
5313
5181
|
rvs1 = xp.linspace(5, 105, 100)
|
5314
5182
|
rvs2 = xp.linspace(1, 100, 100)
|
@@ -5496,6 +5364,7 @@ class Test_ttest_ind_permutations:
|
|
5496
5364
|
(a, b, {'random_state': np.random.default_rng(0), "axis": 1}, p_d_gen),
|
5497
5365
|
]
|
5498
5366
|
|
5367
|
+
@pytest.mark.thread_unsafe
|
5499
5368
|
@pytest.mark.parametrize("a,b,update,p_d", params)
|
5500
5369
|
def test_ttest_ind_permutations(self, a, b, update, p_d):
|
5501
5370
|
options_a = {'axis': None, 'equal_var': False}
|
@@ -5510,10 +5379,10 @@ class Test_ttest_ind_permutations:
|
|
5510
5379
|
assert_array_almost_equal(pvalue, p_d)
|
5511
5380
|
|
5512
5381
|
def test_ttest_ind_exact_alternative(self):
|
5513
|
-
np.random.
|
5382
|
+
rng = np.random.RandomState(0)
|
5514
5383
|
N = 3
|
5515
|
-
a =
|
5516
|
-
b =
|
5384
|
+
a = rng.rand(2, N, 2)
|
5385
|
+
b = rng.rand(2, N, 2)
|
5517
5386
|
|
5518
5387
|
options_p = {'axis': 1, 'permutations': 1000}
|
5519
5388
|
|
@@ -5553,10 +5422,10 @@ class Test_ttest_ind_permutations:
|
|
5553
5422
|
|
5554
5423
|
def test_ttest_ind_exact_selection(self):
|
5555
5424
|
# test the various ways of activating the exact test
|
5556
|
-
np.random.
|
5425
|
+
rng = np.random.RandomState(0)
|
5557
5426
|
N = 3
|
5558
|
-
a =
|
5559
|
-
b =
|
5427
|
+
a = rng.rand(N)
|
5428
|
+
b = rng.rand(N)
|
5560
5429
|
res0 = stats.ttest_ind(a, b)
|
5561
5430
|
res1 = stats.ttest_ind(a, b, permutations=1000)
|
5562
5431
|
res2 = stats.ttest_ind(a, b, permutations=0)
|
@@ -5569,9 +5438,9 @@ class Test_ttest_ind_permutations:
|
|
5569
5438
|
# the exact distribution of the test statistic should have
|
5570
5439
|
# binom(na + nb, na) elements, all unique. This was not always true
|
5571
5440
|
# in gh-4824; fixed by gh-13661.
|
5572
|
-
np.random.
|
5573
|
-
a =
|
5574
|
-
b =
|
5441
|
+
rng = np.random.RandomState(0)
|
5442
|
+
a = rng.rand(3)
|
5443
|
+
b = rng.rand(4)
|
5575
5444
|
|
5576
5445
|
data = np.concatenate((a, b))
|
5577
5446
|
na, nb = len(a), len(b)
|
@@ -5585,10 +5454,10 @@ class Test_ttest_ind_permutations:
|
|
5585
5454
|
assert len(t_stat) == n_unique
|
5586
5455
|
|
5587
5456
|
def test_ttest_ind_randperm_alternative(self):
|
5588
|
-
np.random.
|
5457
|
+
rng = np.random.RandomState(0)
|
5589
5458
|
N = 50
|
5590
|
-
a =
|
5591
|
-
b =
|
5459
|
+
a = rng.rand(2, 3, N)
|
5460
|
+
b = rng.rand(3, N)
|
5592
5461
|
options_p = {'axis': -1, 'permutations': 1000, "random_state": 0}
|
5593
5462
|
|
5594
5463
|
options_p.update(alternative="greater")
|
@@ -5615,10 +5484,10 @@ class Test_ttest_ind_permutations:
|
|
5615
5484
|
|
5616
5485
|
@pytest.mark.slow()
|
5617
5486
|
def test_ttest_ind_randperm_alternative2(self):
|
5618
|
-
np.random.
|
5487
|
+
rng = np.random.RandomState(0)
|
5619
5488
|
N = 50
|
5620
|
-
a =
|
5621
|
-
b =
|
5489
|
+
a = rng.rand(N, 4)
|
5490
|
+
b = rng.rand(N, 4)
|
5622
5491
|
options_p = {'permutations': 20000, "random_state": 0}
|
5623
5492
|
|
5624
5493
|
options_p.update(alternative="greater")
|
@@ -5648,10 +5517,10 @@ class Test_ttest_ind_permutations:
|
|
5648
5517
|
res_2_ab.pvalue[mask], atol=2e-2)
|
5649
5518
|
|
5650
5519
|
def test_ttest_ind_permutation_nanpolicy(self):
|
5651
|
-
np.random.
|
5520
|
+
rng = np.random.RandomState(0)
|
5652
5521
|
N = 50
|
5653
|
-
a =
|
5654
|
-
b =
|
5522
|
+
a = rng.rand(N, 5)
|
5523
|
+
b = rng.rand(N, 5)
|
5655
5524
|
a[5, 1] = np.nan
|
5656
5525
|
b[8, 2] = np.nan
|
5657
5526
|
a[9, 3] = np.nan
|
@@ -5769,10 +5638,7 @@ class Test_ttest_ind_permutations:
|
|
5769
5638
|
with pytest.raises(ValueError, match=message):
|
5770
5639
|
stats.ttest_ind([1, 2, 3], [4, 5, 6], method='migratory')
|
5771
5640
|
|
5772
|
-
@
|
5773
|
-
@pytest.mark.skip_xp_backends(cpu_only=True,
|
5774
|
-
reason='Uses NumPy for pvalue, CI')
|
5775
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
5641
|
+
@skip_xp_backends(cpu_only=True, reason='Uses NumPy for pvalue, CI')
|
5776
5642
|
def test_permutation_not_implement_for_xp(self, xp):
|
5777
5643
|
a2, b2 = xp.asarray(self.a2), xp.asarray(self.b2)
|
5778
5644
|
|
@@ -5845,8 +5711,9 @@ class Test_ttest_ind_common:
|
|
5845
5711
|
def test_nans_on_axis(self, kwds, axis):
|
5846
5712
|
# confirm that with `nan_policy='propagate'`, NaN results are returned
|
5847
5713
|
# on the correct location
|
5848
|
-
|
5849
|
-
|
5714
|
+
rng = np.random.default_rng(363836384995579937222)
|
5715
|
+
a = rng.integers(10, size=(5, 3, 10)).astype('float')
|
5716
|
+
b = rng.integers(10, size=(5, 3, 10)).astype('float')
|
5850
5717
|
# set some indices in `a` and `b` to be `np.nan`.
|
5851
5718
|
a[0][2][3] = np.nan
|
5852
5719
|
b[2][0][6] = np.nan
|
@@ -5998,10 +5865,7 @@ class Test_ttest_trim:
|
|
5998
5865
|
stats.ttest_ind([1, 2], [2, 3], trim=.2, permutations=2,
|
5999
5866
|
random_state=2)
|
6000
5867
|
|
6001
|
-
@
|
6002
|
-
@pytest.mark.skip_xp_backends(cpu_only=True,
|
6003
|
-
reason='Uses NumPy for pvalue, CI')
|
6004
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
5868
|
+
@skip_xp_backends(cpu_only=True, reason='Uses NumPy for pvalue, CI')
|
6005
5869
|
def test_permutation_not_implement_for_xp(self, xp):
|
6006
5870
|
message = "Use of `trim` is compatible only with NumPy arrays."
|
6007
5871
|
a, b = xp.arange(10), xp.arange(10)+1
|
@@ -6018,10 +5882,7 @@ class Test_ttest_trim:
|
|
6018
5882
|
stats.ttest_ind([1, 2], [2, 1], trim=trim)
|
6019
5883
|
|
6020
5884
|
|
6021
|
-
@
|
6022
|
-
@pytest.mark.skip_xp_backends(cpu_only=True,
|
6023
|
-
reason='Uses NumPy for pvalue, CI')
|
6024
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
5885
|
+
@make_xp_test_case(stats.ttest_ind)
|
6025
5886
|
class Test_ttest_CI:
|
6026
5887
|
# indices in order [alternative={two-sided, less, greater},
|
6027
5888
|
# equal_var={False, True}, trim={0, 0.2}]
|
@@ -6068,6 +5929,7 @@ class Test_ttest_CI:
|
|
6068
5929
|
@pytest.mark.parametrize('alternative', ['two-sided', 'less', 'greater'])
|
6069
5930
|
@pytest.mark.parametrize('equal_var', [False, True])
|
6070
5931
|
@pytest.mark.parametrize('trim', [0, 0.2])
|
5932
|
+
@skip_xp_backends('jax.numpy', reason='Generic stdtrit mutates array.')
|
6071
5933
|
def test_confidence_interval(self, alternative, equal_var, trim, xp):
|
6072
5934
|
if equal_var and trim:
|
6073
5935
|
pytest.xfail('Discrepancy in `main`; needs further investigation.')
|
@@ -6115,234 +5977,220 @@ def test__broadcast_concatenate():
|
|
6115
5977
|
assert b[i, j, k, l - a.shape[-3], m, n] == c[i, j, k, l, m, n]
|
6116
5978
|
|
6117
5979
|
|
6118
|
-
@
|
6119
|
-
|
6120
|
-
|
6121
|
-
|
6122
|
-
|
6123
|
-
|
6124
|
-
|
6125
|
-
|
6126
|
-
|
6127
|
-
|
6128
|
-
|
6129
|
-
|
6130
|
-
|
6131
|
-
|
6132
|
-
|
6133
|
-
|
6134
|
-
|
6135
|
-
|
6136
|
-
|
6137
|
-
|
6138
|
-
|
6139
|
-
|
6140
|
-
|
6141
|
-
|
6142
|
-
|
6143
|
-
|
6144
|
-
|
6145
|
-
|
6146
|
-
|
6147
|
-
|
6148
|
-
|
6149
|
-
|
6150
|
-
|
6151
|
-
|
6152
|
-
|
6153
|
-
|
6154
|
-
|
6155
|
-
|
6156
|
-
|
6157
|
-
|
6158
|
-
|
6159
|
-
|
6160
|
-
|
6161
|
-
|
6162
|
-
|
6163
|
-
|
6164
|
-
|
6165
|
-
|
6166
|
-
|
6167
|
-
|
6168
|
-
|
6169
|
-
|
6170
|
-
|
6171
|
-
|
6172
|
-
|
6173
|
-
|
6174
|
-
|
6175
|
-
|
6176
|
-
|
6177
|
-
|
6178
|
-
|
6179
|
-
|
6180
|
-
|
6181
|
-
|
6182
|
-
|
6183
|
-
|
6184
|
-
|
6185
|
-
|
6186
|
-
|
6187
|
-
|
6188
|
-
|
6189
|
-
|
6190
|
-
|
6191
|
-
|
6192
|
-
|
6193
|
-
|
6194
|
-
|
6195
|
-
|
5980
|
+
@make_xp_test_case(stats.ttest_ind)
|
5981
|
+
class TestTTestInd:
|
5982
|
+
@make_xp_test_case(stats.ttest_ind_from_stats)
|
5983
|
+
def test_ttest_ind_with_uneq_var(self, xp):
|
5984
|
+
# check vs. R `t.test`, e.g.
|
5985
|
+
# options(digits=20)
|
5986
|
+
# a = c(1., 2., 3.)
|
5987
|
+
# b = c(1.1, 2.9, 4.2)
|
5988
|
+
# t.test(a, b, equal.var=FALSE)
|
5989
|
+
|
5990
|
+
a = xp.asarray([1., 2., 3.])
|
5991
|
+
b = xp.asarray([1.1, 2.9, 4.2])
|
5992
|
+
pr = xp.asarray(0.53619490753126686)
|
5993
|
+
tr = xp.asarray(-0.686495127355726265)
|
5994
|
+
|
5995
|
+
t, p = stats.ttest_ind(a, b, equal_var=False)
|
5996
|
+
xp_assert_close(t, tr)
|
5997
|
+
xp_assert_close(p, pr)
|
5998
|
+
|
5999
|
+
t, p = stats.ttest_ind_from_stats(*_desc_stats(a, b), equal_var=False)
|
6000
|
+
xp_assert_close(t, tr)
|
6001
|
+
xp_assert_close(p, pr)
|
6002
|
+
|
6003
|
+
a = xp.asarray([1., 2., 3., 4.])
|
6004
|
+
pr = xp.asarray(0.84354139131608252)
|
6005
|
+
tr = xp.asarray(-0.210866331595072315)
|
6006
|
+
|
6007
|
+
t, p = stats.ttest_ind(a, b, equal_var=False)
|
6008
|
+
xp_assert_close(t, tr)
|
6009
|
+
xp_assert_close(p, pr)
|
6010
|
+
|
6011
|
+
t, p = stats.ttest_ind_from_stats(*_desc_stats(a, b), equal_var=False)
|
6012
|
+
xp_assert_close(t, tr)
|
6013
|
+
xp_assert_close(p, pr)
|
6014
|
+
|
6015
|
+
# regression test
|
6016
|
+
tr = xp.asarray(1.0912746897927283)
|
6017
|
+
tr_uneq_n = xp.asarray(0.66745638708050492)
|
6018
|
+
pr = xp.asarray(0.27647831993021388)
|
6019
|
+
pr_uneq_n = xp.asarray(0.50873585065616544)
|
6020
|
+
tr_2D = xp.stack([tr, -tr])
|
6021
|
+
pr_2D = xp.stack([pr, pr])
|
6022
|
+
|
6023
|
+
rvs3 = xp.linspace(1, 100, 25)
|
6024
|
+
rvs2 = xp.linspace(1, 100, 100)
|
6025
|
+
rvs1 = xp.linspace(5, 105, 100)
|
6026
|
+
rvs1_2D = xp.stack([rvs1, rvs2])
|
6027
|
+
rvs2_2D = xp.stack([rvs2, rvs1])
|
6028
|
+
|
6029
|
+
t, p = stats.ttest_ind(rvs1, rvs2, axis=0, equal_var=False)
|
6030
|
+
xp_assert_close(t, tr)
|
6031
|
+
xp_assert_close(p, pr)
|
6032
|
+
|
6033
|
+
t, p = stats.ttest_ind_from_stats(*_desc_stats(rvs1, rvs2), equal_var=False)
|
6034
|
+
xp_assert_close(t, tr)
|
6035
|
+
xp_assert_close(p, pr)
|
6036
|
+
|
6037
|
+
t, p = stats.ttest_ind(rvs1, rvs3, axis=0, equal_var=False)
|
6038
|
+
xp_assert_close(t, tr_uneq_n)
|
6039
|
+
xp_assert_close(p, pr_uneq_n)
|
6040
|
+
|
6041
|
+
t, p = stats.ttest_ind_from_stats(*_desc_stats(rvs1, rvs3), equal_var=False)
|
6042
|
+
xp_assert_close(t, tr_uneq_n)
|
6043
|
+
xp_assert_close(p, pr_uneq_n)
|
6044
|
+
|
6045
|
+
res = stats.ttest_ind(rvs1_2D.T, rvs2_2D.T, axis=0, equal_var=False)
|
6046
|
+
xp_assert_close(res.statistic, tr_2D)
|
6047
|
+
xp_assert_close(res.pvalue, pr_2D)
|
6048
|
+
|
6049
|
+
args = _desc_stats(rvs1_2D.T, rvs2_2D.T)
|
6050
|
+
res = stats.ttest_ind_from_stats(*args, equal_var=False)
|
6051
|
+
xp_assert_close(res.statistic, tr_2D)
|
6052
|
+
xp_assert_close(res.pvalue, pr_2D)
|
6053
|
+
|
6054
|
+
res = stats.ttest_ind(rvs1_2D, rvs2_2D, axis=1, equal_var=False)
|
6055
|
+
xp_assert_close(res.statistic, tr_2D)
|
6056
|
+
xp_assert_close(res.pvalue, pr_2D)
|
6057
|
+
|
6058
|
+
args = _desc_stats(rvs1_2D, rvs2_2D, axis=1)
|
6059
|
+
res = stats.ttest_ind_from_stats(*args, equal_var=False)
|
6060
|
+
xp_assert_close(res.statistic, tr_2D)
|
6061
|
+
xp_assert_close(res.pvalue, pr_2D)
|
6062
|
+
|
6063
|
+
@pytest.mark.filterwarnings(
|
6064
|
+
"ignore:divide by zero encountered:RuntimeWarning"
|
6065
|
+
) # for dask
|
6066
|
+
@pytest.mark.filterwarnings(
|
6067
|
+
"ignore:invalid value encountered:RuntimeWarning"
|
6068
|
+
) # for dask
|
6069
|
+
def test_ttest_ind_zero_division(self, xp):
|
6070
|
+
# test zero division problem
|
6071
|
+
x = xp.zeros(3)
|
6072
|
+
y = xp.ones(3)
|
6073
|
+
|
6074
|
+
with eager_warns(x, RuntimeWarning, match="Precision loss occurred"):
|
6075
|
+
t, p = stats.ttest_ind(x, y, equal_var=False)
|
6076
|
+
|
6077
|
+
xp_assert_equal(t, xp.asarray(-xp.inf))
|
6078
|
+
xp_assert_equal(p, xp.asarray(0.))
|
6196
6079
|
|
6197
|
-
|
6198
|
-
|
6199
|
-
|
6200
|
-
|
6201
|
-
|
6202
|
-
|
6203
|
-
|
6204
|
-
|
6205
|
-
|
6206
|
-
|
6207
|
-
|
6208
|
-
|
6209
|
-
|
6210
|
-
|
6211
|
-
|
6212
|
-
|
6213
|
-
|
6214
|
-
|
6215
|
-
|
6216
|
-
|
6217
|
-
|
6218
|
-
|
6219
|
-
|
6220
|
-
|
6221
|
-
|
6222
|
-
|
6223
|
-
|
6224
|
-
|
6225
|
-
|
6226
|
-
|
6227
|
-
|
6228
|
-
|
6229
|
-
|
6230
|
-
|
6231
|
-
|
6232
|
-
|
6233
|
-
|
6234
|
-
|
6235
|
-
|
6236
|
-
assert_allclose(r2.pvalue, r1.pvalue, atol=1e-15)
|
6237
|
-
|
6238
|
-
# NB: arguments are not paired when NaNs are dropped
|
6239
|
-
r3 = stats.ttest_ind(y, x[1:])
|
6240
|
-
assert_allclose(r2, r3, atol=1e-15)
|
6241
|
-
|
6242
|
-
# .. and this is consistent with R. R code:
|
6243
|
-
# x = c(NA, 2.0, 3.0, 4.0)
|
6244
|
-
# y = c(1.0, 2.0, 1.0, 2.0)
|
6245
|
-
# t.test(x, y, var.equal=TRUE)
|
6246
|
-
assert_allclose(r2, (-2.5354627641855498, 0.052181400457057901),
|
6247
|
-
atol=1e-15)
|
6248
|
-
|
6249
|
-
|
6250
|
-
@array_api_compatible
|
6251
|
-
def test_ttest_ind_empty_1d_returns_nan(xp):
|
6252
|
-
# Two empty inputs should return a TtestResult containing nan
|
6253
|
-
# for both values.
|
6254
|
-
if is_numpy(xp):
|
6255
|
-
with pytest.warns(SmallSampleWarning, match=too_small_1d_not_omit):
|
6080
|
+
with np.errstate(all='ignore'):
|
6081
|
+
t, p = stats.ttest_ind(x, x, equal_var=False)
|
6082
|
+
xp_assert_equal(t, xp.asarray(xp.nan))
|
6083
|
+
xp_assert_equal(p, xp.asarray(xp.nan))
|
6084
|
+
|
6085
|
+
# check that nan in input array result in nan output
|
6086
|
+
anan = xp.asarray([[1, xp.nan], [-1, 1]])
|
6087
|
+
t, p = stats.ttest_ind(anan, xp.zeros((2, 2)), equal_var=False)
|
6088
|
+
xp_assert_equal(t, xp.asarray([0., np.nan]))
|
6089
|
+
xp_assert_equal(p, xp.asarray([1., np.nan]))
|
6090
|
+
|
6091
|
+
def test_ttest_ind_nan_2nd_arg(self):
|
6092
|
+
# regression test for gh-6134: nans in the second arg were not handled
|
6093
|
+
x = [np.nan, 2.0, 3.0, 4.0]
|
6094
|
+
y = [1.0, 2.0, 1.0, 2.0]
|
6095
|
+
|
6096
|
+
r1 = stats.ttest_ind(x, y, nan_policy='omit')
|
6097
|
+
r2 = stats.ttest_ind(y, x, nan_policy='omit')
|
6098
|
+
assert_allclose(r2.statistic, -r1.statistic, atol=1e-15)
|
6099
|
+
assert_allclose(r2.pvalue, r1.pvalue, atol=1e-15)
|
6100
|
+
|
6101
|
+
# NB: arguments are not paired when NaNs are dropped
|
6102
|
+
r3 = stats.ttest_ind(y, x[1:])
|
6103
|
+
assert_allclose(r2, r3, atol=1e-15)
|
6104
|
+
|
6105
|
+
# .. and this is consistent with R. R code:
|
6106
|
+
# x = c(NA, 2.0, 3.0, 4.0)
|
6107
|
+
# y = c(1.0, 2.0, 1.0, 2.0)
|
6108
|
+
# t.test(x, y, var.equal=TRUE)
|
6109
|
+
assert_allclose(r2, (-2.5354627641855498, 0.052181400457057901),
|
6110
|
+
atol=1e-15)
|
6111
|
+
|
6112
|
+
def test_ttest_ind_empty_1d_returns_nan(self, xp):
|
6113
|
+
# Two empty inputs should return a TtestResult containing nan
|
6114
|
+
# for both values.
|
6115
|
+
if is_numpy(xp):
|
6116
|
+
with pytest.warns(SmallSampleWarning, match=too_small_1d_not_omit):
|
6117
|
+
res = stats.ttest_ind(xp.asarray([]), xp.asarray([]))
|
6118
|
+
else:
|
6256
6119
|
res = stats.ttest_ind(xp.asarray([]), xp.asarray([]))
|
6257
|
-
|
6258
|
-
|
6259
|
-
|
6260
|
-
|
6261
|
-
|
6262
|
-
|
6263
|
-
|
6264
|
-
|
6265
|
-
|
6266
|
-
|
6267
|
-
|
6268
|
-
|
6269
|
-
|
6270
|
-
|
6271
|
-
|
6272
|
-
|
6273
|
-
|
6274
|
-
|
6275
|
-
|
6276
|
-
|
6277
|
-
|
6278
|
-
|
6279
|
-
|
6280
|
-
|
6281
|
-
|
6282
|
-
|
6283
|
-
|
6284
|
-
|
6285
|
-
|
6286
|
-
|
6287
|
-
|
6288
|
-
|
6289
|
-
|
6290
|
-
|
6291
|
-
|
6292
|
-
|
6293
|
-
|
6294
|
-
|
6295
|
-
|
6296
|
-
|
6297
|
-
|
6298
|
-
|
6299
|
-
|
6300
|
-
|
6301
|
-
|
6302
|
-
|
6303
|
-
|
6304
|
-
|
6305
|
-
|
6306
|
-
|
6307
|
-
|
6308
|
-
|
6309
|
-
|
6310
|
-
|
6311
|
-
|
6312
|
-
|
6313
|
-
|
6314
|
-
|
6315
|
-
|
6316
|
-
|
6317
|
-
@pytest.mark.
|
6318
|
-
|
6319
|
-
|
6320
|
-
|
6321
|
-
|
6322
|
-
|
6323
|
-
|
6324
|
-
|
6325
|
-
|
6326
|
-
|
6327
|
-
|
6328
|
-
|
6329
|
-
@pytest.mark.skip_xp_backends(cpu_only=True,
|
6330
|
-
|
6331
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
6332
|
-
def test_ttest_ind_from_stats_inputs_zero(xp):
|
6333
|
-
# Regression test for gh-6409.
|
6334
|
-
zero = xp.asarray(0.)
|
6335
|
-
six = xp.asarray(6.)
|
6336
|
-
NaN = xp.asarray(xp.nan)
|
6337
|
-
res = stats.ttest_ind_from_stats(zero, zero, six, zero, zero, six, equal_var=False)
|
6338
|
-
xp_assert_equal(res.statistic, NaN)
|
6339
|
-
xp_assert_equal(res.pvalue, NaN)
|
6340
|
-
|
6341
|
-
|
6342
|
-
@array_api_compatible
|
6343
|
-
@pytest.mark.skip_xp_backends(cpu_only=True,
|
6344
|
-
reason='Uses NumPy for pvalue, CI')
|
6345
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
6120
|
+
assert isinstance(res, stats._stats_py.TtestResult)
|
6121
|
+
NaN = xp.asarray(xp.nan)[()]
|
6122
|
+
xp_assert_equal(res.statistic, NaN)
|
6123
|
+
xp_assert_equal(res.pvalue, NaN)
|
6124
|
+
|
6125
|
+
@pytest.mark.parametrize('b, expected_shape',
|
6126
|
+
[(np.empty((1, 5, 0)), (3, 5)),
|
6127
|
+
(np.empty((1, 0, 0)), (3, 0))])
|
6128
|
+
def test_ttest_ind_axis_size_zero(self, b, expected_shape, xp):
|
6129
|
+
# In this test, the length of the axis dimension is zero.
|
6130
|
+
# The results should be arrays containing nan with shape
|
6131
|
+
# given by the broadcast nonaxis dimensions.
|
6132
|
+
a = xp.empty((3, 1, 0))
|
6133
|
+
b = xp.asarray(b, dtype=a.dtype)
|
6134
|
+
with np.testing.suppress_warnings() as sup:
|
6135
|
+
# first case should warn, second shouldn't?
|
6136
|
+
sup.filter(SmallSampleWarning, too_small_nd_not_omit)
|
6137
|
+
res = stats.ttest_ind(a, b, axis=-1)
|
6138
|
+
assert isinstance(res, stats._stats_py.TtestResult)
|
6139
|
+
expected_value = xp.full(expected_shape, fill_value=xp.nan)
|
6140
|
+
xp_assert_equal(res.statistic, expected_value)
|
6141
|
+
xp_assert_equal(res.pvalue, expected_value)
|
6142
|
+
|
6143
|
+
def test_ttest_ind_nonaxis_size_zero(self, xp):
|
6144
|
+
# In this test, the length of the axis dimension is nonzero,
|
6145
|
+
# but one of the nonaxis dimensions has length 0. Check that
|
6146
|
+
# we still get the correctly broadcast shape, which is (5, 0)
|
6147
|
+
# in this case.
|
6148
|
+
a = xp.empty((1, 8, 0))
|
6149
|
+
b = xp.empty((5, 8, 1))
|
6150
|
+
res = stats.ttest_ind(a, b, axis=1)
|
6151
|
+
assert isinstance(res, stats._stats_py.TtestResult)
|
6152
|
+
assert res.statistic.shape ==(5, 0)
|
6153
|
+
assert res.pvalue.shape == (5, 0)
|
6154
|
+
|
6155
|
+
def test_ttest_ind_nonaxis_size_zero_different_lengths(self, xp):
|
6156
|
+
# In this test, the length of the axis dimension is nonzero,
|
6157
|
+
# and that size is different in the two inputs,
|
6158
|
+
# and one of the nonaxis dimensions has length 0. Check that
|
6159
|
+
# we still get the correctly broadcast shape, which is (5, 0)
|
6160
|
+
# in this case.
|
6161
|
+
a = xp.empty((1, 7, 0))
|
6162
|
+
b = xp.empty((5, 8, 1))
|
6163
|
+
res = stats.ttest_ind(a, b, axis=1)
|
6164
|
+
assert isinstance(res, stats._stats_py.TtestResult)
|
6165
|
+
assert res.statistic.shape ==(5, 0)
|
6166
|
+
assert res.pvalue.shape == (5, 0)
|
6167
|
+
|
6168
|
+
|
6169
|
+
@make_xp_test_case(stats.ttest_ind_from_stats)
|
6170
|
+
class TestTTestIndFromStats:
|
6171
|
+
@pytest.mark.skip_xp_backends(np_only=True,
|
6172
|
+
reason="Other backends don't like integers")
|
6173
|
+
def test_gh5686(self, xp):
|
6174
|
+
mean1, mean2 = xp.asarray([1, 2]), xp.asarray([3, 4])
|
6175
|
+
std1, std2 = xp.asarray([5, 3]), xp.asarray([4, 5])
|
6176
|
+
nobs1, nobs2 = xp.asarray([130, 140]), xp.asarray([100, 150])
|
6177
|
+
# This will raise a TypeError unless gh-5686 is fixed.
|
6178
|
+
stats.ttest_ind_from_stats(mean1, std1, nobs1, mean2, std2, nobs2)
|
6179
|
+
|
6180
|
+
@pytest.mark.filterwarnings("ignore:invalid value encountered:RuntimeWarning")
|
6181
|
+
def test_ttest_ind_from_stats_inputs_zero(self, xp):
|
6182
|
+
# Regression test for gh-6409.
|
6183
|
+
zero = xp.asarray(0.)
|
6184
|
+
six = xp.asarray(6.)
|
6185
|
+
NaN = xp.asarray(xp.nan)
|
6186
|
+
res = stats.ttest_ind_from_stats(zero, zero, six, zero, zero, six,
|
6187
|
+
equal_var=False)
|
6188
|
+
xp_assert_equal(res.statistic, NaN)
|
6189
|
+
xp_assert_equal(res.pvalue, NaN)
|
6190
|
+
|
6191
|
+
|
6192
|
+
@pytest.mark.skip_xp_backends(cpu_only=True, reason='Test uses ks_1samp')
|
6193
|
+
@pytest.mark.filterwarnings("ignore:invalid value encountered:RuntimeWarning")
|
6346
6194
|
def test_ttest_uniform_pvalues(xp):
|
6347
6195
|
# test that p-values are uniformly distributed under the null hypothesis
|
6348
6196
|
rng = np.random.default_rng(246834602926842)
|
@@ -6366,7 +6214,7 @@ def test_ttest_uniform_pvalues(xp):
|
|
6366
6214
|
x, y = xp.asarray([2, 3, 5]), xp.asarray([1.5])
|
6367
6215
|
|
6368
6216
|
res = stats.ttest_ind(x, y, equal_var=True)
|
6369
|
-
rtol = 1e-6 if
|
6217
|
+
rtol = 1e-6 if xp_default_dtype(xp) == xp.float32 else 1e-10
|
6370
6218
|
xp_assert_close(res.statistic, xp.asarray(1.0394023007754), rtol=rtol)
|
6371
6219
|
xp_assert_close(res.pvalue, xp.asarray(0.407779907736), rtol=rtol)
|
6372
6220
|
|
@@ -6381,10 +6229,9 @@ def _convert_pvalue_alternative(t, p, alt, xp):
|
|
6381
6229
|
|
6382
6230
|
|
6383
6231
|
@pytest.mark.slow
|
6384
|
-
@pytest.mark.
|
6385
|
-
|
6386
|
-
@
|
6387
|
-
@array_api_compatible
|
6232
|
+
@pytest.mark.filterwarnings("ignore:divide by zero encountered:RuntimeWarning:dask")
|
6233
|
+
@pytest.mark.filterwarnings("ignore:invalid value encountered:RuntimeWarning:dask")
|
6234
|
+
@make_xp_test_case(stats.ttest_1samp)
|
6388
6235
|
def test_ttest_1samp_new(xp):
|
6389
6236
|
n1, n2, n3 = (10, 15, 20)
|
6390
6237
|
rvn1 = stats.norm.rvs(loc=5, scale=10, size=(n1, n2, n3))
|
@@ -6445,10 +6292,7 @@ def test_ttest_1samp_new(xp):
|
|
6445
6292
|
xp_assert_equal(res.pvalue, xp.asarray([1., xp.nan]))
|
6446
6293
|
|
6447
6294
|
|
6448
|
-
@
|
6449
|
-
reason="Only NumPy has nan_policy='omit' for now")
|
6450
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
6451
|
-
@array_api_compatible
|
6295
|
+
@skip_xp_backends(np_only=True, reason="Only NumPy has nan_policy='omit' for now")
|
6452
6296
|
def test_ttest_1samp_new_omit(xp):
|
6453
6297
|
n1, n2, n3 = (5, 10, 15)
|
6454
6298
|
rvn1 = stats.norm.rvs(loc=5, scale=10, size=(n1, n2, n3))
|
@@ -6471,10 +6315,8 @@ def test_ttest_1samp_new_omit(xp):
|
|
6471
6315
|
xp_assert_close(t, tr)
|
6472
6316
|
|
6473
6317
|
|
6474
|
-
@
|
6475
|
-
|
6476
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
6477
|
-
@array_api_compatible
|
6318
|
+
@make_xp_test_case(stats.ttest_1samp)
|
6319
|
+
@pytest.mark.skip_xp_backends('jax.numpy', reason='Generic stdtrit mutates array.')
|
6478
6320
|
def test_ttest_1samp_popmean_array(xp):
|
6479
6321
|
# when popmean.shape[axis] != 1, raise an error
|
6480
6322
|
# if the user wants to test multiple null hypotheses simultaneously,
|
@@ -6492,10 +6334,9 @@ def test_ttest_1samp_popmean_array(xp):
|
|
6492
6334
|
res = stats.ttest_1samp(x, popmean=popmean, axis=-2)
|
6493
6335
|
assert res.statistic.shape == (5, 20)
|
6494
6336
|
|
6495
|
-
xp_test = array_namespace(x) # torch needs expand_dims
|
6496
6337
|
l, u = res.confidence_interval()
|
6497
|
-
l =
|
6498
|
-
u =
|
6338
|
+
l = xp.expand_dims(l, axis=-2)
|
6339
|
+
u = xp.expand_dims(u, axis=-2)
|
6499
6340
|
|
6500
6341
|
res = stats.ttest_1samp(x, popmean=l, axis=-2)
|
6501
6342
|
ref = xp.broadcast_to(xp.asarray(0.05, dtype=xp.float64), res.pvalue.shape)
|
@@ -6505,8 +6346,9 @@ def test_ttest_1samp_popmean_array(xp):
|
|
6505
6346
|
xp_assert_close(res.pvalue, ref)
|
6506
6347
|
|
6507
6348
|
|
6349
|
+
@make_xp_test_case(stats.describe)
|
6508
6350
|
class TestDescribe:
|
6509
|
-
@
|
6351
|
+
@pytest.mark.filterwarnings("ignore:invalid value encountered:RuntimeWarning:dask")
|
6510
6352
|
def test_describe_scalar(self, xp):
|
6511
6353
|
with suppress_warnings() as sup, \
|
6512
6354
|
np.errstate(invalid="ignore", divide="ignore"):
|
@@ -6520,12 +6362,8 @@ class TestDescribe:
|
|
6520
6362
|
xp_assert_equal(sk, xp.asarray(xp.nan))
|
6521
6363
|
xp_assert_equal(kurt, xp.asarray(xp.nan))
|
6522
6364
|
|
6523
|
-
@skip_xp_backends('cupy', reason='cupy/cupy#8391')
|
6524
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
6525
|
-
@array_api_compatible
|
6526
6365
|
def test_describe_numbers(self, xp):
|
6527
|
-
|
6528
|
-
x = xp_test.concat((xp.ones((3, 4)), xp.full((2, 4), 2.)))
|
6366
|
+
x = xp.concat((xp.ones((3, 4)), xp.full((2, 4), 2.)))
|
6529
6367
|
nc = 5
|
6530
6368
|
mmc = (xp.asarray([1., 1., 1., 1.]), xp.asarray([2., 2., 2., 2.]))
|
6531
6369
|
mc = xp.asarray([1.4, 1.4, 1.4, 1.4])
|
@@ -6567,14 +6405,16 @@ class TestDescribe:
|
|
6567
6405
|
assert_array_almost_equal(sk, skc)
|
6568
6406
|
assert_array_almost_equal(kurt, kurtc, decimal=13)
|
6569
6407
|
|
6570
|
-
@array_api_compatible
|
6571
6408
|
def test_describe_nan_policy_other(self, xp):
|
6572
6409
|
x = xp.arange(10.)
|
6573
|
-
x = xp.where(x==9, xp.
|
6410
|
+
x = xp.where(x==9, xp.nan, x)
|
6574
6411
|
|
6575
|
-
|
6576
|
-
|
6577
|
-
|
6412
|
+
if is_lazy_array(x):
|
6413
|
+
with pytest.raises(TypeError, match='not supported for lazy arrays'):
|
6414
|
+
stats.describe(x, nan_policy='raise')
|
6415
|
+
else:
|
6416
|
+
with pytest.raises(ValueError, match='The input contains nan values'):
|
6417
|
+
stats.describe(x, nan_policy='raise')
|
6578
6418
|
|
6579
6419
|
n, mm, m, v, sk, kurt = stats.describe(x, nan_policy='propagate')
|
6580
6420
|
ref = xp.asarray(xp.nan)[()]
|
@@ -6588,8 +6428,11 @@ class TestDescribe:
|
|
6588
6428
|
|
6589
6429
|
if is_numpy(xp):
|
6590
6430
|
self.describe_nan_policy_omit_test()
|
6431
|
+
elif is_lazy_array(x):
|
6432
|
+
with pytest.raises(TypeError, match='not supported for lazy arrays'):
|
6433
|
+
stats.describe(x, nan_policy='omit')
|
6591
6434
|
else:
|
6592
|
-
message = "
|
6435
|
+
message = "nan_policy='omit' is incompatible with non-NumPy arrays."
|
6593
6436
|
with pytest.raises(ValueError, match=message):
|
6594
6437
|
stats.describe(x, nan_policy='omit')
|
6595
6438
|
|
@@ -6604,12 +6447,8 @@ class TestDescribe:
|
|
6604
6447
|
attributes = ('nobs', 'minmax', 'mean', 'variance', 'skewness', 'kurtosis')
|
6605
6448
|
check_named_results(actual, attributes)
|
6606
6449
|
|
6607
|
-
@skip_xp_backends('cupy', reason='cupy/cupy#8391')
|
6608
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
6609
|
-
@array_api_compatible
|
6610
6450
|
def test_describe_ddof(self, xp):
|
6611
|
-
|
6612
|
-
x = xp_test.concat((xp.ones((3, 4)), xp.full((2, 4), 2.)))
|
6451
|
+
x = xp.concat((xp.ones((3, 4)), xp.full((2, 4), 2.)))
|
6613
6452
|
nc = 5
|
6614
6453
|
mmc = (xp.asarray([1., 1., 1., 1.]), xp.asarray([2., 2., 2., 2.]))
|
6615
6454
|
mc = xp.asarray([1.4, 1.4, 1.4, 1.4])
|
@@ -6625,12 +6464,8 @@ class TestDescribe:
|
|
6625
6464
|
xp_assert_close(sk, skc)
|
6626
6465
|
xp_assert_close(kurt, kurtc)
|
6627
6466
|
|
6628
|
-
@skip_xp_backends('cupy', reason='cupy/cupy#8391')
|
6629
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
6630
|
-
@array_api_compatible
|
6631
6467
|
def test_describe_axis_none(self, xp):
|
6632
|
-
|
6633
|
-
x = xp_test.concat((xp.ones((3, 4)), xp.full((2, 4), 2.)))
|
6468
|
+
x = xp.concat((xp.ones((3, 4)), xp.full((2, 4), 2.)))
|
6634
6469
|
|
6635
6470
|
# expected values
|
6636
6471
|
nc = 20
|
@@ -6651,32 +6486,14 @@ class TestDescribe:
|
|
6651
6486
|
xp_assert_close(sk, skc)
|
6652
6487
|
xp_assert_close(kurt, kurtc)
|
6653
6488
|
|
6654
|
-
@array_api_compatible
|
6655
6489
|
def test_describe_empty(self, xp):
|
6656
6490
|
message = "The input must not be empty."
|
6657
6491
|
with pytest.raises(ValueError, match=message):
|
6658
6492
|
stats.describe(xp.asarray([]))
|
6659
6493
|
|
6660
6494
|
|
6661
|
-
@array_api_compatible
|
6662
6495
|
class NormalityTests:
|
6663
|
-
def test_too_small(self, xp):
|
6664
|
-
# 1D sample has too few observations -> warning/error
|
6665
|
-
test_fun = getattr(stats, self.test_name)
|
6666
|
-
x = xp.asarray(4.)
|
6667
|
-
if is_numpy(xp):
|
6668
|
-
with pytest.warns(SmallSampleWarning, match=too_small_1d_not_omit):
|
6669
|
-
res = test_fun(x)
|
6670
|
-
NaN = xp.asarray(xp.nan)
|
6671
|
-
xp_assert_equal(res.statistic, NaN)
|
6672
|
-
xp_assert_equal(res.pvalue, NaN)
|
6673
|
-
else:
|
6674
|
-
message = "...requires at least..."
|
6675
|
-
with pytest.raises(ValueError, match=message):
|
6676
|
-
test_fun(x)
|
6677
6496
|
|
6678
|
-
@skip_xp_backends('cupy', reason='cupy/cupy#8391')
|
6679
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
6680
6497
|
@pytest.mark.parametrize("alternative", ['two-sided', 'less', 'greater'])
|
6681
6498
|
def test_against_R(self, alternative, xp):
|
6682
6499
|
# testa against R `dagoTest` from package `fBasics`
|
@@ -6708,8 +6525,6 @@ class NormalityTests:
|
|
6708
6525
|
xp_assert_close(res_pvalue, ref_pvalue)
|
6709
6526
|
check_named_results(res, ('statistic', 'pvalue'), xp=xp)
|
6710
6527
|
|
6711
|
-
@skip_xp_backends('cupy', reason='cupy/cupy#8391')
|
6712
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
6713
6528
|
def test_nan(self, xp):
|
6714
6529
|
# nan in input -> nan output (default nan_policy='propagate')
|
6715
6530
|
test_fun = getattr(stats, self.test_name)
|
@@ -6721,6 +6536,8 @@ class NormalityTests:
|
|
6721
6536
|
xp_assert_equal(res.statistic, NaN)
|
6722
6537
|
xp_assert_equal(res.pvalue, NaN)
|
6723
6538
|
|
6539
|
+
|
6540
|
+
@make_xp_test_case(stats.skewtest)
|
6724
6541
|
class TestSkewTest(NormalityTests):
|
6725
6542
|
test_name = 'skewtest'
|
6726
6543
|
case_ref = (1.98078826090875881, 0.04761502382843208) # statistic, pvalue
|
@@ -6734,28 +6551,25 @@ class TestSkewTest(NormalityTests):
|
|
6734
6551
|
|
6735
6552
|
def test_skewtest_too_few_observations(self, xp):
|
6736
6553
|
# Regression test for ticket #1492.
|
6737
|
-
# skewtest requires at least 8 observations; 7 should
|
6554
|
+
# skewtest requires at least 8 observations; 7 should warn and return NaN.
|
6738
6555
|
stats.skewtest(xp.arange(8.0))
|
6739
6556
|
|
6740
6557
|
x = xp.arange(7.0)
|
6741
|
-
if is_numpy(xp):
|
6742
|
-
with pytest.warns(SmallSampleWarning, match=too_small_1d_not_omit):
|
6743
|
-
res = stats.skewtest(x)
|
6744
|
-
NaN = xp.asarray(xp.nan)
|
6745
|
-
xp_assert_equal(res.statistic, NaN)
|
6746
|
-
xp_assert_equal(res.pvalue, NaN)
|
6747
|
-
else:
|
6748
|
-
message = "`skewtest` requires at least 8 observations"
|
6749
|
-
with pytest.raises(ValueError, match=message):
|
6750
|
-
stats.skewtest(x)
|
6751
6558
|
|
6559
|
+
message = (too_small_1d_not_omit if is_numpy(xp)
|
6560
|
+
else "`skewtest` requires at least 8 valid observations")
|
6561
|
+
with pytest.warns(SmallSampleWarning, match=message):
|
6562
|
+
res = stats.skewtest(x)
|
6563
|
+
NaN = xp.asarray(xp.nan)
|
6564
|
+
xp_assert_equal(res.statistic, NaN)
|
6565
|
+
xp_assert_equal(res.pvalue, NaN)
|
6752
6566
|
|
6567
|
+
|
6568
|
+
@make_xp_test_case(stats.kurtosistest)
|
6753
6569
|
class TestKurtosisTest(NormalityTests):
|
6754
6570
|
test_name = 'kurtosistest'
|
6755
6571
|
case_ref = (-0.01403734404759738, 0.98880018772590561) # statistic, pvalue
|
6756
6572
|
|
6757
|
-
@skip_xp_backends('cupy', reason='cupy/cupy#8391')
|
6758
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
6759
6573
|
def test_intuitive(self, xp):
|
6760
6574
|
# intuitive tests; see gh-13549. excess kurtosis of laplace is 3 > 0
|
6761
6575
|
a2 = stats.laplace.rvs(size=10000, random_state=123)
|
@@ -6763,8 +6577,6 @@ class TestKurtosisTest(NormalityTests):
|
|
6763
6577
|
pval = stats.kurtosistest(a2_xp, alternative='greater').pvalue
|
6764
6578
|
xp_assert_close(pval, xp.asarray(0.0, dtype=a2_xp.dtype), atol=1e-15)
|
6765
6579
|
|
6766
|
-
@skip_xp_backends('cupy', reason='cupy/cupy#8391')
|
6767
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
6768
6580
|
def test_gh9033_regression(self, xp):
|
6769
6581
|
# regression test for issue gh-9033: x clearly non-normal but power of
|
6770
6582
|
# negative denom needs to be handled correctly to reject normality
|
@@ -6773,37 +6585,36 @@ class TestKurtosisTest(NormalityTests):
|
|
6773
6585
|
x = xp.asarray(x, dtype=xp.float64)
|
6774
6586
|
assert stats.kurtosistest(x)[1] < 0.01
|
6775
6587
|
|
6776
|
-
@skip_xp_backends('cupy', reason='cupy/cupy#8391')
|
6777
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
6778
6588
|
def test_kurtosistest_too_few_observations(self, xp):
|
6779
|
-
# kurtosistest requires at least 5 observations; 4 should
|
6780
|
-
# At least 20 are needed to avoid warning
|
6589
|
+
# kurtosistest requires at least 5 observations; 4 should warn and return NaN.
|
6781
6590
|
# Regression test for ticket #1425.
|
6782
|
-
stats.kurtosistest(xp.arange(
|
6783
|
-
|
6784
|
-
message = "`kurtosistest` p-value may be inaccurate..."
|
6785
|
-
with pytest.warns(UserWarning, match=message):
|
6786
|
-
stats.kurtosistest(xp.arange(5.0))
|
6787
|
-
with pytest.warns(UserWarning, match=message):
|
6788
|
-
stats.kurtosistest(xp.arange(19.0))
|
6591
|
+
stats.kurtosistest(xp.arange(5.0))
|
6789
6592
|
|
6790
|
-
|
6791
|
-
|
6792
|
-
|
6793
|
-
|
6794
|
-
|
6795
|
-
|
6796
|
-
|
6797
|
-
else:
|
6798
|
-
message = "`kurtosistest` requires at least 5 observations"
|
6799
|
-
with pytest.raises(ValueError, match=message):
|
6800
|
-
stats.kurtosistest(x)
|
6593
|
+
message = (too_small_1d_not_omit if is_numpy(xp)
|
6594
|
+
else "`kurtosistest` requires at least 5 valid")
|
6595
|
+
with pytest.warns(SmallSampleWarning, match=message):
|
6596
|
+
res = stats.kurtosistest(xp.arange(4.))
|
6597
|
+
NaN = xp.asarray(xp.nan)
|
6598
|
+
xp_assert_equal(res.statistic, NaN)
|
6599
|
+
xp_assert_equal(res.pvalue, NaN)
|
6801
6600
|
|
6802
6601
|
|
6602
|
+
@make_xp_test_case(stats.normaltest)
|
6803
6603
|
class TestNormalTest(NormalityTests):
|
6804
6604
|
test_name = 'normaltest'
|
6805
6605
|
case_ref = (3.92371918158185551, 0.14059672529747502) # statistic, pvalue
|
6806
6606
|
|
6607
|
+
def test_too_few_observations(self, xp):
|
6608
|
+
stats.normaltest(xp.arange(8.))
|
6609
|
+
|
6610
|
+
# 1D sample has too few observations -> warning / NaN output
|
6611
|
+
# specific warning messages tested for `skewtest`/`kurtosistest`
|
6612
|
+
with pytest.warns(SmallSampleWarning):
|
6613
|
+
res = stats.normaltest(xp.arange(7.))
|
6614
|
+
NaN = xp.asarray(xp.nan)
|
6615
|
+
xp_assert_equal(res.statistic, NaN)
|
6616
|
+
xp_assert_equal(res.pvalue, NaN)
|
6617
|
+
|
6807
6618
|
|
6808
6619
|
class TestRankSums:
|
6809
6620
|
|
@@ -6828,7 +6639,7 @@ class TestRankSums:
|
|
6828
6639
|
stats.ranksums(self.x, self.y, alternative='foobar')
|
6829
6640
|
|
6830
6641
|
|
6831
|
-
@
|
6642
|
+
@make_xp_test_case(stats.jarque_bera)
|
6832
6643
|
class TestJarqueBera:
|
6833
6644
|
def test_jarque_bera_against_R(self, xp):
|
6834
6645
|
# library(tseries)
|
@@ -6844,8 +6655,7 @@ class TestJarqueBera:
|
|
6844
6655
|
xp_assert_close(res.pvalue, ref[1])
|
6845
6656
|
|
6846
6657
|
@skip_xp_backends(np_only=True)
|
6847
|
-
|
6848
|
-
def test_jarque_bera_array_like(self):
|
6658
|
+
def test_jarque_bera_array_like(self, xp):
|
6849
6659
|
# array-like only relevant for NumPy
|
6850
6660
|
np.random.seed(987654321)
|
6851
6661
|
x = np.random.normal(0, 1, 100000)
|
@@ -6857,18 +6667,15 @@ class TestJarqueBera:
|
|
6857
6667
|
assert JB1 == JB2 == JB3 == jb_test1.statistic == jb_test2.statistic == jb_test3.statistic # noqa: E501
|
6858
6668
|
assert p1 == p2 == p3 == jb_test1.pvalue == jb_test2.pvalue == jb_test3.pvalue
|
6859
6669
|
|
6860
|
-
|
6670
|
+
@skip_xp_backends('array_api_strict', reason='Noisy; see TestSkew')
|
6671
|
+
def test_jarque_bera_too_few_observations(self, xp):
|
6861
6672
|
x = xp.asarray([])
|
6862
|
-
|
6863
|
-
|
6864
|
-
|
6673
|
+
|
6674
|
+
with pytest.warns(SmallSampleWarning, match=too_small_1d_not_omit):
|
6675
|
+
res = stats.jarque_bera(x)
|
6865
6676
|
NaN = xp.asarray(xp.nan)
|
6866
6677
|
xp_assert_equal(res.statistic, NaN)
|
6867
6678
|
xp_assert_equal(res.pvalue, NaN)
|
6868
|
-
else:
|
6869
|
-
message = "At least one observation is required."
|
6870
|
-
with pytest.raises(ValueError, match=message):
|
6871
|
-
res = stats.jarque_bera(x)
|
6872
6679
|
|
6873
6680
|
def test_axis(self, xp):
|
6874
6681
|
rng = np.random.RandomState(seed=122398129)
|
@@ -6882,8 +6689,8 @@ class TestJarqueBera:
|
|
6882
6689
|
res = stats.jarque_bera(x, axis=1)
|
6883
6690
|
s0, p0 = stats.jarque_bera(x[0, :])
|
6884
6691
|
s1, p1 = stats.jarque_bera(x[1, :])
|
6885
|
-
xp_assert_close(res.statistic, xp.
|
6886
|
-
xp_assert_close(res.pvalue, xp.
|
6692
|
+
xp_assert_close(res.statistic, xp.stack([s0, s1]))
|
6693
|
+
xp_assert_close(res.pvalue, xp.stack([p0, p1]))
|
6887
6694
|
|
6888
6695
|
resT = stats.jarque_bera(x.T, axis=0)
|
6889
6696
|
xp_assert_close(res.statistic, resT.statistic)
|
@@ -7104,8 +6911,9 @@ def check_equal_pmean(*args, **kwargs):
|
|
7104
6911
|
return check_equal_xmean(*args, mean_fun=stats.pmean, **kwargs)
|
7105
6912
|
|
7106
6913
|
|
7107
|
-
@
|
6914
|
+
@make_xp_test_case(stats.hmean)
|
7108
6915
|
class TestHMean:
|
6916
|
+
@pytest.mark.filterwarnings("ignore:divide by zero encountered:RuntimeWarning:dask")
|
7109
6917
|
def test_0(self, xp):
|
7110
6918
|
a = [1, 0, 2]
|
7111
6919
|
desired = 0
|
@@ -7121,17 +6929,17 @@ class TestHMean:
|
|
7121
6929
|
desired = 4. / (1. / 1 + 1. / 2 + 1. / 3 + 1. / 4)
|
7122
6930
|
check_equal_hmean(a, desired, xp=xp)
|
7123
6931
|
|
6932
|
+
@pytest.mark.filterwarnings("ignore:divide by zero encountered:RuntimeWarning:dask")
|
6933
|
+
@pytest.mark.filterwarnings("ignore:invalid value encountered:RuntimeWarning:dask")
|
7124
6934
|
def test_1d_with_zero(self, xp):
|
7125
6935
|
a = np.array([1, 0])
|
7126
6936
|
desired = 0.0
|
7127
6937
|
check_equal_hmean(a, desired, xp=xp, rtol=0.0)
|
7128
6938
|
|
7129
|
-
@
|
7130
|
-
|
7131
|
-
|
7132
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
6939
|
+
@pytest.mark.filterwarnings(
|
6940
|
+
"ignore:divide by zero encountered:RuntimeWarning"
|
6941
|
+
) # for dask
|
7133
6942
|
def test_1d_with_negative_value(self, xp):
|
7134
|
-
# Won't work for array_api_strict for now, but see data-apis/array-api#807
|
7135
6943
|
a = np.array([1, 0, -1])
|
7136
6944
|
message = "The harmonic mean is only defined..."
|
7137
6945
|
with pytest.warns(RuntimeWarning, match=message):
|
@@ -7150,6 +6958,7 @@ class TestHMean:
|
|
7150
6958
|
desired = np.array([22.88135593, 39.13043478, 52.90076336, 65.45454545])
|
7151
6959
|
check_equal_hmean(a, desired, axis=0, xp=xp)
|
7152
6960
|
|
6961
|
+
@pytest.mark.filterwarnings("ignore:divide by zero encountered:RuntimeWarning:dask")
|
7153
6962
|
def test_2d_axis0_with_zero(self, xp):
|
7154
6963
|
a = [[10, 0, 30, 40], [50, 60, 70, 80], [90, 100, 110, 120]]
|
7155
6964
|
desired = np.array([22.88135593, 0.0, 52.90076336, 65.45454545])
|
@@ -7161,16 +6970,16 @@ class TestHMean:
|
|
7161
6970
|
desired = np.array([19.2, 63.03939962, 103.80078637])
|
7162
6971
|
check_equal_hmean(a, desired, axis=1, xp=xp)
|
7163
6972
|
|
6973
|
+
@pytest.mark.filterwarnings("ignore:divide by zero encountered:RuntimeWarning:dask")
|
7164
6974
|
def test_2d_axis1_with_zero(self, xp):
|
7165
6975
|
a = [[10, 0, 30, 40], [50, 60, 70, 80], [90, 100, 110, 120]]
|
7166
6976
|
desired = np.array([0.0, 63.03939962, 103.80078637])
|
7167
6977
|
check_equal_hmean(a, desired, axis=1, xp=xp)
|
7168
6978
|
|
7169
|
-
@
|
6979
|
+
@skip_xp_backends(
|
7170
6980
|
np_only=True,
|
7171
6981
|
reason='array-likes only supported for NumPy backend',
|
7172
6982
|
)
|
7173
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
7174
6983
|
def test_weights_1d_list(self, xp):
|
7175
6984
|
# Desired result from:
|
7176
6985
|
# https://www.hackmath.net/en/math-problem/35871
|
@@ -7219,8 +7028,11 @@ class TestHMean:
|
|
7219
7028
|
dtype=np.float64, xp=xp)
|
7220
7029
|
|
7221
7030
|
|
7222
|
-
@
|
7031
|
+
@make_xp_test_case(stats.gmean)
|
7223
7032
|
class TestGMean:
|
7033
|
+
@pytest.mark.filterwarnings(
|
7034
|
+
"ignore:divide by zero encountered in log:RuntimeWarning:dask"
|
7035
|
+
)
|
7224
7036
|
def test_0(self, xp):
|
7225
7037
|
a = [1, 0, 2]
|
7226
7038
|
desired = 0
|
@@ -7273,6 +7085,9 @@ class TestGMean:
|
|
7273
7085
|
desired = 1e200
|
7274
7086
|
check_equal_gmean(a, desired, rtol=1e-13, xp=xp)
|
7275
7087
|
|
7088
|
+
@pytest.mark.filterwarnings(
|
7089
|
+
"ignore:divide by zero encountered in log:RuntimeWarning:dask"
|
7090
|
+
)
|
7276
7091
|
def test_1d_with_0(self, xp):
|
7277
7092
|
# Test a 1d case with zero element
|
7278
7093
|
a = [10, 20, 30, 40, 50, 60, 70, 80, 90, 0]
|
@@ -7280,6 +7095,9 @@ class TestGMean:
|
|
7280
7095
|
with np.errstate(all='ignore'):
|
7281
7096
|
check_equal_gmean(a, desired, xp=xp)
|
7282
7097
|
|
7098
|
+
@pytest.mark.filterwarnings(
|
7099
|
+
"ignore:invalid value encountered in log:RuntimeWarning:dask"
|
7100
|
+
)
|
7283
7101
|
def test_1d_neg(self, xp):
|
7284
7102
|
# Test a 1d case with negative element
|
7285
7103
|
a = [10, 20, 30, 40, 50, 60, 70, 80, 90, -1]
|
@@ -7287,11 +7105,10 @@ class TestGMean:
|
|
7287
7105
|
with np.errstate(invalid='ignore'):
|
7288
7106
|
check_equal_gmean(a, desired, xp=xp)
|
7289
7107
|
|
7290
|
-
@
|
7108
|
+
@skip_xp_backends(
|
7291
7109
|
np_only=True,
|
7292
7110
|
reason='array-likes only supported for NumPy backend',
|
7293
7111
|
)
|
7294
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
7295
7112
|
def test_weights_1d_list(self, xp):
|
7296
7113
|
# Desired result from:
|
7297
7114
|
# https://www.dummies.com/education/math/business-statistics/how-to-find-the-weighted-geometric-mean-of-a-data-set/
|
@@ -7325,7 +7142,7 @@ class TestGMean:
|
|
7325
7142
|
dtype=np.float64, xp=xp)
|
7326
7143
|
|
7327
7144
|
|
7328
|
-
@
|
7145
|
+
@make_xp_test_case(stats.pmean)
|
7329
7146
|
class TestPMean:
|
7330
7147
|
|
7331
7148
|
def pmean_reference(a, p):
|
@@ -7353,15 +7170,13 @@ class TestPMean:
|
|
7353
7170
|
desired = np.sqrt((1**2 + 2**2 + 3**2 + 4**2) / 4)
|
7354
7171
|
check_equal_pmean(a, p, desired, xp=xp)
|
7355
7172
|
|
7173
|
+
@pytest.mark.filterwarnings("ignore:invalid value encountered:RuntimeWarning:dask")
|
7174
|
+
@pytest.mark.filterwarnings("ignore:divide by zero encountered:RuntimeWarning:dask")
|
7356
7175
|
def test_1d_with_zero(self, xp):
|
7357
7176
|
a, p = np.array([1, 0]), -1
|
7358
7177
|
desired = 0.0
|
7359
7178
|
check_equal_pmean(a, p, desired, rtol=0.0, xp=xp)
|
7360
7179
|
|
7361
|
-
@skip_xp_backends('array_api_strict',
|
7362
|
-
reason=("`array_api_strict.where` `fillvalue` doesn't "
|
7363
|
-
"accept Python scalars. See data-apis/array-api#807."))
|
7364
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
7365
7180
|
def test_1d_with_negative_value(self, xp):
|
7366
7181
|
a, p = np.array([1, 0, -1]), 1.23
|
7367
7182
|
message = "The power mean is only defined..."
|
@@ -7406,11 +7221,10 @@ class TestPMean:
|
|
7406
7221
|
desired = TestPMean.wpmean_reference(np.array(a), p, weights)
|
7407
7222
|
check_equal_pmean(a, p, desired, weights=weights, rtol=1e-5, xp=xp)
|
7408
7223
|
|
7409
|
-
@
|
7224
|
+
@skip_xp_backends(
|
7410
7225
|
np_only=True,
|
7411
7226
|
reason='array-likes only supported for NumPy backend',
|
7412
7227
|
)
|
7413
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
7414
7228
|
def test_weights_1d_list(self, xp):
|
7415
7229
|
a, p = [2, 10, 6], -1.23456789
|
7416
7230
|
weights = [10, 5, 3]
|
@@ -7448,87 +7262,90 @@ class TestPMean:
|
|
7448
7262
|
check_equal_pmean(a, p, desired, axis=axis, weights=weights, rtol=1e-5, xp=xp)
|
7449
7263
|
|
7450
7264
|
|
7265
|
+
@make_xp_test_case(stats.gstd)
|
7451
7266
|
class TestGSTD:
|
7452
7267
|
# must add 1 as `gstd` is only defined for positive values
|
7453
|
-
array_1d = np.arange(2 * 3 * 4) + 1
|
7268
|
+
array_1d = (np.arange(2 * 3 * 4) + 1).tolist()
|
7454
7269
|
gstd_array_1d = 2.294407613602
|
7455
|
-
array_3d =
|
7270
|
+
array_3d = np.reshape(array_1d, (2, 3, 4)).tolist()
|
7456
7271
|
|
7457
|
-
def test_1d_array(self):
|
7458
|
-
gstd_actual = stats.gstd(self.array_1d)
|
7459
|
-
|
7272
|
+
def test_1d_array(self, xp):
|
7273
|
+
gstd_actual = stats.gstd(xp.asarray(self.array_1d))
|
7274
|
+
xp_assert_close(gstd_actual, xp.asarray(self.gstd_array_1d))
|
7460
7275
|
|
7461
|
-
|
7276
|
+
@skip_xp_backends(np_only=True, reason="Only NumPy supports array-like input")
|
7277
|
+
def test_1d_numeric_array_like_input(self, xp):
|
7462
7278
|
gstd_actual = stats.gstd(tuple(self.array_1d))
|
7463
7279
|
assert_allclose(gstd_actual, self.gstd_array_1d)
|
7464
7280
|
|
7465
|
-
|
7466
|
-
|
7467
|
-
|
7281
|
+
@skip_xp_invalid_arg
|
7282
|
+
def test_raises_error_non_numeric_input(self, xp):
|
7283
|
+
message = "could not convert string to float|The DType..."
|
7284
|
+
with pytest.raises((ValueError, TypeError), match=message):
|
7468
7285
|
stats.gstd('You cannot take the logarithm of a string.')
|
7469
7286
|
|
7287
|
+
@pytest.mark.filterwarnings("ignore:divide by zero encountered:RuntimeWarning:dask")
|
7288
|
+
@pytest.mark.filterwarnings("ignore:invalid value encountered:RuntimeWarning:dask")
|
7470
7289
|
@pytest.mark.parametrize('bad_value', (0, -1, np.inf, np.nan))
|
7471
|
-
def test_returns_nan_invalid_value(self, bad_value):
|
7472
|
-
x =
|
7473
|
-
if np.isfinite(bad_value):
|
7290
|
+
def test_returns_nan_invalid_value(self, bad_value, xp):
|
7291
|
+
x = xp.asarray(self.array_1d + [bad_value])
|
7292
|
+
if np.isfinite(bad_value) and not is_lazy_array(x):
|
7474
7293
|
message = "The geometric standard deviation is only defined..."
|
7475
7294
|
with pytest.warns(RuntimeWarning, match=message):
|
7476
7295
|
res = stats.gstd(x)
|
7477
7296
|
else:
|
7478
7297
|
res = stats.gstd(x)
|
7479
|
-
|
7298
|
+
xp_assert_equal(res, xp.asarray(np.nan))
|
7480
7299
|
|
7481
|
-
def test_propagates_nan_values(self):
|
7482
|
-
a =
|
7300
|
+
def test_propagates_nan_values(self, xp):
|
7301
|
+
a = xp.asarray([[1, 1, 1, 16], [xp.nan, 1, 2, 3]])
|
7483
7302
|
gstd_actual = stats.gstd(a, axis=1)
|
7484
|
-
|
7303
|
+
xp_assert_close(gstd_actual, xp.asarray([4, np.nan]))
|
7485
7304
|
|
7486
|
-
def test_ddof_equal_to_number_of_observations(self):
|
7487
|
-
|
7488
|
-
|
7489
|
-
|
7490
|
-
def test_3d_array(self):
|
7491
|
-
gstd_actual = stats.gstd(self.array_3d, axis=None)
|
7492
|
-
assert_allclose(gstd_actual, self.gstd_array_1d)
|
7493
|
-
|
7494
|
-
def test_3d_array_axis_type_tuple(self):
|
7495
|
-
gstd_actual = stats.gstd(self.array_3d, axis=(1,2))
|
7496
|
-
assert_allclose(gstd_actual, [2.12939215, 1.22120169])
|
7305
|
+
def test_ddof_equal_to_number_of_observations(self, xp):
|
7306
|
+
x = xp.asarray(self.array_1d)
|
7307
|
+
res = stats.gstd(x, ddof=x.shape[0])
|
7308
|
+
xp_assert_equal(res, xp.asarray(xp.nan))
|
7497
7309
|
|
7498
|
-
def
|
7499
|
-
|
7500
|
-
|
7310
|
+
def test_3d_array(self, xp):
|
7311
|
+
x = xp.asarray(self.array_3d)
|
7312
|
+
gstd_actual = stats.gstd(x, axis=None)
|
7313
|
+
ref = xp.asarray(self.gstd_array_1d)
|
7314
|
+
xp_assert_close(gstd_actual, ref)
|
7315
|
+
|
7316
|
+
def test_3d_array_axis_type_tuple(self, xp):
|
7317
|
+
x = xp.asarray(self.array_3d)
|
7318
|
+
gstd_actual = stats.gstd(x, axis=(1, 2))
|
7319
|
+
ref = xp.asarray([2.12939215, 1.22120169])
|
7320
|
+
xp_assert_close(gstd_actual, ref)
|
7321
|
+
|
7322
|
+
def test_3d_array_axis_0(self, xp):
|
7323
|
+
x = xp.asarray(self.array_3d)
|
7324
|
+
gstd_actual = stats.gstd(x, axis=0)
|
7325
|
+
gstd_desired = xp.asarray([
|
7501
7326
|
[6.1330555493918, 3.958900210120, 3.1206598248344, 2.6651441426902],
|
7502
7327
|
[2.3758135028411, 2.174581428192, 2.0260062829505, 1.9115518327308],
|
7503
7328
|
[1.8205343606803, 1.746342404566, 1.6846557065742, 1.6325269194382]
|
7504
7329
|
])
|
7505
|
-
|
7330
|
+
xp_assert_close(gstd_actual, gstd_desired)
|
7506
7331
|
|
7507
|
-
def test_3d_array_axis_1(self):
|
7508
|
-
|
7509
|
-
|
7332
|
+
def test_3d_array_axis_1(self, xp):
|
7333
|
+
x = xp.asarray(self.array_3d)
|
7334
|
+
gstd_actual = stats.gstd(x, axis=1)
|
7335
|
+
gstd_desired = xp.asarray([
|
7510
7336
|
[3.118993630946, 2.275985934063, 1.933995977619, 1.742896469724],
|
7511
7337
|
[1.271693593916, 1.254158641801, 1.238774141609, 1.225164057869]
|
7512
7338
|
])
|
7513
|
-
|
7339
|
+
xp_assert_close(gstd_actual, gstd_desired)
|
7514
7340
|
|
7515
|
-
def test_3d_array_axis_2(self):
|
7516
|
-
|
7517
|
-
|
7341
|
+
def test_3d_array_axis_2(self, xp):
|
7342
|
+
x = xp.asarray(self.array_3d)
|
7343
|
+
gstd_actual = stats.gstd(x, axis=2)
|
7344
|
+
gstd_desired = xp.asarray([
|
7518
7345
|
[1.8242475707664, 1.2243686572447, 1.1318311657788],
|
7519
7346
|
[1.0934830582351, 1.0724479791887, 1.0591498540749]
|
7520
7347
|
])
|
7521
|
-
|
7522
|
-
|
7523
|
-
def test_masked_3d_array(self):
|
7524
|
-
ma = np.ma.masked_where(self.array_3d > 16, self.array_3d)
|
7525
|
-
message = "`gstd` support for masked array input was deprecated in..."
|
7526
|
-
with pytest.warns(DeprecationWarning, match=message):
|
7527
|
-
gstd_actual = stats.gstd(ma, axis=2)
|
7528
|
-
gstd_desired = stats.gstd(self.array_3d, axis=2)
|
7529
|
-
mask = [[0, 0, 0], [0, 1, 1]]
|
7530
|
-
assert_allclose(gstd_actual, gstd_desired)
|
7531
|
-
assert_equal(gstd_actual.mask, mask)
|
7348
|
+
xp_assert_close(gstd_actual, gstd_desired)
|
7532
7349
|
|
7533
7350
|
|
7534
7351
|
def test_binomtest():
|
@@ -8057,15 +7874,49 @@ class TestFOneWay:
|
|
8057
7874
|
assert_equal(F, 2.0)
|
8058
7875
|
assert_allclose(p, 1 - np.sqrt(0.5), rtol=1e-14)
|
8059
7876
|
|
7877
|
+
def test_unequal_var(self):
|
7878
|
+
# toy samples with unequal variances and different observations
|
7879
|
+
samples = [[-50.42, 40.31, -18.09, 35.58, -6.8, 0.22],
|
7880
|
+
[23.44, 4.5, 15.1, 9.66],
|
7881
|
+
[11.94, 11.1 , 9.87, 9.09, 3.33]]
|
7882
|
+
|
7883
|
+
F, p = stats.f_oneway(*samples, equal_var=False)
|
7884
|
+
|
7885
|
+
# R language as benchmark
|
7886
|
+
# group1 <- c(-50.42, 40.31, -18.09, 35.58, -6.8, 0.22)
|
7887
|
+
# group2 <- c(23.44, 4.5, 15.1, 9.66)
|
7888
|
+
# group3 <- c(11.94, 11.1 , 9.87, 9.09, 3.33)
|
7889
|
+
#
|
7890
|
+
# data <- data.frame(
|
7891
|
+
# value = c(group1, group2, group3),
|
7892
|
+
# group = factor(c(rep("G1", length(group1)),
|
7893
|
+
# rep("G2", length(group2)),
|
7894
|
+
# rep("G3", length(group3))))
|
7895
|
+
# )
|
7896
|
+
# welch_anova <- oneway.test(value ~ group, data = data, var.equal = FALSE)
|
7897
|
+
# welch_anova$statistic
|
7898
|
+
## F: 0.609740409019517
|
7899
|
+
# welch_anova$p.value
|
7900
|
+
## 0.574838941286302
|
7901
|
+
|
7902
|
+
assert_allclose(F, 0.609740409019517, rtol=1e-14)
|
7903
|
+
assert_allclose(p, 0.574838941286302, rtol=1e-14)
|
7904
|
+
|
7905
|
+
def test_equal_var_input_validation(self):
|
7906
|
+
samples = [[-50.42, 40.31, -18.09, 35.58, -6.8, 0.22],
|
7907
|
+
[23.44, 4.5, 15.1, 9.66],
|
7908
|
+
[11.94, 11.1 , 9.87, 9.09, 3.33]]
|
7909
|
+
|
7910
|
+
message = "Expected a boolean value for 'equal_var'"
|
7911
|
+
with pytest.raises(TypeError, match=message):
|
7912
|
+
stats.f_oneway(*samples, equal_var="False")
|
7913
|
+
|
8060
7914
|
def test_known_exact(self):
|
8061
7915
|
# Another trivial dataset for which the exact F and p can be
|
8062
|
-
# calculated
|
7916
|
+
# calculated on most platforms
|
8063
7917
|
F, p = stats.f_oneway([2], [2], [2, 3, 4])
|
8064
|
-
|
8065
|
-
|
8066
|
-
# no loss of precision.
|
8067
|
-
assert_equal(F, 3/5)
|
8068
|
-
assert_equal(p, 5/8)
|
7918
|
+
assert_allclose(F, 3/5, rtol=1e-15) # assert_equal fails on some CI platforms
|
7919
|
+
assert_allclose(p, 5/8, rtol=1e-15)
|
8069
7920
|
|
8070
7921
|
def test_large_integer_array(self):
|
8071
7922
|
a = np.array([655, 788], dtype=np.uint16)
|
@@ -8322,12 +8173,8 @@ class TestKruskal:
|
|
8322
8173
|
stats.kruskal()
|
8323
8174
|
|
8324
8175
|
|
8325
|
-
|
8326
|
-
|
8327
|
-
reason=('Delegation for `special.stdtr` only implemented '
|
8328
|
-
'for CuPy and JAX.'))
|
8329
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
8330
|
-
@array_api_compatible
|
8176
|
+
|
8177
|
+
@make_xp_test_case(stats.combine_pvalues)
|
8331
8178
|
class TestCombinePvalues:
|
8332
8179
|
# Reference values computed using the following R code:
|
8333
8180
|
# options(digits=16)
|
@@ -8372,7 +8219,6 @@ class TestCombinePvalues:
|
|
8372
8219
|
@pytest.mark.parametrize("variant", ["single", "all", "random"])
|
8373
8220
|
@pytest.mark.parametrize("method", methods)
|
8374
8221
|
def test_monotonicity(self, variant, method, xp):
|
8375
|
-
xp_test = array_namespace(xp.asarray(1))
|
8376
8222
|
# Test that result increases monotonically with respect to input.
|
8377
8223
|
m, n = 10, 7
|
8378
8224
|
rng = np.random.default_rng(278448169958891062669391462690811630763)
|
@@ -8383,15 +8229,15 @@ class TestCombinePvalues:
|
|
8383
8229
|
# column (all), or independently down each column (random).
|
8384
8230
|
if variant == "single":
|
8385
8231
|
pvaluess = xp.broadcast_to(xp.asarray(rng.random(n)), (m, n))
|
8386
|
-
pvaluess =
|
8387
|
-
|
8232
|
+
pvaluess = xp.concat([xp.reshape(xp.linspace(0.1, 0.9, m), (-1, 1)),
|
8233
|
+
pvaluess[:, 1:]], axis=1)
|
8388
8234
|
elif variant == "all":
|
8389
8235
|
pvaluess = xp.broadcast_to(xp.linspace(0.1, 0.9, m), (n, m)).T
|
8390
8236
|
elif variant == "random":
|
8391
|
-
pvaluess =
|
8237
|
+
pvaluess = xp.sort(xp.asarray(rng.uniform(0, 1, size=(m, n))), axis=0)
|
8392
8238
|
|
8393
8239
|
combined_pvalues = xp.asarray([
|
8394
|
-
stats.combine_pvalues(pvaluess[i, :], method=method)[1]
|
8240
|
+
float(stats.combine_pvalues(pvaluess[i, :], method=method)[1])
|
8395
8241
|
for i in range(pvaluess.shape[0])
|
8396
8242
|
])
|
8397
8243
|
assert xp.all(combined_pvalues[1:] - combined_pvalues[:-1] >= 0)
|
@@ -9569,7 +9415,6 @@ class TestLMoment:
|
|
9569
9415
|
assert_allclose(res, ref, rtol=1e-15)
|
9570
9416
|
|
9571
9417
|
|
9572
|
-
@array_api_compatible
|
9573
9418
|
class TestXP_Mean:
|
9574
9419
|
@pytest.mark.parametrize('axis', [None, 1, -1, (-2, 2)])
|
9575
9420
|
@pytest.mark.parametrize('weights', [None, True])
|
@@ -9597,6 +9442,8 @@ class TestXP_Mean:
|
|
9597
9442
|
with pytest.raises(ValueError, match=message):
|
9598
9443
|
_xp_mean(x, weights=w)
|
9599
9444
|
|
9445
|
+
@pytest.mark.filterwarnings("ignore:divide by zero encountered:RuntimeWarning:dask")
|
9446
|
+
@pytest.mark.filterwarnings("ignore:invalid value encountered:RuntimeWarning:dask")
|
9600
9447
|
def test_special_cases(self, xp):
|
9601
9448
|
# weights sum to zero
|
9602
9449
|
weights = xp.asarray([-1., 0., 1.])
|
@@ -9610,15 +9457,21 @@ class TestXP_Mean:
|
|
9610
9457
|
res = _xp_mean(xp.asarray([1., 1., 2.]), weights=weights)
|
9611
9458
|
xp_assert_close(res, xp.asarray(np.inf))
|
9612
9459
|
|
9460
|
+
@pytest.mark.filterwarnings(
|
9461
|
+
"ignore:invalid value encountered:RuntimeWarning"
|
9462
|
+
) # for dask
|
9613
9463
|
def test_nan_policy(self, xp):
|
9614
9464
|
x = xp.arange(10.)
|
9615
9465
|
mask = (x == 3)
|
9616
|
-
x = xp.where(mask, xp.
|
9466
|
+
x = xp.where(mask, xp.nan, x)
|
9617
9467
|
|
9618
9468
|
# nan_policy='raise' raises an error
|
9619
|
-
|
9620
|
-
|
9621
|
-
|
9469
|
+
if is_lazy_array(x):
|
9470
|
+
with pytest.raises(TypeError, match='not supported for lazy arrays'):
|
9471
|
+
_xp_mean(x, nan_policy='raise')
|
9472
|
+
else:
|
9473
|
+
with pytest.raises(ValueError, match='The input contains nan values'):
|
9474
|
+
_xp_mean(x, nan_policy='raise')
|
9622
9475
|
|
9623
9476
|
# `nan_policy='propagate'` is the default, and the result is NaN
|
9624
9477
|
res1 = _xp_mean(x)
|
@@ -9634,11 +9487,16 @@ class TestXP_Mean:
|
|
9634
9487
|
|
9635
9488
|
# `nan_policy='omit'` omits NaNs in `weights`, too
|
9636
9489
|
weights = xp.ones(10)
|
9637
|
-
weights = xp.where(mask, xp.
|
9490
|
+
weights = xp.where(mask, xp.nan, weights)
|
9638
9491
|
res = _xp_mean(xp.arange(10.), weights=weights, nan_policy='omit')
|
9639
9492
|
ref = xp.mean(x[~mask])
|
9640
9493
|
xp_assert_close(res, ref)
|
9641
9494
|
|
9495
|
+
@skip_xp_backends(eager_only=True)
|
9496
|
+
def test_nan_policy_warns(self, xp):
|
9497
|
+
x = xp.arange(10.)
|
9498
|
+
x = xp.where(x == 3, xp.nan, x)
|
9499
|
+
|
9642
9500
|
# Check for warning if omitting NaNs causes empty slice
|
9643
9501
|
message = 'After omitting NaNs...'
|
9644
9502
|
with pytest.warns(RuntimeWarning, match=message):
|
@@ -9663,6 +9521,9 @@ class TestXP_Mean:
|
|
9663
9521
|
ref = xp.asarray([])
|
9664
9522
|
xp_assert_equal(res, ref)
|
9665
9523
|
|
9524
|
+
@pytest.mark.filterwarnings(
|
9525
|
+
"ignore:overflow encountered in reduce:RuntimeWarning"
|
9526
|
+
) # for dask
|
9666
9527
|
def test_dtype(self, xp):
|
9667
9528
|
max = xp.finfo(xp.float32).max
|
9668
9529
|
x_np = np.asarray([max, max], dtype=np.float32)
|
@@ -9695,9 +9556,6 @@ class TestXP_Mean:
|
|
9695
9556
|
xp_assert_close(res, xp.asarray(ref))
|
9696
9557
|
|
9697
9558
|
|
9698
|
-
@array_api_compatible
|
9699
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
9700
|
-
@skip_xp_backends('jax.numpy', reason='JAX arrays do not support item assignment')
|
9701
9559
|
class TestXP_Var:
|
9702
9560
|
@pytest.mark.parametrize('axis', [None, 1, -1, (-2, 2)])
|
9703
9561
|
@pytest.mark.parametrize('keepdims', [False, True])
|
@@ -9732,12 +9590,7 @@ class TestXP_Var:
|
|
9732
9590
|
def test_nan_policy(self, xp):
|
9733
9591
|
x = xp.arange(10.)
|
9734
9592
|
mask = (x == 3)
|
9735
|
-
x = xp.where(mask, xp.
|
9736
|
-
|
9737
|
-
# nan_policy='raise' raises an error
|
9738
|
-
message = 'The input contains nan values'
|
9739
|
-
with pytest.raises(ValueError, match=message):
|
9740
|
-
_xp_var(x, nan_policy='raise')
|
9593
|
+
x = xp.where(mask, xp.nan, x)
|
9741
9594
|
|
9742
9595
|
# `nan_policy='propagate'` is the default, and the result is NaN
|
9743
9596
|
res1 = _xp_var(x)
|
@@ -9748,10 +9601,14 @@ class TestXP_Var:
|
|
9748
9601
|
|
9749
9602
|
# `nan_policy='omit'` omits NaNs in `x`
|
9750
9603
|
res = _xp_var(x, nan_policy='omit')
|
9751
|
-
|
9752
|
-
ref = xp_test.var(x[~mask])
|
9604
|
+
ref = xp.var(x[~mask])
|
9753
9605
|
xp_assert_close(res, ref)
|
9754
9606
|
|
9607
|
+
@skip_xp_backends(eager_only=True)
|
9608
|
+
def test_nan_policy_warns(self, xp):
|
9609
|
+
x = xp.arange(10.)
|
9610
|
+
x = xp.where(x == 3, xp.nan, x)
|
9611
|
+
|
9755
9612
|
# Check for warning if omitting NaNs causes empty slice
|
9756
9613
|
message = 'After omitting NaNs...'
|
9757
9614
|
with pytest.warns(RuntimeWarning, match=message):
|
@@ -9759,6 +9616,13 @@ class TestXP_Var:
|
|
9759
9616
|
ref = xp.asarray(xp.nan)
|
9760
9617
|
xp_assert_equal(res, ref)
|
9761
9618
|
|
9619
|
+
@skip_xp_backends(eager_only=True)
|
9620
|
+
def test_nan_policy_raise(self, xp):
|
9621
|
+
# nan_policy='raise' raises an error when NaNs are present
|
9622
|
+
message = 'The input contains nan values'
|
9623
|
+
with pytest.raises(ValueError, match=message):
|
9624
|
+
_xp_var(xp.asarray([1, 2, xp.nan]), nan_policy='raise')
|
9625
|
+
|
9762
9626
|
def test_empty(self, xp):
|
9763
9627
|
message = 'One or more sample arguments is too small...'
|
9764
9628
|
with pytest.warns(SmallSampleWarning, match=message):
|
@@ -9776,17 +9640,18 @@ class TestXP_Var:
|
|
9776
9640
|
ref = xp.asarray([])
|
9777
9641
|
xp_assert_equal(res, ref)
|
9778
9642
|
|
9643
|
+
@pytest.mark.filterwarnings(
|
9644
|
+
"ignore:overflow encountered in reduce:RuntimeWarning"
|
9645
|
+
) # Overflow occurs for float32 input
|
9779
9646
|
def test_dtype(self, xp):
|
9780
9647
|
max = xp.finfo(xp.float32).max
|
9781
9648
|
x_np = np.asarray([max, max/2], dtype=np.float32)
|
9782
9649
|
x_xp = xp.asarray(x_np)
|
9783
9650
|
|
9784
|
-
|
9785
|
-
|
9786
|
-
|
9787
|
-
|
9788
|
-
np.testing.assert_equal(ref, np.inf)
|
9789
|
-
xp_assert_close(res, xp.asarray(ref))
|
9651
|
+
res = _xp_var(x_xp)
|
9652
|
+
ref = np.var(x_np)
|
9653
|
+
np.testing.assert_equal(ref, np.inf)
|
9654
|
+
xp_assert_close(res, xp.asarray(ref))
|
9790
9655
|
|
9791
9656
|
# correct result is returned if `float64` is used
|
9792
9657
|
res = _xp_var(x_xp, dtype=xp.float64)
|
@@ -9799,7 +9664,6 @@ class TestXP_Var:
|
|
9799
9664
|
y = xp.arange(10.)
|
9800
9665
|
xp_assert_equal(_xp_var(x), _xp_var(y))
|
9801
9666
|
|
9802
|
-
@pytest.mark.skip_xp_backends('array_api_strict', reason='needs array-api#850')
|
9803
9667
|
def test_complex_gh22404(self, xp):
|
9804
9668
|
rng = np.random.default_rng(90359458245906)
|
9805
9669
|
x, y = rng.random((2, 20))
|
@@ -9808,7 +9672,6 @@ class TestXP_Var:
|
|
9808
9672
|
xp_assert_close(res, xp.asarray(ref), check_dtype=False)
|
9809
9673
|
|
9810
9674
|
|
9811
|
-
@array_api_compatible
|
9812
9675
|
def test_chk_asarray(xp):
|
9813
9676
|
rng = np.random.default_rng(2348923425434)
|
9814
9677
|
x0 = rng.random(size=(2, 3, 4))
|
@@ -9830,10 +9693,7 @@ def test_chk_asarray(xp):
|
|
9830
9693
|
assert_equal(axis_out, axis)
|
9831
9694
|
|
9832
9695
|
|
9833
|
-
@
|
9834
|
-
reason='These parameters *are* compatible with NumPy')
|
9835
|
-
@pytest.mark.usefixtures("skip_xp_backends")
|
9836
|
-
@array_api_compatible
|
9696
|
+
@skip_xp_backends('numpy', reason='These parameters *are* compatible with NumPy')
|
9837
9697
|
def test_axis_nan_policy_keepdims_nanpolicy(xp):
|
9838
9698
|
# this test does not need to be repeated for every function
|
9839
9699
|
# using the _axis_nan_policy decorator. The test is here
|