scipy 1.16.2__cp313-cp313-win_arm64.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 +161 -0
- scipy/__init__.py +150 -0
- scipy/_cyutility.cp313-win_arm64.lib +0 -0
- scipy/_cyutility.cp313-win_arm64.pyd +0 -0
- scipy/_distributor_init.py +18 -0
- scipy/_lib/__init__.py +14 -0
- scipy/_lib/_array_api.py +931 -0
- scipy/_lib/_array_api_compat_vendor.py +9 -0
- scipy/_lib/_array_api_no_0d.py +103 -0
- scipy/_lib/_bunch.py +229 -0
- scipy/_lib/_ccallback.py +251 -0
- scipy/_lib/_ccallback_c.cp313-win_arm64.lib +0 -0
- scipy/_lib/_ccallback_c.cp313-win_arm64.pyd +0 -0
- scipy/_lib/_disjoint_set.py +254 -0
- scipy/_lib/_docscrape.py +761 -0
- scipy/_lib/_elementwise_iterative_method.py +346 -0
- scipy/_lib/_fpumode.cp313-win_arm64.lib +0 -0
- scipy/_lib/_fpumode.cp313-win_arm64.pyd +0 -0
- scipy/_lib/_gcutils.py +105 -0
- scipy/_lib/_pep440.py +487 -0
- scipy/_lib/_sparse.py +41 -0
- scipy/_lib/_test_ccallback.cp313-win_arm64.lib +0 -0
- scipy/_lib/_test_ccallback.cp313-win_arm64.pyd +0 -0
- scipy/_lib/_test_deprecation_call.cp313-win_arm64.lib +0 -0
- scipy/_lib/_test_deprecation_call.cp313-win_arm64.pyd +0 -0
- scipy/_lib/_test_deprecation_def.cp313-win_arm64.lib +0 -0
- scipy/_lib/_test_deprecation_def.cp313-win_arm64.pyd +0 -0
- scipy/_lib/_testutils.py +373 -0
- scipy/_lib/_threadsafety.py +58 -0
- scipy/_lib/_tmpdirs.py +86 -0
- scipy/_lib/_uarray/LICENSE +29 -0
- scipy/_lib/_uarray/__init__.py +116 -0
- scipy/_lib/_uarray/_backend.py +707 -0
- scipy/_lib/_uarray/_uarray.cp313-win_arm64.lib +0 -0
- scipy/_lib/_uarray/_uarray.cp313-win_arm64.pyd +0 -0
- scipy/_lib/_util.py +1283 -0
- scipy/_lib/array_api_compat/__init__.py +22 -0
- scipy/_lib/array_api_compat/_internal.py +59 -0
- scipy/_lib/array_api_compat/common/__init__.py +1 -0
- scipy/_lib/array_api_compat/common/_aliases.py +727 -0
- scipy/_lib/array_api_compat/common/_fft.py +213 -0
- scipy/_lib/array_api_compat/common/_helpers.py +1058 -0
- scipy/_lib/array_api_compat/common/_linalg.py +232 -0
- scipy/_lib/array_api_compat/common/_typing.py +192 -0
- scipy/_lib/array_api_compat/cupy/__init__.py +13 -0
- scipy/_lib/array_api_compat/cupy/_aliases.py +156 -0
- scipy/_lib/array_api_compat/cupy/_info.py +336 -0
- scipy/_lib/array_api_compat/cupy/_typing.py +31 -0
- scipy/_lib/array_api_compat/cupy/fft.py +36 -0
- scipy/_lib/array_api_compat/cupy/linalg.py +49 -0
- scipy/_lib/array_api_compat/dask/__init__.py +0 -0
- scipy/_lib/array_api_compat/dask/array/__init__.py +12 -0
- scipy/_lib/array_api_compat/dask/array/_aliases.py +376 -0
- scipy/_lib/array_api_compat/dask/array/_info.py +416 -0
- scipy/_lib/array_api_compat/dask/array/fft.py +21 -0
- scipy/_lib/array_api_compat/dask/array/linalg.py +72 -0
- scipy/_lib/array_api_compat/numpy/__init__.py +28 -0
- scipy/_lib/array_api_compat/numpy/_aliases.py +190 -0
- scipy/_lib/array_api_compat/numpy/_info.py +366 -0
- scipy/_lib/array_api_compat/numpy/_typing.py +30 -0
- scipy/_lib/array_api_compat/numpy/fft.py +35 -0
- scipy/_lib/array_api_compat/numpy/linalg.py +143 -0
- scipy/_lib/array_api_compat/torch/__init__.py +22 -0
- scipy/_lib/array_api_compat/torch/_aliases.py +855 -0
- scipy/_lib/array_api_compat/torch/_info.py +369 -0
- scipy/_lib/array_api_compat/torch/_typing.py +3 -0
- scipy/_lib/array_api_compat/torch/fft.py +85 -0
- scipy/_lib/array_api_compat/torch/linalg.py +121 -0
- scipy/_lib/array_api_extra/__init__.py +38 -0
- 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/cobyqa/__init__.py +20 -0
- scipy/_lib/cobyqa/framework.py +1240 -0
- scipy/_lib/cobyqa/main.py +1506 -0
- scipy/_lib/cobyqa/models.py +1529 -0
- scipy/_lib/cobyqa/problem.py +1296 -0
- scipy/_lib/cobyqa/settings.py +132 -0
- scipy/_lib/cobyqa/subsolvers/__init__.py +14 -0
- scipy/_lib/cobyqa/subsolvers/geometry.py +387 -0
- scipy/_lib/cobyqa/subsolvers/optim.py +1203 -0
- scipy/_lib/cobyqa/utils/__init__.py +18 -0
- scipy/_lib/cobyqa/utils/exceptions.py +22 -0
- scipy/_lib/cobyqa/utils/math.py +77 -0
- scipy/_lib/cobyqa/utils/versions.py +67 -0
- scipy/_lib/decorator.py +399 -0
- scipy/_lib/deprecation.py +274 -0
- scipy/_lib/doccer.py +366 -0
- scipy/_lib/messagestream.cp313-win_arm64.lib +0 -0
- scipy/_lib/messagestream.cp313-win_arm64.pyd +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/__init__.py +0 -0
- scipy/_lib/tests/test__gcutils.py +110 -0
- scipy/_lib/tests/test__pep440.py +67 -0
- scipy/_lib/tests/test__testutils.py +32 -0
- scipy/_lib/tests/test__threadsafety.py +51 -0
- scipy/_lib/tests/test__util.py +641 -0
- scipy/_lib/tests/test_array_api.py +322 -0
- scipy/_lib/tests/test_bunch.py +169 -0
- scipy/_lib/tests/test_ccallback.py +196 -0
- scipy/_lib/tests/test_config.py +45 -0
- scipy/_lib/tests/test_deprecation.py +10 -0
- scipy/_lib/tests/test_doccer.py +143 -0
- scipy/_lib/tests/test_import_cycles.py +18 -0
- scipy/_lib/tests/test_public_api.py +482 -0
- scipy/_lib/tests/test_scipy_version.py +28 -0
- scipy/_lib/tests/test_tmpdirs.py +48 -0
- scipy/_lib/tests/test_warnings.py +137 -0
- scipy/_lib/uarray.py +31 -0
- scipy/cluster/__init__.py +31 -0
- scipy/cluster/_hierarchy.cp313-win_arm64.lib +0 -0
- scipy/cluster/_hierarchy.cp313-win_arm64.pyd +0 -0
- scipy/cluster/_optimal_leaf_ordering.cp313-win_arm64.lib +0 -0
- scipy/cluster/_optimal_leaf_ordering.cp313-win_arm64.pyd +0 -0
- scipy/cluster/_vq.cp313-win_arm64.lib +0 -0
- scipy/cluster/_vq.cp313-win_arm64.pyd +0 -0
- scipy/cluster/hierarchy.py +4348 -0
- scipy/cluster/tests/__init__.py +0 -0
- scipy/cluster/tests/hierarchy_test_data.py +145 -0
- scipy/cluster/tests/test_disjoint_set.py +202 -0
- scipy/cluster/tests/test_hierarchy.py +1238 -0
- scipy/cluster/tests/test_vq.py +434 -0
- scipy/cluster/vq.py +832 -0
- scipy/conftest.py +683 -0
- scipy/constants/__init__.py +358 -0
- scipy/constants/_codata.py +2266 -0
- scipy/constants/_constants.py +369 -0
- scipy/constants/codata.py +21 -0
- scipy/constants/constants.py +53 -0
- scipy/constants/tests/__init__.py +0 -0
- scipy/constants/tests/test_codata.py +78 -0
- scipy/constants/tests/test_constants.py +83 -0
- scipy/datasets/__init__.py +90 -0
- scipy/datasets/_download_all.py +71 -0
- scipy/datasets/_fetchers.py +225 -0
- scipy/datasets/_registry.py +26 -0
- scipy/datasets/_utils.py +81 -0
- scipy/datasets/tests/__init__.py +0 -0
- scipy/datasets/tests/test_data.py +128 -0
- scipy/differentiate/__init__.py +27 -0
- scipy/differentiate/_differentiate.py +1129 -0
- scipy/differentiate/tests/__init__.py +0 -0
- scipy/differentiate/tests/test_differentiate.py +694 -0
- scipy/fft/__init__.py +114 -0
- scipy/fft/_backend.py +196 -0
- scipy/fft/_basic.py +1650 -0
- scipy/fft/_basic_backend.py +197 -0
- scipy/fft/_debug_backends.py +22 -0
- scipy/fft/_fftlog.py +223 -0
- scipy/fft/_fftlog_backend.py +200 -0
- scipy/fft/_helper.py +348 -0
- scipy/fft/_pocketfft/LICENSE.md +25 -0
- scipy/fft/_pocketfft/__init__.py +9 -0
- scipy/fft/_pocketfft/basic.py +251 -0
- scipy/fft/_pocketfft/helper.py +249 -0
- scipy/fft/_pocketfft/pypocketfft.cp313-win_arm64.lib +0 -0
- scipy/fft/_pocketfft/pypocketfft.cp313-win_arm64.pyd +0 -0
- scipy/fft/_pocketfft/realtransforms.py +109 -0
- scipy/fft/_pocketfft/tests/__init__.py +0 -0
- scipy/fft/_pocketfft/tests/test_basic.py +1011 -0
- scipy/fft/_pocketfft/tests/test_real_transforms.py +505 -0
- scipy/fft/_realtransforms.py +706 -0
- scipy/fft/_realtransforms_backend.py +63 -0
- scipy/fft/tests/__init__.py +0 -0
- scipy/fft/tests/mock_backend.py +96 -0
- scipy/fft/tests/test_backend.py +98 -0
- scipy/fft/tests/test_basic.py +504 -0
- scipy/fft/tests/test_fftlog.py +215 -0
- scipy/fft/tests/test_helper.py +558 -0
- scipy/fft/tests/test_multithreading.py +84 -0
- scipy/fft/tests/test_real_transforms.py +247 -0
- scipy/fftpack/__init__.py +103 -0
- scipy/fftpack/_basic.py +428 -0
- scipy/fftpack/_helper.py +115 -0
- scipy/fftpack/_pseudo_diffs.py +554 -0
- scipy/fftpack/_realtransforms.py +598 -0
- scipy/fftpack/basic.py +20 -0
- scipy/fftpack/convolve.cp313-win_arm64.lib +0 -0
- scipy/fftpack/convolve.cp313-win_arm64.pyd +0 -0
- scipy/fftpack/helper.py +19 -0
- scipy/fftpack/pseudo_diffs.py +22 -0
- scipy/fftpack/realtransforms.py +19 -0
- scipy/fftpack/tests/__init__.py +0 -0
- scipy/fftpack/tests/fftw_double_ref.npz +0 -0
- scipy/fftpack/tests/fftw_longdouble_ref.npz +0 -0
- scipy/fftpack/tests/fftw_single_ref.npz +0 -0
- scipy/fftpack/tests/test.npz +0 -0
- scipy/fftpack/tests/test_basic.py +877 -0
- scipy/fftpack/tests/test_helper.py +54 -0
- scipy/fftpack/tests/test_import.py +33 -0
- scipy/fftpack/tests/test_pseudo_diffs.py +388 -0
- scipy/fftpack/tests/test_real_transforms.py +836 -0
- scipy/integrate/__init__.py +122 -0
- scipy/integrate/_bvp.py +1160 -0
- scipy/integrate/_cubature.py +729 -0
- scipy/integrate/_dop.cp313-win_arm64.lib +0 -0
- scipy/integrate/_dop.cp313-win_arm64.pyd +0 -0
- scipy/integrate/_ivp/__init__.py +8 -0
- scipy/integrate/_ivp/base.py +290 -0
- scipy/integrate/_ivp/bdf.py +478 -0
- scipy/integrate/_ivp/common.py +451 -0
- scipy/integrate/_ivp/dop853_coefficients.py +193 -0
- scipy/integrate/_ivp/ivp.py +755 -0
- scipy/integrate/_ivp/lsoda.py +224 -0
- scipy/integrate/_ivp/radau.py +572 -0
- scipy/integrate/_ivp/rk.py +601 -0
- scipy/integrate/_ivp/tests/__init__.py +0 -0
- scipy/integrate/_ivp/tests/test_ivp.py +1287 -0
- scipy/integrate/_ivp/tests/test_rk.py +37 -0
- scipy/integrate/_lebedev.py +5450 -0
- scipy/integrate/_lsoda.cp313-win_arm64.lib +0 -0
- scipy/integrate/_lsoda.cp313-win_arm64.pyd +0 -0
- scipy/integrate/_ode.py +1395 -0
- scipy/integrate/_odepack.cp313-win_arm64.lib +0 -0
- scipy/integrate/_odepack.cp313-win_arm64.pyd +0 -0
- scipy/integrate/_odepack_py.py +273 -0
- scipy/integrate/_quad_vec.py +674 -0
- scipy/integrate/_quadpack.cp313-win_arm64.lib +0 -0
- scipy/integrate/_quadpack.cp313-win_arm64.pyd +0 -0
- scipy/integrate/_quadpack_py.py +1283 -0
- scipy/integrate/_quadrature.py +1336 -0
- scipy/integrate/_rules/__init__.py +12 -0
- scipy/integrate/_rules/_base.py +518 -0
- scipy/integrate/_rules/_gauss_kronrod.py +202 -0
- scipy/integrate/_rules/_gauss_legendre.py +62 -0
- scipy/integrate/_rules/_genz_malik.py +210 -0
- scipy/integrate/_tanhsinh.py +1385 -0
- scipy/integrate/_test_multivariate.cp313-win_arm64.lib +0 -0
- scipy/integrate/_test_multivariate.cp313-win_arm64.pyd +0 -0
- scipy/integrate/_test_odeint_banded.cp313-win_arm64.lib +0 -0
- scipy/integrate/_test_odeint_banded.cp313-win_arm64.pyd +0 -0
- scipy/integrate/_vode.cp313-win_arm64.lib +0 -0
- scipy/integrate/_vode.cp313-win_arm64.pyd +0 -0
- scipy/integrate/dop.py +15 -0
- scipy/integrate/lsoda.py +15 -0
- scipy/integrate/odepack.py +17 -0
- scipy/integrate/quadpack.py +23 -0
- scipy/integrate/tests/__init__.py +0 -0
- scipy/integrate/tests/test__quad_vec.py +211 -0
- scipy/integrate/tests/test_banded_ode_solvers.py +305 -0
- scipy/integrate/tests/test_bvp.py +714 -0
- scipy/integrate/tests/test_cubature.py +1375 -0
- scipy/integrate/tests/test_integrate.py +840 -0
- scipy/integrate/tests/test_odeint_jac.py +74 -0
- scipy/integrate/tests/test_quadpack.py +680 -0
- scipy/integrate/tests/test_quadrature.py +730 -0
- scipy/integrate/tests/test_tanhsinh.py +1171 -0
- scipy/integrate/vode.py +15 -0
- scipy/interpolate/__init__.py +228 -0
- scipy/interpolate/_bary_rational.py +715 -0
- scipy/interpolate/_bsplines.py +2469 -0
- scipy/interpolate/_cubic.py +973 -0
- scipy/interpolate/_dfitpack.cp313-win_arm64.lib +0 -0
- scipy/interpolate/_dfitpack.cp313-win_arm64.pyd +0 -0
- scipy/interpolate/_dierckx.cp313-win_arm64.lib +0 -0
- scipy/interpolate/_dierckx.cp313-win_arm64.pyd +0 -0
- scipy/interpolate/_fitpack.cp313-win_arm64.lib +0 -0
- scipy/interpolate/_fitpack.cp313-win_arm64.pyd +0 -0
- scipy/interpolate/_fitpack2.py +2397 -0
- scipy/interpolate/_fitpack_impl.py +811 -0
- scipy/interpolate/_fitpack_py.py +898 -0
- scipy/interpolate/_fitpack_repro.py +996 -0
- scipy/interpolate/_interpnd.cp313-win_arm64.lib +0 -0
- scipy/interpolate/_interpnd.cp313-win_arm64.pyd +0 -0
- scipy/interpolate/_interpolate.py +2266 -0
- scipy/interpolate/_ndbspline.py +415 -0
- scipy/interpolate/_ndgriddata.py +329 -0
- scipy/interpolate/_pade.py +67 -0
- scipy/interpolate/_polyint.py +1025 -0
- scipy/interpolate/_ppoly.cp313-win_arm64.lib +0 -0
- scipy/interpolate/_ppoly.cp313-win_arm64.pyd +0 -0
- scipy/interpolate/_rbf.py +290 -0
- scipy/interpolate/_rbfinterp.py +550 -0
- scipy/interpolate/_rbfinterp_pythran.cp313-win_arm64.lib +0 -0
- scipy/interpolate/_rbfinterp_pythran.cp313-win_arm64.pyd +0 -0
- scipy/interpolate/_rgi.py +764 -0
- scipy/interpolate/_rgi_cython.cp313-win_arm64.lib +0 -0
- scipy/interpolate/_rgi_cython.cp313-win_arm64.pyd +0 -0
- scipy/interpolate/dfitpack.py +24 -0
- scipy/interpolate/fitpack.py +31 -0
- scipy/interpolate/fitpack2.py +29 -0
- scipy/interpolate/interpnd.py +24 -0
- scipy/interpolate/interpolate.py +30 -0
- scipy/interpolate/ndgriddata.py +23 -0
- scipy/interpolate/polyint.py +24 -0
- scipy/interpolate/rbf.py +18 -0
- scipy/interpolate/tests/__init__.py +0 -0
- scipy/interpolate/tests/data/bug-1310.npz +0 -0
- scipy/interpolate/tests/data/estimate_gradients_hang.npy +0 -0
- scipy/interpolate/tests/data/gcvspl.npz +0 -0
- scipy/interpolate/tests/test_bary_rational.py +368 -0
- scipy/interpolate/tests/test_bsplines.py +3754 -0
- scipy/interpolate/tests/test_fitpack.py +519 -0
- scipy/interpolate/tests/test_fitpack2.py +1431 -0
- scipy/interpolate/tests/test_gil.py +64 -0
- scipy/interpolate/tests/test_interpnd.py +452 -0
- scipy/interpolate/tests/test_interpolate.py +2630 -0
- scipy/interpolate/tests/test_ndgriddata.py +308 -0
- scipy/interpolate/tests/test_pade.py +107 -0
- scipy/interpolate/tests/test_polyint.py +972 -0
- scipy/interpolate/tests/test_rbf.py +246 -0
- scipy/interpolate/tests/test_rbfinterp.py +534 -0
- scipy/interpolate/tests/test_rgi.py +1151 -0
- scipy/io/__init__.py +116 -0
- scipy/io/_fast_matrix_market/__init__.py +600 -0
- scipy/io/_fast_matrix_market/_fmm_core.cp313-win_arm64.lib +0 -0
- scipy/io/_fast_matrix_market/_fmm_core.cp313-win_arm64.pyd +0 -0
- scipy/io/_fortran.py +354 -0
- scipy/io/_harwell_boeing/__init__.py +7 -0
- scipy/io/_harwell_boeing/_fortran_format_parser.py +316 -0
- scipy/io/_harwell_boeing/hb.py +571 -0
- scipy/io/_harwell_boeing/tests/__init__.py +0 -0
- scipy/io/_harwell_boeing/tests/test_fortran_format.py +74 -0
- scipy/io/_harwell_boeing/tests/test_hb.py +70 -0
- scipy/io/_idl.py +917 -0
- scipy/io/_mmio.py +968 -0
- scipy/io/_netcdf.py +1104 -0
- scipy/io/_test_fortran.cp313-win_arm64.lib +0 -0
- scipy/io/_test_fortran.cp313-win_arm64.pyd +0 -0
- scipy/io/arff/__init__.py +28 -0
- scipy/io/arff/_arffread.py +873 -0
- scipy/io/arff/arffread.py +19 -0
- scipy/io/arff/tests/__init__.py +0 -0
- scipy/io/arff/tests/data/iris.arff +225 -0
- scipy/io/arff/tests/data/missing.arff +8 -0
- scipy/io/arff/tests/data/nodata.arff +11 -0
- scipy/io/arff/tests/data/quoted_nominal.arff +13 -0
- scipy/io/arff/tests/data/quoted_nominal_spaces.arff +13 -0
- scipy/io/arff/tests/data/test1.arff +10 -0
- scipy/io/arff/tests/data/test10.arff +8 -0
- scipy/io/arff/tests/data/test11.arff +11 -0
- scipy/io/arff/tests/data/test2.arff +15 -0
- scipy/io/arff/tests/data/test3.arff +6 -0
- scipy/io/arff/tests/data/test4.arff +11 -0
- scipy/io/arff/tests/data/test5.arff +26 -0
- scipy/io/arff/tests/data/test6.arff +12 -0
- scipy/io/arff/tests/data/test7.arff +15 -0
- scipy/io/arff/tests/data/test8.arff +12 -0
- scipy/io/arff/tests/data/test9.arff +14 -0
- scipy/io/arff/tests/test_arffread.py +421 -0
- scipy/io/harwell_boeing.py +17 -0
- scipy/io/idl.py +17 -0
- scipy/io/matlab/__init__.py +66 -0
- scipy/io/matlab/_byteordercodes.py +75 -0
- scipy/io/matlab/_mio.py +375 -0
- scipy/io/matlab/_mio4.py +632 -0
- scipy/io/matlab/_mio5.py +901 -0
- scipy/io/matlab/_mio5_params.py +281 -0
- scipy/io/matlab/_mio5_utils.cp313-win_arm64.lib +0 -0
- scipy/io/matlab/_mio5_utils.cp313-win_arm64.pyd +0 -0
- scipy/io/matlab/_mio_utils.cp313-win_arm64.lib +0 -0
- scipy/io/matlab/_mio_utils.cp313-win_arm64.pyd +0 -0
- scipy/io/matlab/_miobase.py +435 -0
- scipy/io/matlab/_streams.cp313-win_arm64.lib +0 -0
- scipy/io/matlab/_streams.cp313-win_arm64.pyd +0 -0
- scipy/io/matlab/byteordercodes.py +17 -0
- scipy/io/matlab/mio.py +16 -0
- scipy/io/matlab/mio4.py +17 -0
- scipy/io/matlab/mio5.py +19 -0
- scipy/io/matlab/mio5_params.py +18 -0
- scipy/io/matlab/mio5_utils.py +17 -0
- scipy/io/matlab/mio_utils.py +17 -0
- scipy/io/matlab/miobase.py +16 -0
- scipy/io/matlab/streams.py +16 -0
- scipy/io/matlab/tests/__init__.py +0 -0
- scipy/io/matlab/tests/data/bad_miuint32.mat +0 -0
- scipy/io/matlab/tests/data/bad_miutf8_array_name.mat +0 -0
- scipy/io/matlab/tests/data/big_endian.mat +0 -0
- scipy/io/matlab/tests/data/broken_utf8.mat +0 -0
- scipy/io/matlab/tests/data/corrupted_zlib_checksum.mat +0 -0
- scipy/io/matlab/tests/data/corrupted_zlib_data.mat +0 -0
- scipy/io/matlab/tests/data/debigged_m4.mat +0 -0
- scipy/io/matlab/tests/data/japanese_utf8.txt +5 -0
- scipy/io/matlab/tests/data/little_endian.mat +0 -0
- scipy/io/matlab/tests/data/logical_sparse.mat +0 -0
- scipy/io/matlab/tests/data/malformed1.mat +0 -0
- scipy/io/matlab/tests/data/miuint32_for_miint32.mat +0 -0
- scipy/io/matlab/tests/data/miutf8_array_name.mat +0 -0
- scipy/io/matlab/tests/data/nasty_duplicate_fieldnames.mat +0 -0
- scipy/io/matlab/tests/data/one_by_zero_char.mat +0 -0
- scipy/io/matlab/tests/data/parabola.mat +0 -0
- scipy/io/matlab/tests/data/single_empty_string.mat +0 -0
- scipy/io/matlab/tests/data/some_functions.mat +0 -0
- scipy/io/matlab/tests/data/sqr.mat +0 -0
- scipy/io/matlab/tests/data/test3dmatrix_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/test3dmatrix_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/test3dmatrix_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/test3dmatrix_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/test_empty_struct.mat +0 -0
- scipy/io/matlab/tests/data/test_mat4_le_floats.mat +0 -0
- scipy/io/matlab/tests/data/test_skip_variable.mat +0 -0
- scipy/io/matlab/tests/data/testbool_8_WIN64.mat +0 -0
- scipy/io/matlab/tests/data/testcell_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testcell_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testcell_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testcell_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testcellnest_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testcellnest_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testcellnest_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testcellnest_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testcomplex_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testcomplex_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testcomplex_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testcomplex_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testcomplex_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testdouble_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testdouble_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testdouble_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testdouble_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testdouble_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testemptycell_5.3_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testemptycell_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testemptycell_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testemptycell_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testfunc_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testhdf5_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testmatrix_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testmatrix_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testmatrix_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testmatrix_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testmatrix_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testminus_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testminus_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testminus_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testminus_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testminus_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testmulti_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testmulti_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testmulti_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testobject_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testobject_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testobject_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testobject_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testonechar_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testonechar_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testonechar_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testonechar_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testonechar_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testscalarcell_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testsimplecell.mat +0 -0
- scipy/io/matlab/tests/data/testsparse_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testsparse_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testsparse_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testsparse_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testsparse_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testsparsecomplex_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testsparsecomplex_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testsparsecomplex_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testsparsecomplex_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testsparsecomplex_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testsparsefloat_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststring_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/teststring_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/teststring_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststring_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststring_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststringarray_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/teststringarray_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/teststringarray_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststringarray_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststringarray_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststruct_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/teststruct_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststruct_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststruct_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststructarr_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/teststructarr_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststructarr_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststructarr_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststructnest_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/teststructnest_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststructnest_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststructnest_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testunicode_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testunicode_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testvec_4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/test_byteordercodes.py +29 -0
- scipy/io/matlab/tests/test_mio.py +1399 -0
- scipy/io/matlab/tests/test_mio5_utils.py +179 -0
- scipy/io/matlab/tests/test_mio_funcs.py +51 -0
- scipy/io/matlab/tests/test_mio_utils.py +45 -0
- scipy/io/matlab/tests/test_miobase.py +32 -0
- scipy/io/matlab/tests/test_pathological.py +33 -0
- scipy/io/matlab/tests/test_streams.py +241 -0
- scipy/io/mmio.py +17 -0
- scipy/io/netcdf.py +17 -0
- scipy/io/tests/__init__.py +0 -0
- scipy/io/tests/data/Transparent Busy.ani +0 -0
- scipy/io/tests/data/array_float32_1d.sav +0 -0
- scipy/io/tests/data/array_float32_2d.sav +0 -0
- scipy/io/tests/data/array_float32_3d.sav +0 -0
- scipy/io/tests/data/array_float32_4d.sav +0 -0
- scipy/io/tests/data/array_float32_5d.sav +0 -0
- scipy/io/tests/data/array_float32_6d.sav +0 -0
- scipy/io/tests/data/array_float32_7d.sav +0 -0
- scipy/io/tests/data/array_float32_8d.sav +0 -0
- scipy/io/tests/data/array_float32_pointer_1d.sav +0 -0
- scipy/io/tests/data/array_float32_pointer_2d.sav +0 -0
- scipy/io/tests/data/array_float32_pointer_3d.sav +0 -0
- scipy/io/tests/data/array_float32_pointer_4d.sav +0 -0
- scipy/io/tests/data/array_float32_pointer_5d.sav +0 -0
- scipy/io/tests/data/array_float32_pointer_6d.sav +0 -0
- scipy/io/tests/data/array_float32_pointer_7d.sav +0 -0
- scipy/io/tests/data/array_float32_pointer_8d.sav +0 -0
- scipy/io/tests/data/example_1.nc +0 -0
- scipy/io/tests/data/example_2.nc +0 -0
- scipy/io/tests/data/example_3_maskedvals.nc +0 -0
- scipy/io/tests/data/fortran-3x3d-2i.dat +0 -0
- scipy/io/tests/data/fortran-mixed.dat +0 -0
- scipy/io/tests/data/fortran-sf8-11x1x10.dat +0 -0
- scipy/io/tests/data/fortran-sf8-15x10x22.dat +0 -0
- scipy/io/tests/data/fortran-sf8-1x1x1.dat +0 -0
- scipy/io/tests/data/fortran-sf8-1x1x5.dat +0 -0
- scipy/io/tests/data/fortran-sf8-1x1x7.dat +0 -0
- scipy/io/tests/data/fortran-sf8-1x3x5.dat +0 -0
- scipy/io/tests/data/fortran-si4-11x1x10.dat +0 -0
- scipy/io/tests/data/fortran-si4-15x10x22.dat +0 -0
- scipy/io/tests/data/fortran-si4-1x1x1.dat +0 -0
- scipy/io/tests/data/fortran-si4-1x1x5.dat +0 -0
- scipy/io/tests/data/fortran-si4-1x1x7.dat +0 -0
- scipy/io/tests/data/fortran-si4-1x3x5.dat +0 -0
- scipy/io/tests/data/invalid_pointer.sav +0 -0
- scipy/io/tests/data/null_pointer.sav +0 -0
- scipy/io/tests/data/scalar_byte.sav +0 -0
- scipy/io/tests/data/scalar_byte_descr.sav +0 -0
- scipy/io/tests/data/scalar_complex32.sav +0 -0
- scipy/io/tests/data/scalar_complex64.sav +0 -0
- scipy/io/tests/data/scalar_float32.sav +0 -0
- scipy/io/tests/data/scalar_float64.sav +0 -0
- scipy/io/tests/data/scalar_heap_pointer.sav +0 -0
- scipy/io/tests/data/scalar_int16.sav +0 -0
- scipy/io/tests/data/scalar_int32.sav +0 -0
- scipy/io/tests/data/scalar_int64.sav +0 -0
- scipy/io/tests/data/scalar_string.sav +0 -0
- scipy/io/tests/data/scalar_uint16.sav +0 -0
- scipy/io/tests/data/scalar_uint32.sav +0 -0
- scipy/io/tests/data/scalar_uint64.sav +0 -0
- scipy/io/tests/data/struct_arrays.sav +0 -0
- scipy/io/tests/data/struct_arrays_byte_idl80.sav +0 -0
- scipy/io/tests/data/struct_arrays_replicated.sav +0 -0
- scipy/io/tests/data/struct_arrays_replicated_3d.sav +0 -0
- scipy/io/tests/data/struct_inherit.sav +0 -0
- scipy/io/tests/data/struct_pointer_arrays.sav +0 -0
- scipy/io/tests/data/struct_pointer_arrays_replicated.sav +0 -0
- scipy/io/tests/data/struct_pointer_arrays_replicated_3d.sav +0 -0
- scipy/io/tests/data/struct_pointers.sav +0 -0
- scipy/io/tests/data/struct_pointers_replicated.sav +0 -0
- scipy/io/tests/data/struct_pointers_replicated_3d.sav +0 -0
- scipy/io/tests/data/struct_scalars.sav +0 -0
- scipy/io/tests/data/struct_scalars_replicated.sav +0 -0
- scipy/io/tests/data/struct_scalars_replicated_3d.sav +0 -0
- scipy/io/tests/data/test-1234Hz-le-1ch-10S-20bit-extra.wav +0 -0
- scipy/io/tests/data/test-44100Hz-2ch-32bit-float-be.wav +0 -0
- scipy/io/tests/data/test-44100Hz-2ch-32bit-float-le.wav +0 -0
- scipy/io/tests/data/test-44100Hz-be-1ch-4bytes.wav +0 -0
- scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-early-eof-no-data.wav +0 -0
- scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-early-eof.wav +0 -0
- scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-incomplete-chunk.wav +0 -0
- scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-rf64.wav +0 -0
- scipy/io/tests/data/test-44100Hz-le-1ch-4bytes.wav +0 -0
- scipy/io/tests/data/test-48000Hz-2ch-64bit-float-le-wavex.wav +0 -0
- scipy/io/tests/data/test-8000Hz-be-3ch-5S-24bit.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-1ch-1byte-ulaw.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-2ch-1byteu.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-3ch-5S-24bit-inconsistent.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-3ch-5S-24bit-rf64.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-3ch-5S-24bit.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-3ch-5S-36bit.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-3ch-5S-45bit.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-3ch-5S-53bit.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-3ch-5S-64bit.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-4ch-9S-12bit.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-5ch-9S-5bit.wav +0 -0
- scipy/io/tests/data/various_compressed.sav +0 -0
- scipy/io/tests/test_fortran.py +264 -0
- scipy/io/tests/test_idl.py +483 -0
- scipy/io/tests/test_mmio.py +831 -0
- scipy/io/tests/test_netcdf.py +550 -0
- scipy/io/tests/test_paths.py +93 -0
- scipy/io/tests/test_wavfile.py +501 -0
- scipy/io/wavfile.py +938 -0
- scipy/linalg/__init__.pxd +1 -0
- scipy/linalg/__init__.py +236 -0
- scipy/linalg/_basic.py +2146 -0
- scipy/linalg/_blas_subroutines.h +164 -0
- scipy/linalg/_cythonized_array_utils.cp313-win_arm64.lib +0 -0
- scipy/linalg/_cythonized_array_utils.cp313-win_arm64.pyd +0 -0
- scipy/linalg/_cythonized_array_utils.pxd +40 -0
- scipy/linalg/_cythonized_array_utils.pyi +16 -0
- scipy/linalg/_decomp.py +1645 -0
- scipy/linalg/_decomp_cholesky.py +413 -0
- scipy/linalg/_decomp_cossin.py +236 -0
- scipy/linalg/_decomp_interpolative.cp313-win_arm64.lib +0 -0
- scipy/linalg/_decomp_interpolative.cp313-win_arm64.pyd +0 -0
- scipy/linalg/_decomp_ldl.py +356 -0
- scipy/linalg/_decomp_lu.py +401 -0
- scipy/linalg/_decomp_lu_cython.cp313-win_arm64.lib +0 -0
- scipy/linalg/_decomp_lu_cython.cp313-win_arm64.pyd +0 -0
- scipy/linalg/_decomp_lu_cython.pyi +6 -0
- scipy/linalg/_decomp_polar.py +113 -0
- scipy/linalg/_decomp_qr.py +494 -0
- scipy/linalg/_decomp_qz.py +452 -0
- scipy/linalg/_decomp_schur.py +336 -0
- scipy/linalg/_decomp_svd.py +545 -0
- scipy/linalg/_decomp_update.cp313-win_arm64.lib +0 -0
- scipy/linalg/_decomp_update.cp313-win_arm64.pyd +0 -0
- scipy/linalg/_expm_frechet.py +417 -0
- scipy/linalg/_fblas.cp313-win_arm64.lib +0 -0
- scipy/linalg/_fblas.cp313-win_arm64.pyd +0 -0
- scipy/linalg/_flapack.cp313-win_arm64.lib +0 -0
- scipy/linalg/_flapack.cp313-win_arm64.pyd +0 -0
- scipy/linalg/_lapack_subroutines.h +1521 -0
- scipy/linalg/_linalg_pythran.cp313-win_arm64.lib +0 -0
- scipy/linalg/_linalg_pythran.cp313-win_arm64.pyd +0 -0
- scipy/linalg/_matfuncs.py +1050 -0
- scipy/linalg/_matfuncs_expm.cp313-win_arm64.lib +0 -0
- scipy/linalg/_matfuncs_expm.cp313-win_arm64.pyd +0 -0
- scipy/linalg/_matfuncs_expm.pyi +6 -0
- scipy/linalg/_matfuncs_inv_ssq.py +886 -0
- scipy/linalg/_matfuncs_schur_sqrtm.cp313-win_arm64.lib +0 -0
- scipy/linalg/_matfuncs_schur_sqrtm.cp313-win_arm64.pyd +0 -0
- scipy/linalg/_matfuncs_sqrtm.py +107 -0
- scipy/linalg/_matfuncs_sqrtm_triu.cp313-win_arm64.lib +0 -0
- scipy/linalg/_matfuncs_sqrtm_triu.cp313-win_arm64.pyd +0 -0
- scipy/linalg/_misc.py +191 -0
- scipy/linalg/_procrustes.py +113 -0
- scipy/linalg/_sketches.py +189 -0
- scipy/linalg/_solve_toeplitz.cp313-win_arm64.lib +0 -0
- scipy/linalg/_solve_toeplitz.cp313-win_arm64.pyd +0 -0
- scipy/linalg/_solvers.py +862 -0
- scipy/linalg/_special_matrices.py +1322 -0
- scipy/linalg/_testutils.py +65 -0
- scipy/linalg/basic.py +23 -0
- scipy/linalg/blas.py +495 -0
- scipy/linalg/cython_blas.cp313-win_arm64.lib +0 -0
- scipy/linalg/cython_blas.cp313-win_arm64.pyd +0 -0
- scipy/linalg/cython_blas.pxd +169 -0
- scipy/linalg/cython_blas.pyx +1432 -0
- scipy/linalg/cython_lapack.cp313-win_arm64.lib +0 -0
- scipy/linalg/cython_lapack.cp313-win_arm64.pyd +0 -0
- scipy/linalg/cython_lapack.pxd +1528 -0
- scipy/linalg/cython_lapack.pyx +12045 -0
- scipy/linalg/decomp.py +23 -0
- scipy/linalg/decomp_cholesky.py +21 -0
- scipy/linalg/decomp_lu.py +21 -0
- scipy/linalg/decomp_qr.py +20 -0
- scipy/linalg/decomp_schur.py +21 -0
- scipy/linalg/decomp_svd.py +21 -0
- scipy/linalg/interpolative.py +989 -0
- scipy/linalg/lapack.py +1081 -0
- scipy/linalg/matfuncs.py +23 -0
- scipy/linalg/misc.py +21 -0
- scipy/linalg/special_matrices.py +22 -0
- scipy/linalg/tests/__init__.py +0 -0
- scipy/linalg/tests/_cython_examples/extending.pyx +23 -0
- scipy/linalg/tests/_cython_examples/meson.build +34 -0
- scipy/linalg/tests/data/carex_15_data.npz +0 -0
- scipy/linalg/tests/data/carex_18_data.npz +0 -0
- scipy/linalg/tests/data/carex_19_data.npz +0 -0
- scipy/linalg/tests/data/carex_20_data.npz +0 -0
- scipy/linalg/tests/data/carex_6_data.npz +0 -0
- scipy/linalg/tests/data/gendare_20170120_data.npz +0 -0
- scipy/linalg/tests/test_basic.py +2074 -0
- scipy/linalg/tests/test_batch.py +588 -0
- scipy/linalg/tests/test_blas.py +1127 -0
- scipy/linalg/tests/test_cython_blas.py +118 -0
- scipy/linalg/tests/test_cython_lapack.py +22 -0
- scipy/linalg/tests/test_cythonized_array_utils.py +130 -0
- scipy/linalg/tests/test_decomp.py +3189 -0
- scipy/linalg/tests/test_decomp_cholesky.py +268 -0
- scipy/linalg/tests/test_decomp_cossin.py +314 -0
- scipy/linalg/tests/test_decomp_ldl.py +137 -0
- scipy/linalg/tests/test_decomp_lu.py +308 -0
- scipy/linalg/tests/test_decomp_polar.py +110 -0
- scipy/linalg/tests/test_decomp_update.py +1701 -0
- scipy/linalg/tests/test_extending.py +46 -0
- scipy/linalg/tests/test_fblas.py +607 -0
- scipy/linalg/tests/test_interpolative.py +232 -0
- scipy/linalg/tests/test_lapack.py +3620 -0
- scipy/linalg/tests/test_matfuncs.py +1125 -0
- scipy/linalg/tests/test_matmul_toeplitz.py +136 -0
- scipy/linalg/tests/test_procrustes.py +214 -0
- scipy/linalg/tests/test_sketches.py +118 -0
- scipy/linalg/tests/test_solve_toeplitz.py +150 -0
- scipy/linalg/tests/test_solvers.py +844 -0
- scipy/linalg/tests/test_special_matrices.py +636 -0
- scipy/misc/__init__.py +6 -0
- scipy/misc/common.py +6 -0
- scipy/misc/doccer.py +6 -0
- scipy/ndimage/__init__.py +174 -0
- scipy/ndimage/_ctest.cp313-win_arm64.lib +0 -0
- scipy/ndimage/_ctest.cp313-win_arm64.pyd +0 -0
- scipy/ndimage/_cytest.cp313-win_arm64.lib +0 -0
- scipy/ndimage/_cytest.cp313-win_arm64.pyd +0 -0
- scipy/ndimage/_delegators.py +303 -0
- scipy/ndimage/_filters.py +2422 -0
- scipy/ndimage/_fourier.py +306 -0
- scipy/ndimage/_interpolation.py +1033 -0
- scipy/ndimage/_measurements.py +1689 -0
- scipy/ndimage/_morphology.py +2634 -0
- scipy/ndimage/_nd_image.cp313-win_arm64.lib +0 -0
- scipy/ndimage/_nd_image.cp313-win_arm64.pyd +0 -0
- scipy/ndimage/_ndimage_api.py +16 -0
- scipy/ndimage/_ni_docstrings.py +214 -0
- scipy/ndimage/_ni_label.cp313-win_arm64.lib +0 -0
- scipy/ndimage/_ni_label.cp313-win_arm64.pyd +0 -0
- scipy/ndimage/_ni_support.py +139 -0
- scipy/ndimage/_rank_filter_1d.cp313-win_arm64.lib +0 -0
- scipy/ndimage/_rank_filter_1d.cp313-win_arm64.pyd +0 -0
- scipy/ndimage/_support_alternative_backends.py +84 -0
- scipy/ndimage/filters.py +27 -0
- scipy/ndimage/fourier.py +21 -0
- scipy/ndimage/interpolation.py +22 -0
- scipy/ndimage/measurements.py +24 -0
- scipy/ndimage/morphology.py +27 -0
- scipy/ndimage/tests/__init__.py +12 -0
- scipy/ndimage/tests/data/label_inputs.txt +21 -0
- scipy/ndimage/tests/data/label_results.txt +294 -0
- scipy/ndimage/tests/data/label_strels.txt +42 -0
- scipy/ndimage/tests/dots.png +0 -0
- scipy/ndimage/tests/test_c_api.py +102 -0
- scipy/ndimage/tests/test_datatypes.py +67 -0
- scipy/ndimage/tests/test_filters.py +3083 -0
- scipy/ndimage/tests/test_fourier.py +187 -0
- scipy/ndimage/tests/test_interpolation.py +1491 -0
- scipy/ndimage/tests/test_measurements.py +1592 -0
- scipy/ndimage/tests/test_morphology.py +2950 -0
- scipy/ndimage/tests/test_ni_support.py +78 -0
- scipy/ndimage/tests/test_splines.py +70 -0
- scipy/odr/__init__.py +131 -0
- scipy/odr/__odrpack.cp313-win_arm64.lib +0 -0
- scipy/odr/__odrpack.cp313-win_arm64.pyd +0 -0
- scipy/odr/_add_newdocs.py +34 -0
- scipy/odr/_models.py +315 -0
- scipy/odr/_odrpack.py +1154 -0
- scipy/odr/models.py +20 -0
- scipy/odr/odrpack.py +21 -0
- scipy/odr/tests/__init__.py +0 -0
- scipy/odr/tests/test_odr.py +607 -0
- scipy/optimize/__init__.pxd +1 -0
- scipy/optimize/__init__.py +460 -0
- scipy/optimize/_basinhopping.py +741 -0
- scipy/optimize/_bglu_dense.cp313-win_arm64.lib +0 -0
- scipy/optimize/_bglu_dense.cp313-win_arm64.pyd +0 -0
- scipy/optimize/_bracket.py +706 -0
- scipy/optimize/_chandrupatla.py +551 -0
- scipy/optimize/_cobyla_py.py +297 -0
- scipy/optimize/_cobyqa_py.py +72 -0
- scipy/optimize/_constraints.py +598 -0
- scipy/optimize/_dcsrch.py +728 -0
- scipy/optimize/_differentiable_functions.py +835 -0
- scipy/optimize/_differentialevolution.py +1970 -0
- scipy/optimize/_direct.cp313-win_arm64.lib +0 -0
- scipy/optimize/_direct.cp313-win_arm64.pyd +0 -0
- scipy/optimize/_direct_py.py +280 -0
- scipy/optimize/_dual_annealing.py +732 -0
- scipy/optimize/_elementwise.py +798 -0
- scipy/optimize/_group_columns.cp313-win_arm64.lib +0 -0
- scipy/optimize/_group_columns.cp313-win_arm64.pyd +0 -0
- scipy/optimize/_hessian_update_strategy.py +479 -0
- scipy/optimize/_highspy/__init__.py +0 -0
- scipy/optimize/_highspy/_core.cp313-win_arm64.lib +0 -0
- scipy/optimize/_highspy/_core.cp313-win_arm64.pyd +0 -0
- scipy/optimize/_highspy/_highs_options.cp313-win_arm64.lib +0 -0
- scipy/optimize/_highspy/_highs_options.cp313-win_arm64.pyd +0 -0
- scipy/optimize/_highspy/_highs_wrapper.py +338 -0
- scipy/optimize/_isotonic.py +157 -0
- scipy/optimize/_lbfgsb.cp313-win_arm64.lib +0 -0
- scipy/optimize/_lbfgsb.cp313-win_arm64.pyd +0 -0
- scipy/optimize/_lbfgsb_py.py +634 -0
- scipy/optimize/_linesearch.py +896 -0
- scipy/optimize/_linprog.py +733 -0
- scipy/optimize/_linprog_doc.py +1434 -0
- scipy/optimize/_linprog_highs.py +422 -0
- scipy/optimize/_linprog_ip.py +1141 -0
- scipy/optimize/_linprog_rs.py +572 -0
- scipy/optimize/_linprog_simplex.py +663 -0
- scipy/optimize/_linprog_util.py +1521 -0
- scipy/optimize/_lsap.cp313-win_arm64.lib +0 -0
- scipy/optimize/_lsap.cp313-win_arm64.pyd +0 -0
- scipy/optimize/_lsq/__init__.py +5 -0
- scipy/optimize/_lsq/bvls.py +183 -0
- scipy/optimize/_lsq/common.py +731 -0
- scipy/optimize/_lsq/dogbox.py +345 -0
- scipy/optimize/_lsq/givens_elimination.cp313-win_arm64.lib +0 -0
- scipy/optimize/_lsq/givens_elimination.cp313-win_arm64.pyd +0 -0
- scipy/optimize/_lsq/least_squares.py +1044 -0
- scipy/optimize/_lsq/lsq_linear.py +361 -0
- scipy/optimize/_lsq/trf.py +587 -0
- scipy/optimize/_lsq/trf_linear.py +249 -0
- scipy/optimize/_milp.py +394 -0
- scipy/optimize/_minimize.py +1199 -0
- scipy/optimize/_minpack.cp313-win_arm64.lib +0 -0
- scipy/optimize/_minpack.cp313-win_arm64.pyd +0 -0
- scipy/optimize/_minpack_py.py +1178 -0
- scipy/optimize/_moduleTNC.cp313-win_arm64.lib +0 -0
- scipy/optimize/_moduleTNC.cp313-win_arm64.pyd +0 -0
- scipy/optimize/_nnls.py +96 -0
- scipy/optimize/_nonlin.py +1634 -0
- scipy/optimize/_numdiff.py +963 -0
- scipy/optimize/_optimize.py +4169 -0
- scipy/optimize/_pava_pybind.cp313-win_arm64.lib +0 -0
- scipy/optimize/_pava_pybind.cp313-win_arm64.pyd +0 -0
- scipy/optimize/_qap.py +760 -0
- scipy/optimize/_remove_redundancy.py +522 -0
- scipy/optimize/_root.py +732 -0
- scipy/optimize/_root_scalar.py +538 -0
- scipy/optimize/_shgo.py +1606 -0
- scipy/optimize/_shgo_lib/__init__.py +0 -0
- scipy/optimize/_shgo_lib/_complex.py +1225 -0
- scipy/optimize/_shgo_lib/_vertex.py +460 -0
- scipy/optimize/_slsqp_py.py +603 -0
- scipy/optimize/_slsqplib.cp313-win_arm64.lib +0 -0
- scipy/optimize/_slsqplib.cp313-win_arm64.pyd +0 -0
- scipy/optimize/_spectral.py +260 -0
- scipy/optimize/_tnc.py +438 -0
- scipy/optimize/_trlib/__init__.py +12 -0
- scipy/optimize/_trlib/_trlib.cp313-win_arm64.lib +0 -0
- scipy/optimize/_trlib/_trlib.cp313-win_arm64.pyd +0 -0
- scipy/optimize/_trustregion.py +318 -0
- scipy/optimize/_trustregion_constr/__init__.py +6 -0
- scipy/optimize/_trustregion_constr/canonical_constraint.py +390 -0
- scipy/optimize/_trustregion_constr/equality_constrained_sqp.py +231 -0
- scipy/optimize/_trustregion_constr/minimize_trustregion_constr.py +584 -0
- scipy/optimize/_trustregion_constr/projections.py +411 -0
- scipy/optimize/_trustregion_constr/qp_subproblem.py +637 -0
- scipy/optimize/_trustregion_constr/report.py +49 -0
- scipy/optimize/_trustregion_constr/tests/__init__.py +0 -0
- scipy/optimize/_trustregion_constr/tests/test_canonical_constraint.py +296 -0
- scipy/optimize/_trustregion_constr/tests/test_nested_minimize.py +39 -0
- scipy/optimize/_trustregion_constr/tests/test_projections.py +214 -0
- scipy/optimize/_trustregion_constr/tests/test_qp_subproblem.py +645 -0
- scipy/optimize/_trustregion_constr/tests/test_report.py +34 -0
- scipy/optimize/_trustregion_constr/tr_interior_point.py +361 -0
- scipy/optimize/_trustregion_dogleg.py +122 -0
- scipy/optimize/_trustregion_exact.py +437 -0
- scipy/optimize/_trustregion_krylov.py +65 -0
- scipy/optimize/_trustregion_ncg.py +126 -0
- scipy/optimize/_tstutils.py +972 -0
- scipy/optimize/_zeros.cp313-win_arm64.lib +0 -0
- scipy/optimize/_zeros.cp313-win_arm64.pyd +0 -0
- scipy/optimize/_zeros_py.py +1475 -0
- scipy/optimize/cobyla.py +19 -0
- scipy/optimize/cython_optimize/__init__.py +133 -0
- scipy/optimize/cython_optimize/_zeros.cp313-win_arm64.lib +0 -0
- scipy/optimize/cython_optimize/_zeros.cp313-win_arm64.pyd +0 -0
- scipy/optimize/cython_optimize/_zeros.pxd +33 -0
- scipy/optimize/cython_optimize/c_zeros.pxd +26 -0
- scipy/optimize/cython_optimize.pxd +11 -0
- scipy/optimize/elementwise.py +38 -0
- scipy/optimize/lbfgsb.py +23 -0
- scipy/optimize/linesearch.py +18 -0
- scipy/optimize/minpack.py +27 -0
- scipy/optimize/minpack2.py +17 -0
- scipy/optimize/moduleTNC.py +19 -0
- scipy/optimize/nonlin.py +29 -0
- scipy/optimize/optimize.py +40 -0
- scipy/optimize/slsqp.py +22 -0
- scipy/optimize/tests/__init__.py +0 -0
- scipy/optimize/tests/_cython_examples/extending.pyx +43 -0
- scipy/optimize/tests/_cython_examples/meson.build +32 -0
- scipy/optimize/tests/test__basinhopping.py +535 -0
- scipy/optimize/tests/test__differential_evolution.py +1703 -0
- scipy/optimize/tests/test__dual_annealing.py +416 -0
- scipy/optimize/tests/test__linprog_clean_inputs.py +312 -0
- scipy/optimize/tests/test__numdiff.py +885 -0
- scipy/optimize/tests/test__remove_redundancy.py +228 -0
- scipy/optimize/tests/test__root.py +124 -0
- scipy/optimize/tests/test__shgo.py +1164 -0
- scipy/optimize/tests/test__spectral.py +226 -0
- scipy/optimize/tests/test_bracket.py +896 -0
- scipy/optimize/tests/test_chandrupatla.py +982 -0
- scipy/optimize/tests/test_cobyla.py +195 -0
- scipy/optimize/tests/test_cobyqa.py +252 -0
- scipy/optimize/tests/test_constraint_conversion.py +286 -0
- scipy/optimize/tests/test_constraints.py +255 -0
- scipy/optimize/tests/test_cython_optimize.py +92 -0
- scipy/optimize/tests/test_differentiable_functions.py +1025 -0
- scipy/optimize/tests/test_direct.py +321 -0
- scipy/optimize/tests/test_extending.py +28 -0
- scipy/optimize/tests/test_hessian_update_strategy.py +300 -0
- scipy/optimize/tests/test_isotonic_regression.py +167 -0
- scipy/optimize/tests/test_lbfgsb_hessinv.py +65 -0
- scipy/optimize/tests/test_lbfgsb_setulb.py +122 -0
- scipy/optimize/tests/test_least_squares.py +986 -0
- scipy/optimize/tests/test_linear_assignment.py +116 -0
- scipy/optimize/tests/test_linesearch.py +328 -0
- scipy/optimize/tests/test_linprog.py +2577 -0
- scipy/optimize/tests/test_lsq_common.py +297 -0
- scipy/optimize/tests/test_lsq_linear.py +287 -0
- scipy/optimize/tests/test_milp.py +459 -0
- scipy/optimize/tests/test_minimize_constrained.py +845 -0
- scipy/optimize/tests/test_minpack.py +1194 -0
- scipy/optimize/tests/test_nnls.py +469 -0
- scipy/optimize/tests/test_nonlin.py +572 -0
- scipy/optimize/tests/test_optimize.py +3344 -0
- scipy/optimize/tests/test_quadratic_assignment.py +455 -0
- scipy/optimize/tests/test_regression.py +40 -0
- scipy/optimize/tests/test_slsqp.py +645 -0
- scipy/optimize/tests/test_tnc.py +345 -0
- scipy/optimize/tests/test_trustregion.py +110 -0
- scipy/optimize/tests/test_trustregion_exact.py +351 -0
- scipy/optimize/tests/test_trustregion_krylov.py +170 -0
- scipy/optimize/tests/test_zeros.py +998 -0
- scipy/optimize/tnc.py +22 -0
- scipy/optimize/zeros.py +26 -0
- scipy/signal/__init__.py +316 -0
- scipy/signal/_arraytools.py +264 -0
- scipy/signal/_czt.py +575 -0
- scipy/signal/_delegators.py +568 -0
- scipy/signal/_filter_design.py +5893 -0
- scipy/signal/_fir_filter_design.py +1458 -0
- scipy/signal/_lti_conversion.py +534 -0
- scipy/signal/_ltisys.py +3546 -0
- scipy/signal/_max_len_seq.py +139 -0
- scipy/signal/_max_len_seq_inner.cp313-win_arm64.lib +0 -0
- scipy/signal/_max_len_seq_inner.cp313-win_arm64.pyd +0 -0
- scipy/signal/_peak_finding.py +1310 -0
- scipy/signal/_peak_finding_utils.cp313-win_arm64.lib +0 -0
- scipy/signal/_peak_finding_utils.cp313-win_arm64.pyd +0 -0
- scipy/signal/_polyutils.py +172 -0
- scipy/signal/_savitzky_golay.py +357 -0
- scipy/signal/_short_time_fft.py +2228 -0
- scipy/signal/_signal_api.py +30 -0
- scipy/signal/_signaltools.py +5309 -0
- scipy/signal/_sigtools.cp313-win_arm64.lib +0 -0
- scipy/signal/_sigtools.cp313-win_arm64.pyd +0 -0
- scipy/signal/_sosfilt.cp313-win_arm64.lib +0 -0
- scipy/signal/_sosfilt.cp313-win_arm64.pyd +0 -0
- scipy/signal/_spectral_py.py +2471 -0
- scipy/signal/_spline.cp313-win_arm64.lib +0 -0
- scipy/signal/_spline.cp313-win_arm64.pyd +0 -0
- scipy/signal/_spline.pyi +34 -0
- scipy/signal/_spline_filters.py +848 -0
- scipy/signal/_support_alternative_backends.py +73 -0
- scipy/signal/_upfirdn.py +219 -0
- scipy/signal/_upfirdn_apply.cp313-win_arm64.lib +0 -0
- scipy/signal/_upfirdn_apply.cp313-win_arm64.pyd +0 -0
- scipy/signal/_waveforms.py +687 -0
- scipy/signal/_wavelets.py +29 -0
- scipy/signal/bsplines.py +21 -0
- scipy/signal/filter_design.py +28 -0
- scipy/signal/fir_filter_design.py +21 -0
- scipy/signal/lti_conversion.py +20 -0
- scipy/signal/ltisys.py +25 -0
- scipy/signal/signaltools.py +27 -0
- scipy/signal/spectral.py +21 -0
- scipy/signal/spline.py +18 -0
- scipy/signal/tests/__init__.py +0 -0
- scipy/signal/tests/_scipy_spectral_test_shim.py +311 -0
- scipy/signal/tests/mpsig.py +122 -0
- scipy/signal/tests/test_array_tools.py +111 -0
- scipy/signal/tests/test_bsplines.py +365 -0
- scipy/signal/tests/test_cont2discrete.py +424 -0
- scipy/signal/tests/test_czt.py +221 -0
- scipy/signal/tests/test_dltisys.py +599 -0
- scipy/signal/tests/test_filter_design.py +4744 -0
- scipy/signal/tests/test_fir_filter_design.py +851 -0
- scipy/signal/tests/test_ltisys.py +1225 -0
- scipy/signal/tests/test_max_len_seq.py +71 -0
- scipy/signal/tests/test_peak_finding.py +915 -0
- scipy/signal/tests/test_result_type.py +51 -0
- scipy/signal/tests/test_savitzky_golay.py +363 -0
- scipy/signal/tests/test_short_time_fft.py +1107 -0
- scipy/signal/tests/test_signaltools.py +4735 -0
- scipy/signal/tests/test_spectral.py +2141 -0
- scipy/signal/tests/test_splines.py +427 -0
- scipy/signal/tests/test_upfirdn.py +322 -0
- scipy/signal/tests/test_waveforms.py +400 -0
- scipy/signal/tests/test_wavelets.py +59 -0
- scipy/signal/tests/test_windows.py +987 -0
- scipy/signal/waveforms.py +20 -0
- scipy/signal/wavelets.py +17 -0
- scipy/signal/windows/__init__.py +52 -0
- scipy/signal/windows/_windows.py +2513 -0
- scipy/signal/windows/windows.py +23 -0
- scipy/sparse/__init__.py +350 -0
- scipy/sparse/_base.py +1613 -0
- scipy/sparse/_bsr.py +880 -0
- scipy/sparse/_compressed.py +1328 -0
- scipy/sparse/_construct.py +1454 -0
- scipy/sparse/_coo.py +1581 -0
- scipy/sparse/_csc.py +367 -0
- scipy/sparse/_csparsetools.cp313-win_arm64.lib +0 -0
- scipy/sparse/_csparsetools.cp313-win_arm64.pyd +0 -0
- scipy/sparse/_csr.py +558 -0
- scipy/sparse/_data.py +569 -0
- scipy/sparse/_dia.py +677 -0
- scipy/sparse/_dok.py +669 -0
- scipy/sparse/_extract.py +178 -0
- scipy/sparse/_index.py +444 -0
- scipy/sparse/_lil.py +632 -0
- scipy/sparse/_matrix.py +169 -0
- scipy/sparse/_matrix_io.py +167 -0
- scipy/sparse/_sparsetools.cp313-win_arm64.lib +0 -0
- scipy/sparse/_sparsetools.cp313-win_arm64.pyd +0 -0
- scipy/sparse/_spfuncs.py +76 -0
- scipy/sparse/_sputils.py +632 -0
- scipy/sparse/base.py +24 -0
- scipy/sparse/bsr.py +22 -0
- scipy/sparse/compressed.py +20 -0
- scipy/sparse/construct.py +38 -0
- scipy/sparse/coo.py +23 -0
- scipy/sparse/csc.py +22 -0
- scipy/sparse/csgraph/__init__.py +210 -0
- scipy/sparse/csgraph/_flow.cp313-win_arm64.lib +0 -0
- scipy/sparse/csgraph/_flow.cp313-win_arm64.pyd +0 -0
- scipy/sparse/csgraph/_laplacian.py +563 -0
- scipy/sparse/csgraph/_matching.cp313-win_arm64.lib +0 -0
- scipy/sparse/csgraph/_matching.cp313-win_arm64.pyd +0 -0
- scipy/sparse/csgraph/_min_spanning_tree.cp313-win_arm64.lib +0 -0
- scipy/sparse/csgraph/_min_spanning_tree.cp313-win_arm64.pyd +0 -0
- scipy/sparse/csgraph/_reordering.cp313-win_arm64.lib +0 -0
- scipy/sparse/csgraph/_reordering.cp313-win_arm64.pyd +0 -0
- scipy/sparse/csgraph/_shortest_path.cp313-win_arm64.lib +0 -0
- scipy/sparse/csgraph/_shortest_path.cp313-win_arm64.pyd +0 -0
- scipy/sparse/csgraph/_tools.cp313-win_arm64.lib +0 -0
- scipy/sparse/csgraph/_tools.cp313-win_arm64.pyd +0 -0
- scipy/sparse/csgraph/_traversal.cp313-win_arm64.lib +0 -0
- scipy/sparse/csgraph/_traversal.cp313-win_arm64.pyd +0 -0
- scipy/sparse/csgraph/_validation.py +66 -0
- scipy/sparse/csgraph/tests/__init__.py +0 -0
- scipy/sparse/csgraph/tests/test_connected_components.py +119 -0
- scipy/sparse/csgraph/tests/test_conversions.py +61 -0
- scipy/sparse/csgraph/tests/test_flow.py +209 -0
- scipy/sparse/csgraph/tests/test_graph_laplacian.py +368 -0
- scipy/sparse/csgraph/tests/test_matching.py +307 -0
- scipy/sparse/csgraph/tests/test_pydata_sparse.py +197 -0
- scipy/sparse/csgraph/tests/test_reordering.py +70 -0
- scipy/sparse/csgraph/tests/test_shortest_path.py +540 -0
- scipy/sparse/csgraph/tests/test_spanning_tree.py +66 -0
- scipy/sparse/csgraph/tests/test_traversal.py +148 -0
- scipy/sparse/csr.py +22 -0
- scipy/sparse/data.py +18 -0
- scipy/sparse/dia.py +22 -0
- scipy/sparse/dok.py +22 -0
- scipy/sparse/extract.py +23 -0
- scipy/sparse/lil.py +22 -0
- scipy/sparse/linalg/__init__.py +148 -0
- scipy/sparse/linalg/_dsolve/__init__.py +71 -0
- scipy/sparse/linalg/_dsolve/_add_newdocs.py +147 -0
- scipy/sparse/linalg/_dsolve/_superlu.cp313-win_arm64.lib +0 -0
- scipy/sparse/linalg/_dsolve/_superlu.cp313-win_arm64.pyd +0 -0
- scipy/sparse/linalg/_dsolve/linsolve.py +882 -0
- scipy/sparse/linalg/_dsolve/tests/__init__.py +0 -0
- scipy/sparse/linalg/_dsolve/tests/test_linsolve.py +928 -0
- scipy/sparse/linalg/_eigen/__init__.py +22 -0
- scipy/sparse/linalg/_eigen/_svds.py +540 -0
- scipy/sparse/linalg/_eigen/_svds_doc.py +382 -0
- scipy/sparse/linalg/_eigen/arpack/COPYING +45 -0
- scipy/sparse/linalg/_eigen/arpack/__init__.py +20 -0
- scipy/sparse/linalg/_eigen/arpack/_arpack.cp313-win_arm64.lib +0 -0
- scipy/sparse/linalg/_eigen/arpack/_arpack.cp313-win_arm64.pyd +0 -0
- scipy/sparse/linalg/_eigen/arpack/arpack.py +1706 -0
- scipy/sparse/linalg/_eigen/arpack/tests/__init__.py +0 -0
- scipy/sparse/linalg/_eigen/arpack/tests/test_arpack.py +717 -0
- scipy/sparse/linalg/_eigen/lobpcg/__init__.py +16 -0
- scipy/sparse/linalg/_eigen/lobpcg/lobpcg.py +1110 -0
- scipy/sparse/linalg/_eigen/lobpcg/tests/__init__.py +0 -0
- scipy/sparse/linalg/_eigen/lobpcg/tests/test_lobpcg.py +725 -0
- scipy/sparse/linalg/_eigen/tests/__init__.py +0 -0
- scipy/sparse/linalg/_eigen/tests/test_svds.py +886 -0
- scipy/sparse/linalg/_expm_multiply.py +816 -0
- scipy/sparse/linalg/_interface.py +920 -0
- scipy/sparse/linalg/_isolve/__init__.py +20 -0
- scipy/sparse/linalg/_isolve/_gcrotmk.py +503 -0
- scipy/sparse/linalg/_isolve/iterative.py +1051 -0
- scipy/sparse/linalg/_isolve/lgmres.py +230 -0
- scipy/sparse/linalg/_isolve/lsmr.py +486 -0
- scipy/sparse/linalg/_isolve/lsqr.py +589 -0
- scipy/sparse/linalg/_isolve/minres.py +372 -0
- scipy/sparse/linalg/_isolve/tests/__init__.py +0 -0
- scipy/sparse/linalg/_isolve/tests/test_gcrotmk.py +183 -0
- scipy/sparse/linalg/_isolve/tests/test_iterative.py +809 -0
- scipy/sparse/linalg/_isolve/tests/test_lgmres.py +225 -0
- scipy/sparse/linalg/_isolve/tests/test_lsmr.py +185 -0
- scipy/sparse/linalg/_isolve/tests/test_lsqr.py +120 -0
- scipy/sparse/linalg/_isolve/tests/test_minres.py +97 -0
- scipy/sparse/linalg/_isolve/tests/test_utils.py +9 -0
- scipy/sparse/linalg/_isolve/tfqmr.py +179 -0
- scipy/sparse/linalg/_isolve/utils.py +121 -0
- scipy/sparse/linalg/_matfuncs.py +940 -0
- scipy/sparse/linalg/_norm.py +195 -0
- scipy/sparse/linalg/_onenormest.py +467 -0
- scipy/sparse/linalg/_propack/_cpropack.cp313-win_arm64.lib +0 -0
- scipy/sparse/linalg/_propack/_cpropack.cp313-win_arm64.pyd +0 -0
- scipy/sparse/linalg/_propack/_dpropack.cp313-win_arm64.lib +0 -0
- scipy/sparse/linalg/_propack/_dpropack.cp313-win_arm64.pyd +0 -0
- scipy/sparse/linalg/_propack/_spropack.cp313-win_arm64.lib +0 -0
- scipy/sparse/linalg/_propack/_spropack.cp313-win_arm64.pyd +0 -0
- scipy/sparse/linalg/_propack/_zpropack.cp313-win_arm64.lib +0 -0
- scipy/sparse/linalg/_propack/_zpropack.cp313-win_arm64.pyd +0 -0
- scipy/sparse/linalg/_special_sparse_arrays.py +949 -0
- scipy/sparse/linalg/_svdp.py +309 -0
- scipy/sparse/linalg/dsolve.py +22 -0
- scipy/sparse/linalg/eigen.py +21 -0
- scipy/sparse/linalg/interface.py +20 -0
- scipy/sparse/linalg/isolve.py +22 -0
- scipy/sparse/linalg/matfuncs.py +18 -0
- scipy/sparse/linalg/tests/__init__.py +0 -0
- scipy/sparse/linalg/tests/propack_test_data.npz +0 -0
- scipy/sparse/linalg/tests/test_expm_multiply.py +367 -0
- scipy/sparse/linalg/tests/test_interface.py +561 -0
- scipy/sparse/linalg/tests/test_matfuncs.py +592 -0
- scipy/sparse/linalg/tests/test_norm.py +154 -0
- scipy/sparse/linalg/tests/test_onenormest.py +252 -0
- scipy/sparse/linalg/tests/test_propack.py +165 -0
- scipy/sparse/linalg/tests/test_pydata_sparse.py +272 -0
- scipy/sparse/linalg/tests/test_special_sparse_arrays.py +337 -0
- scipy/sparse/sparsetools.py +17 -0
- scipy/sparse/spfuncs.py +17 -0
- scipy/sparse/sputils.py +17 -0
- scipy/sparse/tests/__init__.py +0 -0
- scipy/sparse/tests/data/csc_py2.npz +0 -0
- scipy/sparse/tests/data/csc_py3.npz +0 -0
- scipy/sparse/tests/test_arithmetic1d.py +341 -0
- scipy/sparse/tests/test_array_api.py +561 -0
- scipy/sparse/tests/test_base.py +5870 -0
- scipy/sparse/tests/test_common1d.py +447 -0
- scipy/sparse/tests/test_construct.py +872 -0
- scipy/sparse/tests/test_coo.py +1119 -0
- scipy/sparse/tests/test_csc.py +98 -0
- scipy/sparse/tests/test_csr.py +214 -0
- scipy/sparse/tests/test_dok.py +209 -0
- scipy/sparse/tests/test_extract.py +51 -0
- scipy/sparse/tests/test_indexing1d.py +603 -0
- scipy/sparse/tests/test_matrix_io.py +109 -0
- scipy/sparse/tests/test_minmax1d.py +128 -0
- scipy/sparse/tests/test_sparsetools.py +344 -0
- scipy/sparse/tests/test_spfuncs.py +97 -0
- scipy/sparse/tests/test_sputils.py +424 -0
- scipy/spatial/__init__.py +129 -0
- scipy/spatial/_ckdtree.cp313-win_arm64.lib +0 -0
- scipy/spatial/_ckdtree.cp313-win_arm64.pyd +0 -0
- scipy/spatial/_distance_pybind.cp313-win_arm64.lib +0 -0
- scipy/spatial/_distance_pybind.cp313-win_arm64.pyd +0 -0
- scipy/spatial/_distance_wrap.cp313-win_arm64.lib +0 -0
- scipy/spatial/_distance_wrap.cp313-win_arm64.pyd +0 -0
- scipy/spatial/_geometric_slerp.py +238 -0
- scipy/spatial/_hausdorff.cp313-win_arm64.lib +0 -0
- scipy/spatial/_hausdorff.cp313-win_arm64.pyd +0 -0
- scipy/spatial/_kdtree.py +920 -0
- scipy/spatial/_plotutils.py +274 -0
- scipy/spatial/_procrustes.py +132 -0
- scipy/spatial/_qhull.cp313-win_arm64.lib +0 -0
- scipy/spatial/_qhull.cp313-win_arm64.pyd +0 -0
- scipy/spatial/_qhull.pyi +213 -0
- scipy/spatial/_spherical_voronoi.py +341 -0
- scipy/spatial/_voronoi.cp313-win_arm64.lib +0 -0
- scipy/spatial/_voronoi.cp313-win_arm64.pyd +0 -0
- scipy/spatial/_voronoi.pyi +4 -0
- scipy/spatial/ckdtree.py +18 -0
- scipy/spatial/distance.py +3147 -0
- scipy/spatial/distance.pyi +210 -0
- scipy/spatial/kdtree.py +25 -0
- scipy/spatial/qhull.py +25 -0
- scipy/spatial/qhull_src/COPYING_QHULL.txt +39 -0
- scipy/spatial/tests/__init__.py +0 -0
- scipy/spatial/tests/data/cdist-X1.txt +10 -0
- scipy/spatial/tests/data/cdist-X2.txt +20 -0
- scipy/spatial/tests/data/degenerate_pointset.npz +0 -0
- scipy/spatial/tests/data/iris.txt +150 -0
- scipy/spatial/tests/data/pdist-boolean-inp.txt +20 -0
- scipy/spatial/tests/data/pdist-chebyshev-ml-iris.txt +1 -0
- scipy/spatial/tests/data/pdist-chebyshev-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-cityblock-ml-iris.txt +1 -0
- scipy/spatial/tests/data/pdist-cityblock-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-correlation-ml-iris.txt +1 -0
- scipy/spatial/tests/data/pdist-correlation-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-cosine-ml-iris.txt +1 -0
- scipy/spatial/tests/data/pdist-cosine-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-double-inp.txt +20 -0
- scipy/spatial/tests/data/pdist-euclidean-ml-iris.txt +1 -0
- scipy/spatial/tests/data/pdist-euclidean-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-hamming-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-jaccard-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-jensenshannon-ml-iris.txt +1 -0
- scipy/spatial/tests/data/pdist-jensenshannon-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-minkowski-3.2-ml-iris.txt +1 -0
- scipy/spatial/tests/data/pdist-minkowski-3.2-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-minkowski-5.8-ml-iris.txt +1 -0
- scipy/spatial/tests/data/pdist-seuclidean-ml-iris.txt +1 -0
- scipy/spatial/tests/data/pdist-seuclidean-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-spearman-ml.txt +1 -0
- scipy/spatial/tests/data/random-bool-data.txt +100 -0
- scipy/spatial/tests/data/random-double-data.txt +100 -0
- scipy/spatial/tests/data/random-int-data.txt +100 -0
- scipy/spatial/tests/data/random-uint-data.txt +100 -0
- scipy/spatial/tests/data/selfdual-4d-polytope.txt +27 -0
- scipy/spatial/tests/test__plotutils.py +91 -0
- scipy/spatial/tests/test__procrustes.py +116 -0
- scipy/spatial/tests/test_distance.py +2389 -0
- scipy/spatial/tests/test_hausdorff.py +199 -0
- scipy/spatial/tests/test_kdtree.py +1536 -0
- scipy/spatial/tests/test_qhull.py +1313 -0
- scipy/spatial/tests/test_slerp.py +417 -0
- scipy/spatial/tests/test_spherical_voronoi.py +358 -0
- scipy/spatial/transform/__init__.py +31 -0
- scipy/spatial/transform/_rigid_transform.cp313-win_arm64.lib +0 -0
- scipy/spatial/transform/_rigid_transform.cp313-win_arm64.pyd +0 -0
- scipy/spatial/transform/_rotation.cp313-win_arm64.lib +0 -0
- scipy/spatial/transform/_rotation.cp313-win_arm64.pyd +0 -0
- scipy/spatial/transform/_rotation_groups.py +140 -0
- scipy/spatial/transform/_rotation_spline.py +460 -0
- scipy/spatial/transform/rotation.py +21 -0
- scipy/spatial/transform/tests/__init__.py +0 -0
- scipy/spatial/transform/tests/test_rigid_transform.py +1221 -0
- scipy/spatial/transform/tests/test_rotation.py +2569 -0
- scipy/spatial/transform/tests/test_rotation_groups.py +169 -0
- scipy/spatial/transform/tests/test_rotation_spline.py +183 -0
- scipy/special/__init__.pxd +1 -0
- scipy/special/__init__.py +841 -0
- scipy/special/_add_newdocs.py +9961 -0
- scipy/special/_basic.py +3576 -0
- scipy/special/_comb.cp313-win_arm64.lib +0 -0
- scipy/special/_comb.cp313-win_arm64.pyd +0 -0
- scipy/special/_ellip_harm.py +214 -0
- scipy/special/_ellip_harm_2.cp313-win_arm64.lib +0 -0
- scipy/special/_ellip_harm_2.cp313-win_arm64.pyd +0 -0
- scipy/special/_gufuncs.cp313-win_arm64.lib +0 -0
- scipy/special/_gufuncs.cp313-win_arm64.pyd +0 -0
- scipy/special/_input_validation.py +17 -0
- scipy/special/_lambertw.py +149 -0
- scipy/special/_logsumexp.py +426 -0
- scipy/special/_mptestutils.py +453 -0
- scipy/special/_multiufuncs.py +610 -0
- scipy/special/_orthogonal.py +2592 -0
- scipy/special/_orthogonal.pyi +330 -0
- scipy/special/_precompute/__init__.py +0 -0
- scipy/special/_precompute/cosine_cdf.py +17 -0
- scipy/special/_precompute/expn_asy.py +54 -0
- scipy/special/_precompute/gammainc_asy.py +116 -0
- scipy/special/_precompute/gammainc_data.py +124 -0
- scipy/special/_precompute/hyp2f1_data.py +484 -0
- scipy/special/_precompute/lambertw.py +68 -0
- scipy/special/_precompute/loggamma.py +43 -0
- scipy/special/_precompute/struve_convergence.py +131 -0
- scipy/special/_precompute/utils.py +38 -0
- scipy/special/_precompute/wright_bessel.py +342 -0
- scipy/special/_precompute/wright_bessel_data.py +152 -0
- scipy/special/_precompute/wrightomega.py +41 -0
- scipy/special/_precompute/zetac.py +27 -0
- scipy/special/_sf_error.py +15 -0
- scipy/special/_specfun.cp313-win_arm64.lib +0 -0
- scipy/special/_specfun.cp313-win_arm64.pyd +0 -0
- scipy/special/_special_ufuncs.cp313-win_arm64.lib +0 -0
- scipy/special/_special_ufuncs.cp313-win_arm64.pyd +0 -0
- scipy/special/_spfun_stats.py +106 -0
- scipy/special/_spherical_bessel.py +397 -0
- scipy/special/_support_alternative_backends.py +295 -0
- scipy/special/_test_internal.cp313-win_arm64.lib +0 -0
- scipy/special/_test_internal.cp313-win_arm64.pyd +0 -0
- scipy/special/_test_internal.pyi +9 -0
- scipy/special/_testutils.py +321 -0
- scipy/special/_ufuncs.cp313-win_arm64.lib +0 -0
- scipy/special/_ufuncs.cp313-win_arm64.pyd +0 -0
- scipy/special/_ufuncs.pyi +522 -0
- scipy/special/_ufuncs.pyx +13173 -0
- scipy/special/_ufuncs_cxx.cp313-win_arm64.lib +0 -0
- scipy/special/_ufuncs_cxx.cp313-win_arm64.pyd +0 -0
- scipy/special/_ufuncs_cxx.pxd +142 -0
- scipy/special/_ufuncs_cxx.pyx +427 -0
- scipy/special/_ufuncs_cxx_defs.h +147 -0
- scipy/special/_ufuncs_defs.h +57 -0
- scipy/special/add_newdocs.py +15 -0
- scipy/special/basic.py +87 -0
- scipy/special/cython_special.cp313-win_arm64.lib +0 -0
- scipy/special/cython_special.cp313-win_arm64.pyd +0 -0
- scipy/special/cython_special.pxd +259 -0
- scipy/special/cython_special.pyi +3 -0
- scipy/special/orthogonal.py +45 -0
- scipy/special/sf_error.py +20 -0
- scipy/special/specfun.py +24 -0
- scipy/special/spfun_stats.py +17 -0
- scipy/special/tests/__init__.py +0 -0
- scipy/special/tests/_cython_examples/extending.pyx +12 -0
- scipy/special/tests/_cython_examples/meson.build +34 -0
- scipy/special/tests/data/__init__.py +0 -0
- scipy/special/tests/data/boost.npz +0 -0
- scipy/special/tests/data/gsl.npz +0 -0
- scipy/special/tests/data/local.npz +0 -0
- scipy/special/tests/test_basic.py +4815 -0
- scipy/special/tests/test_bdtr.py +112 -0
- scipy/special/tests/test_boost_ufuncs.py +64 -0
- scipy/special/tests/test_boxcox.py +125 -0
- scipy/special/tests/test_cdflib.py +712 -0
- scipy/special/tests/test_cdft_asymptotic.py +49 -0
- scipy/special/tests/test_cephes_intp_cast.py +29 -0
- scipy/special/tests/test_cosine_distr.py +83 -0
- scipy/special/tests/test_cython_special.py +363 -0
- scipy/special/tests/test_data.py +719 -0
- scipy/special/tests/test_dd.py +42 -0
- scipy/special/tests/test_digamma.py +45 -0
- scipy/special/tests/test_ellip_harm.py +278 -0
- scipy/special/tests/test_erfinv.py +89 -0
- scipy/special/tests/test_exponential_integrals.py +118 -0
- scipy/special/tests/test_extending.py +28 -0
- scipy/special/tests/test_faddeeva.py +85 -0
- scipy/special/tests/test_gamma.py +12 -0
- scipy/special/tests/test_gammainc.py +152 -0
- scipy/special/tests/test_hyp2f1.py +2566 -0
- scipy/special/tests/test_hypergeometric.py +234 -0
- scipy/special/tests/test_iv_ratio.py +249 -0
- scipy/special/tests/test_kolmogorov.py +491 -0
- scipy/special/tests/test_lambertw.py +109 -0
- scipy/special/tests/test_legendre.py +1518 -0
- scipy/special/tests/test_log1mexp.py +85 -0
- scipy/special/tests/test_loggamma.py +70 -0
- scipy/special/tests/test_logit.py +162 -0
- scipy/special/tests/test_logsumexp.py +469 -0
- scipy/special/tests/test_mpmath.py +2293 -0
- scipy/special/tests/test_nan_inputs.py +65 -0
- scipy/special/tests/test_ndtr.py +77 -0
- scipy/special/tests/test_ndtri_exp.py +94 -0
- scipy/special/tests/test_orthogonal.py +821 -0
- scipy/special/tests/test_orthogonal_eval.py +275 -0
- scipy/special/tests/test_owens_t.py +53 -0
- scipy/special/tests/test_pcf.py +24 -0
- scipy/special/tests/test_pdtr.py +48 -0
- scipy/special/tests/test_powm1.py +65 -0
- scipy/special/tests/test_precompute_expn_asy.py +24 -0
- scipy/special/tests/test_precompute_gammainc.py +108 -0
- scipy/special/tests/test_precompute_utils.py +36 -0
- scipy/special/tests/test_round.py +18 -0
- scipy/special/tests/test_sf_error.py +146 -0
- scipy/special/tests/test_sici.py +36 -0
- scipy/special/tests/test_specfun.py +48 -0
- scipy/special/tests/test_spence.py +32 -0
- scipy/special/tests/test_spfun_stats.py +61 -0
- scipy/special/tests/test_sph_harm.py +85 -0
- scipy/special/tests/test_spherical_bessel.py +400 -0
- scipy/special/tests/test_support_alternative_backends.py +248 -0
- scipy/special/tests/test_trig.py +72 -0
- scipy/special/tests/test_ufunc_signatures.py +46 -0
- scipy/special/tests/test_wright_bessel.py +205 -0
- scipy/special/tests/test_wrightomega.py +117 -0
- scipy/special/tests/test_zeta.py +301 -0
- scipy/stats/__init__.py +670 -0
- scipy/stats/_ansari_swilk_statistics.cp313-win_arm64.lib +0 -0
- scipy/stats/_ansari_swilk_statistics.cp313-win_arm64.pyd +0 -0
- scipy/stats/_axis_nan_policy.py +692 -0
- scipy/stats/_biasedurn.cp313-win_arm64.lib +0 -0
- scipy/stats/_biasedurn.cp313-win_arm64.pyd +0 -0
- scipy/stats/_biasedurn.pxd +27 -0
- scipy/stats/_binned_statistic.py +795 -0
- scipy/stats/_binomtest.py +375 -0
- scipy/stats/_bws_test.py +177 -0
- scipy/stats/_censored_data.py +459 -0
- scipy/stats/_common.py +5 -0
- scipy/stats/_constants.py +42 -0
- scipy/stats/_continued_fraction.py +387 -0
- scipy/stats/_continuous_distns.py +12486 -0
- scipy/stats/_correlation.py +210 -0
- scipy/stats/_covariance.py +636 -0
- scipy/stats/_crosstab.py +204 -0
- scipy/stats/_discrete_distns.py +2098 -0
- scipy/stats/_distn_infrastructure.py +4201 -0
- scipy/stats/_distr_params.py +299 -0
- scipy/stats/_distribution_infrastructure.py +5750 -0
- scipy/stats/_entropy.py +428 -0
- scipy/stats/_finite_differences.py +145 -0
- scipy/stats/_fit.py +1351 -0
- scipy/stats/_hypotests.py +2060 -0
- scipy/stats/_kde.py +732 -0
- scipy/stats/_ksstats.py +600 -0
- scipy/stats/_levy_stable/__init__.py +1231 -0
- scipy/stats/_levy_stable/levyst.cp313-win_arm64.lib +0 -0
- scipy/stats/_levy_stable/levyst.cp313-win_arm64.pyd +0 -0
- scipy/stats/_mannwhitneyu.py +492 -0
- scipy/stats/_mgc.py +550 -0
- scipy/stats/_morestats.py +4626 -0
- scipy/stats/_mstats_basic.py +3658 -0
- scipy/stats/_mstats_extras.py +521 -0
- scipy/stats/_multicomp.py +449 -0
- scipy/stats/_multivariate.py +7281 -0
- scipy/stats/_new_distributions.py +452 -0
- scipy/stats/_odds_ratio.py +466 -0
- scipy/stats/_page_trend_test.py +486 -0
- scipy/stats/_probability_distribution.py +1964 -0
- scipy/stats/_qmc.py +2956 -0
- scipy/stats/_qmc_cy.cp313-win_arm64.lib +0 -0
- scipy/stats/_qmc_cy.cp313-win_arm64.pyd +0 -0
- scipy/stats/_qmc_cy.pyi +54 -0
- scipy/stats/_qmvnt.py +454 -0
- scipy/stats/_qmvnt_cy.cp313-win_arm64.lib +0 -0
- scipy/stats/_qmvnt_cy.cp313-win_arm64.pyd +0 -0
- scipy/stats/_quantile.py +335 -0
- scipy/stats/_rcont/__init__.py +4 -0
- scipy/stats/_rcont/rcont.cp313-win_arm64.lib +0 -0
- scipy/stats/_rcont/rcont.cp313-win_arm64.pyd +0 -0
- scipy/stats/_relative_risk.py +263 -0
- scipy/stats/_resampling.py +2352 -0
- scipy/stats/_result_classes.py +40 -0
- scipy/stats/_sampling.py +1314 -0
- scipy/stats/_sensitivity_analysis.py +713 -0
- scipy/stats/_sobol.cp313-win_arm64.lib +0 -0
- scipy/stats/_sobol.cp313-win_arm64.pyd +0 -0
- scipy/stats/_sobol.pyi +54 -0
- scipy/stats/_sobol_direction_numbers.npz +0 -0
- scipy/stats/_stats.cp313-win_arm64.lib +0 -0
- scipy/stats/_stats.cp313-win_arm64.pyd +0 -0
- scipy/stats/_stats.pxd +10 -0
- scipy/stats/_stats_mstats_common.py +322 -0
- scipy/stats/_stats_py.py +11089 -0
- scipy/stats/_stats_pythran.cp313-win_arm64.lib +0 -0
- scipy/stats/_stats_pythran.cp313-win_arm64.pyd +0 -0
- scipy/stats/_survival.py +683 -0
- scipy/stats/_tukeylambda_stats.py +199 -0
- scipy/stats/_unuran/__init__.py +0 -0
- scipy/stats/_unuran/unuran_wrapper.cp313-win_arm64.lib +0 -0
- scipy/stats/_unuran/unuran_wrapper.cp313-win_arm64.pyd +0 -0
- scipy/stats/_unuran/unuran_wrapper.pyi +179 -0
- scipy/stats/_variation.py +126 -0
- scipy/stats/_warnings_errors.py +38 -0
- scipy/stats/_wilcoxon.py +265 -0
- scipy/stats/biasedurn.py +16 -0
- scipy/stats/contingency.py +521 -0
- scipy/stats/distributions.py +24 -0
- scipy/stats/kde.py +18 -0
- scipy/stats/morestats.py +27 -0
- scipy/stats/mstats.py +140 -0
- scipy/stats/mstats_basic.py +42 -0
- scipy/stats/mstats_extras.py +25 -0
- scipy/stats/mvn.py +17 -0
- scipy/stats/qmc.py +236 -0
- scipy/stats/sampling.py +73 -0
- scipy/stats/stats.py +41 -0
- scipy/stats/tests/__init__.py +0 -0
- scipy/stats/tests/common_tests.py +356 -0
- scipy/stats/tests/data/_mvt.py +171 -0
- scipy/stats/tests/data/fisher_exact_results_from_r.py +607 -0
- scipy/stats/tests/data/jf_skew_t_gamlss_pdf_data.npy +0 -0
- scipy/stats/tests/data/levy_stable/stable-Z1-cdf-sample-data.npy +0 -0
- scipy/stats/tests/data/levy_stable/stable-Z1-pdf-sample-data.npy +0 -0
- scipy/stats/tests/data/levy_stable/stable-loc-scale-sample-data.npy +0 -0
- scipy/stats/tests/data/nist_anova/AtmWtAg.dat +108 -0
- scipy/stats/tests/data/nist_anova/SiRstv.dat +85 -0
- scipy/stats/tests/data/nist_anova/SmLs01.dat +249 -0
- scipy/stats/tests/data/nist_anova/SmLs02.dat +1869 -0
- scipy/stats/tests/data/nist_anova/SmLs03.dat +18069 -0
- scipy/stats/tests/data/nist_anova/SmLs04.dat +249 -0
- scipy/stats/tests/data/nist_anova/SmLs05.dat +1869 -0
- scipy/stats/tests/data/nist_anova/SmLs06.dat +18069 -0
- scipy/stats/tests/data/nist_anova/SmLs07.dat +249 -0
- scipy/stats/tests/data/nist_anova/SmLs08.dat +1869 -0
- scipy/stats/tests/data/nist_anova/SmLs09.dat +18069 -0
- scipy/stats/tests/data/nist_linregress/Norris.dat +97 -0
- scipy/stats/tests/data/rel_breitwigner_pdf_sample_data_ROOT.npy +0 -0
- scipy/stats/tests/data/studentized_range_mpmath_ref.json +1499 -0
- scipy/stats/tests/test_axis_nan_policy.py +1388 -0
- scipy/stats/tests/test_binned_statistic.py +568 -0
- scipy/stats/tests/test_censored_data.py +152 -0
- scipy/stats/tests/test_contingency.py +294 -0
- scipy/stats/tests/test_continued_fraction.py +173 -0
- scipy/stats/tests/test_continuous.py +2198 -0
- scipy/stats/tests/test_continuous_basic.py +1053 -0
- scipy/stats/tests/test_continuous_fit_censored.py +683 -0
- scipy/stats/tests/test_correlation.py +80 -0
- scipy/stats/tests/test_crosstab.py +115 -0
- scipy/stats/tests/test_discrete_basic.py +580 -0
- scipy/stats/tests/test_discrete_distns.py +700 -0
- scipy/stats/tests/test_distributions.py +10413 -0
- scipy/stats/tests/test_entropy.py +322 -0
- scipy/stats/tests/test_fast_gen_inversion.py +435 -0
- scipy/stats/tests/test_fit.py +1090 -0
- scipy/stats/tests/test_hypotests.py +1991 -0
- scipy/stats/tests/test_kdeoth.py +676 -0
- scipy/stats/tests/test_marray.py +289 -0
- scipy/stats/tests/test_mgc.py +217 -0
- scipy/stats/tests/test_morestats.py +3259 -0
- scipy/stats/tests/test_mstats_basic.py +2071 -0
- scipy/stats/tests/test_mstats_extras.py +172 -0
- scipy/stats/tests/test_multicomp.py +405 -0
- scipy/stats/tests/test_multivariate.py +4381 -0
- scipy/stats/tests/test_odds_ratio.py +148 -0
- scipy/stats/tests/test_qmc.py +1492 -0
- scipy/stats/tests/test_quantile.py +199 -0
- scipy/stats/tests/test_rank.py +345 -0
- scipy/stats/tests/test_relative_risk.py +95 -0
- scipy/stats/tests/test_resampling.py +2000 -0
- scipy/stats/tests/test_sampling.py +1450 -0
- scipy/stats/tests/test_sensitivity_analysis.py +310 -0
- scipy/stats/tests/test_stats.py +9707 -0
- scipy/stats/tests/test_survival.py +466 -0
- scipy/stats/tests/test_tukeylambda_stats.py +85 -0
- scipy/stats/tests/test_variation.py +216 -0
- scipy/version.py +12 -0
- scipy-1.16.2.dist-info/DELVEWHEEL +2 -0
- scipy-1.16.2.dist-info/LICENSE.txt +912 -0
- scipy-1.16.2.dist-info/METADATA +1061 -0
- scipy-1.16.2.dist-info/RECORD +1530 -0
- scipy-1.16.2.dist-info/WHEEL +4 -0
- scipy.libs/msvcp140-5f1c5dd31916990d94181e07bc3afb32.dll +0 -0
- scipy.libs/scipy_openblas-f3ac85b1f412f7e86514c923dc4058d1.dll +0 -0
@@ -0,0 +1,4348 @@
|
|
1
|
+
"""
|
2
|
+
Hierarchical clustering (:mod:`scipy.cluster.hierarchy`)
|
3
|
+
========================================================
|
4
|
+
|
5
|
+
.. currentmodule:: scipy.cluster.hierarchy
|
6
|
+
|
7
|
+
These functions cut hierarchical clusterings into flat clusterings
|
8
|
+
or find the roots of the forest formed by a cut by providing the flat
|
9
|
+
cluster ids of each observation.
|
10
|
+
|
11
|
+
.. autosummary::
|
12
|
+
:toctree: generated/
|
13
|
+
|
14
|
+
fcluster
|
15
|
+
fclusterdata
|
16
|
+
leaders
|
17
|
+
|
18
|
+
These are routines for agglomerative clustering.
|
19
|
+
|
20
|
+
.. autosummary::
|
21
|
+
:toctree: generated/
|
22
|
+
|
23
|
+
linkage
|
24
|
+
single
|
25
|
+
complete
|
26
|
+
average
|
27
|
+
weighted
|
28
|
+
centroid
|
29
|
+
median
|
30
|
+
ward
|
31
|
+
|
32
|
+
These routines compute statistics on hierarchies.
|
33
|
+
|
34
|
+
.. autosummary::
|
35
|
+
:toctree: generated/
|
36
|
+
|
37
|
+
cophenet
|
38
|
+
from_mlab_linkage
|
39
|
+
inconsistent
|
40
|
+
maxinconsts
|
41
|
+
maxdists
|
42
|
+
maxRstat
|
43
|
+
to_mlab_linkage
|
44
|
+
|
45
|
+
Routines for visualizing flat clusters.
|
46
|
+
|
47
|
+
.. autosummary::
|
48
|
+
:toctree: generated/
|
49
|
+
|
50
|
+
dendrogram
|
51
|
+
|
52
|
+
These are data structures and routines for representing hierarchies as
|
53
|
+
tree objects.
|
54
|
+
|
55
|
+
.. autosummary::
|
56
|
+
:toctree: generated/
|
57
|
+
|
58
|
+
ClusterNode
|
59
|
+
leaves_list
|
60
|
+
to_tree
|
61
|
+
cut_tree
|
62
|
+
optimal_leaf_ordering
|
63
|
+
|
64
|
+
These are predicates for checking the validity of linkage and
|
65
|
+
inconsistency matrices as well as for checking isomorphism of two
|
66
|
+
flat cluster assignments.
|
67
|
+
|
68
|
+
.. autosummary::
|
69
|
+
:toctree: generated/
|
70
|
+
|
71
|
+
is_valid_im
|
72
|
+
is_valid_linkage
|
73
|
+
is_isomorphic
|
74
|
+
is_monotonic
|
75
|
+
correspond
|
76
|
+
num_obs_linkage
|
77
|
+
|
78
|
+
Utility routines for plotting:
|
79
|
+
|
80
|
+
.. autosummary::
|
81
|
+
:toctree: generated/
|
82
|
+
|
83
|
+
set_link_color_palette
|
84
|
+
|
85
|
+
Utility classes:
|
86
|
+
|
87
|
+
.. autosummary::
|
88
|
+
:toctree: generated/
|
89
|
+
|
90
|
+
DisjointSet -- data structure for incremental connectivity queries
|
91
|
+
|
92
|
+
"""
|
93
|
+
# Copyright (C) Damian Eads, 2007-2008. New BSD License.
|
94
|
+
|
95
|
+
# hierarchy.py (derived from cluster.py, http://scipy-cluster.googlecode.com)
|
96
|
+
#
|
97
|
+
# Author: Damian Eads
|
98
|
+
# Date: September 22, 2007
|
99
|
+
#
|
100
|
+
# Copyright (c) 2007, 2008, Damian Eads
|
101
|
+
#
|
102
|
+
# All rights reserved.
|
103
|
+
#
|
104
|
+
# Redistribution and use in source and binary forms, with or without
|
105
|
+
# modification, are permitted provided that the following conditions
|
106
|
+
# are met:
|
107
|
+
# - Redistributions of source code must retain the above
|
108
|
+
# copyright notice, this list of conditions and the
|
109
|
+
# following disclaimer.
|
110
|
+
# - Redistributions in binary form must reproduce the above copyright
|
111
|
+
# notice, this list of conditions and the following disclaimer
|
112
|
+
# in the documentation and/or other materials provided with the
|
113
|
+
# distribution.
|
114
|
+
# - Neither the name of the author nor the names of its
|
115
|
+
# contributors may be used to endorse or promote products derived
|
116
|
+
# from this software without specific prior written permission.
|
117
|
+
#
|
118
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
119
|
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
120
|
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
121
|
+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
122
|
+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
123
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
124
|
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
125
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
126
|
+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
127
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
128
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
129
|
+
|
130
|
+
import warnings
|
131
|
+
import bisect
|
132
|
+
from collections import deque
|
133
|
+
|
134
|
+
import numpy as np
|
135
|
+
from . import _hierarchy, _optimal_leaf_ordering
|
136
|
+
import scipy.spatial.distance as distance
|
137
|
+
from scipy._lib._array_api import (_asarray, array_namespace, is_dask,
|
138
|
+
is_lazy_array, xp_capabilities, xp_copy)
|
139
|
+
from scipy._lib._disjoint_set import DisjointSet
|
140
|
+
import scipy._lib.array_api_extra as xpx
|
141
|
+
|
142
|
+
|
143
|
+
_LINKAGE_METHODS = {'single': 0, 'complete': 1, 'average': 2, 'centroid': 3,
|
144
|
+
'median': 4, 'ward': 5, 'weighted': 6}
|
145
|
+
_EUCLIDEAN_METHODS = ('centroid', 'median', 'ward')
|
146
|
+
|
147
|
+
__all__ = ['ClusterNode', 'DisjointSet', 'average', 'centroid', 'complete',
|
148
|
+
'cophenet', 'correspond', 'cut_tree', 'dendrogram', 'fcluster',
|
149
|
+
'fclusterdata', 'from_mlab_linkage', 'inconsistent',
|
150
|
+
'is_isomorphic', 'is_monotonic', 'is_valid_im', 'is_valid_linkage',
|
151
|
+
'leaders', 'leaves_list', 'linkage', 'maxRstat', 'maxdists',
|
152
|
+
'maxinconsts', 'median', 'num_obs_linkage', 'optimal_leaf_ordering',
|
153
|
+
'set_link_color_palette', 'single', 'to_mlab_linkage', 'to_tree',
|
154
|
+
'ward', 'weighted']
|
155
|
+
|
156
|
+
|
157
|
+
class ClusterWarning(UserWarning):
|
158
|
+
pass
|
159
|
+
|
160
|
+
|
161
|
+
def _warning(s):
|
162
|
+
warnings.warn(f'scipy.cluster: {s}', ClusterWarning, stacklevel=3)
|
163
|
+
|
164
|
+
|
165
|
+
def int_floor(arr, xp):
|
166
|
+
# array_api_strict is strict about not allowing `int()` on a float array.
|
167
|
+
# That's typically not needed, here it is - so explicitly convert
|
168
|
+
return int(xp.asarray(arr, dtype=xp.int64))
|
169
|
+
|
170
|
+
|
171
|
+
lazy_cython = xp_capabilities(
|
172
|
+
cpu_only=True, reason="Cython code",
|
173
|
+
warnings=[("dask.array", "merges chunks")])
|
174
|
+
|
175
|
+
|
176
|
+
@lazy_cython
|
177
|
+
def single(y):
|
178
|
+
"""
|
179
|
+
Perform single/min/nearest linkage on the condensed distance matrix ``y``.
|
180
|
+
|
181
|
+
Parameters
|
182
|
+
----------
|
183
|
+
y : ndarray
|
184
|
+
The upper triangular of the distance matrix. The result of
|
185
|
+
``pdist`` is returned in this form.
|
186
|
+
|
187
|
+
Returns
|
188
|
+
-------
|
189
|
+
Z : ndarray
|
190
|
+
The linkage matrix.
|
191
|
+
|
192
|
+
See Also
|
193
|
+
--------
|
194
|
+
linkage : for advanced creation of hierarchical clusterings.
|
195
|
+
scipy.spatial.distance.pdist : pairwise distance metrics
|
196
|
+
|
197
|
+
Examples
|
198
|
+
--------
|
199
|
+
>>> from scipy.cluster.hierarchy import single, fcluster
|
200
|
+
>>> from scipy.spatial.distance import pdist
|
201
|
+
|
202
|
+
First, we need a toy dataset to play with::
|
203
|
+
|
204
|
+
x x x x
|
205
|
+
x x
|
206
|
+
|
207
|
+
x x
|
208
|
+
x x x x
|
209
|
+
|
210
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
211
|
+
... [0, 4], [0, 3], [1, 4],
|
212
|
+
... [4, 0], [3, 0], [4, 1],
|
213
|
+
... [4, 4], [3, 4], [4, 3]]
|
214
|
+
|
215
|
+
Then, we get a condensed distance matrix from this dataset:
|
216
|
+
|
217
|
+
>>> y = pdist(X)
|
218
|
+
|
219
|
+
Finally, we can perform the clustering:
|
220
|
+
|
221
|
+
>>> Z = single(y)
|
222
|
+
>>> Z
|
223
|
+
array([[ 0., 1., 1., 2.],
|
224
|
+
[ 2., 12., 1., 3.],
|
225
|
+
[ 3., 4., 1., 2.],
|
226
|
+
[ 5., 14., 1., 3.],
|
227
|
+
[ 6., 7., 1., 2.],
|
228
|
+
[ 8., 16., 1., 3.],
|
229
|
+
[ 9., 10., 1., 2.],
|
230
|
+
[11., 18., 1., 3.],
|
231
|
+
[13., 15., 2., 6.],
|
232
|
+
[17., 20., 2., 9.],
|
233
|
+
[19., 21., 2., 12.]])
|
234
|
+
|
235
|
+
The linkage matrix ``Z`` represents a dendrogram - see
|
236
|
+
`scipy.cluster.hierarchy.linkage` for a detailed explanation of its
|
237
|
+
contents.
|
238
|
+
|
239
|
+
We can use `scipy.cluster.hierarchy.fcluster` to see to which cluster
|
240
|
+
each initial point would belong given a distance threshold:
|
241
|
+
|
242
|
+
>>> fcluster(Z, 0.9, criterion='distance')
|
243
|
+
array([ 7, 8, 9, 10, 11, 12, 4, 5, 6, 1, 2, 3], dtype=int32)
|
244
|
+
>>> fcluster(Z, 1, criterion='distance')
|
245
|
+
array([3, 3, 3, 4, 4, 4, 2, 2, 2, 1, 1, 1], dtype=int32)
|
246
|
+
>>> fcluster(Z, 2, criterion='distance')
|
247
|
+
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int32)
|
248
|
+
|
249
|
+
Also, `scipy.cluster.hierarchy.dendrogram` can be used to generate a
|
250
|
+
plot of the dendrogram.
|
251
|
+
"""
|
252
|
+
return linkage(y, method='single', metric='euclidean')
|
253
|
+
|
254
|
+
|
255
|
+
@lazy_cython
|
256
|
+
def complete(y):
|
257
|
+
"""
|
258
|
+
Perform complete/max/farthest point linkage on a condensed distance matrix.
|
259
|
+
|
260
|
+
Parameters
|
261
|
+
----------
|
262
|
+
y : ndarray
|
263
|
+
The upper triangular of the distance matrix. The result of
|
264
|
+
``pdist`` is returned in this form.
|
265
|
+
|
266
|
+
Returns
|
267
|
+
-------
|
268
|
+
Z : ndarray
|
269
|
+
A linkage matrix containing the hierarchical clustering. See
|
270
|
+
the `linkage` function documentation for more information
|
271
|
+
on its structure.
|
272
|
+
|
273
|
+
See Also
|
274
|
+
--------
|
275
|
+
linkage : for advanced creation of hierarchical clusterings.
|
276
|
+
scipy.spatial.distance.pdist : pairwise distance metrics
|
277
|
+
|
278
|
+
Examples
|
279
|
+
--------
|
280
|
+
>>> from scipy.cluster.hierarchy import complete, fcluster
|
281
|
+
>>> from scipy.spatial.distance import pdist
|
282
|
+
|
283
|
+
First, we need a toy dataset to play with::
|
284
|
+
|
285
|
+
x x x x
|
286
|
+
x x
|
287
|
+
|
288
|
+
x x
|
289
|
+
x x x x
|
290
|
+
|
291
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
292
|
+
... [0, 4], [0, 3], [1, 4],
|
293
|
+
... [4, 0], [3, 0], [4, 1],
|
294
|
+
... [4, 4], [3, 4], [4, 3]]
|
295
|
+
|
296
|
+
Then, we get a condensed distance matrix from this dataset:
|
297
|
+
|
298
|
+
>>> y = pdist(X)
|
299
|
+
|
300
|
+
Finally, we can perform the clustering:
|
301
|
+
|
302
|
+
>>> Z = complete(y)
|
303
|
+
>>> Z
|
304
|
+
array([[ 0. , 1. , 1. , 2. ],
|
305
|
+
[ 3. , 4. , 1. , 2. ],
|
306
|
+
[ 6. , 7. , 1. , 2. ],
|
307
|
+
[ 9. , 10. , 1. , 2. ],
|
308
|
+
[ 2. , 12. , 1.41421356, 3. ],
|
309
|
+
[ 5. , 13. , 1.41421356, 3. ],
|
310
|
+
[ 8. , 14. , 1.41421356, 3. ],
|
311
|
+
[11. , 15. , 1.41421356, 3. ],
|
312
|
+
[16. , 17. , 4.12310563, 6. ],
|
313
|
+
[18. , 19. , 4.12310563, 6. ],
|
314
|
+
[20. , 21. , 5.65685425, 12. ]])
|
315
|
+
|
316
|
+
The linkage matrix ``Z`` represents a dendrogram - see
|
317
|
+
`scipy.cluster.hierarchy.linkage` for a detailed explanation of its
|
318
|
+
contents.
|
319
|
+
|
320
|
+
We can use `scipy.cluster.hierarchy.fcluster` to see to which cluster
|
321
|
+
each initial point would belong given a distance threshold:
|
322
|
+
|
323
|
+
>>> fcluster(Z, 0.9, criterion='distance')
|
324
|
+
array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], dtype=int32)
|
325
|
+
>>> fcluster(Z, 1.5, criterion='distance')
|
326
|
+
array([1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4], dtype=int32)
|
327
|
+
>>> fcluster(Z, 4.5, criterion='distance')
|
328
|
+
array([1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2], dtype=int32)
|
329
|
+
>>> fcluster(Z, 6, criterion='distance')
|
330
|
+
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int32)
|
331
|
+
|
332
|
+
Also, `scipy.cluster.hierarchy.dendrogram` can be used to generate a
|
333
|
+
plot of the dendrogram.
|
334
|
+
"""
|
335
|
+
return linkage(y, method='complete', metric='euclidean')
|
336
|
+
|
337
|
+
|
338
|
+
@lazy_cython
|
339
|
+
def average(y):
|
340
|
+
"""
|
341
|
+
Perform average/UPGMA linkage on a condensed distance matrix.
|
342
|
+
|
343
|
+
Parameters
|
344
|
+
----------
|
345
|
+
y : ndarray
|
346
|
+
The upper triangular of the distance matrix. The result of
|
347
|
+
``pdist`` is returned in this form.
|
348
|
+
|
349
|
+
Returns
|
350
|
+
-------
|
351
|
+
Z : ndarray
|
352
|
+
A linkage matrix containing the hierarchical clustering. See
|
353
|
+
`linkage` for more information on its structure.
|
354
|
+
|
355
|
+
See Also
|
356
|
+
--------
|
357
|
+
linkage : for advanced creation of hierarchical clusterings.
|
358
|
+
scipy.spatial.distance.pdist : pairwise distance metrics
|
359
|
+
|
360
|
+
Examples
|
361
|
+
--------
|
362
|
+
>>> from scipy.cluster.hierarchy import average, fcluster
|
363
|
+
>>> from scipy.spatial.distance import pdist
|
364
|
+
|
365
|
+
First, we need a toy dataset to play with::
|
366
|
+
|
367
|
+
x x x x
|
368
|
+
x x
|
369
|
+
|
370
|
+
x x
|
371
|
+
x x x x
|
372
|
+
|
373
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
374
|
+
... [0, 4], [0, 3], [1, 4],
|
375
|
+
... [4, 0], [3, 0], [4, 1],
|
376
|
+
... [4, 4], [3, 4], [4, 3]]
|
377
|
+
|
378
|
+
Then, we get a condensed distance matrix from this dataset:
|
379
|
+
|
380
|
+
>>> y = pdist(X)
|
381
|
+
|
382
|
+
Finally, we can perform the clustering:
|
383
|
+
|
384
|
+
>>> Z = average(y)
|
385
|
+
>>> Z
|
386
|
+
array([[ 0. , 1. , 1. , 2. ],
|
387
|
+
[ 3. , 4. , 1. , 2. ],
|
388
|
+
[ 6. , 7. , 1. , 2. ],
|
389
|
+
[ 9. , 10. , 1. , 2. ],
|
390
|
+
[ 2. , 12. , 1.20710678, 3. ],
|
391
|
+
[ 5. , 13. , 1.20710678, 3. ],
|
392
|
+
[ 8. , 14. , 1.20710678, 3. ],
|
393
|
+
[11. , 15. , 1.20710678, 3. ],
|
394
|
+
[16. , 17. , 3.39675184, 6. ],
|
395
|
+
[18. , 19. , 3.39675184, 6. ],
|
396
|
+
[20. , 21. , 4.09206523, 12. ]])
|
397
|
+
|
398
|
+
The linkage matrix ``Z`` represents a dendrogram - see
|
399
|
+
`scipy.cluster.hierarchy.linkage` for a detailed explanation of its
|
400
|
+
contents.
|
401
|
+
|
402
|
+
We can use `scipy.cluster.hierarchy.fcluster` to see to which cluster
|
403
|
+
each initial point would belong given a distance threshold:
|
404
|
+
|
405
|
+
>>> fcluster(Z, 0.9, criterion='distance')
|
406
|
+
array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], dtype=int32)
|
407
|
+
>>> fcluster(Z, 1.5, criterion='distance')
|
408
|
+
array([1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4], dtype=int32)
|
409
|
+
>>> fcluster(Z, 4, criterion='distance')
|
410
|
+
array([1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2], dtype=int32)
|
411
|
+
>>> fcluster(Z, 6, criterion='distance')
|
412
|
+
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int32)
|
413
|
+
|
414
|
+
Also, `scipy.cluster.hierarchy.dendrogram` can be used to generate a
|
415
|
+
plot of the dendrogram.
|
416
|
+
|
417
|
+
"""
|
418
|
+
return linkage(y, method='average', metric='euclidean')
|
419
|
+
|
420
|
+
|
421
|
+
@lazy_cython
|
422
|
+
def weighted(y):
|
423
|
+
"""
|
424
|
+
Perform weighted/WPGMA linkage on the condensed distance matrix.
|
425
|
+
|
426
|
+
See `linkage` for more information on the return
|
427
|
+
structure and algorithm.
|
428
|
+
|
429
|
+
Parameters
|
430
|
+
----------
|
431
|
+
y : ndarray
|
432
|
+
The upper triangular of the distance matrix. The result of
|
433
|
+
``pdist`` is returned in this form.
|
434
|
+
|
435
|
+
Returns
|
436
|
+
-------
|
437
|
+
Z : ndarray
|
438
|
+
A linkage matrix containing the hierarchical clustering. See
|
439
|
+
`linkage` for more information on its structure.
|
440
|
+
|
441
|
+
See Also
|
442
|
+
--------
|
443
|
+
linkage : for advanced creation of hierarchical clusterings.
|
444
|
+
scipy.spatial.distance.pdist : pairwise distance metrics
|
445
|
+
|
446
|
+
Examples
|
447
|
+
--------
|
448
|
+
>>> from scipy.cluster.hierarchy import weighted, fcluster
|
449
|
+
>>> from scipy.spatial.distance import pdist
|
450
|
+
|
451
|
+
First, we need a toy dataset to play with::
|
452
|
+
|
453
|
+
x x x x
|
454
|
+
x x
|
455
|
+
|
456
|
+
x x
|
457
|
+
x x x x
|
458
|
+
|
459
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
460
|
+
... [0, 4], [0, 3], [1, 4],
|
461
|
+
... [4, 0], [3, 0], [4, 1],
|
462
|
+
... [4, 4], [3, 4], [4, 3]]
|
463
|
+
|
464
|
+
Then, we get a condensed distance matrix from this dataset:
|
465
|
+
|
466
|
+
>>> y = pdist(X)
|
467
|
+
|
468
|
+
Finally, we can perform the clustering:
|
469
|
+
|
470
|
+
>>> Z = weighted(y)
|
471
|
+
>>> Z
|
472
|
+
array([[ 0. , 1. , 1. , 2. ],
|
473
|
+
[ 6. , 7. , 1. , 2. ],
|
474
|
+
[ 3. , 4. , 1. , 2. ],
|
475
|
+
[ 9. , 11. , 1. , 2. ],
|
476
|
+
[ 2. , 12. , 1.20710678, 3. ],
|
477
|
+
[ 8. , 13. , 1.20710678, 3. ],
|
478
|
+
[ 5. , 14. , 1.20710678, 3. ],
|
479
|
+
[10. , 15. , 1.20710678, 3. ],
|
480
|
+
[18. , 19. , 3.05595762, 6. ],
|
481
|
+
[16. , 17. , 3.32379407, 6. ],
|
482
|
+
[20. , 21. , 4.06357713, 12. ]])
|
483
|
+
|
484
|
+
The linkage matrix ``Z`` represents a dendrogram - see
|
485
|
+
`scipy.cluster.hierarchy.linkage` for a detailed explanation of its
|
486
|
+
contents.
|
487
|
+
|
488
|
+
We can use `scipy.cluster.hierarchy.fcluster` to see to which cluster
|
489
|
+
each initial point would belong given a distance threshold:
|
490
|
+
|
491
|
+
>>> fcluster(Z, 0.9, criterion='distance')
|
492
|
+
array([ 7, 8, 9, 1, 2, 3, 10, 11, 12, 4, 6, 5], dtype=int32)
|
493
|
+
>>> fcluster(Z, 1.5, criterion='distance')
|
494
|
+
array([3, 3, 3, 1, 1, 1, 4, 4, 4, 2, 2, 2], dtype=int32)
|
495
|
+
>>> fcluster(Z, 4, criterion='distance')
|
496
|
+
array([2, 2, 2, 1, 1, 1, 2, 2, 2, 1, 1, 1], dtype=int32)
|
497
|
+
>>> fcluster(Z, 6, criterion='distance')
|
498
|
+
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int32)
|
499
|
+
|
500
|
+
Also, `scipy.cluster.hierarchy.dendrogram` can be used to generate a
|
501
|
+
plot of the dendrogram.
|
502
|
+
|
503
|
+
"""
|
504
|
+
return linkage(y, method='weighted', metric='euclidean')
|
505
|
+
|
506
|
+
|
507
|
+
@lazy_cython
|
508
|
+
def centroid(y):
|
509
|
+
"""
|
510
|
+
Perform centroid/UPGMC linkage.
|
511
|
+
|
512
|
+
See `linkage` for more information on the input matrix,
|
513
|
+
return structure, and algorithm.
|
514
|
+
|
515
|
+
The following are common calling conventions:
|
516
|
+
|
517
|
+
1. ``Z = centroid(y)``
|
518
|
+
|
519
|
+
Performs centroid/UPGMC linkage on the condensed distance
|
520
|
+
matrix ``y``.
|
521
|
+
|
522
|
+
2. ``Z = centroid(X)``
|
523
|
+
|
524
|
+
Performs centroid/UPGMC linkage on the observation matrix ``X``
|
525
|
+
using Euclidean distance as the distance metric.
|
526
|
+
|
527
|
+
Parameters
|
528
|
+
----------
|
529
|
+
y : ndarray
|
530
|
+
A condensed distance matrix. A condensed
|
531
|
+
distance matrix is a flat array containing the upper
|
532
|
+
triangular of the distance matrix. This is the form that
|
533
|
+
``pdist`` returns. Alternatively, a collection of
|
534
|
+
m observation vectors in n dimensions may be passed as
|
535
|
+
an m by n array.
|
536
|
+
|
537
|
+
Returns
|
538
|
+
-------
|
539
|
+
Z : ndarray
|
540
|
+
A linkage matrix containing the hierarchical clustering. See
|
541
|
+
the `linkage` function documentation for more information
|
542
|
+
on its structure.
|
543
|
+
|
544
|
+
See Also
|
545
|
+
--------
|
546
|
+
linkage : for advanced creation of hierarchical clusterings.
|
547
|
+
scipy.spatial.distance.pdist : pairwise distance metrics
|
548
|
+
|
549
|
+
Examples
|
550
|
+
--------
|
551
|
+
>>> from scipy.cluster.hierarchy import centroid, fcluster
|
552
|
+
>>> from scipy.spatial.distance import pdist
|
553
|
+
|
554
|
+
First, we need a toy dataset to play with::
|
555
|
+
|
556
|
+
x x x x
|
557
|
+
x x
|
558
|
+
|
559
|
+
x x
|
560
|
+
x x x x
|
561
|
+
|
562
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
563
|
+
... [0, 4], [0, 3], [1, 4],
|
564
|
+
... [4, 0], [3, 0], [4, 1],
|
565
|
+
... [4, 4], [3, 4], [4, 3]]
|
566
|
+
|
567
|
+
Then, we get a condensed distance matrix from this dataset:
|
568
|
+
|
569
|
+
>>> y = pdist(X)
|
570
|
+
|
571
|
+
Finally, we can perform the clustering:
|
572
|
+
|
573
|
+
>>> Z = centroid(y)
|
574
|
+
>>> Z
|
575
|
+
array([[ 0. , 1. , 1. , 2. ],
|
576
|
+
[ 3. , 4. , 1. , 2. ],
|
577
|
+
[ 9. , 10. , 1. , 2. ],
|
578
|
+
[ 6. , 7. , 1. , 2. ],
|
579
|
+
[ 2. , 12. , 1.11803399, 3. ],
|
580
|
+
[ 5. , 13. , 1.11803399, 3. ],
|
581
|
+
[ 8. , 15. , 1.11803399, 3. ],
|
582
|
+
[11. , 14. , 1.11803399, 3. ],
|
583
|
+
[18. , 19. , 3.33333333, 6. ],
|
584
|
+
[16. , 17. , 3.33333333, 6. ],
|
585
|
+
[20. , 21. , 3.33333333, 12. ]]) # may vary
|
586
|
+
|
587
|
+
The linkage matrix ``Z`` represents a dendrogram - see
|
588
|
+
`scipy.cluster.hierarchy.linkage` for a detailed explanation of its
|
589
|
+
contents.
|
590
|
+
|
591
|
+
We can use `scipy.cluster.hierarchy.fcluster` to see to which cluster
|
592
|
+
each initial point would belong given a distance threshold:
|
593
|
+
|
594
|
+
>>> fcluster(Z, 0.9, criterion='distance')
|
595
|
+
array([ 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6], dtype=int32) # may vary
|
596
|
+
>>> fcluster(Z, 1.1, criterion='distance')
|
597
|
+
array([5, 5, 6, 7, 7, 8, 1, 1, 2, 3, 3, 4], dtype=int32) # may vary
|
598
|
+
>>> fcluster(Z, 2, criterion='distance')
|
599
|
+
array([3, 3, 3, 4, 4, 4, 1, 1, 1, 2, 2, 2], dtype=int32) # may vary
|
600
|
+
>>> fcluster(Z, 4, criterion='distance')
|
601
|
+
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int32)
|
602
|
+
|
603
|
+
Also, `scipy.cluster.hierarchy.dendrogram` can be used to generate a
|
604
|
+
plot of the dendrogram.
|
605
|
+
|
606
|
+
"""
|
607
|
+
return linkage(y, method='centroid', metric='euclidean')
|
608
|
+
|
609
|
+
|
610
|
+
@lazy_cython
|
611
|
+
def median(y):
|
612
|
+
"""
|
613
|
+
Perform median/WPGMC linkage.
|
614
|
+
|
615
|
+
See `linkage` for more information on the return structure
|
616
|
+
and algorithm.
|
617
|
+
|
618
|
+
The following are common calling conventions:
|
619
|
+
|
620
|
+
1. ``Z = median(y)``
|
621
|
+
|
622
|
+
Performs median/WPGMC linkage on the condensed distance matrix
|
623
|
+
``y``. See ``linkage`` for more information on the return
|
624
|
+
structure and algorithm.
|
625
|
+
|
626
|
+
2. ``Z = median(X)``
|
627
|
+
|
628
|
+
Performs median/WPGMC linkage on the observation matrix ``X``
|
629
|
+
using Euclidean distance as the distance metric. See `linkage`
|
630
|
+
for more information on the return structure and algorithm.
|
631
|
+
|
632
|
+
Parameters
|
633
|
+
----------
|
634
|
+
y : ndarray
|
635
|
+
A condensed distance matrix. A condensed
|
636
|
+
distance matrix is a flat array containing the upper
|
637
|
+
triangular of the distance matrix. This is the form that
|
638
|
+
``pdist`` returns. Alternatively, a collection of
|
639
|
+
m observation vectors in n dimensions may be passed as
|
640
|
+
an m by n array.
|
641
|
+
|
642
|
+
Returns
|
643
|
+
-------
|
644
|
+
Z : ndarray
|
645
|
+
The hierarchical clustering encoded as a linkage matrix.
|
646
|
+
|
647
|
+
See Also
|
648
|
+
--------
|
649
|
+
linkage : for advanced creation of hierarchical clusterings.
|
650
|
+
scipy.spatial.distance.pdist : pairwise distance metrics
|
651
|
+
|
652
|
+
Examples
|
653
|
+
--------
|
654
|
+
>>> from scipy.cluster.hierarchy import median, fcluster
|
655
|
+
>>> from scipy.spatial.distance import pdist
|
656
|
+
|
657
|
+
First, we need a toy dataset to play with::
|
658
|
+
|
659
|
+
x x x x
|
660
|
+
x x
|
661
|
+
|
662
|
+
x x
|
663
|
+
x x x x
|
664
|
+
|
665
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
666
|
+
... [0, 4], [0, 3], [1, 4],
|
667
|
+
... [4, 0], [3, 0], [4, 1],
|
668
|
+
... [4, 4], [3, 4], [4, 3]]
|
669
|
+
|
670
|
+
Then, we get a condensed distance matrix from this dataset:
|
671
|
+
|
672
|
+
>>> y = pdist(X)
|
673
|
+
|
674
|
+
Finally, we can perform the clustering:
|
675
|
+
|
676
|
+
>>> Z = median(y)
|
677
|
+
>>> Z
|
678
|
+
array([[ 0. , 1. , 1. , 2. ],
|
679
|
+
[ 3. , 4. , 1. , 2. ],
|
680
|
+
[ 9. , 10. , 1. , 2. ],
|
681
|
+
[ 6. , 7. , 1. , 2. ],
|
682
|
+
[ 2. , 12. , 1.11803399, 3. ],
|
683
|
+
[ 5. , 13. , 1.11803399, 3. ],
|
684
|
+
[ 8. , 15. , 1.11803399, 3. ],
|
685
|
+
[11. , 14. , 1.11803399, 3. ],
|
686
|
+
[18. , 19. , 3. , 6. ],
|
687
|
+
[16. , 17. , 3.5 , 6. ],
|
688
|
+
[20. , 21. , 3.25 , 12. ]])
|
689
|
+
|
690
|
+
The linkage matrix ``Z`` represents a dendrogram - see
|
691
|
+
`scipy.cluster.hierarchy.linkage` for a detailed explanation of its
|
692
|
+
contents.
|
693
|
+
|
694
|
+
We can use `scipy.cluster.hierarchy.fcluster` to see to which cluster
|
695
|
+
each initial point would belong given a distance threshold:
|
696
|
+
|
697
|
+
>>> fcluster(Z, 0.9, criterion='distance')
|
698
|
+
array([ 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6], dtype=int32)
|
699
|
+
>>> fcluster(Z, 1.1, criterion='distance')
|
700
|
+
array([5, 5, 6, 7, 7, 8, 1, 1, 2, 3, 3, 4], dtype=int32)
|
701
|
+
>>> fcluster(Z, 2, criterion='distance')
|
702
|
+
array([3, 3, 3, 4, 4, 4, 1, 1, 1, 2, 2, 2], dtype=int32)
|
703
|
+
>>> fcluster(Z, 4, criterion='distance')
|
704
|
+
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int32)
|
705
|
+
|
706
|
+
Also, `scipy.cluster.hierarchy.dendrogram` can be used to generate a
|
707
|
+
plot of the dendrogram.
|
708
|
+
|
709
|
+
"""
|
710
|
+
return linkage(y, method='median', metric='euclidean')
|
711
|
+
|
712
|
+
|
713
|
+
@lazy_cython
|
714
|
+
def ward(y):
|
715
|
+
"""
|
716
|
+
Perform Ward's linkage on a condensed distance matrix.
|
717
|
+
|
718
|
+
See `linkage` for more information on the return structure
|
719
|
+
and algorithm.
|
720
|
+
|
721
|
+
The following are common calling conventions:
|
722
|
+
|
723
|
+
1. ``Z = ward(y)``
|
724
|
+
Performs Ward's linkage on the condensed distance matrix ``y``.
|
725
|
+
|
726
|
+
2. ``Z = ward(X)``
|
727
|
+
Performs Ward's linkage on the observation matrix ``X`` using
|
728
|
+
Euclidean distance as the distance metric.
|
729
|
+
|
730
|
+
Parameters
|
731
|
+
----------
|
732
|
+
y : ndarray
|
733
|
+
A condensed distance matrix. A condensed
|
734
|
+
distance matrix is a flat array containing the upper
|
735
|
+
triangular of the distance matrix. This is the form that
|
736
|
+
``pdist`` returns. Alternatively, a collection of
|
737
|
+
m observation vectors in n dimensions may be passed as
|
738
|
+
an m by n array.
|
739
|
+
|
740
|
+
Returns
|
741
|
+
-------
|
742
|
+
Z : ndarray
|
743
|
+
The hierarchical clustering encoded as a linkage matrix. See
|
744
|
+
`linkage` for more information on the return structure and
|
745
|
+
algorithm.
|
746
|
+
|
747
|
+
See Also
|
748
|
+
--------
|
749
|
+
linkage : for advanced creation of hierarchical clusterings.
|
750
|
+
scipy.spatial.distance.pdist : pairwise distance metrics
|
751
|
+
|
752
|
+
Examples
|
753
|
+
--------
|
754
|
+
>>> from scipy.cluster.hierarchy import ward, fcluster
|
755
|
+
>>> from scipy.spatial.distance import pdist
|
756
|
+
|
757
|
+
First, we need a toy dataset to play with::
|
758
|
+
|
759
|
+
x x x x
|
760
|
+
x x
|
761
|
+
|
762
|
+
x x
|
763
|
+
x x x x
|
764
|
+
|
765
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
766
|
+
... [0, 4], [0, 3], [1, 4],
|
767
|
+
... [4, 0], [3, 0], [4, 1],
|
768
|
+
... [4, 4], [3, 4], [4, 3]]
|
769
|
+
|
770
|
+
Then, we get a condensed distance matrix from this dataset:
|
771
|
+
|
772
|
+
>>> y = pdist(X)
|
773
|
+
|
774
|
+
Finally, we can perform the clustering:
|
775
|
+
|
776
|
+
>>> Z = ward(y)
|
777
|
+
>>> Z
|
778
|
+
array([[ 0. , 1. , 1. , 2. ],
|
779
|
+
[ 3. , 4. , 1. , 2. ],
|
780
|
+
[ 6. , 7. , 1. , 2. ],
|
781
|
+
[ 9. , 10. , 1. , 2. ],
|
782
|
+
[ 2. , 12. , 1.29099445, 3. ],
|
783
|
+
[ 5. , 13. , 1.29099445, 3. ],
|
784
|
+
[ 8. , 14. , 1.29099445, 3. ],
|
785
|
+
[11. , 15. , 1.29099445, 3. ],
|
786
|
+
[16. , 17. , 5.77350269, 6. ],
|
787
|
+
[18. , 19. , 5.77350269, 6. ],
|
788
|
+
[20. , 21. , 8.16496581, 12. ]])
|
789
|
+
|
790
|
+
The linkage matrix ``Z`` represents a dendrogram - see
|
791
|
+
`scipy.cluster.hierarchy.linkage` for a detailed explanation of its
|
792
|
+
contents.
|
793
|
+
|
794
|
+
We can use `scipy.cluster.hierarchy.fcluster` to see to which cluster
|
795
|
+
each initial point would belong given a distance threshold:
|
796
|
+
|
797
|
+
>>> fcluster(Z, 0.9, criterion='distance')
|
798
|
+
array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], dtype=int32)
|
799
|
+
>>> fcluster(Z, 1.1, criterion='distance')
|
800
|
+
array([1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8], dtype=int32)
|
801
|
+
>>> fcluster(Z, 3, criterion='distance')
|
802
|
+
array([1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4], dtype=int32)
|
803
|
+
>>> fcluster(Z, 9, criterion='distance')
|
804
|
+
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int32)
|
805
|
+
|
806
|
+
Also, `scipy.cluster.hierarchy.dendrogram` can be used to generate a
|
807
|
+
plot of the dendrogram.
|
808
|
+
|
809
|
+
"""
|
810
|
+
return linkage(y, method='ward', metric='euclidean')
|
811
|
+
|
812
|
+
|
813
|
+
@lazy_cython
|
814
|
+
def linkage(y, method='single', metric='euclidean', optimal_ordering=False):
|
815
|
+
"""
|
816
|
+
Perform hierarchical/agglomerative clustering.
|
817
|
+
|
818
|
+
The input y may be either a 1-D condensed distance matrix
|
819
|
+
or a 2-D array of observation vectors.
|
820
|
+
|
821
|
+
If y is a 1-D condensed distance matrix,
|
822
|
+
then y must be a :math:`\\binom{n}{2}` sized
|
823
|
+
vector, where n is the number of original observations paired
|
824
|
+
in the distance matrix. The behavior of this function is very
|
825
|
+
similar to the MATLAB linkage function.
|
826
|
+
|
827
|
+
A :math:`(n-1)` by 4 matrix ``Z`` is returned. At the
|
828
|
+
:math:`i`-th iteration, clusters with indices ``Z[i, 0]`` and
|
829
|
+
``Z[i, 1]`` are combined to form cluster :math:`n + i`. A
|
830
|
+
cluster with an index less than :math:`n` corresponds to one of
|
831
|
+
the :math:`n` original observations. The distance between
|
832
|
+
clusters ``Z[i, 0]`` and ``Z[i, 1]`` is given by ``Z[i, 2]``. The
|
833
|
+
fourth value ``Z[i, 3]`` represents the number of original
|
834
|
+
observations in the newly formed cluster.
|
835
|
+
|
836
|
+
The following linkage methods are used to compute the distance
|
837
|
+
:math:`d(s, t)` between two clusters :math:`s` and
|
838
|
+
:math:`t`. The algorithm begins with a forest of clusters that
|
839
|
+
have yet to be used in the hierarchy being formed. When two
|
840
|
+
clusters :math:`s` and :math:`t` from this forest are combined
|
841
|
+
into a single cluster :math:`u`, :math:`s` and :math:`t` are
|
842
|
+
removed from the forest, and :math:`u` is added to the
|
843
|
+
forest. When only one cluster remains in the forest, the algorithm
|
844
|
+
stops, and this cluster becomes the root.
|
845
|
+
|
846
|
+
A distance matrix is maintained at each iteration. The ``d[i,j]``
|
847
|
+
entry corresponds to the distance between cluster :math:`i` and
|
848
|
+
:math:`j` in the original forest.
|
849
|
+
|
850
|
+
At each iteration, the algorithm must update the distance matrix
|
851
|
+
to reflect the distance of the newly formed cluster u with the
|
852
|
+
remaining clusters in the forest.
|
853
|
+
|
854
|
+
Suppose there are :math:`|u|` original observations
|
855
|
+
:math:`u[0], \\ldots, u[|u|-1]` in cluster :math:`u` and
|
856
|
+
:math:`|v|` original objects :math:`v[0], \\ldots, v[|v|-1]` in
|
857
|
+
cluster :math:`v`. Recall, :math:`s` and :math:`t` are
|
858
|
+
combined to form cluster :math:`u`. Let :math:`v` be any
|
859
|
+
remaining cluster in the forest that is not :math:`u`.
|
860
|
+
|
861
|
+
The following are methods for calculating the distance between the
|
862
|
+
newly formed cluster :math:`u` and each :math:`v`.
|
863
|
+
|
864
|
+
* method='single' assigns
|
865
|
+
|
866
|
+
.. math::
|
867
|
+
d(u,v) = \\min(dist(u[i],v[j]))
|
868
|
+
|
869
|
+
for all points :math:`i` in cluster :math:`u` and
|
870
|
+
:math:`j` in cluster :math:`v`. This is also known as the
|
871
|
+
Nearest Point Algorithm.
|
872
|
+
|
873
|
+
* method='complete' assigns
|
874
|
+
|
875
|
+
.. math::
|
876
|
+
d(u, v) = \\max(dist(u[i],v[j]))
|
877
|
+
|
878
|
+
for all points :math:`i` in cluster u and :math:`j` in
|
879
|
+
cluster :math:`v`. This is also known by the Farthest Point
|
880
|
+
Algorithm or Voor Hees Algorithm.
|
881
|
+
|
882
|
+
* method='average' assigns
|
883
|
+
|
884
|
+
.. math::
|
885
|
+
d(u,v) = \\sum_{ij} \\frac{d(u[i], v[j])}
|
886
|
+
{(|u|*|v|)}
|
887
|
+
|
888
|
+
for all points :math:`i` and :math:`j` where :math:`|u|`
|
889
|
+
and :math:`|v|` are the cardinalities of clusters :math:`u`
|
890
|
+
and :math:`v`, respectively. This is also called the UPGMA
|
891
|
+
algorithm.
|
892
|
+
|
893
|
+
* method='weighted' assigns
|
894
|
+
|
895
|
+
.. math::
|
896
|
+
d(u,v) = (dist(s,v) + dist(t,v))/2
|
897
|
+
|
898
|
+
where cluster u was formed with cluster s and t and v
|
899
|
+
is a remaining cluster in the forest (also called WPGMA).
|
900
|
+
|
901
|
+
* method='centroid' assigns
|
902
|
+
|
903
|
+
.. math::
|
904
|
+
dist(s,t) = ||c_s-c_t||_2
|
905
|
+
|
906
|
+
where :math:`c_s` and :math:`c_t` are the centroids of
|
907
|
+
clusters :math:`s` and :math:`t`, respectively. When two
|
908
|
+
clusters :math:`s` and :math:`t` are combined into a new
|
909
|
+
cluster :math:`u`, the new centroid is computed over all the
|
910
|
+
original objects in clusters :math:`s` and :math:`t`. The
|
911
|
+
distance then becomes the Euclidean distance between the
|
912
|
+
centroid of :math:`u` and the centroid of a remaining cluster
|
913
|
+
:math:`v` in the forest. This is also known as the UPGMC
|
914
|
+
algorithm.
|
915
|
+
|
916
|
+
* method='median' assigns :math:`d(s,t)` like the ``centroid``
|
917
|
+
method. When two clusters :math:`s` and :math:`t` are combined
|
918
|
+
into a new cluster :math:`u`, the average of centroids s and t
|
919
|
+
give the new centroid :math:`u`. This is also known as the
|
920
|
+
WPGMC algorithm.
|
921
|
+
|
922
|
+
* method='ward' uses the Ward variance minimization algorithm.
|
923
|
+
The new entry :math:`d(u,v)` is computed as follows,
|
924
|
+
|
925
|
+
.. math::
|
926
|
+
|
927
|
+
d(u,v) = \\sqrt{\\frac{|v|+|s|}
|
928
|
+
{T}d(v,s)^2
|
929
|
+
+ \\frac{|v|+|t|}
|
930
|
+
{T}d(v,t)^2
|
931
|
+
- \\frac{|v|}
|
932
|
+
{T}d(s,t)^2}
|
933
|
+
|
934
|
+
where :math:`u` is the newly joined cluster consisting of
|
935
|
+
clusters :math:`s` and :math:`t`, :math:`v` is an unused
|
936
|
+
cluster in the forest, :math:`T=|v|+|s|+|t|`, and
|
937
|
+
:math:`|*|` is the cardinality of its argument. This is also
|
938
|
+
known as the incremental algorithm.
|
939
|
+
|
940
|
+
Warning: When the minimum distance pair in the forest is chosen, there
|
941
|
+
may be two or more pairs with the same minimum distance. This
|
942
|
+
implementation may choose a different minimum than the MATLAB
|
943
|
+
version.
|
944
|
+
|
945
|
+
Parameters
|
946
|
+
----------
|
947
|
+
y : ndarray
|
948
|
+
A condensed distance matrix. A condensed distance matrix
|
949
|
+
is a flat array containing the upper triangular of the distance matrix.
|
950
|
+
This is the form that ``pdist`` returns. Alternatively, a collection of
|
951
|
+
:math:`m` observation vectors in :math:`n` dimensions may be passed as
|
952
|
+
an :math:`m` by :math:`n` array. All elements of the condensed distance
|
953
|
+
matrix must be finite, i.e., no NaNs or infs.
|
954
|
+
method : str, optional
|
955
|
+
The linkage algorithm to use. See the ``Linkage Methods`` section below
|
956
|
+
for full descriptions.
|
957
|
+
metric : str or function, optional
|
958
|
+
The distance metric to use in the case that y is a collection of
|
959
|
+
observation vectors; ignored otherwise. See the ``pdist``
|
960
|
+
function for a list of valid distance metrics. A custom distance
|
961
|
+
function can also be used.
|
962
|
+
optimal_ordering : bool, optional
|
963
|
+
If True, the linkage matrix will be reordered so that the distance
|
964
|
+
between successive leaves is minimal. This results in a more intuitive
|
965
|
+
tree structure when the data are visualized. defaults to False, because
|
966
|
+
this algorithm can be slow, particularly on large datasets [2]_. See
|
967
|
+
also the `optimal_leaf_ordering` function.
|
968
|
+
|
969
|
+
.. versionadded:: 1.0.0
|
970
|
+
|
971
|
+
Returns
|
972
|
+
-------
|
973
|
+
Z : ndarray
|
974
|
+
The hierarchical clustering encoded as a linkage matrix.
|
975
|
+
|
976
|
+
Notes
|
977
|
+
-----
|
978
|
+
1. For method 'single', an optimized algorithm based on minimum spanning
|
979
|
+
tree is implemented. It has time complexity :math:`O(n^2)`.
|
980
|
+
For methods 'complete', 'average', 'weighted' and 'ward', an algorithm
|
981
|
+
called nearest-neighbors chain is implemented. It also has time
|
982
|
+
complexity :math:`O(n^2)`.
|
983
|
+
For other methods, a naive algorithm is implemented with :math:`O(n^3)`
|
984
|
+
time complexity.
|
985
|
+
All algorithms use :math:`O(n^2)` memory.
|
986
|
+
Refer to [1]_ for details about the algorithms.
|
987
|
+
2. Methods 'centroid', 'median', and 'ward' are correctly defined only if
|
988
|
+
Euclidean pairwise metric is used. If `y` is passed as precomputed
|
989
|
+
pairwise distances, then it is the user's responsibility to assure that
|
990
|
+
these distances are in fact Euclidean, otherwise the produced result
|
991
|
+
will be incorrect.
|
992
|
+
|
993
|
+
See Also
|
994
|
+
--------
|
995
|
+
scipy.spatial.distance.pdist : pairwise distance metrics
|
996
|
+
|
997
|
+
References
|
998
|
+
----------
|
999
|
+
.. [1] Daniel Mullner, "Modern hierarchical, agglomerative clustering
|
1000
|
+
algorithms", :arXiv:`1109.2378v1`.
|
1001
|
+
.. [2] Ziv Bar-Joseph, David K. Gifford, Tommi S. Jaakkola, "Fast optimal
|
1002
|
+
leaf ordering for hierarchical clustering", 2001. Bioinformatics
|
1003
|
+
:doi:`10.1093/bioinformatics/17.suppl_1.S22`
|
1004
|
+
|
1005
|
+
Examples
|
1006
|
+
--------
|
1007
|
+
>>> from scipy.cluster.hierarchy import dendrogram, linkage
|
1008
|
+
>>> from matplotlib import pyplot as plt
|
1009
|
+
>>> X = [[i] for i in [2, 8, 0, 4, 1, 9, 9, 0]]
|
1010
|
+
|
1011
|
+
>>> Z = linkage(X, 'ward')
|
1012
|
+
>>> fig = plt.figure(figsize=(25, 10))
|
1013
|
+
>>> dn = dendrogram(Z)
|
1014
|
+
|
1015
|
+
>>> Z = linkage(X, 'single')
|
1016
|
+
>>> fig = plt.figure(figsize=(25, 10))
|
1017
|
+
>>> dn = dendrogram(Z)
|
1018
|
+
>>> plt.show()
|
1019
|
+
"""
|
1020
|
+
xp = array_namespace(y)
|
1021
|
+
y = _asarray(y, order='C', dtype=xp.float64, xp=xp)
|
1022
|
+
lazy = is_lazy_array(y)
|
1023
|
+
|
1024
|
+
if method not in _LINKAGE_METHODS:
|
1025
|
+
raise ValueError(f"Invalid method: {method}")
|
1026
|
+
|
1027
|
+
if method in _EUCLIDEAN_METHODS and metric != 'euclidean' and y.ndim == 2:
|
1028
|
+
msg = f"`method={method}` requires the distance metric to be Euclidean"
|
1029
|
+
raise ValueError(msg)
|
1030
|
+
|
1031
|
+
if y.ndim == 1:
|
1032
|
+
distance.is_valid_y(y, throw=True, name='y')
|
1033
|
+
elif y.ndim == 2:
|
1034
|
+
if (not lazy and y.shape[0] == y.shape[1]
|
1035
|
+
and xp.all(xpx.isclose(xp.linalg.diagonal(y), 0))
|
1036
|
+
and xp.all(y >= 0) and xp.all(xpx.isclose(y, y.T))):
|
1037
|
+
warnings.warn('The symmetric non-negative hollow observation '
|
1038
|
+
'matrix looks suspiciously like an uncondensed '
|
1039
|
+
'distance matrix',
|
1040
|
+
ClusterWarning, stacklevel=2)
|
1041
|
+
y = distance.pdist(y, metric)
|
1042
|
+
else:
|
1043
|
+
raise ValueError("`y` must be 1 or 2 dimensional.")
|
1044
|
+
|
1045
|
+
if not lazy and not xp.all(xp.isfinite(y)):
|
1046
|
+
raise ValueError("The condensed distance matrix must contain only "
|
1047
|
+
"finite values.")
|
1048
|
+
|
1049
|
+
n = distance.num_obs_y(y)
|
1050
|
+
method_code = _LINKAGE_METHODS[method]
|
1051
|
+
|
1052
|
+
def cy_linkage(y, validate):
|
1053
|
+
if validate and not np.all(np.isfinite(y)):
|
1054
|
+
raise ValueError("The condensed distance matrix must contain only "
|
1055
|
+
"finite values.")
|
1056
|
+
|
1057
|
+
if method == 'single':
|
1058
|
+
return _hierarchy.mst_single_linkage(y, n)
|
1059
|
+
elif method in ('complete', 'average', 'weighted', 'ward'):
|
1060
|
+
return _hierarchy.nn_chain(y, n, method_code)
|
1061
|
+
else:
|
1062
|
+
return _hierarchy.fast_linkage(y, n, method_code)
|
1063
|
+
|
1064
|
+
result = xpx.lazy_apply(cy_linkage, y, validate=lazy,
|
1065
|
+
shape=(n - 1, 4), dtype=xp.float64,
|
1066
|
+
as_numpy=True, xp=xp)
|
1067
|
+
|
1068
|
+
if optimal_ordering:
|
1069
|
+
return optimal_leaf_ordering(result, y)
|
1070
|
+
else:
|
1071
|
+
return result
|
1072
|
+
|
1073
|
+
|
1074
|
+
class ClusterNode:
|
1075
|
+
"""
|
1076
|
+
A tree node class for representing a cluster.
|
1077
|
+
|
1078
|
+
Leaf nodes correspond to original observations, while non-leaf nodes
|
1079
|
+
correspond to non-singleton clusters.
|
1080
|
+
|
1081
|
+
The `to_tree` function converts a matrix returned by the linkage
|
1082
|
+
function into an easy-to-use tree representation.
|
1083
|
+
|
1084
|
+
All parameter names are also attributes.
|
1085
|
+
|
1086
|
+
Parameters
|
1087
|
+
----------
|
1088
|
+
id : int
|
1089
|
+
The node id.
|
1090
|
+
left : ClusterNode instance, optional
|
1091
|
+
The left child tree node.
|
1092
|
+
right : ClusterNode instance, optional
|
1093
|
+
The right child tree node.
|
1094
|
+
dist : float, optional
|
1095
|
+
Distance for this cluster in the linkage matrix.
|
1096
|
+
count : int, optional
|
1097
|
+
The number of samples in this cluster.
|
1098
|
+
|
1099
|
+
See Also
|
1100
|
+
--------
|
1101
|
+
to_tree : for converting a linkage matrix ``Z`` into a tree object.
|
1102
|
+
|
1103
|
+
"""
|
1104
|
+
|
1105
|
+
def __init__(self, id, left=None, right=None, dist=0.0, count=1):
|
1106
|
+
if id < 0:
|
1107
|
+
raise ValueError('The id must be non-negative.')
|
1108
|
+
if dist < 0:
|
1109
|
+
raise ValueError('The distance must be non-negative.')
|
1110
|
+
if (left is None and right is not None) or \
|
1111
|
+
(left is not None and right is None):
|
1112
|
+
raise ValueError('Only full or proper binary trees are permitted.'
|
1113
|
+
' This node has one child.')
|
1114
|
+
if count < 1:
|
1115
|
+
raise ValueError('A cluster must contain at least one original '
|
1116
|
+
'observation.')
|
1117
|
+
self.id = id
|
1118
|
+
self.left = left
|
1119
|
+
self.right = right
|
1120
|
+
self.dist = dist
|
1121
|
+
if self.left is None:
|
1122
|
+
self.count = count
|
1123
|
+
else:
|
1124
|
+
self.count = left.count + right.count
|
1125
|
+
|
1126
|
+
def __lt__(self, node):
|
1127
|
+
if not isinstance(node, ClusterNode):
|
1128
|
+
raise ValueError("Can't compare ClusterNode "
|
1129
|
+
f"to type {type(node)}")
|
1130
|
+
return self.dist < node.dist
|
1131
|
+
|
1132
|
+
def __gt__(self, node):
|
1133
|
+
if not isinstance(node, ClusterNode):
|
1134
|
+
raise ValueError("Can't compare ClusterNode "
|
1135
|
+
f"to type {type(node)}")
|
1136
|
+
return self.dist > node.dist
|
1137
|
+
|
1138
|
+
def __eq__(self, node):
|
1139
|
+
if not isinstance(node, ClusterNode):
|
1140
|
+
raise ValueError("Can't compare ClusterNode "
|
1141
|
+
f"to type {type(node)}")
|
1142
|
+
return self.dist == node.dist
|
1143
|
+
|
1144
|
+
def get_id(self):
|
1145
|
+
"""
|
1146
|
+
The identifier of the target node.
|
1147
|
+
|
1148
|
+
For ``0 <= i < n``, `i` corresponds to original observation i.
|
1149
|
+
For ``n <= i < 2n-1``, `i` corresponds to non-singleton cluster formed
|
1150
|
+
at iteration ``i-n``.
|
1151
|
+
|
1152
|
+
Returns
|
1153
|
+
-------
|
1154
|
+
id : int
|
1155
|
+
The identifier of the target node.
|
1156
|
+
|
1157
|
+
"""
|
1158
|
+
return self.id
|
1159
|
+
|
1160
|
+
def get_count(self):
|
1161
|
+
"""
|
1162
|
+
The number of leaf nodes (original observations) belonging to
|
1163
|
+
the cluster node nd. If the target node is a leaf, 1 is
|
1164
|
+
returned.
|
1165
|
+
|
1166
|
+
Returns
|
1167
|
+
-------
|
1168
|
+
get_count : int
|
1169
|
+
The number of leaf nodes below the target node.
|
1170
|
+
|
1171
|
+
"""
|
1172
|
+
return self.count
|
1173
|
+
|
1174
|
+
def get_left(self):
|
1175
|
+
"""
|
1176
|
+
Return a reference to the left child tree object.
|
1177
|
+
|
1178
|
+
Returns
|
1179
|
+
-------
|
1180
|
+
left : ClusterNode
|
1181
|
+
The left child of the target node. If the node is a leaf,
|
1182
|
+
None is returned.
|
1183
|
+
|
1184
|
+
"""
|
1185
|
+
return self.left
|
1186
|
+
|
1187
|
+
def get_right(self):
|
1188
|
+
"""
|
1189
|
+
Return a reference to the right child tree object.
|
1190
|
+
|
1191
|
+
Returns
|
1192
|
+
-------
|
1193
|
+
right : ClusterNode
|
1194
|
+
The left child of the target node. If the node is a leaf,
|
1195
|
+
None is returned.
|
1196
|
+
|
1197
|
+
"""
|
1198
|
+
return self.right
|
1199
|
+
|
1200
|
+
def is_leaf(self):
|
1201
|
+
"""
|
1202
|
+
Return True if the target node is a leaf.
|
1203
|
+
|
1204
|
+
Returns
|
1205
|
+
-------
|
1206
|
+
leafness : bool
|
1207
|
+
True if the target node is a leaf node.
|
1208
|
+
|
1209
|
+
"""
|
1210
|
+
return self.left is None
|
1211
|
+
|
1212
|
+
def pre_order(self, func=(lambda x: x.id)):
|
1213
|
+
"""
|
1214
|
+
Perform pre-order traversal without recursive function calls.
|
1215
|
+
|
1216
|
+
When a leaf node is first encountered, ``func`` is called with
|
1217
|
+
the leaf node as its argument, and its result is appended to
|
1218
|
+
the list.
|
1219
|
+
|
1220
|
+
For example, the statement::
|
1221
|
+
|
1222
|
+
ids = root.pre_order(lambda x: x.id)
|
1223
|
+
|
1224
|
+
returns a list of the node ids corresponding to the leaf nodes
|
1225
|
+
of the tree as they appear from left to right.
|
1226
|
+
|
1227
|
+
Parameters
|
1228
|
+
----------
|
1229
|
+
func : function
|
1230
|
+
Applied to each leaf ClusterNode object in the pre-order traversal.
|
1231
|
+
Given the ``i``-th leaf node in the pre-order traversal ``n[i]``,
|
1232
|
+
the result of ``func(n[i])`` is stored in ``L[i]``. If not
|
1233
|
+
provided, the index of the original observation to which the node
|
1234
|
+
corresponds is used.
|
1235
|
+
|
1236
|
+
Returns
|
1237
|
+
-------
|
1238
|
+
L : list
|
1239
|
+
The pre-order traversal.
|
1240
|
+
|
1241
|
+
"""
|
1242
|
+
# Do a preorder traversal, caching the result. To avoid having to do
|
1243
|
+
# recursion, we'll store the previous index we've visited in a vector.
|
1244
|
+
n = self.count
|
1245
|
+
|
1246
|
+
curNode = [None] * (2 * n)
|
1247
|
+
lvisited = set()
|
1248
|
+
rvisited = set()
|
1249
|
+
curNode[0] = self
|
1250
|
+
k = 0
|
1251
|
+
preorder = []
|
1252
|
+
while k >= 0:
|
1253
|
+
nd = curNode[k]
|
1254
|
+
ndid = nd.id
|
1255
|
+
if nd.is_leaf():
|
1256
|
+
preorder.append(func(nd))
|
1257
|
+
k = k - 1
|
1258
|
+
else:
|
1259
|
+
if ndid not in lvisited:
|
1260
|
+
curNode[k + 1] = nd.left
|
1261
|
+
lvisited.add(ndid)
|
1262
|
+
k = k + 1
|
1263
|
+
elif ndid not in rvisited:
|
1264
|
+
curNode[k + 1] = nd.right
|
1265
|
+
rvisited.add(ndid)
|
1266
|
+
k = k + 1
|
1267
|
+
# If we've visited the left and right of this non-leaf
|
1268
|
+
# node already, go up in the tree.
|
1269
|
+
else:
|
1270
|
+
k = k - 1
|
1271
|
+
|
1272
|
+
return preorder
|
1273
|
+
|
1274
|
+
|
1275
|
+
_cnode_bare = ClusterNode(0)
|
1276
|
+
_cnode_type = type(ClusterNode)
|
1277
|
+
|
1278
|
+
|
1279
|
+
def _order_cluster_tree(Z):
|
1280
|
+
"""
|
1281
|
+
Return clustering nodes in bottom-up order by distance.
|
1282
|
+
|
1283
|
+
Parameters
|
1284
|
+
----------
|
1285
|
+
Z : scipy.cluster.linkage array
|
1286
|
+
The linkage matrix.
|
1287
|
+
|
1288
|
+
Returns
|
1289
|
+
-------
|
1290
|
+
nodes : list
|
1291
|
+
A list of ClusterNode objects.
|
1292
|
+
"""
|
1293
|
+
q = deque()
|
1294
|
+
tree = to_tree(Z)
|
1295
|
+
q.append(tree)
|
1296
|
+
nodes = []
|
1297
|
+
|
1298
|
+
while q:
|
1299
|
+
node = q.popleft()
|
1300
|
+
if not node.is_leaf():
|
1301
|
+
bisect.insort_left(nodes, node)
|
1302
|
+
q.append(node.get_right())
|
1303
|
+
q.append(node.get_left())
|
1304
|
+
return nodes
|
1305
|
+
|
1306
|
+
|
1307
|
+
@xp_capabilities(np_only=True, reason="non-standard indexing")
|
1308
|
+
def cut_tree(Z, n_clusters=None, height=None):
|
1309
|
+
"""
|
1310
|
+
Given a linkage matrix Z, return the cut tree.
|
1311
|
+
|
1312
|
+
Parameters
|
1313
|
+
----------
|
1314
|
+
Z : scipy.cluster.linkage array
|
1315
|
+
The linkage matrix.
|
1316
|
+
n_clusters : array_like, optional
|
1317
|
+
Number of clusters in the tree at the cut point.
|
1318
|
+
height : array_like, optional
|
1319
|
+
The height at which to cut the tree. Only possible for ultrametric
|
1320
|
+
trees.
|
1321
|
+
|
1322
|
+
Returns
|
1323
|
+
-------
|
1324
|
+
cutree : array
|
1325
|
+
An array indicating group membership at each agglomeration step. I.e.,
|
1326
|
+
for a full cut tree, in the first column each data point is in its own
|
1327
|
+
cluster. At the next step, two nodes are merged. Finally, all
|
1328
|
+
singleton and non-singleton clusters are in one group. If `n_clusters`
|
1329
|
+
or `height` are given, the columns correspond to the columns of
|
1330
|
+
`n_clusters` or `height`.
|
1331
|
+
|
1332
|
+
Examples
|
1333
|
+
--------
|
1334
|
+
>>> from scipy import cluster
|
1335
|
+
>>> import numpy as np
|
1336
|
+
>>> from numpy.random import default_rng
|
1337
|
+
>>> rng = default_rng()
|
1338
|
+
>>> X = rng.random((50, 4))
|
1339
|
+
>>> Z = cluster.hierarchy.ward(X)
|
1340
|
+
>>> cutree = cluster.hierarchy.cut_tree(Z, n_clusters=[5, 10])
|
1341
|
+
>>> cutree[:10]
|
1342
|
+
array([[0, 0],
|
1343
|
+
[1, 1],
|
1344
|
+
[2, 2],
|
1345
|
+
[3, 3],
|
1346
|
+
[3, 4],
|
1347
|
+
[2, 2],
|
1348
|
+
[0, 0],
|
1349
|
+
[1, 5],
|
1350
|
+
[3, 6],
|
1351
|
+
[4, 7]]) # random
|
1352
|
+
|
1353
|
+
"""
|
1354
|
+
xp = array_namespace(Z)
|
1355
|
+
nobs = num_obs_linkage(Z)
|
1356
|
+
nodes = _order_cluster_tree(Z)
|
1357
|
+
|
1358
|
+
if height is not None and n_clusters is not None:
|
1359
|
+
raise ValueError("At least one of either height or n_clusters "
|
1360
|
+
"must be None")
|
1361
|
+
elif height is None and n_clusters is None: # return the full cut tree
|
1362
|
+
cols_idx = xp.arange(nobs)
|
1363
|
+
elif height is not None:
|
1364
|
+
height = xp.asarray(height)
|
1365
|
+
heights = xp.asarray([x.dist for x in nodes])
|
1366
|
+
cols_idx = xp.searchsorted(heights, height)
|
1367
|
+
else:
|
1368
|
+
n_clusters = xp.asarray(n_clusters)
|
1369
|
+
cols_idx = nobs - xp.searchsorted(xp.arange(nobs), n_clusters)
|
1370
|
+
|
1371
|
+
try:
|
1372
|
+
n_cols = len(cols_idx)
|
1373
|
+
except TypeError: # scalar
|
1374
|
+
n_cols = 1
|
1375
|
+
cols_idx = xp.asarray([cols_idx])
|
1376
|
+
|
1377
|
+
groups = xp.zeros((n_cols, nobs), dtype=xp.int64)
|
1378
|
+
last_group = xp.arange(nobs)
|
1379
|
+
if 0 in cols_idx:
|
1380
|
+
groups[0] = last_group
|
1381
|
+
|
1382
|
+
for i, node in enumerate(nodes):
|
1383
|
+
idx = node.pre_order()
|
1384
|
+
this_group = xp_copy(last_group, xp=xp)
|
1385
|
+
# TODO ARRAY_API complex indexing not supported
|
1386
|
+
this_group[idx] = xp.min(last_group[idx])
|
1387
|
+
this_group[this_group > xp.max(last_group[idx])] -= 1
|
1388
|
+
if i + 1 in cols_idx:
|
1389
|
+
groups[np.nonzero(i + 1 == cols_idx)[0]] = this_group
|
1390
|
+
last_group = this_group
|
1391
|
+
|
1392
|
+
return groups.T
|
1393
|
+
|
1394
|
+
|
1395
|
+
@xp_capabilities(jax_jit=False, allow_dask_compute=True)
|
1396
|
+
def to_tree(Z, rd=False):
|
1397
|
+
"""
|
1398
|
+
Convert a linkage matrix into an easy-to-use tree object.
|
1399
|
+
|
1400
|
+
The reference to the root `ClusterNode` object is returned (by default).
|
1401
|
+
|
1402
|
+
Each `ClusterNode` object has a ``left``, ``right``, ``dist``, ``id``,
|
1403
|
+
and ``count`` attribute. The left and right attributes point to
|
1404
|
+
ClusterNode objects that were combined to generate the cluster.
|
1405
|
+
If both are None then the `ClusterNode` object is a leaf node, its count
|
1406
|
+
must be 1, and its distance is meaningless but set to 0.
|
1407
|
+
|
1408
|
+
*Note: This function is provided for the convenience of the library
|
1409
|
+
user. ClusterNodes are not used as input to any of the functions in this
|
1410
|
+
library.*
|
1411
|
+
|
1412
|
+
Parameters
|
1413
|
+
----------
|
1414
|
+
Z : ndarray
|
1415
|
+
The linkage matrix in proper form (see the `linkage`
|
1416
|
+
function documentation).
|
1417
|
+
rd : bool, optional
|
1418
|
+
When False (default), a reference to the root `ClusterNode` object is
|
1419
|
+
returned. Otherwise, a tuple ``(r, d)`` is returned. ``r`` is a
|
1420
|
+
reference to the root node while ``d`` is a list of `ClusterNode`
|
1421
|
+
objects - one per original entry in the linkage matrix plus entries
|
1422
|
+
for all clustering steps. If a cluster id is
|
1423
|
+
less than the number of samples ``n`` in the data that the linkage
|
1424
|
+
matrix describes, then it corresponds to a singleton cluster (leaf
|
1425
|
+
node).
|
1426
|
+
See `linkage` for more information on the assignment of cluster ids
|
1427
|
+
to clusters.
|
1428
|
+
|
1429
|
+
Returns
|
1430
|
+
-------
|
1431
|
+
tree : ClusterNode or tuple (ClusterNode, list of ClusterNode)
|
1432
|
+
If ``rd`` is False, a `ClusterNode`.
|
1433
|
+
If ``rd`` is True, a list of length ``2*n - 1``, with ``n`` the number
|
1434
|
+
of samples. See the description of `rd` above for more details.
|
1435
|
+
|
1436
|
+
See Also
|
1437
|
+
--------
|
1438
|
+
linkage, is_valid_linkage, ClusterNode
|
1439
|
+
|
1440
|
+
Examples
|
1441
|
+
--------
|
1442
|
+
>>> import numpy as np
|
1443
|
+
>>> from scipy.cluster import hierarchy
|
1444
|
+
>>> rng = np.random.default_rng()
|
1445
|
+
>>> x = rng.random((5, 2))
|
1446
|
+
>>> Z = hierarchy.linkage(x)
|
1447
|
+
>>> hierarchy.to_tree(Z)
|
1448
|
+
<scipy.cluster.hierarchy.ClusterNode object at ...
|
1449
|
+
>>> rootnode, nodelist = hierarchy.to_tree(Z, rd=True)
|
1450
|
+
>>> rootnode
|
1451
|
+
<scipy.cluster.hierarchy.ClusterNode object at ...
|
1452
|
+
>>> len(nodelist)
|
1453
|
+
9
|
1454
|
+
|
1455
|
+
"""
|
1456
|
+
xp = array_namespace(Z)
|
1457
|
+
Z = _asarray(Z, order='C', xp=xp)
|
1458
|
+
_is_valid_linkage(Z, throw=True, name='Z', materialize=True, xp=xp)
|
1459
|
+
|
1460
|
+
# Number of original objects is equal to the number of rows plus 1.
|
1461
|
+
n = Z.shape[0] + 1
|
1462
|
+
|
1463
|
+
# Create a list full of None's to store the node objects
|
1464
|
+
d = [None] * (n * 2 - 1)
|
1465
|
+
|
1466
|
+
# Create the nodes corresponding to the n original objects.
|
1467
|
+
for i in range(0, n):
|
1468
|
+
d[i] = ClusterNode(i)
|
1469
|
+
|
1470
|
+
nd = None
|
1471
|
+
|
1472
|
+
for i in range(Z.shape[0]):
|
1473
|
+
row = Z[i, :]
|
1474
|
+
|
1475
|
+
fi = int_floor(row[0], xp)
|
1476
|
+
fj = int_floor(row[1], xp)
|
1477
|
+
if fi > i + n:
|
1478
|
+
raise ValueError('Corrupt matrix Z. Index to derivative cluster '
|
1479
|
+
f'is used before it is formed. See row {fi}, '
|
1480
|
+
'column 0')
|
1481
|
+
if fj > i + n:
|
1482
|
+
raise ValueError('Corrupt matrix Z. Index to derivative cluster '
|
1483
|
+
f'is used before it is formed. See row {fj}, '
|
1484
|
+
'column 1')
|
1485
|
+
|
1486
|
+
nd = ClusterNode(i + n, d[fi], d[fj], row[2])
|
1487
|
+
# ^ id ^ left ^ right ^ dist
|
1488
|
+
if row[3] != nd.count:
|
1489
|
+
raise ValueError(f'Corrupt matrix Z. The count Z[{i},3] is '
|
1490
|
+
'incorrect.')
|
1491
|
+
d[n + i] = nd
|
1492
|
+
|
1493
|
+
if rd:
|
1494
|
+
return (nd, d)
|
1495
|
+
else:
|
1496
|
+
return nd
|
1497
|
+
|
1498
|
+
|
1499
|
+
@lazy_cython
|
1500
|
+
def optimal_leaf_ordering(Z, y, metric='euclidean'):
|
1501
|
+
"""
|
1502
|
+
Given a linkage matrix Z and distance, reorder the cut tree.
|
1503
|
+
|
1504
|
+
Parameters
|
1505
|
+
----------
|
1506
|
+
Z : ndarray
|
1507
|
+
The hierarchical clustering encoded as a linkage matrix. See
|
1508
|
+
`linkage` for more information on the return structure and
|
1509
|
+
algorithm.
|
1510
|
+
y : ndarray
|
1511
|
+
The condensed distance matrix from which Z was generated.
|
1512
|
+
Alternatively, a collection of m observation vectors in n
|
1513
|
+
dimensions may be passed as an m by n array.
|
1514
|
+
metric : str or function, optional
|
1515
|
+
The distance metric to use in the case that y is a collection of
|
1516
|
+
observation vectors; ignored otherwise. See the ``pdist``
|
1517
|
+
function for a list of valid distance metrics. A custom distance
|
1518
|
+
function can also be used.
|
1519
|
+
|
1520
|
+
Returns
|
1521
|
+
-------
|
1522
|
+
Z_ordered : ndarray
|
1523
|
+
A copy of the linkage matrix Z, reordered to minimize the distance
|
1524
|
+
between adjacent leaves.
|
1525
|
+
|
1526
|
+
Examples
|
1527
|
+
--------
|
1528
|
+
>>> import numpy as np
|
1529
|
+
>>> from scipy.cluster import hierarchy
|
1530
|
+
>>> rng = np.random.default_rng()
|
1531
|
+
>>> X = rng.standard_normal((10, 10))
|
1532
|
+
>>> Z = hierarchy.ward(X)
|
1533
|
+
>>> hierarchy.leaves_list(Z)
|
1534
|
+
array([0, 3, 1, 9, 2, 5, 7, 4, 6, 8], dtype=int32)
|
1535
|
+
>>> hierarchy.leaves_list(hierarchy.optimal_leaf_ordering(Z, X))
|
1536
|
+
array([3, 0, 2, 5, 7, 4, 8, 6, 9, 1], dtype=int32)
|
1537
|
+
|
1538
|
+
"""
|
1539
|
+
xp = array_namespace(Z, y)
|
1540
|
+
Z = _asarray(Z, order='C', xp=xp)
|
1541
|
+
y = _asarray(y, order='C', dtype=xp.float64, xp=xp)
|
1542
|
+
lazy = is_lazy_array(Z)
|
1543
|
+
_is_valid_linkage(Z, throw=True, name='Z', xp=xp)
|
1544
|
+
|
1545
|
+
if y.ndim == 1:
|
1546
|
+
distance.is_valid_y(y, throw=True, name='y')
|
1547
|
+
elif y.ndim == 2:
|
1548
|
+
if (not lazy and y.shape[0] == y.shape[1]
|
1549
|
+
and xp.all(xpx.isclose(xp.linalg.diagonal(y), 0))
|
1550
|
+
and xp.all(y >= 0) and xp.all(xpx.isclose(y, y.T))):
|
1551
|
+
warnings.warn('The symmetric non-negative hollow observation '
|
1552
|
+
'matrix looks suspiciously like an uncondensed '
|
1553
|
+
'distance matrix',
|
1554
|
+
ClusterWarning, stacklevel=2)
|
1555
|
+
y = distance.pdist(y, metric)
|
1556
|
+
else:
|
1557
|
+
raise ValueError("`y` must be 1 or 2 dimensional.")
|
1558
|
+
if not lazy and not xp.all(xp.isfinite(y)):
|
1559
|
+
raise ValueError("The condensed distance matrix must contain only "
|
1560
|
+
"finite values.")
|
1561
|
+
|
1562
|
+
# The function name is prominently visible on the user-facing Dask dashboard;
|
1563
|
+
# make sure it is meaningful.
|
1564
|
+
def cy_optimal_leaf_ordering(Z, y, validate):
|
1565
|
+
if validate:
|
1566
|
+
_is_valid_linkage(Z, throw=True, name='Z', xp=np)
|
1567
|
+
if not np.all(np.isfinite(y)):
|
1568
|
+
raise ValueError("The condensed distance matrix must contain only "
|
1569
|
+
"finite values.")
|
1570
|
+
return _optimal_leaf_ordering.optimal_leaf_ordering(Z, y)
|
1571
|
+
|
1572
|
+
return xpx.lazy_apply(cy_optimal_leaf_ordering, Z, y, validate=lazy,
|
1573
|
+
shape=Z.shape, dtype=Z.dtype, as_numpy=True, xp=xp)
|
1574
|
+
|
1575
|
+
|
1576
|
+
@lazy_cython
|
1577
|
+
def cophenet(Z, Y=None):
|
1578
|
+
"""
|
1579
|
+
Calculate the cophenetic distances between each observation in
|
1580
|
+
the hierarchical clustering defined by the linkage ``Z``.
|
1581
|
+
|
1582
|
+
Suppose ``p`` and ``q`` are original observations in
|
1583
|
+
disjoint clusters ``s`` and ``t``, respectively and
|
1584
|
+
``s`` and ``t`` are joined by a direct parent cluster
|
1585
|
+
``u``. The cophenetic distance between observations
|
1586
|
+
``i`` and ``j`` is simply the distance between
|
1587
|
+
clusters ``s`` and ``t``.
|
1588
|
+
|
1589
|
+
Parameters
|
1590
|
+
----------
|
1591
|
+
Z : ndarray
|
1592
|
+
The hierarchical clustering encoded as an array
|
1593
|
+
(see `linkage` function).
|
1594
|
+
Y : ndarray (optional)
|
1595
|
+
Calculates the cophenetic correlation coefficient ``c`` of a
|
1596
|
+
hierarchical clustering defined by the linkage matrix `Z`
|
1597
|
+
of a set of :math:`n` observations in :math:`m`
|
1598
|
+
dimensions. `Y` is the condensed distance matrix from which
|
1599
|
+
`Z` was generated.
|
1600
|
+
|
1601
|
+
Returns
|
1602
|
+
-------
|
1603
|
+
c : ndarray
|
1604
|
+
The cophentic correlation distance (if ``Y`` is passed).
|
1605
|
+
d : ndarray
|
1606
|
+
The cophenetic distance matrix in condensed form. The
|
1607
|
+
:math:`ij` th entry is the cophenetic distance between
|
1608
|
+
original observations :math:`i` and :math:`j`.
|
1609
|
+
|
1610
|
+
See Also
|
1611
|
+
--------
|
1612
|
+
linkage :
|
1613
|
+
for a description of what a linkage matrix is.
|
1614
|
+
scipy.spatial.distance.squareform :
|
1615
|
+
transforming condensed matrices into square ones.
|
1616
|
+
|
1617
|
+
Examples
|
1618
|
+
--------
|
1619
|
+
>>> from scipy.cluster.hierarchy import single, cophenet
|
1620
|
+
>>> from scipy.spatial.distance import pdist, squareform
|
1621
|
+
|
1622
|
+
Given a dataset ``X`` and a linkage matrix ``Z``, the cophenetic distance
|
1623
|
+
between two points of ``X`` is the distance between the largest two
|
1624
|
+
distinct clusters that each of the points:
|
1625
|
+
|
1626
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
1627
|
+
... [0, 4], [0, 3], [1, 4],
|
1628
|
+
... [4, 0], [3, 0], [4, 1],
|
1629
|
+
... [4, 4], [3, 4], [4, 3]]
|
1630
|
+
|
1631
|
+
``X`` corresponds to this dataset ::
|
1632
|
+
|
1633
|
+
x x x x
|
1634
|
+
x x
|
1635
|
+
|
1636
|
+
x x
|
1637
|
+
x x x x
|
1638
|
+
|
1639
|
+
>>> Z = single(pdist(X))
|
1640
|
+
>>> Z
|
1641
|
+
array([[ 0., 1., 1., 2.],
|
1642
|
+
[ 2., 12., 1., 3.],
|
1643
|
+
[ 3., 4., 1., 2.],
|
1644
|
+
[ 5., 14., 1., 3.],
|
1645
|
+
[ 6., 7., 1., 2.],
|
1646
|
+
[ 8., 16., 1., 3.],
|
1647
|
+
[ 9., 10., 1., 2.],
|
1648
|
+
[11., 18., 1., 3.],
|
1649
|
+
[13., 15., 2., 6.],
|
1650
|
+
[17., 20., 2., 9.],
|
1651
|
+
[19., 21., 2., 12.]])
|
1652
|
+
>>> cophenet(Z)
|
1653
|
+
array([1., 1., 2., 2., 2., 2., 2., 2., 2., 2., 2., 1., 2., 2., 2., 2., 2.,
|
1654
|
+
2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 1., 1., 2., 2.,
|
1655
|
+
2., 2., 2., 2., 1., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2.,
|
1656
|
+
1., 1., 2., 2., 2., 1., 2., 2., 2., 2., 2., 2., 1., 1., 1.])
|
1657
|
+
|
1658
|
+
The output of the `scipy.cluster.hierarchy.cophenet` method is
|
1659
|
+
represented in condensed form. We can use
|
1660
|
+
`scipy.spatial.distance.squareform` to see the output as a
|
1661
|
+
regular matrix (where each element ``ij`` denotes the cophenetic distance
|
1662
|
+
between each ``i``, ``j`` pair of points in ``X``):
|
1663
|
+
|
1664
|
+
>>> squareform(cophenet(Z))
|
1665
|
+
array([[0., 1., 1., 2., 2., 2., 2., 2., 2., 2., 2., 2.],
|
1666
|
+
[1., 0., 1., 2., 2., 2., 2., 2., 2., 2., 2., 2.],
|
1667
|
+
[1., 1., 0., 2., 2., 2., 2., 2., 2., 2., 2., 2.],
|
1668
|
+
[2., 2., 2., 0., 1., 1., 2., 2., 2., 2., 2., 2.],
|
1669
|
+
[2., 2., 2., 1., 0., 1., 2., 2., 2., 2., 2., 2.],
|
1670
|
+
[2., 2., 2., 1., 1., 0., 2., 2., 2., 2., 2., 2.],
|
1671
|
+
[2., 2., 2., 2., 2., 2., 0., 1., 1., 2., 2., 2.],
|
1672
|
+
[2., 2., 2., 2., 2., 2., 1., 0., 1., 2., 2., 2.],
|
1673
|
+
[2., 2., 2., 2., 2., 2., 1., 1., 0., 2., 2., 2.],
|
1674
|
+
[2., 2., 2., 2., 2., 2., 2., 2., 2., 0., 1., 1.],
|
1675
|
+
[2., 2., 2., 2., 2., 2., 2., 2., 2., 1., 0., 1.],
|
1676
|
+
[2., 2., 2., 2., 2., 2., 2., 2., 2., 1., 1., 0.]])
|
1677
|
+
|
1678
|
+
In this example, the cophenetic distance between points on ``X`` that are
|
1679
|
+
very close (i.e., in the same corner) is 1. For other pairs of points is 2,
|
1680
|
+
because the points will be located in clusters at different
|
1681
|
+
corners - thus, the distance between these clusters will be larger.
|
1682
|
+
|
1683
|
+
"""
|
1684
|
+
xp = array_namespace(Z, Y)
|
1685
|
+
# Ensure float64 C-contiguous array. Cython code doesn't deal with striding.
|
1686
|
+
Z = _asarray(Z, order='C', dtype=xp.float64, xp=xp)
|
1687
|
+
_is_valid_linkage(Z, throw=True, name='Z', xp=xp)
|
1688
|
+
|
1689
|
+
def cy_cophenet(Z, validate):
|
1690
|
+
if validate:
|
1691
|
+
_is_valid_linkage(Z, throw=True, name='Z', xp=np)
|
1692
|
+
n = Z.shape[0] + 1
|
1693
|
+
zz = np.zeros((n * (n-1)) // 2, dtype=np.float64)
|
1694
|
+
_hierarchy.cophenetic_distances(Z, zz, n)
|
1695
|
+
return zz
|
1696
|
+
|
1697
|
+
n = Z.shape[0] + 1
|
1698
|
+
zz = xpx.lazy_apply(cy_cophenet, Z, validate=is_lazy_array(Z),
|
1699
|
+
shape=((n * (n-1)) // 2, ), dtype=xp.float64,
|
1700
|
+
as_numpy=True, xp=xp)
|
1701
|
+
|
1702
|
+
if Y is None:
|
1703
|
+
return zz
|
1704
|
+
|
1705
|
+
Y = _asarray(Y, order='C', xp=xp)
|
1706
|
+
distance.is_valid_y(Y, throw=True, name='Y')
|
1707
|
+
|
1708
|
+
z = xp.mean(zz)
|
1709
|
+
y = xp.mean(Y)
|
1710
|
+
Yy = Y - y
|
1711
|
+
Zz = zz - z
|
1712
|
+
numerator = (Yy * Zz)
|
1713
|
+
denomA = Yy**2
|
1714
|
+
denomB = Zz**2
|
1715
|
+
c = xp.sum(numerator) / xp.sqrt(xp.sum(denomA) * xp.sum(denomB))
|
1716
|
+
return (c, zz)
|
1717
|
+
|
1718
|
+
|
1719
|
+
@lazy_cython
|
1720
|
+
def inconsistent(Z, d=2):
|
1721
|
+
r"""
|
1722
|
+
Calculate inconsistency statistics on a linkage matrix.
|
1723
|
+
|
1724
|
+
Parameters
|
1725
|
+
----------
|
1726
|
+
Z : ndarray
|
1727
|
+
The :math:`(n-1)` by 4 matrix encoding the linkage (hierarchical
|
1728
|
+
clustering). See `linkage` documentation for more information on its
|
1729
|
+
form.
|
1730
|
+
d : int, optional
|
1731
|
+
The number of links up to `d` levels below each non-singleton cluster.
|
1732
|
+
|
1733
|
+
Returns
|
1734
|
+
-------
|
1735
|
+
R : ndarray
|
1736
|
+
A :math:`(n-1)` by 4 matrix where the ``i``'th row contains the link
|
1737
|
+
statistics for the non-singleton cluster ``i``. The link statistics are
|
1738
|
+
computed over the link heights for links :math:`d` levels below the
|
1739
|
+
cluster ``i``. ``R[i,0]`` and ``R[i,1]`` are the mean and standard
|
1740
|
+
deviation of the link heights, respectively; ``R[i,2]`` is the number
|
1741
|
+
of links included in the calculation; and ``R[i,3]`` is the
|
1742
|
+
inconsistency coefficient,
|
1743
|
+
|
1744
|
+
.. math:: \frac{\mathtt{Z[i,2]} - \mathtt{R[i,0]}} {R[i,1]}
|
1745
|
+
|
1746
|
+
Notes
|
1747
|
+
-----
|
1748
|
+
This function behaves similarly to the MATLAB(TM) ``inconsistent``
|
1749
|
+
function.
|
1750
|
+
|
1751
|
+
Examples
|
1752
|
+
--------
|
1753
|
+
>>> from scipy.cluster.hierarchy import inconsistent, linkage
|
1754
|
+
>>> from matplotlib import pyplot as plt
|
1755
|
+
>>> X = [[i] for i in [2, 8, 0, 4, 1, 9, 9, 0]]
|
1756
|
+
>>> Z = linkage(X, 'ward')
|
1757
|
+
>>> print(Z)
|
1758
|
+
[[ 5. 6. 0. 2. ]
|
1759
|
+
[ 2. 7. 0. 2. ]
|
1760
|
+
[ 0. 4. 1. 2. ]
|
1761
|
+
[ 1. 8. 1.15470054 3. ]
|
1762
|
+
[ 9. 10. 2.12132034 4. ]
|
1763
|
+
[ 3. 12. 4.11096096 5. ]
|
1764
|
+
[11. 13. 14.07183949 8. ]]
|
1765
|
+
>>> inconsistent(Z)
|
1766
|
+
array([[ 0. , 0. , 1. , 0. ],
|
1767
|
+
[ 0. , 0. , 1. , 0. ],
|
1768
|
+
[ 1. , 0. , 1. , 0. ],
|
1769
|
+
[ 0.57735027, 0.81649658, 2. , 0.70710678],
|
1770
|
+
[ 1.04044011, 1.06123822, 3. , 1.01850858],
|
1771
|
+
[ 3.11614065, 1.40688837, 2. , 0.70710678],
|
1772
|
+
[ 6.44583366, 6.76770586, 3. , 1.12682288]])
|
1773
|
+
|
1774
|
+
"""
|
1775
|
+
xp = array_namespace(Z)
|
1776
|
+
Z = _asarray(Z, order='C', dtype=xp.float64, xp=xp)
|
1777
|
+
_is_valid_linkage(Z, throw=True, name='Z', xp=xp)
|
1778
|
+
|
1779
|
+
if d != np.floor(d) or d < 0:
|
1780
|
+
raise ValueError('The second argument d must be a nonnegative '
|
1781
|
+
'integer value.')
|
1782
|
+
|
1783
|
+
def cy_inconsistent(Z, d, validate):
|
1784
|
+
if validate:
|
1785
|
+
_is_valid_linkage(Z, throw=True, name='Z', xp=np)
|
1786
|
+
R = np.zeros((Z.shape[0], 4), dtype=np.float64)
|
1787
|
+
n = Z.shape[0] + 1
|
1788
|
+
_hierarchy.inconsistent(Z, R, n, d)
|
1789
|
+
return R
|
1790
|
+
|
1791
|
+
return xpx.lazy_apply(cy_inconsistent, Z, d=int(d), validate=is_lazy_array(Z),
|
1792
|
+
shape=(Z.shape[0], 4), dtype=xp.float64,
|
1793
|
+
as_numpy=True, xp=xp)
|
1794
|
+
|
1795
|
+
|
1796
|
+
@lazy_cython
|
1797
|
+
def from_mlab_linkage(Z):
|
1798
|
+
"""
|
1799
|
+
Convert a linkage matrix generated by MATLAB(TM) to a new
|
1800
|
+
linkage matrix compatible with this module.
|
1801
|
+
|
1802
|
+
The conversion does two things:
|
1803
|
+
|
1804
|
+
* the indices are converted from ``1..N`` to ``0..(N-1)`` form,
|
1805
|
+
and
|
1806
|
+
|
1807
|
+
* a fourth column ``Z[:,3]`` is added where ``Z[i,3]`` represents the
|
1808
|
+
number of original observations (leaves) in the non-singleton
|
1809
|
+
cluster ``i``.
|
1810
|
+
|
1811
|
+
This function is useful when loading in linkages from legacy data
|
1812
|
+
files generated by MATLAB.
|
1813
|
+
|
1814
|
+
Parameters
|
1815
|
+
----------
|
1816
|
+
Z : ndarray
|
1817
|
+
A linkage matrix generated by MATLAB(TM).
|
1818
|
+
|
1819
|
+
Returns
|
1820
|
+
-------
|
1821
|
+
ZS : ndarray
|
1822
|
+
A linkage matrix compatible with ``scipy.cluster.hierarchy``.
|
1823
|
+
|
1824
|
+
See Also
|
1825
|
+
--------
|
1826
|
+
linkage : for a description of what a linkage matrix is.
|
1827
|
+
to_mlab_linkage : transform from SciPy to MATLAB format.
|
1828
|
+
|
1829
|
+
Examples
|
1830
|
+
--------
|
1831
|
+
>>> import numpy as np
|
1832
|
+
>>> from scipy.cluster.hierarchy import ward, from_mlab_linkage
|
1833
|
+
|
1834
|
+
Given a linkage matrix in MATLAB format ``mZ``, we can use
|
1835
|
+
`scipy.cluster.hierarchy.from_mlab_linkage` to import
|
1836
|
+
it into SciPy format:
|
1837
|
+
|
1838
|
+
>>> mZ = np.array([[1, 2, 1], [4, 5, 1], [7, 8, 1],
|
1839
|
+
... [10, 11, 1], [3, 13, 1.29099445],
|
1840
|
+
... [6, 14, 1.29099445],
|
1841
|
+
... [9, 15, 1.29099445],
|
1842
|
+
... [12, 16, 1.29099445],
|
1843
|
+
... [17, 18, 5.77350269],
|
1844
|
+
... [19, 20, 5.77350269],
|
1845
|
+
... [21, 22, 8.16496581]])
|
1846
|
+
|
1847
|
+
>>> Z = from_mlab_linkage(mZ)
|
1848
|
+
>>> Z
|
1849
|
+
array([[ 0. , 1. , 1. , 2. ],
|
1850
|
+
[ 3. , 4. , 1. , 2. ],
|
1851
|
+
[ 6. , 7. , 1. , 2. ],
|
1852
|
+
[ 9. , 10. , 1. , 2. ],
|
1853
|
+
[ 2. , 12. , 1.29099445, 3. ],
|
1854
|
+
[ 5. , 13. , 1.29099445, 3. ],
|
1855
|
+
[ 8. , 14. , 1.29099445, 3. ],
|
1856
|
+
[ 11. , 15. , 1.29099445, 3. ],
|
1857
|
+
[ 16. , 17. , 5.77350269, 6. ],
|
1858
|
+
[ 18. , 19. , 5.77350269, 6. ],
|
1859
|
+
[ 20. , 21. , 8.16496581, 12. ]])
|
1860
|
+
|
1861
|
+
As expected, the linkage matrix ``Z`` returned includes an
|
1862
|
+
additional column counting the number of original samples in
|
1863
|
+
each cluster. Also, all cluster indices are reduced by 1
|
1864
|
+
(MATLAB format uses 1-indexing, whereas SciPy uses 0-indexing).
|
1865
|
+
|
1866
|
+
"""
|
1867
|
+
xp = array_namespace(Z)
|
1868
|
+
Z = _asarray(Z, dtype=xp.float64, order='C', xp=xp)
|
1869
|
+
|
1870
|
+
# If it's empty, return it.
|
1871
|
+
if Z.shape in ((), (0, )):
|
1872
|
+
return xp_copy(Z, xp=xp)
|
1873
|
+
|
1874
|
+
if Z.ndim != 2:
|
1875
|
+
raise ValueError("The linkage array must be rectangular.")
|
1876
|
+
|
1877
|
+
# If it contains no rows, return it.
|
1878
|
+
n = Z.shape[0]
|
1879
|
+
if n == 0:
|
1880
|
+
return xp_copy(Z, xp=xp)
|
1881
|
+
|
1882
|
+
lazy = is_lazy_array(Z)
|
1883
|
+
|
1884
|
+
if not lazy and xp.min(Z[:, :2]) != 1.0 and xp.max(Z[:, :2]) != 2 * n:
|
1885
|
+
raise ValueError('The format of the indices is not 1..N')
|
1886
|
+
|
1887
|
+
res = xp.empty((Z.shape[0], Z.shape[1] + 1), dtype=Z.dtype)
|
1888
|
+
res = xpx.at(res)[:, :2].set(Z[:, :2] - 1.0)
|
1889
|
+
res = xpx.at(res)[:, 2:-1].set(Z[:, 2:])
|
1890
|
+
|
1891
|
+
def cy_from_mlab_linkage(Zpart, validate):
|
1892
|
+
n = Zpart.shape[0]
|
1893
|
+
if validate and np.min(Zpart[:, :2]) != 0.0 and np.max(Zpart[:, :2]) != 2 * n:
|
1894
|
+
raise ValueError('The format of the indices is not 1..N')
|
1895
|
+
|
1896
|
+
if not Zpart.flags.writeable:
|
1897
|
+
Zpart = Zpart.copy() # xp=jax.numpy
|
1898
|
+
|
1899
|
+
CS = np.zeros((n,))
|
1900
|
+
_hierarchy.calculate_cluster_sizes(Zpart, CS, n + 1)
|
1901
|
+
return CS
|
1902
|
+
|
1903
|
+
CS = xpx.lazy_apply(cy_from_mlab_linkage, res[:, :-1], validate=lazy,
|
1904
|
+
shape=(res.shape[0],), dtype=xp.float64,
|
1905
|
+
as_numpy=True, xp=xp)
|
1906
|
+
|
1907
|
+
return xpx.at(res)[:, -1].set(CS)
|
1908
|
+
|
1909
|
+
|
1910
|
+
@xp_capabilities()
|
1911
|
+
def to_mlab_linkage(Z):
|
1912
|
+
"""
|
1913
|
+
Convert a linkage matrix to a MATLAB(TM) compatible one.
|
1914
|
+
|
1915
|
+
Converts a linkage matrix ``Z`` generated by the linkage function
|
1916
|
+
of this module to a MATLAB(TM) compatible one. The return linkage
|
1917
|
+
matrix has the last column removed and the cluster indices are
|
1918
|
+
converted to ``1..N`` indexing.
|
1919
|
+
|
1920
|
+
Parameters
|
1921
|
+
----------
|
1922
|
+
Z : ndarray
|
1923
|
+
A linkage matrix generated by ``scipy.cluster.hierarchy``.
|
1924
|
+
|
1925
|
+
Returns
|
1926
|
+
-------
|
1927
|
+
to_mlab_linkage : ndarray
|
1928
|
+
A linkage matrix compatible with MATLAB(TM)'s hierarchical
|
1929
|
+
clustering functions.
|
1930
|
+
|
1931
|
+
The return linkage matrix has the last column removed
|
1932
|
+
and the cluster indices are converted to ``1..N`` indexing.
|
1933
|
+
|
1934
|
+
See Also
|
1935
|
+
--------
|
1936
|
+
linkage : for a description of what a linkage matrix is.
|
1937
|
+
from_mlab_linkage : transform from Matlab to SciPy format.
|
1938
|
+
|
1939
|
+
Examples
|
1940
|
+
--------
|
1941
|
+
>>> from scipy.cluster.hierarchy import ward, to_mlab_linkage
|
1942
|
+
>>> from scipy.spatial.distance import pdist
|
1943
|
+
|
1944
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
1945
|
+
... [0, 4], [0, 3], [1, 4],
|
1946
|
+
... [4, 0], [3, 0], [4, 1],
|
1947
|
+
... [4, 4], [3, 4], [4, 3]]
|
1948
|
+
|
1949
|
+
>>> Z = ward(pdist(X))
|
1950
|
+
>>> Z
|
1951
|
+
array([[ 0. , 1. , 1. , 2. ],
|
1952
|
+
[ 3. , 4. , 1. , 2. ],
|
1953
|
+
[ 6. , 7. , 1. , 2. ],
|
1954
|
+
[ 9. , 10. , 1. , 2. ],
|
1955
|
+
[ 2. , 12. , 1.29099445, 3. ],
|
1956
|
+
[ 5. , 13. , 1.29099445, 3. ],
|
1957
|
+
[ 8. , 14. , 1.29099445, 3. ],
|
1958
|
+
[11. , 15. , 1.29099445, 3. ],
|
1959
|
+
[16. , 17. , 5.77350269, 6. ],
|
1960
|
+
[18. , 19. , 5.77350269, 6. ],
|
1961
|
+
[20. , 21. , 8.16496581, 12. ]])
|
1962
|
+
|
1963
|
+
After a linkage matrix ``Z`` has been created, we can use
|
1964
|
+
`scipy.cluster.hierarchy.to_mlab_linkage` to convert it
|
1965
|
+
into MATLAB format:
|
1966
|
+
|
1967
|
+
>>> mZ = to_mlab_linkage(Z)
|
1968
|
+
>>> mZ
|
1969
|
+
array([[ 1. , 2. , 1. ],
|
1970
|
+
[ 4. , 5. , 1. ],
|
1971
|
+
[ 7. , 8. , 1. ],
|
1972
|
+
[ 10. , 11. , 1. ],
|
1973
|
+
[ 3. , 13. , 1.29099445],
|
1974
|
+
[ 6. , 14. , 1.29099445],
|
1975
|
+
[ 9. , 15. , 1.29099445],
|
1976
|
+
[ 12. , 16. , 1.29099445],
|
1977
|
+
[ 17. , 18. , 5.77350269],
|
1978
|
+
[ 19. , 20. , 5.77350269],
|
1979
|
+
[ 21. , 22. , 8.16496581]])
|
1980
|
+
|
1981
|
+
The new linkage matrix ``mZ`` uses 1-indexing for all the
|
1982
|
+
clusters (instead of 0-indexing). Also, the last column of
|
1983
|
+
the original linkage matrix has been dropped.
|
1984
|
+
|
1985
|
+
"""
|
1986
|
+
xp = array_namespace(Z)
|
1987
|
+
Z = _asarray(Z, dtype=xp.float64, xp=xp)
|
1988
|
+
if Z.shape in ((), (0, )):
|
1989
|
+
return xp_copy(Z, xp=xp)
|
1990
|
+
_is_valid_linkage(Z, throw=True, name='Z', xp=xp)
|
1991
|
+
|
1992
|
+
return xp.concat((Z[:, :2] + 1.0, Z[:, 2:3]), axis=1)
|
1993
|
+
|
1994
|
+
|
1995
|
+
@xp_capabilities()
|
1996
|
+
def is_monotonic(Z):
|
1997
|
+
"""
|
1998
|
+
Return True if the linkage passed is monotonic.
|
1999
|
+
|
2000
|
+
The linkage is monotonic if for every cluster :math:`s` and :math:`t`
|
2001
|
+
joined, the distance between them is no less than the distance
|
2002
|
+
between any previously joined clusters.
|
2003
|
+
|
2004
|
+
Parameters
|
2005
|
+
----------
|
2006
|
+
Z : ndarray
|
2007
|
+
The linkage matrix to check for monotonicity.
|
2008
|
+
|
2009
|
+
Returns
|
2010
|
+
-------
|
2011
|
+
b : bool
|
2012
|
+
A boolean indicating whether the linkage is monotonic.
|
2013
|
+
|
2014
|
+
See Also
|
2015
|
+
--------
|
2016
|
+
linkage : for a description of what a linkage matrix is.
|
2017
|
+
|
2018
|
+
Examples
|
2019
|
+
--------
|
2020
|
+
>>> from scipy.cluster.hierarchy import median, ward, is_monotonic
|
2021
|
+
>>> from scipy.spatial.distance import pdist
|
2022
|
+
|
2023
|
+
By definition, some hierarchical clustering algorithms - such as
|
2024
|
+
`scipy.cluster.hierarchy.ward` - produce monotonic assignments of
|
2025
|
+
samples to clusters; however, this is not always true for other
|
2026
|
+
hierarchical methods - e.g. `scipy.cluster.hierarchy.median`.
|
2027
|
+
|
2028
|
+
Given a linkage matrix ``Z`` (as the result of a hierarchical clustering
|
2029
|
+
method) we can test programmatically whether it has the monotonicity
|
2030
|
+
property or not, using `scipy.cluster.hierarchy.is_monotonic`:
|
2031
|
+
|
2032
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
2033
|
+
... [0, 4], [0, 3], [1, 4],
|
2034
|
+
... [4, 0], [3, 0], [4, 1],
|
2035
|
+
... [4, 4], [3, 4], [4, 3]]
|
2036
|
+
|
2037
|
+
>>> Z = ward(pdist(X))
|
2038
|
+
>>> Z
|
2039
|
+
array([[ 0. , 1. , 1. , 2. ],
|
2040
|
+
[ 3. , 4. , 1. , 2. ],
|
2041
|
+
[ 6. , 7. , 1. , 2. ],
|
2042
|
+
[ 9. , 10. , 1. , 2. ],
|
2043
|
+
[ 2. , 12. , 1.29099445, 3. ],
|
2044
|
+
[ 5. , 13. , 1.29099445, 3. ],
|
2045
|
+
[ 8. , 14. , 1.29099445, 3. ],
|
2046
|
+
[11. , 15. , 1.29099445, 3. ],
|
2047
|
+
[16. , 17. , 5.77350269, 6. ],
|
2048
|
+
[18. , 19. , 5.77350269, 6. ],
|
2049
|
+
[20. , 21. , 8.16496581, 12. ]])
|
2050
|
+
>>> is_monotonic(Z)
|
2051
|
+
True
|
2052
|
+
|
2053
|
+
>>> Z = median(pdist(X))
|
2054
|
+
>>> Z
|
2055
|
+
array([[ 0. , 1. , 1. , 2. ],
|
2056
|
+
[ 3. , 4. , 1. , 2. ],
|
2057
|
+
[ 9. , 10. , 1. , 2. ],
|
2058
|
+
[ 6. , 7. , 1. , 2. ],
|
2059
|
+
[ 2. , 12. , 1.11803399, 3. ],
|
2060
|
+
[ 5. , 13. , 1.11803399, 3. ],
|
2061
|
+
[ 8. , 15. , 1.11803399, 3. ],
|
2062
|
+
[11. , 14. , 1.11803399, 3. ],
|
2063
|
+
[18. , 19. , 3. , 6. ],
|
2064
|
+
[16. , 17. , 3.5 , 6. ],
|
2065
|
+
[20. , 21. , 3.25 , 12. ]])
|
2066
|
+
>>> is_monotonic(Z)
|
2067
|
+
False
|
2068
|
+
|
2069
|
+
Note that this method is equivalent to just verifying that the distances
|
2070
|
+
in the third column of the linkage matrix appear in a monotonically
|
2071
|
+
increasing order.
|
2072
|
+
|
2073
|
+
"""
|
2074
|
+
xp = array_namespace(Z)
|
2075
|
+
Z = _asarray(Z, xp=xp)
|
2076
|
+
_is_valid_linkage(Z, throw=True, name='Z', xp=xp)
|
2077
|
+
|
2078
|
+
# We expect the i'th value to be greater than its successor.
|
2079
|
+
return xp.all(Z[1:, 2] >= Z[:-1, 2])
|
2080
|
+
|
2081
|
+
|
2082
|
+
@xp_capabilities(warnings=[("dask.array", "see notes"), ("jax.numpy", "see notes")])
|
2083
|
+
def is_valid_im(R, warning=False, throw=False, name=None):
|
2084
|
+
"""Return True if the inconsistency matrix passed is valid.
|
2085
|
+
|
2086
|
+
It must be a :math:`n` by 4 array of doubles. The standard
|
2087
|
+
deviations ``R[:,1]`` must be nonnegative. The link counts
|
2088
|
+
``R[:,2]`` must be positive and no greater than :math:`n-1`.
|
2089
|
+
|
2090
|
+
Parameters
|
2091
|
+
----------
|
2092
|
+
R : ndarray
|
2093
|
+
The inconsistency matrix to check for validity.
|
2094
|
+
warning : bool, optional
|
2095
|
+
When True, issues a Python warning if the linkage
|
2096
|
+
matrix passed is invalid.
|
2097
|
+
throw : bool, optional
|
2098
|
+
When True, throws a Python exception if the linkage
|
2099
|
+
matrix passed is invalid.
|
2100
|
+
name : str, optional
|
2101
|
+
This string refers to the variable name of the invalid
|
2102
|
+
linkage matrix.
|
2103
|
+
|
2104
|
+
Returns
|
2105
|
+
-------
|
2106
|
+
b : bool
|
2107
|
+
True if the inconsistency matrix is valid; False otherwise.
|
2108
|
+
|
2109
|
+
Notes
|
2110
|
+
-----
|
2111
|
+
*Array API support (experimental):* If the input is a lazy Array (e.g. Dask
|
2112
|
+
or JAX), the return value may be a 0-dimensional bool Array. When warning=True
|
2113
|
+
or throw=True, calling this function materializes the array.
|
2114
|
+
|
2115
|
+
See Also
|
2116
|
+
--------
|
2117
|
+
linkage : for a description of what a linkage matrix is.
|
2118
|
+
inconsistent : for the creation of a inconsistency matrix.
|
2119
|
+
|
2120
|
+
Examples
|
2121
|
+
--------
|
2122
|
+
>>> from scipy.cluster.hierarchy import ward, inconsistent, is_valid_im
|
2123
|
+
>>> from scipy.spatial.distance import pdist
|
2124
|
+
|
2125
|
+
Given a data set ``X``, we can apply a clustering method to obtain a
|
2126
|
+
linkage matrix ``Z``. `scipy.cluster.hierarchy.inconsistent` can
|
2127
|
+
be also used to obtain the inconsistency matrix ``R`` associated to
|
2128
|
+
this clustering process:
|
2129
|
+
|
2130
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
2131
|
+
... [0, 4], [0, 3], [1, 4],
|
2132
|
+
... [4, 0], [3, 0], [4, 1],
|
2133
|
+
... [4, 4], [3, 4], [4, 3]]
|
2134
|
+
|
2135
|
+
>>> Z = ward(pdist(X))
|
2136
|
+
>>> R = inconsistent(Z)
|
2137
|
+
>>> Z
|
2138
|
+
array([[ 0. , 1. , 1. , 2. ],
|
2139
|
+
[ 3. , 4. , 1. , 2. ],
|
2140
|
+
[ 6. , 7. , 1. , 2. ],
|
2141
|
+
[ 9. , 10. , 1. , 2. ],
|
2142
|
+
[ 2. , 12. , 1.29099445, 3. ],
|
2143
|
+
[ 5. , 13. , 1.29099445, 3. ],
|
2144
|
+
[ 8. , 14. , 1.29099445, 3. ],
|
2145
|
+
[11. , 15. , 1.29099445, 3. ],
|
2146
|
+
[16. , 17. , 5.77350269, 6. ],
|
2147
|
+
[18. , 19. , 5.77350269, 6. ],
|
2148
|
+
[20. , 21. , 8.16496581, 12. ]])
|
2149
|
+
>>> R
|
2150
|
+
array([[1. , 0. , 1. , 0. ],
|
2151
|
+
[1. , 0. , 1. , 0. ],
|
2152
|
+
[1. , 0. , 1. , 0. ],
|
2153
|
+
[1. , 0. , 1. , 0. ],
|
2154
|
+
[1.14549722, 0.20576415, 2. , 0.70710678],
|
2155
|
+
[1.14549722, 0.20576415, 2. , 0.70710678],
|
2156
|
+
[1.14549722, 0.20576415, 2. , 0.70710678],
|
2157
|
+
[1.14549722, 0.20576415, 2. , 0.70710678],
|
2158
|
+
[2.78516386, 2.58797734, 3. , 1.15470054],
|
2159
|
+
[2.78516386, 2.58797734, 3. , 1.15470054],
|
2160
|
+
[6.57065706, 1.38071187, 3. , 1.15470054]])
|
2161
|
+
|
2162
|
+
Now we can use `scipy.cluster.hierarchy.is_valid_im` to verify that
|
2163
|
+
``R`` is correct:
|
2164
|
+
|
2165
|
+
>>> is_valid_im(R)
|
2166
|
+
True
|
2167
|
+
|
2168
|
+
However, if ``R`` is wrongly constructed (e.g., one of the standard
|
2169
|
+
deviations is set to a negative value), then the check will fail:
|
2170
|
+
|
2171
|
+
>>> R[-1,1] = R[-1,1] * -1
|
2172
|
+
>>> is_valid_im(R)
|
2173
|
+
False
|
2174
|
+
|
2175
|
+
"""
|
2176
|
+
xp = array_namespace(R)
|
2177
|
+
R = _asarray(R, xp=xp)
|
2178
|
+
return _is_valid_im(R, warning=warning, throw=throw, name=name,
|
2179
|
+
materialize=True, xp=xp)
|
2180
|
+
|
2181
|
+
|
2182
|
+
def _is_valid_im(R, warning=False, throw=False, name=None, materialize=False, *, xp):
|
2183
|
+
"""Variant of `is_valid_im` to be called internally by other scipy functions,
|
2184
|
+
which by default does not materialize lazy input arrays (Dask, JAX, etc.) when
|
2185
|
+
warning=True or throw=True.
|
2186
|
+
"""
|
2187
|
+
name_str = f"{name!r} " if name else ''
|
2188
|
+
try:
|
2189
|
+
if R.dtype != xp.float64:
|
2190
|
+
raise TypeError(f'Inconsistency matrix {name_str}must contain doubles '
|
2191
|
+
'(double).')
|
2192
|
+
if len(R.shape) != 2:
|
2193
|
+
raise ValueError(f'Inconsistency matrix {name_str}must have shape=2 (i.e. '
|
2194
|
+
'be two-dimensional).')
|
2195
|
+
if R.shape[1] != 4:
|
2196
|
+
raise ValueError(f'Inconsistency matrix {name_str}'
|
2197
|
+
'must have 4 columns.')
|
2198
|
+
if R.shape[0] < 1:
|
2199
|
+
raise ValueError(f'Inconsistency matrix {name_str}'
|
2200
|
+
'must have at least one row.')
|
2201
|
+
except (TypeError, ValueError) as e:
|
2202
|
+
if throw:
|
2203
|
+
raise
|
2204
|
+
if warning:
|
2205
|
+
_warning(str(e))
|
2206
|
+
return False
|
2207
|
+
|
2208
|
+
return _lazy_valid_checks(
|
2209
|
+
(xp.any(R[:, 0] < 0),
|
2210
|
+
f'Inconsistency matrix {name_str} contains negative link height means.'),
|
2211
|
+
(xp.any(R[:, 1] < 0),
|
2212
|
+
f'Inconsistency matrix {name_str} contains negative link height standard '
|
2213
|
+
'deviations.'),
|
2214
|
+
(xp.any(R[:, 2] < 0),
|
2215
|
+
f'Inconsistency matrix {name_str} contains negative link counts.'),
|
2216
|
+
throw=throw, warning=warning, materialize=materialize, xp=xp
|
2217
|
+
)
|
2218
|
+
|
2219
|
+
|
2220
|
+
@xp_capabilities(warnings=[("dask.array", "see notes"), ("jax.numpy", "see notes")])
|
2221
|
+
def is_valid_linkage(Z, warning=False, throw=False, name=None):
|
2222
|
+
"""
|
2223
|
+
Check the validity of a linkage matrix.
|
2224
|
+
|
2225
|
+
A linkage matrix is valid if it is a 2-D array (type double)
|
2226
|
+
with :math:`n` rows and 4 columns. The first two columns must contain
|
2227
|
+
indices between 0 and :math:`2n-1`. For a given row ``i``, the following
|
2228
|
+
two expressions have to hold:
|
2229
|
+
|
2230
|
+
.. math::
|
2231
|
+
|
2232
|
+
0 \\leq \\mathtt{Z[i,0]} \\leq i+n-1
|
2233
|
+
0 \\leq Z[i,1] \\leq i+n-1
|
2234
|
+
|
2235
|
+
I.e., a cluster cannot join another cluster unless the cluster being joined
|
2236
|
+
has been generated.
|
2237
|
+
|
2238
|
+
The fourth column of `Z` represents the number of original observations
|
2239
|
+
in a cluster, so a valid ``Z[i, 3]`` value may not exceed the number of
|
2240
|
+
original observations.
|
2241
|
+
|
2242
|
+
Parameters
|
2243
|
+
----------
|
2244
|
+
Z : array_like
|
2245
|
+
Linkage matrix.
|
2246
|
+
warning : bool, optional
|
2247
|
+
When True, issues a Python warning if the linkage
|
2248
|
+
matrix passed is invalid.
|
2249
|
+
throw : bool, optional
|
2250
|
+
When True, throws a Python exception if the linkage
|
2251
|
+
matrix passed is invalid.
|
2252
|
+
name : str, optional
|
2253
|
+
This string refers to the variable name of the invalid
|
2254
|
+
linkage matrix.
|
2255
|
+
|
2256
|
+
Returns
|
2257
|
+
-------
|
2258
|
+
b : bool
|
2259
|
+
True if the inconsistency matrix is valid; False otherwise.
|
2260
|
+
|
2261
|
+
Notes
|
2262
|
+
-----
|
2263
|
+
*Array API support (experimental):* If the input is a lazy Array (e.g. Dask
|
2264
|
+
or JAX), the return value may be a 0-dimensional bool Array. When warning=True
|
2265
|
+
or throw=True, calling this function materializes the array.
|
2266
|
+
|
2267
|
+
See Also
|
2268
|
+
--------
|
2269
|
+
linkage: for a description of what a linkage matrix is.
|
2270
|
+
|
2271
|
+
Examples
|
2272
|
+
--------
|
2273
|
+
>>> from scipy.cluster.hierarchy import ward, is_valid_linkage
|
2274
|
+
>>> from scipy.spatial.distance import pdist
|
2275
|
+
|
2276
|
+
All linkage matrices generated by the clustering methods in this module
|
2277
|
+
will be valid (i.e., they will have the appropriate dimensions and the two
|
2278
|
+
required expressions will hold for all the rows).
|
2279
|
+
|
2280
|
+
We can check this using `scipy.cluster.hierarchy.is_valid_linkage`:
|
2281
|
+
|
2282
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
2283
|
+
... [0, 4], [0, 3], [1, 4],
|
2284
|
+
... [4, 0], [3, 0], [4, 1],
|
2285
|
+
... [4, 4], [3, 4], [4, 3]]
|
2286
|
+
|
2287
|
+
>>> Z = ward(pdist(X))
|
2288
|
+
>>> Z
|
2289
|
+
array([[ 0. , 1. , 1. , 2. ],
|
2290
|
+
[ 3. , 4. , 1. , 2. ],
|
2291
|
+
[ 6. , 7. , 1. , 2. ],
|
2292
|
+
[ 9. , 10. , 1. , 2. ],
|
2293
|
+
[ 2. , 12. , 1.29099445, 3. ],
|
2294
|
+
[ 5. , 13. , 1.29099445, 3. ],
|
2295
|
+
[ 8. , 14. , 1.29099445, 3. ],
|
2296
|
+
[11. , 15. , 1.29099445, 3. ],
|
2297
|
+
[16. , 17. , 5.77350269, 6. ],
|
2298
|
+
[18. , 19. , 5.77350269, 6. ],
|
2299
|
+
[20. , 21. , 8.16496581, 12. ]])
|
2300
|
+
>>> is_valid_linkage(Z)
|
2301
|
+
True
|
2302
|
+
|
2303
|
+
However, if we create a linkage matrix in a wrong way - or if we modify
|
2304
|
+
a valid one in a way that any of the required expressions don't hold
|
2305
|
+
anymore, then the check will fail:
|
2306
|
+
|
2307
|
+
>>> Z[3][1] = 20 # the cluster number 20 is not defined at this point
|
2308
|
+
>>> is_valid_linkage(Z)
|
2309
|
+
False
|
2310
|
+
|
2311
|
+
"""
|
2312
|
+
xp = array_namespace(Z)
|
2313
|
+
Z = _asarray(Z, xp=xp)
|
2314
|
+
return _is_valid_linkage(Z, warning=warning, throw=throw,
|
2315
|
+
name=name, materialize=True, xp=xp)
|
2316
|
+
|
2317
|
+
|
2318
|
+
def _is_valid_linkage(Z, warning=False, throw=False, name=None,
|
2319
|
+
materialize=False, *, xp):
|
2320
|
+
"""Variant of `is_valid_linkage` to be called internally by other scipy functions,
|
2321
|
+
which by default does not materialize lazy input arrays (Dask, JAX, etc.) when
|
2322
|
+
warning=True or throw=True.
|
2323
|
+
"""
|
2324
|
+
name_str = f"{name!r} " if name else ''
|
2325
|
+
try:
|
2326
|
+
if Z.dtype != xp.float64:
|
2327
|
+
raise TypeError(f'Linkage matrix {name_str}must contain doubles.')
|
2328
|
+
if len(Z.shape) != 2:
|
2329
|
+
raise ValueError(f'Linkage matrix {name_str}must have shape=2 (i.e. be'
|
2330
|
+
' two-dimensional).')
|
2331
|
+
if Z.shape[1] != 4:
|
2332
|
+
raise ValueError(f'Linkage matrix {name_str}must have 4 columns.')
|
2333
|
+
if Z.shape[0] == 0:
|
2334
|
+
raise ValueError('Linkage must be computed on at least two '
|
2335
|
+
'observations.')
|
2336
|
+
except (TypeError, ValueError) as e:
|
2337
|
+
if throw:
|
2338
|
+
raise
|
2339
|
+
if warning:
|
2340
|
+
_warning(str(e))
|
2341
|
+
return False
|
2342
|
+
|
2343
|
+
n = Z.shape[0]
|
2344
|
+
if n < 2:
|
2345
|
+
return True
|
2346
|
+
|
2347
|
+
return _lazy_valid_checks(
|
2348
|
+
(xp.any(Z[:, :2] < 0),
|
2349
|
+
f'Linkage {name_str}contains negative indices.'),
|
2350
|
+
(xp.any(Z[:, 2] < 0),
|
2351
|
+
f'Linkage {name_str}contains negative distances.'),
|
2352
|
+
(xp.any(Z[:, 3] < 0),
|
2353
|
+
f'Linkage {name_str}contains negative counts.'),
|
2354
|
+
(xp.any(Z[:, 3] > n + 1),
|
2355
|
+
f'Linkage {name_str}contains excessive observations in a cluster'),
|
2356
|
+
(xp.any(xp.max(Z[:, :2], axis=1) >= xp.arange(n + 1, 2 * n + 1, dtype=Z.dtype)),
|
2357
|
+
f'Linkage {name_str}uses non-singleton cluster before it is formed.'),
|
2358
|
+
(xpx.nunique(Z[:, :2]) < n * 2,
|
2359
|
+
f'Linkage {name_str}uses the same cluster more than once.'),
|
2360
|
+
throw=throw, warning=warning, materialize=materialize, xp=xp
|
2361
|
+
)
|
2362
|
+
|
2363
|
+
|
2364
|
+
def _lazy_valid_checks(*args, throw=False, warning=False, materialize=False, xp):
|
2365
|
+
"""Validate a set of conditions on the contents of possibly lazy arrays.
|
2366
|
+
|
2367
|
+
Parameters
|
2368
|
+
----------
|
2369
|
+
args : tuples of (Array, str)
|
2370
|
+
The first element of each tuple must be a 0-dimensional Array
|
2371
|
+
that evaluates to bool; the second element must be the message to convey
|
2372
|
+
if the first element evaluates to True.
|
2373
|
+
throw: bool
|
2374
|
+
Set to True to `raise ValueError(args[i][1])` if `args[i][0]` is True.
|
2375
|
+
warning: bool
|
2376
|
+
Set to True to issue a warning with message `args[i][1]` if `args[i][0]`
|
2377
|
+
is True.
|
2378
|
+
materialize: bool
|
2379
|
+
Set to True to force materialization of lazy arrays when throw=True or
|
2380
|
+
warning=True. If the inputs are lazy and materialize=False, ignore the
|
2381
|
+
`throw` and `warning` flags.
|
2382
|
+
xp: module
|
2383
|
+
Array API namespace
|
2384
|
+
|
2385
|
+
Returns
|
2386
|
+
-------
|
2387
|
+
If xp is an eager backend (e.g. numpy) and all conditions are False, return True.
|
2388
|
+
If throw is True, raise. Otherwise, return False.
|
2389
|
+
|
2390
|
+
If xp is a lazy backend (e.g. Dask or JAX), return a 0-dimensional bool Array.
|
2391
|
+
"""
|
2392
|
+
conds = xp.concat([xp.reshape(cond, (1, )) for cond, _ in args])
|
2393
|
+
|
2394
|
+
lazy = is_lazy_array(conds)
|
2395
|
+
if not throw and not warning or (lazy and not materialize):
|
2396
|
+
out = ~xp.any(conds)
|
2397
|
+
return out if lazy else bool(out)
|
2398
|
+
|
2399
|
+
if is_dask(xp):
|
2400
|
+
# Only materialize the graph once, instead of once per check
|
2401
|
+
conds = conds.compute()
|
2402
|
+
|
2403
|
+
# Don't call np.asarray(conds), as it would be blocked by the device transfer
|
2404
|
+
# guard on CuPy and PyTorch and the densification guard on Sparse, whereas
|
2405
|
+
# bool() will not.
|
2406
|
+
conds = [bool(cond) for cond in conds]
|
2407
|
+
|
2408
|
+
for cond, (_, msg) in zip(conds, args):
|
2409
|
+
if throw and cond:
|
2410
|
+
raise ValueError(msg)
|
2411
|
+
elif warning and cond:
|
2412
|
+
warnings.warn(msg, ClusterWarning, stacklevel=3)
|
2413
|
+
|
2414
|
+
return not any(conds)
|
2415
|
+
|
2416
|
+
|
2417
|
+
@xp_capabilities()
|
2418
|
+
def num_obs_linkage(Z):
|
2419
|
+
"""
|
2420
|
+
Return the number of original observations of the linkage matrix passed.
|
2421
|
+
|
2422
|
+
Parameters
|
2423
|
+
----------
|
2424
|
+
Z : ndarray
|
2425
|
+
The linkage matrix on which to perform the operation.
|
2426
|
+
|
2427
|
+
Returns
|
2428
|
+
-------
|
2429
|
+
n : int
|
2430
|
+
The number of original observations in the linkage.
|
2431
|
+
|
2432
|
+
Examples
|
2433
|
+
--------
|
2434
|
+
>>> from scipy.cluster.hierarchy import ward, num_obs_linkage
|
2435
|
+
>>> from scipy.spatial.distance import pdist
|
2436
|
+
|
2437
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
2438
|
+
... [0, 4], [0, 3], [1, 4],
|
2439
|
+
... [4, 0], [3, 0], [4, 1],
|
2440
|
+
... [4, 4], [3, 4], [4, 3]]
|
2441
|
+
|
2442
|
+
>>> Z = ward(pdist(X))
|
2443
|
+
|
2444
|
+
``Z`` is a linkage matrix obtained after using the Ward clustering method
|
2445
|
+
with ``X``, a dataset with 12 data points.
|
2446
|
+
|
2447
|
+
>>> num_obs_linkage(Z)
|
2448
|
+
12
|
2449
|
+
|
2450
|
+
"""
|
2451
|
+
xp = array_namespace(Z)
|
2452
|
+
Z = _asarray(Z, xp=xp)
|
2453
|
+
_is_valid_linkage(Z, throw=True, name='Z', xp=xp)
|
2454
|
+
return Z.shape[0] + 1
|
2455
|
+
|
2456
|
+
|
2457
|
+
@xp_capabilities()
|
2458
|
+
def correspond(Z, Y):
|
2459
|
+
"""
|
2460
|
+
Check for correspondence between linkage and condensed distance matrices.
|
2461
|
+
|
2462
|
+
They must have the same number of original observations for
|
2463
|
+
the check to succeed.
|
2464
|
+
|
2465
|
+
This function is useful as a sanity check in algorithms that make
|
2466
|
+
extensive use of linkage and distance matrices that must
|
2467
|
+
correspond to the same set of original observations.
|
2468
|
+
|
2469
|
+
Parameters
|
2470
|
+
----------
|
2471
|
+
Z : array_like
|
2472
|
+
The linkage matrix to check for correspondence.
|
2473
|
+
Y : array_like
|
2474
|
+
The condensed distance matrix to check for correspondence.
|
2475
|
+
|
2476
|
+
Returns
|
2477
|
+
-------
|
2478
|
+
b : bool
|
2479
|
+
A boolean indicating whether the linkage matrix and distance
|
2480
|
+
matrix could possibly correspond to one another.
|
2481
|
+
|
2482
|
+
See Also
|
2483
|
+
--------
|
2484
|
+
linkage : for a description of what a linkage matrix is.
|
2485
|
+
|
2486
|
+
Examples
|
2487
|
+
--------
|
2488
|
+
>>> from scipy.cluster.hierarchy import ward, correspond
|
2489
|
+
>>> from scipy.spatial.distance import pdist
|
2490
|
+
|
2491
|
+
This method can be used to check if a given linkage matrix ``Z`` has been
|
2492
|
+
obtained from the application of a cluster method over a dataset ``X``:
|
2493
|
+
|
2494
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
2495
|
+
... [0, 4], [0, 3], [1, 4],
|
2496
|
+
... [4, 0], [3, 0], [4, 1],
|
2497
|
+
... [4, 4], [3, 4], [4, 3]]
|
2498
|
+
>>> X_condensed = pdist(X)
|
2499
|
+
>>> Z = ward(X_condensed)
|
2500
|
+
|
2501
|
+
Here, we can compare ``Z`` and ``X`` (in condensed form):
|
2502
|
+
|
2503
|
+
>>> correspond(Z, X_condensed)
|
2504
|
+
True
|
2505
|
+
|
2506
|
+
"""
|
2507
|
+
xp = array_namespace(Z, Y)
|
2508
|
+
Z = _asarray(Z, xp=xp)
|
2509
|
+
Y = _asarray(Y, xp=xp)
|
2510
|
+
_is_valid_linkage(Z, throw=True, xp=xp)
|
2511
|
+
distance.is_valid_y(Y, throw=True)
|
2512
|
+
return distance.num_obs_y(Y) == num_obs_linkage(Z)
|
2513
|
+
|
2514
|
+
|
2515
|
+
@xp_capabilities(cpu_only=True, reason="Cython code",
|
2516
|
+
jax_jit=False, allow_dask_compute=True)
|
2517
|
+
def fcluster(Z, t, criterion='inconsistent', depth=2, R=None, monocrit=None):
|
2518
|
+
"""
|
2519
|
+
Form flat clusters from the hierarchical clustering defined by
|
2520
|
+
the given linkage matrix.
|
2521
|
+
|
2522
|
+
Parameters
|
2523
|
+
----------
|
2524
|
+
Z : ndarray
|
2525
|
+
The hierarchical clustering encoded with the matrix returned
|
2526
|
+
by the `linkage` function.
|
2527
|
+
t : scalar
|
2528
|
+
For criteria 'inconsistent', 'distance' or 'monocrit',
|
2529
|
+
this is the threshold to apply when forming flat clusters.
|
2530
|
+
For 'maxclust' or 'maxclust_monocrit' criteria,
|
2531
|
+
this would be max number of clusters requested.
|
2532
|
+
criterion : str, optional
|
2533
|
+
The criterion to use in forming flat clusters. This can
|
2534
|
+
be any of the following values:
|
2535
|
+
|
2536
|
+
``inconsistent`` :
|
2537
|
+
If a cluster node and all its
|
2538
|
+
descendants have an inconsistent value less than or equal
|
2539
|
+
to `t`, then all its leaf descendants belong to the
|
2540
|
+
same flat cluster. When no non-singleton cluster meets
|
2541
|
+
this criterion, every node is assigned to its own
|
2542
|
+
cluster. (Default)
|
2543
|
+
|
2544
|
+
``distance`` :
|
2545
|
+
Forms flat clusters so that the original
|
2546
|
+
observations in each flat cluster have no greater a
|
2547
|
+
cophenetic distance than `t`.
|
2548
|
+
|
2549
|
+
``maxclust`` :
|
2550
|
+
Finds a minimum threshold ``r`` so that
|
2551
|
+
the cophenetic distance between any two original
|
2552
|
+
observations in the same flat cluster is no more than
|
2553
|
+
``r`` and no more than `t` flat clusters are formed.
|
2554
|
+
|
2555
|
+
``monocrit`` :
|
2556
|
+
Forms a flat cluster from a cluster node c
|
2557
|
+
with index i when ``monocrit[j] <= t``.
|
2558
|
+
|
2559
|
+
For example, to threshold on the maximum mean distance
|
2560
|
+
as computed in the inconsistency matrix R with a
|
2561
|
+
threshold of 0.8 do::
|
2562
|
+
|
2563
|
+
MR = maxRstat(Z, R, 3)
|
2564
|
+
fcluster(Z, t=0.8, criterion='monocrit', monocrit=MR)
|
2565
|
+
|
2566
|
+
``maxclust_monocrit`` :
|
2567
|
+
Forms a flat cluster from a
|
2568
|
+
non-singleton cluster node ``c`` when ``monocrit[i] <=
|
2569
|
+
r`` for all cluster indices ``i`` below and including
|
2570
|
+
``c``. ``r`` is minimized such that no more than ``t``
|
2571
|
+
flat clusters are formed. monocrit must be
|
2572
|
+
monotonic. For example, to minimize the threshold t on
|
2573
|
+
maximum inconsistency values so that no more than 3 flat
|
2574
|
+
clusters are formed, do::
|
2575
|
+
|
2576
|
+
MI = maxinconsts(Z, R)
|
2577
|
+
fcluster(Z, t=3, criterion='maxclust_monocrit', monocrit=MI)
|
2578
|
+
depth : int, optional
|
2579
|
+
The maximum depth to perform the inconsistency calculation.
|
2580
|
+
It has no meaning for the other criteria. Default is 2.
|
2581
|
+
R : ndarray, optional
|
2582
|
+
The inconsistency matrix to use for the ``'inconsistent'``
|
2583
|
+
criterion. This matrix is computed if not provided.
|
2584
|
+
monocrit : ndarray, optional
|
2585
|
+
An array of length n-1. `monocrit[i]` is the
|
2586
|
+
statistics upon which non-singleton i is thresholded. The
|
2587
|
+
monocrit vector must be monotonic, i.e., given a node c with
|
2588
|
+
index i, for all node indices j corresponding to nodes
|
2589
|
+
below c, ``monocrit[i] >= monocrit[j]``.
|
2590
|
+
|
2591
|
+
Returns
|
2592
|
+
-------
|
2593
|
+
fcluster : ndarray
|
2594
|
+
An array of length ``n``. ``T[i]`` is the flat cluster number to
|
2595
|
+
which original observation ``i`` belongs.
|
2596
|
+
|
2597
|
+
See Also
|
2598
|
+
--------
|
2599
|
+
linkage : for information about hierarchical clustering methods work.
|
2600
|
+
|
2601
|
+
Examples
|
2602
|
+
--------
|
2603
|
+
>>> from scipy.cluster.hierarchy import ward, fcluster
|
2604
|
+
>>> from scipy.spatial.distance import pdist
|
2605
|
+
|
2606
|
+
All cluster linkage methods - e.g., `scipy.cluster.hierarchy.ward`
|
2607
|
+
generate a linkage matrix ``Z`` as their output:
|
2608
|
+
|
2609
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
2610
|
+
... [0, 4], [0, 3], [1, 4],
|
2611
|
+
... [4, 0], [3, 0], [4, 1],
|
2612
|
+
... [4, 4], [3, 4], [4, 3]]
|
2613
|
+
|
2614
|
+
>>> Z = ward(pdist(X))
|
2615
|
+
|
2616
|
+
>>> Z
|
2617
|
+
array([[ 0. , 1. , 1. , 2. ],
|
2618
|
+
[ 3. , 4. , 1. , 2. ],
|
2619
|
+
[ 6. , 7. , 1. , 2. ],
|
2620
|
+
[ 9. , 10. , 1. , 2. ],
|
2621
|
+
[ 2. , 12. , 1.29099445, 3. ],
|
2622
|
+
[ 5. , 13. , 1.29099445, 3. ],
|
2623
|
+
[ 8. , 14. , 1.29099445, 3. ],
|
2624
|
+
[11. , 15. , 1.29099445, 3. ],
|
2625
|
+
[16. , 17. , 5.77350269, 6. ],
|
2626
|
+
[18. , 19. , 5.77350269, 6. ],
|
2627
|
+
[20. , 21. , 8.16496581, 12. ]])
|
2628
|
+
|
2629
|
+
This matrix represents a dendrogram, where the first and second elements
|
2630
|
+
are the two clusters merged at each step, the third element is the
|
2631
|
+
distance between these clusters, and the fourth element is the size of
|
2632
|
+
the new cluster - the number of original data points included.
|
2633
|
+
|
2634
|
+
`scipy.cluster.hierarchy.fcluster` can be used to flatten the
|
2635
|
+
dendrogram, obtaining as a result an assignation of the original data
|
2636
|
+
points to single clusters.
|
2637
|
+
|
2638
|
+
This assignation mostly depends on a distance threshold ``t`` - the maximum
|
2639
|
+
inter-cluster distance allowed:
|
2640
|
+
|
2641
|
+
>>> fcluster(Z, t=0.9, criterion='distance')
|
2642
|
+
array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], dtype=int32)
|
2643
|
+
|
2644
|
+
>>> fcluster(Z, t=1.1, criterion='distance')
|
2645
|
+
array([1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8], dtype=int32)
|
2646
|
+
|
2647
|
+
>>> fcluster(Z, t=3, criterion='distance')
|
2648
|
+
array([1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4], dtype=int32)
|
2649
|
+
|
2650
|
+
>>> fcluster(Z, t=9, criterion='distance')
|
2651
|
+
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int32)
|
2652
|
+
|
2653
|
+
In the first case, the threshold ``t`` is too small to allow any two
|
2654
|
+
samples in the data to form a cluster, so 12 different clusters are
|
2655
|
+
returned.
|
2656
|
+
|
2657
|
+
In the second case, the threshold is large enough to allow the first
|
2658
|
+
4 points to be merged with their nearest neighbors. So, here, only 8
|
2659
|
+
clusters are returned.
|
2660
|
+
|
2661
|
+
The third case, with a much higher threshold, allows for up to 8 data
|
2662
|
+
points to be connected - so 4 clusters are returned here.
|
2663
|
+
|
2664
|
+
Lastly, the threshold of the fourth case is large enough to allow for
|
2665
|
+
all data points to be merged together - so a single cluster is returned.
|
2666
|
+
|
2667
|
+
"""
|
2668
|
+
xp = array_namespace(Z)
|
2669
|
+
Z = _asarray(Z, order='C', dtype=xp.float64, xp=xp)
|
2670
|
+
_is_valid_linkage(Z, throw=True, name='Z', materialize=True, xp=xp)
|
2671
|
+
|
2672
|
+
n = Z.shape[0] + 1
|
2673
|
+
T = np.zeros((n,), dtype='i')
|
2674
|
+
|
2675
|
+
if monocrit is not None:
|
2676
|
+
monocrit = np.asarray(monocrit, order='C', dtype=np.float64)
|
2677
|
+
|
2678
|
+
Z = np.asarray(Z)
|
2679
|
+
monocrit = np.asarray(monocrit)
|
2680
|
+
if criterion == 'inconsistent':
|
2681
|
+
if R is None:
|
2682
|
+
R = inconsistent(Z, depth)
|
2683
|
+
else:
|
2684
|
+
R = _asarray(R, order='C', dtype=xp.float64, xp=xp)
|
2685
|
+
_is_valid_im(R, throw=True, name='R', materialize=True, xp=xp)
|
2686
|
+
# Since the C code does not support striding using strides.
|
2687
|
+
# The dimensions are used instead.
|
2688
|
+
R = np.asarray(R)
|
2689
|
+
_hierarchy.cluster_in(Z, R, T, float(t), int(n))
|
2690
|
+
elif criterion == 'distance':
|
2691
|
+
_hierarchy.cluster_dist(Z, T, float(t), int(n))
|
2692
|
+
elif criterion == 'maxclust':
|
2693
|
+
_hierarchy.cluster_maxclust_dist(Z, T, int(n), t)
|
2694
|
+
elif criterion == 'monocrit':
|
2695
|
+
_hierarchy.cluster_monocrit(Z, monocrit, T, float(t), int(n))
|
2696
|
+
elif criterion == 'maxclust_monocrit':
|
2697
|
+
_hierarchy.cluster_maxclust_monocrit(Z, monocrit, T, int(n), int(t))
|
2698
|
+
else:
|
2699
|
+
raise ValueError(f'Invalid cluster formation criterion: {str(criterion)}')
|
2700
|
+
return xp.asarray(T)
|
2701
|
+
|
2702
|
+
|
2703
|
+
@xp_capabilities(cpu_only=True, reason="Cython code",
|
2704
|
+
jax_jit=False, allow_dask_compute=True)
|
2705
|
+
def fclusterdata(X, t, criterion='inconsistent',
|
2706
|
+
metric='euclidean', depth=2, method='single', R=None):
|
2707
|
+
"""
|
2708
|
+
Cluster observation data using a given metric.
|
2709
|
+
|
2710
|
+
Clusters the original observations in the n-by-m data
|
2711
|
+
matrix X (n observations in m dimensions), using the euclidean
|
2712
|
+
distance metric to calculate distances between original observations,
|
2713
|
+
performs hierarchical clustering using the single linkage algorithm,
|
2714
|
+
and forms flat clusters using the inconsistency method with `t` as the
|
2715
|
+
cut-off threshold.
|
2716
|
+
|
2717
|
+
A 1-D array ``T`` of length ``n`` is returned. ``T[i]`` is
|
2718
|
+
the index of the flat cluster to which the original observation ``i``
|
2719
|
+
belongs.
|
2720
|
+
|
2721
|
+
Parameters
|
2722
|
+
----------
|
2723
|
+
X : (N, M) ndarray
|
2724
|
+
N by M data matrix with N observations in M dimensions.
|
2725
|
+
t : scalar
|
2726
|
+
For criteria 'inconsistent', 'distance' or 'monocrit',
|
2727
|
+
this is the threshold to apply when forming flat clusters.
|
2728
|
+
For 'maxclust' or 'maxclust_monocrit' criteria,
|
2729
|
+
this would be max number of clusters requested.
|
2730
|
+
criterion : str, optional
|
2731
|
+
Specifies the criterion for forming flat clusters. Valid
|
2732
|
+
values are 'inconsistent' (default), 'distance', or 'maxclust'
|
2733
|
+
cluster formation algorithms. See `fcluster` for descriptions.
|
2734
|
+
metric : str or function, optional
|
2735
|
+
The distance metric for calculating pairwise distances. See
|
2736
|
+
``distance.pdist`` for descriptions and linkage to verify
|
2737
|
+
compatibility with the linkage method.
|
2738
|
+
depth : int, optional
|
2739
|
+
The maximum depth for the inconsistency calculation. See
|
2740
|
+
`inconsistent` for more information.
|
2741
|
+
method : str, optional
|
2742
|
+
The linkage method to use (single, complete, average,
|
2743
|
+
weighted, median centroid, ward). See `linkage` for more
|
2744
|
+
information. Default is "single".
|
2745
|
+
R : ndarray, optional
|
2746
|
+
The inconsistency matrix. It will be computed if necessary
|
2747
|
+
if it is not passed.
|
2748
|
+
|
2749
|
+
Returns
|
2750
|
+
-------
|
2751
|
+
fclusterdata : ndarray
|
2752
|
+
A vector of length n. T[i] is the flat cluster number to
|
2753
|
+
which original observation i belongs.
|
2754
|
+
|
2755
|
+
See Also
|
2756
|
+
--------
|
2757
|
+
scipy.spatial.distance.pdist : pairwise distance metrics
|
2758
|
+
|
2759
|
+
Notes
|
2760
|
+
-----
|
2761
|
+
This function is similar to the MATLAB function ``clusterdata``.
|
2762
|
+
|
2763
|
+
Examples
|
2764
|
+
--------
|
2765
|
+
>>> from scipy.cluster.hierarchy import fclusterdata
|
2766
|
+
|
2767
|
+
This is a convenience method that abstracts all the steps to perform in a
|
2768
|
+
typical SciPy's hierarchical clustering workflow.
|
2769
|
+
|
2770
|
+
* Transform the input data into a condensed matrix with
|
2771
|
+
`scipy.spatial.distance.pdist`.
|
2772
|
+
|
2773
|
+
* Apply a clustering method.
|
2774
|
+
|
2775
|
+
* Obtain flat clusters at a user defined distance threshold ``t`` using
|
2776
|
+
`scipy.cluster.hierarchy.fcluster`.
|
2777
|
+
|
2778
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
2779
|
+
... [0, 4], [0, 3], [1, 4],
|
2780
|
+
... [4, 0], [3, 0], [4, 1],
|
2781
|
+
... [4, 4], [3, 4], [4, 3]]
|
2782
|
+
|
2783
|
+
>>> fclusterdata(X, t=1)
|
2784
|
+
array([3, 3, 3, 4, 4, 4, 2, 2, 2, 1, 1, 1], dtype=int32)
|
2785
|
+
|
2786
|
+
The output here (for the dataset ``X``, distance threshold ``t``, and the
|
2787
|
+
default settings) is four clusters with three data points each.
|
2788
|
+
|
2789
|
+
"""
|
2790
|
+
xp = array_namespace(X)
|
2791
|
+
X = _asarray(X, order='C', dtype=xp.float64, xp=xp)
|
2792
|
+
|
2793
|
+
if X.ndim != 2:
|
2794
|
+
raise TypeError('The observation matrix X must be an n by m array.')
|
2795
|
+
|
2796
|
+
Y = distance.pdist(X, metric=metric)
|
2797
|
+
Z = linkage(Y, method=method)
|
2798
|
+
if R is None:
|
2799
|
+
R = inconsistent(Z, d=depth)
|
2800
|
+
else:
|
2801
|
+
R = _asarray(R, order='C', xp=xp)
|
2802
|
+
T = fcluster(Z, criterion=criterion, depth=depth, R=R, t=t)
|
2803
|
+
return T
|
2804
|
+
|
2805
|
+
|
2806
|
+
@lazy_cython
|
2807
|
+
def leaves_list(Z):
|
2808
|
+
"""
|
2809
|
+
Return a list of leaf node ids.
|
2810
|
+
|
2811
|
+
The return corresponds to the observation vector index as it appears
|
2812
|
+
in the tree from left to right. Z is a linkage matrix.
|
2813
|
+
|
2814
|
+
Parameters
|
2815
|
+
----------
|
2816
|
+
Z : ndarray
|
2817
|
+
The hierarchical clustering encoded as a matrix. `Z` is
|
2818
|
+
a linkage matrix. See `linkage` for more information.
|
2819
|
+
|
2820
|
+
Returns
|
2821
|
+
-------
|
2822
|
+
leaves_list : ndarray
|
2823
|
+
The list of leaf node ids.
|
2824
|
+
|
2825
|
+
See Also
|
2826
|
+
--------
|
2827
|
+
dendrogram : for information about dendrogram structure.
|
2828
|
+
|
2829
|
+
Examples
|
2830
|
+
--------
|
2831
|
+
>>> from scipy.cluster.hierarchy import ward, dendrogram, leaves_list
|
2832
|
+
>>> from scipy.spatial.distance import pdist
|
2833
|
+
>>> from matplotlib import pyplot as plt
|
2834
|
+
|
2835
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
2836
|
+
... [0, 4], [0, 3], [1, 4],
|
2837
|
+
... [4, 0], [3, 0], [4, 1],
|
2838
|
+
... [4, 4], [3, 4], [4, 3]]
|
2839
|
+
|
2840
|
+
>>> Z = ward(pdist(X))
|
2841
|
+
|
2842
|
+
The linkage matrix ``Z`` represents a dendrogram, that is, a tree that
|
2843
|
+
encodes the structure of the clustering performed.
|
2844
|
+
`scipy.cluster.hierarchy.leaves_list` shows the mapping between
|
2845
|
+
indices in the ``X`` dataset and leaves in the dendrogram:
|
2846
|
+
|
2847
|
+
>>> leaves_list(Z)
|
2848
|
+
array([ 2, 0, 1, 5, 3, 4, 8, 6, 7, 11, 9, 10], dtype=int32)
|
2849
|
+
|
2850
|
+
>>> fig = plt.figure(figsize=(25, 10))
|
2851
|
+
>>> dn = dendrogram(Z)
|
2852
|
+
>>> plt.show()
|
2853
|
+
|
2854
|
+
"""
|
2855
|
+
xp = array_namespace(Z)
|
2856
|
+
Z = _asarray(Z, order='C', xp=xp)
|
2857
|
+
_is_valid_linkage(Z, throw=True, name='Z', xp=xp)
|
2858
|
+
|
2859
|
+
def cy_leaves_list(Z, validate):
|
2860
|
+
if validate:
|
2861
|
+
_is_valid_linkage(Z, throw=True, name='Z', xp=np)
|
2862
|
+
n = Z.shape[0] + 1
|
2863
|
+
ML = np.zeros((n,), dtype=np.int32)
|
2864
|
+
_hierarchy.prelist(Z, ML, n)
|
2865
|
+
return ML
|
2866
|
+
|
2867
|
+
n = Z.shape[0] + 1
|
2868
|
+
return xpx.lazy_apply(cy_leaves_list, Z, validate=is_lazy_array(Z),
|
2869
|
+
shape=(n, ), dtype=xp.int32,
|
2870
|
+
as_numpy=True, xp=xp)
|
2871
|
+
|
2872
|
+
|
2873
|
+
# Maps number of leaves to text size.
|
2874
|
+
#
|
2875
|
+
# p <= 20, size="12"
|
2876
|
+
# 20 < p <= 30, size="10"
|
2877
|
+
# 30 < p <= 50, size="8"
|
2878
|
+
# 50 < p <= np.inf, size="6"
|
2879
|
+
|
2880
|
+
_dtextsizes = {20: 12, 30: 10, 50: 8, 85: 6, np.inf: 5}
|
2881
|
+
_drotation = {20: 0, 40: 45, np.inf: 90}
|
2882
|
+
_dtextsortedkeys = list(_dtextsizes.keys())
|
2883
|
+
_dtextsortedkeys.sort()
|
2884
|
+
_drotationsortedkeys = list(_drotation.keys())
|
2885
|
+
_drotationsortedkeys.sort()
|
2886
|
+
|
2887
|
+
|
2888
|
+
def _remove_dups(L):
|
2889
|
+
"""
|
2890
|
+
Remove duplicates AND preserve the original order of the elements.
|
2891
|
+
|
2892
|
+
The set class is not guaranteed to do this.
|
2893
|
+
"""
|
2894
|
+
seen_before = set()
|
2895
|
+
L2 = []
|
2896
|
+
for i in L:
|
2897
|
+
if i not in seen_before:
|
2898
|
+
seen_before.add(i)
|
2899
|
+
L2.append(i)
|
2900
|
+
return L2
|
2901
|
+
|
2902
|
+
|
2903
|
+
def _get_tick_text_size(p):
|
2904
|
+
for k in _dtextsortedkeys:
|
2905
|
+
if p <= k:
|
2906
|
+
return _dtextsizes[k]
|
2907
|
+
|
2908
|
+
|
2909
|
+
def _get_tick_rotation(p):
|
2910
|
+
for k in _drotationsortedkeys:
|
2911
|
+
if p <= k:
|
2912
|
+
return _drotation[k]
|
2913
|
+
|
2914
|
+
|
2915
|
+
def _plot_dendrogram(icoords, dcoords, ivl, p, n, mh, orientation,
|
2916
|
+
no_labels, color_list, leaf_font_size=None,
|
2917
|
+
leaf_rotation=None, contraction_marks=None,
|
2918
|
+
ax=None, above_threshold_color='C0'):
|
2919
|
+
# Import matplotlib here so that it's not imported unless dendrograms
|
2920
|
+
# are plotted. Raise an informative error if importing fails.
|
2921
|
+
try:
|
2922
|
+
# if an axis is provided, don't use pylab at all
|
2923
|
+
if ax is None:
|
2924
|
+
import matplotlib.pylab
|
2925
|
+
import matplotlib.patches
|
2926
|
+
import matplotlib.collections
|
2927
|
+
except ImportError as e:
|
2928
|
+
raise ImportError("You must install the matplotlib library to plot "
|
2929
|
+
"the dendrogram. Use no_plot=True to calculate the "
|
2930
|
+
"dendrogram without plotting.") from e
|
2931
|
+
|
2932
|
+
if ax is None:
|
2933
|
+
ax = matplotlib.pylab.gca()
|
2934
|
+
# if we're using pylab, we want to trigger a draw at the end
|
2935
|
+
trigger_redraw = True
|
2936
|
+
else:
|
2937
|
+
trigger_redraw = False
|
2938
|
+
|
2939
|
+
# Independent variable plot width
|
2940
|
+
ivw = len(ivl) * 10
|
2941
|
+
# Dependent variable plot height
|
2942
|
+
dvw = mh + mh * 0.05
|
2943
|
+
|
2944
|
+
iv_ticks = np.arange(5, len(ivl) * 10 + 5, 10)
|
2945
|
+
if orientation in ('top', 'bottom'):
|
2946
|
+
if orientation == 'top':
|
2947
|
+
ax.set_ylim([0, dvw])
|
2948
|
+
ax.set_xlim([0, ivw])
|
2949
|
+
else:
|
2950
|
+
ax.set_ylim([dvw, 0])
|
2951
|
+
ax.set_xlim([0, ivw])
|
2952
|
+
|
2953
|
+
xlines = icoords
|
2954
|
+
ylines = dcoords
|
2955
|
+
if no_labels:
|
2956
|
+
ax.set_xticks([])
|
2957
|
+
ax.set_xticklabels([])
|
2958
|
+
else:
|
2959
|
+
ax.set_xticks(iv_ticks)
|
2960
|
+
|
2961
|
+
if orientation == 'top':
|
2962
|
+
ax.xaxis.set_ticks_position('bottom')
|
2963
|
+
else:
|
2964
|
+
ax.xaxis.set_ticks_position('top')
|
2965
|
+
|
2966
|
+
# Make the tick marks invisible because they cover up the links
|
2967
|
+
for line in ax.get_xticklines():
|
2968
|
+
line.set_visible(False)
|
2969
|
+
|
2970
|
+
leaf_rot = (float(_get_tick_rotation(len(ivl)))
|
2971
|
+
if (leaf_rotation is None) else leaf_rotation)
|
2972
|
+
leaf_font = (float(_get_tick_text_size(len(ivl)))
|
2973
|
+
if (leaf_font_size is None) else leaf_font_size)
|
2974
|
+
ax.set_xticklabels(ivl, rotation=leaf_rot, size=leaf_font)
|
2975
|
+
|
2976
|
+
elif orientation in ('left', 'right'):
|
2977
|
+
if orientation == 'left':
|
2978
|
+
ax.set_xlim([dvw, 0])
|
2979
|
+
ax.set_ylim([0, ivw])
|
2980
|
+
else:
|
2981
|
+
ax.set_xlim([0, dvw])
|
2982
|
+
ax.set_ylim([0, ivw])
|
2983
|
+
|
2984
|
+
xlines = dcoords
|
2985
|
+
ylines = icoords
|
2986
|
+
if no_labels:
|
2987
|
+
ax.set_yticks([])
|
2988
|
+
ax.set_yticklabels([])
|
2989
|
+
else:
|
2990
|
+
ax.set_yticks(iv_ticks)
|
2991
|
+
|
2992
|
+
if orientation == 'left':
|
2993
|
+
ax.yaxis.set_ticks_position('right')
|
2994
|
+
else:
|
2995
|
+
ax.yaxis.set_ticks_position('left')
|
2996
|
+
|
2997
|
+
# Make the tick marks invisible because they cover up the links
|
2998
|
+
for line in ax.get_yticklines():
|
2999
|
+
line.set_visible(False)
|
3000
|
+
|
3001
|
+
leaf_font = (float(_get_tick_text_size(len(ivl)))
|
3002
|
+
if (leaf_font_size is None) else leaf_font_size)
|
3003
|
+
|
3004
|
+
if leaf_rotation is not None:
|
3005
|
+
ax.set_yticklabels(ivl, rotation=leaf_rotation, size=leaf_font)
|
3006
|
+
else:
|
3007
|
+
ax.set_yticklabels(ivl, size=leaf_font)
|
3008
|
+
|
3009
|
+
# Let's use collections instead. This way there is a separate legend item
|
3010
|
+
# for each tree grouping, rather than stupidly one for each line segment.
|
3011
|
+
colors_used = _remove_dups(color_list)
|
3012
|
+
color_to_lines = {}
|
3013
|
+
for color in colors_used:
|
3014
|
+
color_to_lines[color] = []
|
3015
|
+
for (xline, yline, color) in zip(xlines, ylines, color_list):
|
3016
|
+
color_to_lines[color].append(list(zip(xline, yline)))
|
3017
|
+
|
3018
|
+
colors_to_collections = {}
|
3019
|
+
# Construct the collections.
|
3020
|
+
for color in colors_used:
|
3021
|
+
coll = matplotlib.collections.LineCollection(color_to_lines[color],
|
3022
|
+
colors=(color,))
|
3023
|
+
colors_to_collections[color] = coll
|
3024
|
+
|
3025
|
+
# Add all the groupings below the color threshold.
|
3026
|
+
for color in colors_used:
|
3027
|
+
if color != above_threshold_color:
|
3028
|
+
ax.add_collection(colors_to_collections[color])
|
3029
|
+
# If there's a grouping of links above the color threshold, it goes last.
|
3030
|
+
if above_threshold_color in colors_to_collections:
|
3031
|
+
ax.add_collection(colors_to_collections[above_threshold_color])
|
3032
|
+
|
3033
|
+
if contraction_marks is not None:
|
3034
|
+
Ellipse = matplotlib.patches.Ellipse
|
3035
|
+
for (x, y) in contraction_marks:
|
3036
|
+
if orientation in ('left', 'right'):
|
3037
|
+
e = Ellipse((y, x), width=dvw / 100, height=1.0)
|
3038
|
+
else:
|
3039
|
+
e = Ellipse((x, y), width=1.0, height=dvw / 100)
|
3040
|
+
ax.add_artist(e)
|
3041
|
+
e.set_clip_box(ax.bbox)
|
3042
|
+
e.set_alpha(0.5)
|
3043
|
+
e.set_facecolor('k')
|
3044
|
+
|
3045
|
+
if trigger_redraw:
|
3046
|
+
matplotlib.pylab.draw_if_interactive()
|
3047
|
+
|
3048
|
+
|
3049
|
+
# C0 is used for above threshold color
|
3050
|
+
_link_line_colors_default = ('C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9')
|
3051
|
+
_link_line_colors = list(_link_line_colors_default)
|
3052
|
+
|
3053
|
+
|
3054
|
+
def set_link_color_palette(palette):
|
3055
|
+
"""
|
3056
|
+
Set list of matplotlib color codes for use by dendrogram.
|
3057
|
+
|
3058
|
+
Note that this palette is global (i.e., setting it once changes the colors
|
3059
|
+
for all subsequent calls to `dendrogram`) and that it affects only the
|
3060
|
+
the colors below ``color_threshold``.
|
3061
|
+
|
3062
|
+
Note that `dendrogram` also accepts a custom coloring function through its
|
3063
|
+
``link_color_func`` keyword, which is more flexible and non-global.
|
3064
|
+
|
3065
|
+
Parameters
|
3066
|
+
----------
|
3067
|
+
palette : list of str or None
|
3068
|
+
A list of matplotlib color codes. The order of the color codes is the
|
3069
|
+
order in which the colors are cycled through when color thresholding in
|
3070
|
+
the dendrogram.
|
3071
|
+
|
3072
|
+
If ``None``, resets the palette to its default (which are matplotlib
|
3073
|
+
default colors C1 to C9).
|
3074
|
+
|
3075
|
+
Returns
|
3076
|
+
-------
|
3077
|
+
None
|
3078
|
+
|
3079
|
+
See Also
|
3080
|
+
--------
|
3081
|
+
dendrogram
|
3082
|
+
|
3083
|
+
Notes
|
3084
|
+
-----
|
3085
|
+
Ability to reset the palette with ``None`` added in SciPy 0.17.0.
|
3086
|
+
|
3087
|
+
Thread safety: using this function in a multi-threaded fashion may
|
3088
|
+
result in `dendrogram` producing plots with unexpected colors.
|
3089
|
+
|
3090
|
+
Examples
|
3091
|
+
--------
|
3092
|
+
>>> import numpy as np
|
3093
|
+
>>> from scipy.cluster import hierarchy
|
3094
|
+
>>> ytdist = np.array([662., 877., 255., 412., 996., 295., 468., 268.,
|
3095
|
+
... 400., 754., 564., 138., 219., 869., 669.])
|
3096
|
+
>>> Z = hierarchy.linkage(ytdist, 'single')
|
3097
|
+
>>> dn = hierarchy.dendrogram(Z, no_plot=True)
|
3098
|
+
>>> dn['color_list']
|
3099
|
+
['C1', 'C0', 'C0', 'C0', 'C0']
|
3100
|
+
>>> hierarchy.set_link_color_palette(['c', 'm', 'y', 'k'])
|
3101
|
+
>>> dn = hierarchy.dendrogram(Z, no_plot=True, above_threshold_color='b')
|
3102
|
+
>>> dn['color_list']
|
3103
|
+
['c', 'b', 'b', 'b', 'b']
|
3104
|
+
>>> dn = hierarchy.dendrogram(Z, no_plot=True, color_threshold=267,
|
3105
|
+
... above_threshold_color='k')
|
3106
|
+
>>> dn['color_list']
|
3107
|
+
['c', 'm', 'm', 'k', 'k']
|
3108
|
+
|
3109
|
+
Now, reset the color palette to its default:
|
3110
|
+
|
3111
|
+
>>> hierarchy.set_link_color_palette(None)
|
3112
|
+
|
3113
|
+
"""
|
3114
|
+
if palette is None:
|
3115
|
+
# reset to its default
|
3116
|
+
palette = _link_line_colors_default
|
3117
|
+
elif not isinstance(palette, list | tuple):
|
3118
|
+
raise TypeError("palette must be a list or tuple")
|
3119
|
+
_ptypes = [isinstance(p, str) for p in palette]
|
3120
|
+
|
3121
|
+
if False in _ptypes:
|
3122
|
+
raise TypeError("all palette list elements must be color strings")
|
3123
|
+
|
3124
|
+
global _link_line_colors
|
3125
|
+
_link_line_colors = palette
|
3126
|
+
|
3127
|
+
|
3128
|
+
@xp_capabilities(cpu_only=True, jax_jit=False, allow_dask_compute=True)
|
3129
|
+
def dendrogram(Z, p=30, truncate_mode=None, color_threshold=None,
|
3130
|
+
get_leaves=True, orientation='top', labels=None,
|
3131
|
+
count_sort=False, distance_sort=False, show_leaf_counts=True,
|
3132
|
+
no_plot=False, no_labels=False, leaf_font_size=None,
|
3133
|
+
leaf_rotation=None, leaf_label_func=None,
|
3134
|
+
show_contracted=False, link_color_func=None, ax=None,
|
3135
|
+
above_threshold_color='C0'):
|
3136
|
+
"""
|
3137
|
+
Plot the hierarchical clustering as a dendrogram.
|
3138
|
+
|
3139
|
+
The dendrogram illustrates how each cluster is
|
3140
|
+
composed by drawing a U-shaped link between a non-singleton
|
3141
|
+
cluster and its children. The top of the U-link indicates a
|
3142
|
+
cluster merge. The two legs of the U-link indicate which clusters
|
3143
|
+
were merged. The length of the two legs of the U-link represents
|
3144
|
+
the distance between the child clusters. It is also the
|
3145
|
+
cophenetic distance between original observations in the two
|
3146
|
+
children clusters.
|
3147
|
+
|
3148
|
+
Parameters
|
3149
|
+
----------
|
3150
|
+
Z : ndarray
|
3151
|
+
The linkage matrix encoding the hierarchical clustering to
|
3152
|
+
render as a dendrogram. See the ``linkage`` function for more
|
3153
|
+
information on the format of ``Z``.
|
3154
|
+
p : int, optional
|
3155
|
+
The ``p`` parameter for ``truncate_mode``.
|
3156
|
+
truncate_mode : str, optional
|
3157
|
+
The dendrogram can be hard to read when the original
|
3158
|
+
observation matrix from which the linkage is derived is
|
3159
|
+
large. Truncation is used to condense the dendrogram. There
|
3160
|
+
are several modes:
|
3161
|
+
|
3162
|
+
``None``
|
3163
|
+
No truncation is performed (default).
|
3164
|
+
Note: ``'none'`` is an alias for ``None`` that's kept for
|
3165
|
+
backward compatibility.
|
3166
|
+
|
3167
|
+
``'lastp'``
|
3168
|
+
The last ``p`` non-singleton clusters formed in the linkage are the
|
3169
|
+
only non-leaf nodes in the linkage; they correspond to rows
|
3170
|
+
``Z[n-p-2:end]`` in ``Z``. All other non-singleton clusters are
|
3171
|
+
contracted into leaf nodes.
|
3172
|
+
|
3173
|
+
``'level'``
|
3174
|
+
No more than ``p`` levels of the dendrogram tree are displayed.
|
3175
|
+
A "level" includes all nodes with ``p`` merges from the final merge.
|
3176
|
+
|
3177
|
+
Note: ``'mtica'`` is an alias for ``'level'`` that's kept for
|
3178
|
+
backward compatibility.
|
3179
|
+
|
3180
|
+
color_threshold : double, optional
|
3181
|
+
For brevity, let :math:`t` be the ``color_threshold``.
|
3182
|
+
Colors all the descendent links below a cluster node
|
3183
|
+
:math:`k` the same color if :math:`k` is the first node below
|
3184
|
+
the cut threshold :math:`t`. All links connecting nodes with
|
3185
|
+
distances greater than or equal to the threshold are colored
|
3186
|
+
with de default matplotlib color ``'C0'``. If :math:`t` is less
|
3187
|
+
than or equal to zero, all nodes are colored ``'C0'``.
|
3188
|
+
If ``color_threshold`` is None or 'default',
|
3189
|
+
corresponding with MATLAB(TM) behavior, the threshold is set to
|
3190
|
+
``0.7*max(Z[:,2])``.
|
3191
|
+
|
3192
|
+
get_leaves : bool, optional
|
3193
|
+
Includes a list ``R['leaves']=H`` in the result
|
3194
|
+
dictionary. For each :math:`i`, ``H[i] == j``, cluster node
|
3195
|
+
``j`` appears in position ``i`` in the left-to-right traversal
|
3196
|
+
of the leaves, where :math:`j < 2n-1` and :math:`i < n`.
|
3197
|
+
orientation : str, optional
|
3198
|
+
The direction to plot the dendrogram, which can be any
|
3199
|
+
of the following strings:
|
3200
|
+
|
3201
|
+
``'top'``
|
3202
|
+
Plots the root at the top, and plot descendent links going downwards.
|
3203
|
+
(default).
|
3204
|
+
|
3205
|
+
``'bottom'``
|
3206
|
+
Plots the root at the bottom, and plot descendent links going
|
3207
|
+
upwards.
|
3208
|
+
|
3209
|
+
``'left'``
|
3210
|
+
Plots the root at the left, and plot descendent links going right.
|
3211
|
+
|
3212
|
+
``'right'``
|
3213
|
+
Plots the root at the right, and plot descendent links going left.
|
3214
|
+
|
3215
|
+
labels : ndarray, optional
|
3216
|
+
By default, ``labels`` is None so the index of the original observation
|
3217
|
+
is used to label the leaf nodes. Otherwise, this is an :math:`n`-sized
|
3218
|
+
sequence, with ``n == Z.shape[0] + 1``. The ``labels[i]`` value is the
|
3219
|
+
text to put under the :math:`i` th leaf node only if it corresponds to
|
3220
|
+
an original observation and not a non-singleton cluster.
|
3221
|
+
count_sort : str or bool, optional
|
3222
|
+
For each node n, the order (visually, from left-to-right) n's
|
3223
|
+
two descendent links are plotted is determined by this
|
3224
|
+
parameter, which can be any of the following values:
|
3225
|
+
|
3226
|
+
``False``
|
3227
|
+
Nothing is done.
|
3228
|
+
|
3229
|
+
``'ascending'`` or ``True``
|
3230
|
+
The child with the minimum number of original objects in its cluster
|
3231
|
+
is plotted first.
|
3232
|
+
|
3233
|
+
``'descending'``
|
3234
|
+
The child with the maximum number of original objects in its cluster
|
3235
|
+
is plotted first.
|
3236
|
+
|
3237
|
+
Note, ``distance_sort`` and ``count_sort`` cannot both be True.
|
3238
|
+
distance_sort : str or bool, optional
|
3239
|
+
For each node n, the order (visually, from left-to-right) n's
|
3240
|
+
two descendent links are plotted is determined by this
|
3241
|
+
parameter, which can be any of the following values:
|
3242
|
+
|
3243
|
+
``False``
|
3244
|
+
Nothing is done.
|
3245
|
+
|
3246
|
+
``'ascending'`` or ``True``
|
3247
|
+
The child with the minimum distance between its direct descendents is
|
3248
|
+
plotted first.
|
3249
|
+
|
3250
|
+
``'descending'``
|
3251
|
+
The child with the maximum distance between its direct descendents is
|
3252
|
+
plotted first.
|
3253
|
+
|
3254
|
+
Note ``distance_sort`` and ``count_sort`` cannot both be True.
|
3255
|
+
show_leaf_counts : bool, optional
|
3256
|
+
When True, leaf nodes representing :math:`k>1` original
|
3257
|
+
observation are labeled with the number of observations they
|
3258
|
+
contain in parentheses.
|
3259
|
+
no_plot : bool, optional
|
3260
|
+
When True, the final rendering is not performed. This is
|
3261
|
+
useful if only the data structures computed for the rendering
|
3262
|
+
are needed or if matplotlib is not available.
|
3263
|
+
no_labels : bool, optional
|
3264
|
+
When True, no labels appear next to the leaf nodes in the
|
3265
|
+
rendering of the dendrogram.
|
3266
|
+
leaf_rotation : double, optional
|
3267
|
+
Specifies the angle (in degrees) to rotate the leaf
|
3268
|
+
labels. When unspecified, the rotation is based on the number of
|
3269
|
+
nodes in the dendrogram (default is 0).
|
3270
|
+
leaf_font_size : int, optional
|
3271
|
+
Specifies the font size (in points) of the leaf labels. When
|
3272
|
+
unspecified, the size based on the number of nodes in the
|
3273
|
+
dendrogram.
|
3274
|
+
leaf_label_func : lambda or function, optional
|
3275
|
+
When ``leaf_label_func`` is a callable function, for each
|
3276
|
+
leaf with cluster index :math:`k < 2n-1`. The function
|
3277
|
+
is expected to return a string with the label for the
|
3278
|
+
leaf.
|
3279
|
+
|
3280
|
+
Indices :math:`k < n` correspond to original observations
|
3281
|
+
while indices :math:`k \\geq n` correspond to non-singleton
|
3282
|
+
clusters.
|
3283
|
+
|
3284
|
+
For example, to label singletons with their node id and
|
3285
|
+
non-singletons with their id, count, and inconsistency
|
3286
|
+
coefficient, simply do::
|
3287
|
+
|
3288
|
+
# First define the leaf label function.
|
3289
|
+
def llf(id):
|
3290
|
+
if id < n:
|
3291
|
+
return str(id)
|
3292
|
+
else:
|
3293
|
+
return '[%d %d %1.2f]' % (id, count, R[n-id,3])
|
3294
|
+
|
3295
|
+
# The text for the leaf nodes is going to be big so force
|
3296
|
+
# a rotation of 90 degrees.
|
3297
|
+
dendrogram(Z, leaf_label_func=llf, leaf_rotation=90)
|
3298
|
+
|
3299
|
+
# leaf_label_func can also be used together with ``truncate_mode``,
|
3300
|
+
# in which case you will get your leaves labeled after truncation:
|
3301
|
+
dendrogram(Z, leaf_label_func=llf, leaf_rotation=90,
|
3302
|
+
truncate_mode='level', p=2)
|
3303
|
+
|
3304
|
+
show_contracted : bool, optional
|
3305
|
+
When True the heights of non-singleton nodes contracted
|
3306
|
+
into a leaf node are plotted as crosses along the link
|
3307
|
+
connecting that leaf node. This really is only useful when
|
3308
|
+
truncation is used (see ``truncate_mode`` parameter).
|
3309
|
+
link_color_func : callable, optional
|
3310
|
+
If given, `link_color_function` is called with each non-singleton id
|
3311
|
+
corresponding to each U-shaped link it will paint. The function is
|
3312
|
+
expected to return the color to paint the link, encoded as a matplotlib
|
3313
|
+
color string code. For example::
|
3314
|
+
|
3315
|
+
dendrogram(Z, link_color_func=lambda k: colors[k])
|
3316
|
+
|
3317
|
+
colors the direct links below each untruncated non-singleton node
|
3318
|
+
``k`` using ``colors[k]``.
|
3319
|
+
ax : matplotlib Axes instance, optional
|
3320
|
+
If None and `no_plot` is not True, the dendrogram will be plotted
|
3321
|
+
on the current axes. Otherwise if `no_plot` is not True the
|
3322
|
+
dendrogram will be plotted on the given ``Axes`` instance. This can be
|
3323
|
+
useful if the dendrogram is part of a more complex figure.
|
3324
|
+
above_threshold_color : str, optional
|
3325
|
+
This matplotlib color string sets the color of the links above the
|
3326
|
+
color_threshold. The default is ``'C0'``.
|
3327
|
+
|
3328
|
+
Returns
|
3329
|
+
-------
|
3330
|
+
R : dict
|
3331
|
+
A dictionary of data structures computed to render the
|
3332
|
+
dendrogram. Its has the following keys:
|
3333
|
+
|
3334
|
+
``'color_list'``
|
3335
|
+
A list of color names. The k'th element represents the color of the
|
3336
|
+
k'th link.
|
3337
|
+
|
3338
|
+
``'icoord'`` and ``'dcoord'``
|
3339
|
+
Each of them is a list of lists. Let ``icoord = [I1, I2, ..., Ip]``
|
3340
|
+
where ``Ik = [xk1, xk2, xk3, xk4]`` and ``dcoord = [D1, D2, ..., Dp]``
|
3341
|
+
where ``Dk = [yk1, yk2, yk3, yk4]``, then the k'th link painted is
|
3342
|
+
``(xk1, yk1)`` - ``(xk2, yk2)`` - ``(xk3, yk3)`` - ``(xk4, yk4)``.
|
3343
|
+
|
3344
|
+
``'ivl'``
|
3345
|
+
A list of labels corresponding to the leaf nodes.
|
3346
|
+
|
3347
|
+
``'leaves'``
|
3348
|
+
For each i, ``H[i] == j``, cluster node ``j`` appears in position
|
3349
|
+
``i`` in the left-to-right traversal of the leaves, where
|
3350
|
+
:math:`j < 2n-1` and :math:`i < n`. If ``j`` is less than ``n``, the
|
3351
|
+
``i``-th leaf node corresponds to an original observation.
|
3352
|
+
Otherwise, it corresponds to a non-singleton cluster.
|
3353
|
+
|
3354
|
+
``'leaves_color_list'``
|
3355
|
+
A list of color names. The k'th element represents the color of the
|
3356
|
+
k'th leaf.
|
3357
|
+
|
3358
|
+
See Also
|
3359
|
+
--------
|
3360
|
+
linkage, set_link_color_palette
|
3361
|
+
|
3362
|
+
Notes
|
3363
|
+
-----
|
3364
|
+
It is expected that the distances in ``Z[:,2]`` be monotonic, otherwise
|
3365
|
+
crossings appear in the dendrogram.
|
3366
|
+
|
3367
|
+
Examples
|
3368
|
+
--------
|
3369
|
+
>>> import numpy as np
|
3370
|
+
>>> from scipy.cluster import hierarchy
|
3371
|
+
>>> import matplotlib.pyplot as plt
|
3372
|
+
|
3373
|
+
A very basic example:
|
3374
|
+
|
3375
|
+
>>> ytdist = np.array([662., 877., 255., 412., 996., 295., 468., 268.,
|
3376
|
+
... 400., 754., 564., 138., 219., 869., 669.])
|
3377
|
+
>>> Z = hierarchy.linkage(ytdist, 'single')
|
3378
|
+
>>> plt.figure()
|
3379
|
+
>>> dn = hierarchy.dendrogram(Z)
|
3380
|
+
|
3381
|
+
Now, plot in given axes, improve the color scheme and use both vertical and
|
3382
|
+
horizontal orientations:
|
3383
|
+
|
3384
|
+
>>> hierarchy.set_link_color_palette(['m', 'c', 'y', 'k'])
|
3385
|
+
>>> fig, axes = plt.subplots(1, 2, figsize=(8, 3))
|
3386
|
+
>>> dn1 = hierarchy.dendrogram(Z, ax=axes[0], above_threshold_color='y',
|
3387
|
+
... orientation='top')
|
3388
|
+
>>> dn2 = hierarchy.dendrogram(Z, ax=axes[1],
|
3389
|
+
... above_threshold_color='#bcbddc',
|
3390
|
+
... orientation='right')
|
3391
|
+
>>> hierarchy.set_link_color_palette(None) # reset to default after use
|
3392
|
+
>>> plt.show()
|
3393
|
+
|
3394
|
+
"""
|
3395
|
+
# This feature was thought about but never implemented (still useful?):
|
3396
|
+
#
|
3397
|
+
# ... = dendrogram(..., leaves_order=None)
|
3398
|
+
#
|
3399
|
+
# Plots the leaves in the order specified by a vector of
|
3400
|
+
# original observation indices. If the vector contains duplicates
|
3401
|
+
# or results in a crossing, an exception will be thrown. Passing
|
3402
|
+
# None orders leaf nodes based on the order they appear in the
|
3403
|
+
# pre-order traversal.
|
3404
|
+
xp = array_namespace(Z)
|
3405
|
+
Z = _asarray(Z, order='C', xp=xp)
|
3406
|
+
|
3407
|
+
if orientation not in ["top", "left", "bottom", "right"]:
|
3408
|
+
raise ValueError("orientation must be one of 'top', 'left', "
|
3409
|
+
"'bottom', or 'right'")
|
3410
|
+
|
3411
|
+
if labels is not None:
|
3412
|
+
try:
|
3413
|
+
len_labels = len(labels)
|
3414
|
+
except (TypeError, AttributeError):
|
3415
|
+
len_labels = labels.shape[0]
|
3416
|
+
if Z.shape[0] + 1 != len_labels:
|
3417
|
+
raise ValueError("Dimensions of Z and labels must be consistent.")
|
3418
|
+
|
3419
|
+
_is_valid_linkage(Z, throw=True, name='Z', materialize=True, xp=xp)
|
3420
|
+
Zs = Z.shape
|
3421
|
+
n = Zs[0] + 1
|
3422
|
+
if isinstance(p, int | float):
|
3423
|
+
p = int(p)
|
3424
|
+
else:
|
3425
|
+
raise TypeError('The second argument must be a number')
|
3426
|
+
|
3427
|
+
if truncate_mode not in ('lastp', 'mtica', 'level', 'none', None):
|
3428
|
+
# 'mtica' is kept working for backwards compat.
|
3429
|
+
raise ValueError('Invalid truncation mode.')
|
3430
|
+
|
3431
|
+
if truncate_mode == 'lastp':
|
3432
|
+
if p > n or p == 0:
|
3433
|
+
p = n
|
3434
|
+
|
3435
|
+
if truncate_mode == 'mtica':
|
3436
|
+
# 'mtica' is an alias
|
3437
|
+
truncate_mode = 'level'
|
3438
|
+
|
3439
|
+
if truncate_mode == 'level':
|
3440
|
+
if p <= 0:
|
3441
|
+
p = np.inf
|
3442
|
+
|
3443
|
+
if get_leaves:
|
3444
|
+
lvs = []
|
3445
|
+
else:
|
3446
|
+
lvs = None
|
3447
|
+
|
3448
|
+
icoord_list = []
|
3449
|
+
dcoord_list = []
|
3450
|
+
color_list = []
|
3451
|
+
current_color = [0]
|
3452
|
+
currently_below_threshold = [False]
|
3453
|
+
ivl = [] # list of leaves
|
3454
|
+
|
3455
|
+
if color_threshold is None or (isinstance(color_threshold, str) and
|
3456
|
+
color_threshold == 'default'):
|
3457
|
+
color_threshold = xp.max(Z[:, 2]) * 0.7
|
3458
|
+
|
3459
|
+
R = {'icoord': icoord_list, 'dcoord': dcoord_list, 'ivl': ivl,
|
3460
|
+
'leaves': lvs, 'color_list': color_list}
|
3461
|
+
|
3462
|
+
# Empty list will be filled in _dendrogram_calculate_info
|
3463
|
+
contraction_marks = [] if show_contracted else None
|
3464
|
+
|
3465
|
+
_dendrogram_calculate_info(
|
3466
|
+
Z=Z, p=p,
|
3467
|
+
truncate_mode=truncate_mode,
|
3468
|
+
color_threshold=color_threshold,
|
3469
|
+
get_leaves=get_leaves,
|
3470
|
+
orientation=orientation,
|
3471
|
+
labels=labels,
|
3472
|
+
count_sort=count_sort,
|
3473
|
+
distance_sort=distance_sort,
|
3474
|
+
show_leaf_counts=show_leaf_counts,
|
3475
|
+
i=2*n - 2,
|
3476
|
+
iv=0.0,
|
3477
|
+
ivl=ivl,
|
3478
|
+
n=n,
|
3479
|
+
icoord_list=icoord_list,
|
3480
|
+
dcoord_list=dcoord_list,
|
3481
|
+
lvs=lvs,
|
3482
|
+
current_color=current_color,
|
3483
|
+
color_list=color_list,
|
3484
|
+
currently_below_threshold=currently_below_threshold,
|
3485
|
+
leaf_label_func=leaf_label_func,
|
3486
|
+
contraction_marks=contraction_marks,
|
3487
|
+
link_color_func=link_color_func,
|
3488
|
+
above_threshold_color=above_threshold_color)
|
3489
|
+
|
3490
|
+
if not no_plot:
|
3491
|
+
mh = xp.max(Z[:, 2])
|
3492
|
+
_plot_dendrogram(icoord_list, dcoord_list, ivl, p, n, mh, orientation,
|
3493
|
+
no_labels, color_list,
|
3494
|
+
leaf_font_size=leaf_font_size,
|
3495
|
+
leaf_rotation=leaf_rotation,
|
3496
|
+
contraction_marks=contraction_marks,
|
3497
|
+
ax=ax,
|
3498
|
+
above_threshold_color=above_threshold_color)
|
3499
|
+
|
3500
|
+
R["leaves_color_list"] = _get_leaves_color_list(R)
|
3501
|
+
|
3502
|
+
return R
|
3503
|
+
|
3504
|
+
|
3505
|
+
def _get_leaves_color_list(R):
|
3506
|
+
leaves_color_list = [None] * len(R['leaves'])
|
3507
|
+
for link_x, link_y, link_color in zip(R['icoord'],
|
3508
|
+
R['dcoord'],
|
3509
|
+
R['color_list']):
|
3510
|
+
for (xi, yi) in zip(link_x, link_y):
|
3511
|
+
if yi == 0.0 and (xi % 5 == 0 and xi % 2 == 1):
|
3512
|
+
# if yi is 0.0 and xi is divisible by 5 and odd,
|
3513
|
+
# the point is a leaf
|
3514
|
+
# xi of leaves are 5, 15, 25, 35, ... (see `iv_ticks`)
|
3515
|
+
# index of leaves are 0, 1, 2, 3, ... as below
|
3516
|
+
leaf_index = (int(xi) - 5) // 10
|
3517
|
+
# each leaf has a same color of its link.
|
3518
|
+
leaves_color_list[leaf_index] = link_color
|
3519
|
+
return leaves_color_list
|
3520
|
+
|
3521
|
+
|
3522
|
+
def _append_singleton_leaf_node(Z, p, n, level, lvs, ivl, leaf_label_func,
|
3523
|
+
i, labels):
|
3524
|
+
# If the leaf id structure is not None and is a list then the caller
|
3525
|
+
# to dendrogram has indicated that cluster id's corresponding to the
|
3526
|
+
# leaf nodes should be recorded.
|
3527
|
+
|
3528
|
+
if lvs is not None:
|
3529
|
+
lvs.append(int(i))
|
3530
|
+
|
3531
|
+
# If leaf node labels are to be displayed...
|
3532
|
+
if ivl is not None:
|
3533
|
+
# If a leaf_label_func has been provided, the label comes from the
|
3534
|
+
# string returned from the leaf_label_func, which is a function
|
3535
|
+
# passed to dendrogram.
|
3536
|
+
if leaf_label_func:
|
3537
|
+
ivl.append(leaf_label_func(int(i)))
|
3538
|
+
else:
|
3539
|
+
# Otherwise, if the dendrogram caller has passed a labels list
|
3540
|
+
# for the leaf nodes, use it.
|
3541
|
+
if labels is not None:
|
3542
|
+
ivl.append(labels[int(i - n)])
|
3543
|
+
else:
|
3544
|
+
# Otherwise, use the id as the label for the leaf.x
|
3545
|
+
ivl.append(str(int(i)))
|
3546
|
+
|
3547
|
+
|
3548
|
+
def _append_nonsingleton_leaf_node(Z, p, n, level, lvs, ivl, leaf_label_func,
|
3549
|
+
i, labels, show_leaf_counts):
|
3550
|
+
# If the leaf id structure is not None and is a list then the caller
|
3551
|
+
# to dendrogram has indicated that cluster id's corresponding to the
|
3552
|
+
# leaf nodes should be recorded.
|
3553
|
+
|
3554
|
+
if lvs is not None:
|
3555
|
+
lvs.append(int(i))
|
3556
|
+
if ivl is not None:
|
3557
|
+
if leaf_label_func:
|
3558
|
+
ivl.append(leaf_label_func(int(i)))
|
3559
|
+
else:
|
3560
|
+
if show_leaf_counts:
|
3561
|
+
ivl.append("(" + str(np.asarray(Z[i - n, 3], dtype=np.int64)) + ")")
|
3562
|
+
else:
|
3563
|
+
ivl.append("")
|
3564
|
+
|
3565
|
+
|
3566
|
+
def _append_contraction_marks(Z, iv, i, n, contraction_marks, xp):
|
3567
|
+
_append_contraction_marks_sub(Z, iv, int_floor(Z[i - n, 0], xp),
|
3568
|
+
n, contraction_marks, xp)
|
3569
|
+
_append_contraction_marks_sub(Z, iv, int_floor(Z[i - n, 1], xp),
|
3570
|
+
n, contraction_marks, xp)
|
3571
|
+
|
3572
|
+
|
3573
|
+
def _append_contraction_marks_sub(Z, iv, i, n, contraction_marks, xp):
|
3574
|
+
if i >= n:
|
3575
|
+
contraction_marks.append((iv, Z[i - n, 2]))
|
3576
|
+
_append_contraction_marks_sub(Z, iv, int_floor(Z[i - n, 0], xp),
|
3577
|
+
n, contraction_marks, xp)
|
3578
|
+
_append_contraction_marks_sub(Z, iv, int_floor(Z[i - n, 1], xp),
|
3579
|
+
n, contraction_marks, xp)
|
3580
|
+
|
3581
|
+
|
3582
|
+
def _dendrogram_calculate_info(Z, p, truncate_mode,
|
3583
|
+
color_threshold=np.inf, get_leaves=True,
|
3584
|
+
orientation='top', labels=None,
|
3585
|
+
count_sort=False, distance_sort=False,
|
3586
|
+
show_leaf_counts=False, i=-1, iv=0.0,
|
3587
|
+
ivl=None, n=0, icoord_list=None, dcoord_list=None,
|
3588
|
+
lvs=None, mhr=False,
|
3589
|
+
current_color=None, color_list=None,
|
3590
|
+
currently_below_threshold=None,
|
3591
|
+
leaf_label_func=None, level=0,
|
3592
|
+
contraction_marks=None,
|
3593
|
+
link_color_func=None,
|
3594
|
+
above_threshold_color='C0'):
|
3595
|
+
"""
|
3596
|
+
Calculate the endpoints of the links as well as the labels for the
|
3597
|
+
the dendrogram rooted at the node with index i. iv is the independent
|
3598
|
+
variable value to plot the left-most leaf node below the root node i
|
3599
|
+
(if orientation='top', this would be the left-most x value where the
|
3600
|
+
plotting of this root node i and its descendents should begin).
|
3601
|
+
|
3602
|
+
ivl is a list to store the labels of the leaf nodes. The leaf_label_func
|
3603
|
+
is called whenever ivl != None, labels == None, and
|
3604
|
+
leaf_label_func != None. When ivl != None and labels != None, the
|
3605
|
+
labels list is used only for labeling the leaf nodes. When
|
3606
|
+
ivl == None, no labels are generated for leaf nodes.
|
3607
|
+
|
3608
|
+
When get_leaves==True, a list of leaves is built as they are visited
|
3609
|
+
in the dendrogram.
|
3610
|
+
|
3611
|
+
Returns a tuple with l being the independent variable coordinate that
|
3612
|
+
corresponds to the midpoint of cluster to the left of cluster i if
|
3613
|
+
i is non-singleton, otherwise the independent coordinate of the leaf
|
3614
|
+
node if i is a leaf node.
|
3615
|
+
|
3616
|
+
Returns
|
3617
|
+
-------
|
3618
|
+
A tuple (left, w, h, md), where:
|
3619
|
+
* left is the independent variable coordinate of the center of the
|
3620
|
+
the U of the subtree
|
3621
|
+
|
3622
|
+
* w is the amount of space used for the subtree (in independent
|
3623
|
+
variable units)
|
3624
|
+
|
3625
|
+
* h is the height of the subtree in dependent variable units
|
3626
|
+
|
3627
|
+
* md is the ``max(Z[*,2]``) for all nodes ``*`` below and including
|
3628
|
+
the target node.
|
3629
|
+
|
3630
|
+
"""
|
3631
|
+
xp = array_namespace(Z)
|
3632
|
+
if n == 0:
|
3633
|
+
raise ValueError("Invalid singleton cluster count n.")
|
3634
|
+
|
3635
|
+
if i == -1:
|
3636
|
+
raise ValueError("Invalid root cluster index i.")
|
3637
|
+
|
3638
|
+
if truncate_mode == 'lastp':
|
3639
|
+
# If the node is a leaf node but corresponds to a non-singleton
|
3640
|
+
# cluster, its label is either the empty string or the number of
|
3641
|
+
# original observations belonging to cluster i.
|
3642
|
+
if 2*n - p > i >= n:
|
3643
|
+
d = Z[i - n, 2]
|
3644
|
+
_append_nonsingleton_leaf_node(Z, p, n, level, lvs, ivl,
|
3645
|
+
leaf_label_func, i, labels,
|
3646
|
+
show_leaf_counts)
|
3647
|
+
if contraction_marks is not None:
|
3648
|
+
_append_contraction_marks(Z, iv + 5.0, i, n, contraction_marks, xp)
|
3649
|
+
return (iv + 5.0, 10.0, 0.0, d)
|
3650
|
+
elif i < n:
|
3651
|
+
_append_singleton_leaf_node(Z, p, n, level, lvs, ivl,
|
3652
|
+
leaf_label_func, i, labels)
|
3653
|
+
return (iv + 5.0, 10.0, 0.0, 0.0)
|
3654
|
+
elif truncate_mode == 'level':
|
3655
|
+
if i > n and level > p:
|
3656
|
+
d = Z[i - n, 2]
|
3657
|
+
_append_nonsingleton_leaf_node(Z, p, n, level, lvs, ivl,
|
3658
|
+
leaf_label_func, i, labels,
|
3659
|
+
show_leaf_counts)
|
3660
|
+
if contraction_marks is not None:
|
3661
|
+
_append_contraction_marks(Z, iv + 5.0, i, n, contraction_marks, xp)
|
3662
|
+
return (iv + 5.0, 10.0, 0.0, d)
|
3663
|
+
elif i < n:
|
3664
|
+
_append_singleton_leaf_node(Z, p, n, level, lvs, ivl,
|
3665
|
+
leaf_label_func, i, labels)
|
3666
|
+
return (iv + 5.0, 10.0, 0.0, 0.0)
|
3667
|
+
|
3668
|
+
# Otherwise, only truncate if we have a leaf node.
|
3669
|
+
#
|
3670
|
+
# Only place leaves if they correspond to original observations.
|
3671
|
+
if i < n:
|
3672
|
+
_append_singleton_leaf_node(Z, p, n, level, lvs, ivl,
|
3673
|
+
leaf_label_func, i, labels)
|
3674
|
+
return (iv + 5.0, 10.0, 0.0, 0.0)
|
3675
|
+
|
3676
|
+
# !!! Otherwise, we don't have a leaf node, so work on plotting a
|
3677
|
+
# non-leaf node.
|
3678
|
+
# Actual indices of a and b
|
3679
|
+
aa = int_floor(Z[i - n, 0], xp)
|
3680
|
+
ab = int_floor(Z[i - n, 1], xp)
|
3681
|
+
if aa >= n:
|
3682
|
+
# The number of singletons below cluster a
|
3683
|
+
na = Z[aa - n, 3]
|
3684
|
+
# The distance between a's two direct children.
|
3685
|
+
da = Z[aa - n, 2]
|
3686
|
+
else:
|
3687
|
+
na = 1
|
3688
|
+
da = 0.0
|
3689
|
+
if ab >= n:
|
3690
|
+
nb = Z[ab - n, 3]
|
3691
|
+
db = Z[ab - n, 2]
|
3692
|
+
else:
|
3693
|
+
nb = 1
|
3694
|
+
db = 0.0
|
3695
|
+
|
3696
|
+
if count_sort == 'ascending' or count_sort is True:
|
3697
|
+
# If a has a count greater than b, it and its descendents should
|
3698
|
+
# be drawn to the right. Otherwise, to the left.
|
3699
|
+
if na > nb:
|
3700
|
+
# The cluster index to draw to the left (ua) will be ab
|
3701
|
+
# and the one to draw to the right (ub) will be aa
|
3702
|
+
ua = ab
|
3703
|
+
ub = aa
|
3704
|
+
else:
|
3705
|
+
ua = aa
|
3706
|
+
ub = ab
|
3707
|
+
elif count_sort == 'descending':
|
3708
|
+
# If a has a count less than or equal to b, it and its
|
3709
|
+
# descendents should be drawn to the left. Otherwise, to
|
3710
|
+
# the right.
|
3711
|
+
if na > nb:
|
3712
|
+
ua = aa
|
3713
|
+
ub = ab
|
3714
|
+
else:
|
3715
|
+
ua = ab
|
3716
|
+
ub = aa
|
3717
|
+
elif distance_sort == 'ascending' or distance_sort is True:
|
3718
|
+
# If a has a distance greater than b, it and its descendents should
|
3719
|
+
# be drawn to the right. Otherwise, to the left.
|
3720
|
+
if da > db:
|
3721
|
+
ua = ab
|
3722
|
+
ub = aa
|
3723
|
+
else:
|
3724
|
+
ua = aa
|
3725
|
+
ub = ab
|
3726
|
+
elif distance_sort == 'descending':
|
3727
|
+
# If a has a distance less than or equal to b, it and its
|
3728
|
+
# descendents should be drawn to the left. Otherwise, to
|
3729
|
+
# the right.
|
3730
|
+
if da > db:
|
3731
|
+
ua = aa
|
3732
|
+
ub = ab
|
3733
|
+
else:
|
3734
|
+
ua = ab
|
3735
|
+
ub = aa
|
3736
|
+
else:
|
3737
|
+
ua = aa
|
3738
|
+
ub = ab
|
3739
|
+
|
3740
|
+
# Updated iv variable and the amount of space used.
|
3741
|
+
(uiva, uwa, uah, uamd) = \
|
3742
|
+
_dendrogram_calculate_info(
|
3743
|
+
Z=Z, p=p,
|
3744
|
+
truncate_mode=truncate_mode,
|
3745
|
+
color_threshold=color_threshold,
|
3746
|
+
get_leaves=get_leaves,
|
3747
|
+
orientation=orientation,
|
3748
|
+
labels=labels,
|
3749
|
+
count_sort=count_sort,
|
3750
|
+
distance_sort=distance_sort,
|
3751
|
+
show_leaf_counts=show_leaf_counts,
|
3752
|
+
i=ua, iv=iv, ivl=ivl, n=n,
|
3753
|
+
icoord_list=icoord_list,
|
3754
|
+
dcoord_list=dcoord_list, lvs=lvs,
|
3755
|
+
current_color=current_color,
|
3756
|
+
color_list=color_list,
|
3757
|
+
currently_below_threshold=currently_below_threshold,
|
3758
|
+
leaf_label_func=leaf_label_func,
|
3759
|
+
level=level + 1, contraction_marks=contraction_marks,
|
3760
|
+
link_color_func=link_color_func,
|
3761
|
+
above_threshold_color=above_threshold_color)
|
3762
|
+
|
3763
|
+
h = Z[i - n, 2]
|
3764
|
+
if h >= color_threshold or color_threshold <= 0:
|
3765
|
+
c = above_threshold_color
|
3766
|
+
|
3767
|
+
if currently_below_threshold[0]:
|
3768
|
+
current_color[0] = (current_color[0] + 1) % len(_link_line_colors)
|
3769
|
+
currently_below_threshold[0] = False
|
3770
|
+
else:
|
3771
|
+
currently_below_threshold[0] = True
|
3772
|
+
c = _link_line_colors[current_color[0]]
|
3773
|
+
|
3774
|
+
(uivb, uwb, ubh, ubmd) = \
|
3775
|
+
_dendrogram_calculate_info(
|
3776
|
+
Z=Z, p=p,
|
3777
|
+
truncate_mode=truncate_mode,
|
3778
|
+
color_threshold=color_threshold,
|
3779
|
+
get_leaves=get_leaves,
|
3780
|
+
orientation=orientation,
|
3781
|
+
labels=labels,
|
3782
|
+
count_sort=count_sort,
|
3783
|
+
distance_sort=distance_sort,
|
3784
|
+
show_leaf_counts=show_leaf_counts,
|
3785
|
+
i=ub, iv=iv + uwa, ivl=ivl, n=n,
|
3786
|
+
icoord_list=icoord_list,
|
3787
|
+
dcoord_list=dcoord_list, lvs=lvs,
|
3788
|
+
current_color=current_color,
|
3789
|
+
color_list=color_list,
|
3790
|
+
currently_below_threshold=currently_below_threshold,
|
3791
|
+
leaf_label_func=leaf_label_func,
|
3792
|
+
level=level + 1, contraction_marks=contraction_marks,
|
3793
|
+
link_color_func=link_color_func,
|
3794
|
+
above_threshold_color=above_threshold_color)
|
3795
|
+
|
3796
|
+
max_dist = max(uamd, ubmd, h)
|
3797
|
+
|
3798
|
+
icoord_list.append([uiva, uiva, uivb, uivb])
|
3799
|
+
dcoord_list.append([uah, h, h, ubh])
|
3800
|
+
if link_color_func is not None:
|
3801
|
+
v = link_color_func(int(i))
|
3802
|
+
if not isinstance(v, str):
|
3803
|
+
raise TypeError("link_color_func must return a matplotlib "
|
3804
|
+
"color string!")
|
3805
|
+
color_list.append(v)
|
3806
|
+
else:
|
3807
|
+
color_list.append(c)
|
3808
|
+
|
3809
|
+
return (((uiva + uivb) / 2), uwa + uwb, h, max_dist)
|
3810
|
+
|
3811
|
+
|
3812
|
+
@xp_capabilities(cpu_only=True,
|
3813
|
+
warnings=[("dask.array", "see notes"), ("jax.numpy", "see notes")])
|
3814
|
+
def is_isomorphic(T1, T2):
|
3815
|
+
"""
|
3816
|
+
Determine if two different cluster assignments are equivalent.
|
3817
|
+
|
3818
|
+
Parameters
|
3819
|
+
----------
|
3820
|
+
T1 : array_like
|
3821
|
+
An assignment of singleton cluster ids to flat cluster ids.
|
3822
|
+
T2 : array_like
|
3823
|
+
An assignment of singleton cluster ids to flat cluster ids.
|
3824
|
+
|
3825
|
+
Returns
|
3826
|
+
-------
|
3827
|
+
b : bool
|
3828
|
+
Whether the flat cluster assignments `T1` and `T2` are
|
3829
|
+
equivalent.
|
3830
|
+
|
3831
|
+
Notes
|
3832
|
+
-----
|
3833
|
+
*Array API support (experimental):* If the input is a lazy Array (e.g. Dask
|
3834
|
+
or JAX), the return value will be a 0-dimensional bool Array.
|
3835
|
+
|
3836
|
+
See Also
|
3837
|
+
--------
|
3838
|
+
linkage : for a description of what a linkage matrix is.
|
3839
|
+
fcluster : for the creation of flat cluster assignments.
|
3840
|
+
|
3841
|
+
Examples
|
3842
|
+
--------
|
3843
|
+
>>> from scipy.cluster.hierarchy import fcluster, is_isomorphic
|
3844
|
+
>>> from scipy.cluster.hierarchy import single, complete
|
3845
|
+
>>> from scipy.spatial.distance import pdist
|
3846
|
+
|
3847
|
+
Two flat cluster assignments can be isomorphic if they represent the same
|
3848
|
+
cluster assignment, with different labels.
|
3849
|
+
|
3850
|
+
For example, we can use the `scipy.cluster.hierarchy.single` method
|
3851
|
+
and flatten the output to four clusters:
|
3852
|
+
|
3853
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
3854
|
+
... [0, 4], [0, 3], [1, 4],
|
3855
|
+
... [4, 0], [3, 0], [4, 1],
|
3856
|
+
... [4, 4], [3, 4], [4, 3]]
|
3857
|
+
|
3858
|
+
>>> Z = single(pdist(X))
|
3859
|
+
>>> T = fcluster(Z, 1, criterion='distance')
|
3860
|
+
>>> T
|
3861
|
+
array([3, 3, 3, 4, 4, 4, 2, 2, 2, 1, 1, 1], dtype=int32)
|
3862
|
+
|
3863
|
+
We can then do the same using the
|
3864
|
+
`scipy.cluster.hierarchy.complete`: method:
|
3865
|
+
|
3866
|
+
>>> Z = complete(pdist(X))
|
3867
|
+
>>> T_ = fcluster(Z, 1.5, criterion='distance')
|
3868
|
+
>>> T_
|
3869
|
+
array([1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4], dtype=int32)
|
3870
|
+
|
3871
|
+
As we can see, in both cases we obtain four clusters and all the data
|
3872
|
+
points are distributed in the same way - the only thing that changes
|
3873
|
+
are the flat cluster labels (3 => 1, 4 =>2, 2 =>3 and 4 =>1), so both
|
3874
|
+
cluster assignments are isomorphic:
|
3875
|
+
|
3876
|
+
>>> is_isomorphic(T, T_)
|
3877
|
+
True
|
3878
|
+
|
3879
|
+
"""
|
3880
|
+
xp = array_namespace(T1, T2)
|
3881
|
+
T1 = _asarray(T1, xp=xp)
|
3882
|
+
T2 = _asarray(T2, xp=xp)
|
3883
|
+
|
3884
|
+
if T1.ndim != 1:
|
3885
|
+
raise ValueError('T1 must be one-dimensional.')
|
3886
|
+
if T2.ndim != 1:
|
3887
|
+
raise ValueError('T2 must be one-dimensional.')
|
3888
|
+
if T1.shape != T2.shape:
|
3889
|
+
raise ValueError('T1 and T2 must have the same number of elements.')
|
3890
|
+
|
3891
|
+
def py_is_isomorphic(T1, T2):
|
3892
|
+
d1 = {}
|
3893
|
+
d2 = {}
|
3894
|
+
for t1, t2 in zip(T1, T2):
|
3895
|
+
if t1 in d1:
|
3896
|
+
if t2 not in d2:
|
3897
|
+
return False
|
3898
|
+
if d1[t1] != t2 or d2[t2] != t1:
|
3899
|
+
return False
|
3900
|
+
elif t2 in d2:
|
3901
|
+
return False
|
3902
|
+
else:
|
3903
|
+
d1[t1] = t2
|
3904
|
+
d2[t2] = t1
|
3905
|
+
return True
|
3906
|
+
|
3907
|
+
res = xpx.lazy_apply(py_is_isomorphic, T1, T2,
|
3908
|
+
shape=(), dtype=xp.bool,
|
3909
|
+
as_numpy=True, xp=xp)
|
3910
|
+
return res if is_lazy_array(res) else bool(res)
|
3911
|
+
|
3912
|
+
|
3913
|
+
@lazy_cython
|
3914
|
+
def maxdists(Z):
|
3915
|
+
"""
|
3916
|
+
Return the maximum distance between any non-singleton cluster.
|
3917
|
+
|
3918
|
+
Parameters
|
3919
|
+
----------
|
3920
|
+
Z : ndarray
|
3921
|
+
The hierarchical clustering encoded as a matrix. See
|
3922
|
+
``linkage`` for more information.
|
3923
|
+
|
3924
|
+
Returns
|
3925
|
+
-------
|
3926
|
+
maxdists : ndarray
|
3927
|
+
A ``(n-1)`` sized numpy array of doubles; ``MD[i]`` represents
|
3928
|
+
the maximum distance between any cluster (including
|
3929
|
+
singletons) below and including the node with index i. More
|
3930
|
+
specifically, ``MD[i] = Z[Q(i)-n, 2].max()`` where ``Q(i)`` is the
|
3931
|
+
set of all node indices below and including node i.
|
3932
|
+
|
3933
|
+
See Also
|
3934
|
+
--------
|
3935
|
+
linkage : for a description of what a linkage matrix is.
|
3936
|
+
is_monotonic : for testing for monotonicity of a linkage matrix.
|
3937
|
+
|
3938
|
+
Examples
|
3939
|
+
--------
|
3940
|
+
>>> from scipy.cluster.hierarchy import median, maxdists
|
3941
|
+
>>> from scipy.spatial.distance import pdist
|
3942
|
+
|
3943
|
+
Given a linkage matrix ``Z``, `scipy.cluster.hierarchy.maxdists`
|
3944
|
+
computes for each new cluster generated (i.e., for each row of the linkage
|
3945
|
+
matrix) what is the maximum distance between any two child clusters.
|
3946
|
+
|
3947
|
+
Due to the nature of hierarchical clustering, in many cases this is going
|
3948
|
+
to be just the distance between the two child clusters that were merged
|
3949
|
+
to form the current one - that is, Z[:,2].
|
3950
|
+
|
3951
|
+
However, for non-monotonic cluster assignments such as
|
3952
|
+
`scipy.cluster.hierarchy.median` clustering this is not always the
|
3953
|
+
case: There may be cluster formations were the distance between the two
|
3954
|
+
clusters merged is smaller than the distance between their children.
|
3955
|
+
|
3956
|
+
We can see this in an example:
|
3957
|
+
|
3958
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
3959
|
+
... [0, 4], [0, 3], [1, 4],
|
3960
|
+
... [4, 0], [3, 0], [4, 1],
|
3961
|
+
... [4, 4], [3, 4], [4, 3]]
|
3962
|
+
|
3963
|
+
>>> Z = median(pdist(X))
|
3964
|
+
>>> Z
|
3965
|
+
array([[ 0. , 1. , 1. , 2. ],
|
3966
|
+
[ 3. , 4. , 1. , 2. ],
|
3967
|
+
[ 9. , 10. , 1. , 2. ],
|
3968
|
+
[ 6. , 7. , 1. , 2. ],
|
3969
|
+
[ 2. , 12. , 1.11803399, 3. ],
|
3970
|
+
[ 5. , 13. , 1.11803399, 3. ],
|
3971
|
+
[ 8. , 15. , 1.11803399, 3. ],
|
3972
|
+
[11. , 14. , 1.11803399, 3. ],
|
3973
|
+
[18. , 19. , 3. , 6. ],
|
3974
|
+
[16. , 17. , 3.5 , 6. ],
|
3975
|
+
[20. , 21. , 3.25 , 12. ]])
|
3976
|
+
>>> maxdists(Z)
|
3977
|
+
array([1. , 1. , 1. , 1. , 1.11803399,
|
3978
|
+
1.11803399, 1.11803399, 1.11803399, 3. , 3.5 ,
|
3979
|
+
3.5 ])
|
3980
|
+
|
3981
|
+
Note that while the distance between the two clusters merged when creating the
|
3982
|
+
last cluster is 3.25, there are two children (clusters 16 and 17) whose distance
|
3983
|
+
is larger (3.5). Thus, `scipy.cluster.hierarchy.maxdists` returns 3.5 in
|
3984
|
+
this case.
|
3985
|
+
|
3986
|
+
"""
|
3987
|
+
xp = array_namespace(Z)
|
3988
|
+
Z = _asarray(Z, order='C', dtype=xp.float64, xp=xp)
|
3989
|
+
_is_valid_linkage(Z, throw=True, name='Z', xp=xp)
|
3990
|
+
|
3991
|
+
def cy_maxdists(Z, validate):
|
3992
|
+
if validate:
|
3993
|
+
_is_valid_linkage(Z, throw=True, name='Z', xp=np)
|
3994
|
+
MD = np.zeros((Z.shape[0],))
|
3995
|
+
_hierarchy.get_max_dist_for_each_cluster(Z, MD, Z.shape[0] + 1)
|
3996
|
+
return MD
|
3997
|
+
|
3998
|
+
return xpx.lazy_apply(cy_maxdists, Z, validate=is_lazy_array(Z),
|
3999
|
+
shape=(Z.shape[0], ), dtype=xp.float64,
|
4000
|
+
as_numpy=True, xp=xp)
|
4001
|
+
|
4002
|
+
|
4003
|
+
@lazy_cython
|
4004
|
+
def maxinconsts(Z, R):
|
4005
|
+
"""
|
4006
|
+
Return the maximum inconsistency coefficient for each
|
4007
|
+
non-singleton cluster and its children.
|
4008
|
+
|
4009
|
+
Parameters
|
4010
|
+
----------
|
4011
|
+
Z : ndarray
|
4012
|
+
The hierarchical clustering encoded as a matrix. See
|
4013
|
+
`linkage` for more information.
|
4014
|
+
R : ndarray
|
4015
|
+
The inconsistency matrix.
|
4016
|
+
|
4017
|
+
Returns
|
4018
|
+
-------
|
4019
|
+
MI : ndarray
|
4020
|
+
A monotonic ``(n-1)``-sized numpy array of doubles.
|
4021
|
+
|
4022
|
+
See Also
|
4023
|
+
--------
|
4024
|
+
linkage : for a description of what a linkage matrix is.
|
4025
|
+
inconsistent : for the creation of a inconsistency matrix.
|
4026
|
+
|
4027
|
+
Examples
|
4028
|
+
--------
|
4029
|
+
>>> from scipy.cluster.hierarchy import median, inconsistent, maxinconsts
|
4030
|
+
>>> from scipy.spatial.distance import pdist
|
4031
|
+
|
4032
|
+
Given a data set ``X``, we can apply a clustering method to obtain a
|
4033
|
+
linkage matrix ``Z``. `scipy.cluster.hierarchy.inconsistent` can
|
4034
|
+
be also used to obtain the inconsistency matrix ``R`` associated to
|
4035
|
+
this clustering process:
|
4036
|
+
|
4037
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
4038
|
+
... [0, 4], [0, 3], [1, 4],
|
4039
|
+
... [4, 0], [3, 0], [4, 1],
|
4040
|
+
... [4, 4], [3, 4], [4, 3]]
|
4041
|
+
|
4042
|
+
>>> Z = median(pdist(X))
|
4043
|
+
>>> R = inconsistent(Z)
|
4044
|
+
>>> Z
|
4045
|
+
array([[ 0. , 1. , 1. , 2. ],
|
4046
|
+
[ 3. , 4. , 1. , 2. ],
|
4047
|
+
[ 9. , 10. , 1. , 2. ],
|
4048
|
+
[ 6. , 7. , 1. , 2. ],
|
4049
|
+
[ 2. , 12. , 1.11803399, 3. ],
|
4050
|
+
[ 5. , 13. , 1.11803399, 3. ],
|
4051
|
+
[ 8. , 15. , 1.11803399, 3. ],
|
4052
|
+
[11. , 14. , 1.11803399, 3. ],
|
4053
|
+
[18. , 19. , 3. , 6. ],
|
4054
|
+
[16. , 17. , 3.5 , 6. ],
|
4055
|
+
[20. , 21. , 3.25 , 12. ]])
|
4056
|
+
>>> R
|
4057
|
+
array([[1. , 0. , 1. , 0. ],
|
4058
|
+
[1. , 0. , 1. , 0. ],
|
4059
|
+
[1. , 0. , 1. , 0. ],
|
4060
|
+
[1. , 0. , 1. , 0. ],
|
4061
|
+
[1.05901699, 0.08346263, 2. , 0.70710678],
|
4062
|
+
[1.05901699, 0.08346263, 2. , 0.70710678],
|
4063
|
+
[1.05901699, 0.08346263, 2. , 0.70710678],
|
4064
|
+
[1.05901699, 0.08346263, 2. , 0.70710678],
|
4065
|
+
[1.74535599, 1.08655358, 3. , 1.15470054],
|
4066
|
+
[1.91202266, 1.37522872, 3. , 1.15470054],
|
4067
|
+
[3.25 , 0.25 , 3. , 0. ]])
|
4068
|
+
|
4069
|
+
Here, `scipy.cluster.hierarchy.maxinconsts` can be used to compute
|
4070
|
+
the maximum value of the inconsistency statistic (the last column of
|
4071
|
+
``R``) for each non-singleton cluster and its children:
|
4072
|
+
|
4073
|
+
>>> maxinconsts(Z, R)
|
4074
|
+
array([0. , 0. , 0. , 0. , 0.70710678,
|
4075
|
+
0.70710678, 0.70710678, 0.70710678, 1.15470054, 1.15470054,
|
4076
|
+
1.15470054])
|
4077
|
+
|
4078
|
+
"""
|
4079
|
+
xp = array_namespace(Z, R)
|
4080
|
+
Z = _asarray(Z, order='C', dtype=xp.float64, xp=xp)
|
4081
|
+
R = _asarray(R, order='C', dtype=xp.float64, xp=xp)
|
4082
|
+
_is_valid_linkage(Z, throw=True, name='Z', xp=xp)
|
4083
|
+
_is_valid_im(R, throw=True, name='R', xp=xp)
|
4084
|
+
|
4085
|
+
if Z.shape[0] != R.shape[0]:
|
4086
|
+
raise ValueError("The inconsistency matrix and linkage matrix each "
|
4087
|
+
"have a different number of rows.")
|
4088
|
+
|
4089
|
+
def cy_maxinconsts(Z, R, validate):
|
4090
|
+
if validate:
|
4091
|
+
_is_valid_linkage(Z, throw=True, name='Z', xp=np)
|
4092
|
+
_is_valid_im(R, throw=True, name='R', xp=np)
|
4093
|
+
n = Z.shape[0] + 1
|
4094
|
+
MI = np.zeros((n - 1,))
|
4095
|
+
_hierarchy.get_max_Rfield_for_each_cluster(Z, R, MI, n, 3)
|
4096
|
+
return MI
|
4097
|
+
|
4098
|
+
return xpx.lazy_apply(cy_maxinconsts, Z, R, validate=is_lazy_array(Z),
|
4099
|
+
shape=(Z.shape[0], ), dtype=xp.float64,
|
4100
|
+
as_numpy=True, xp=xp)
|
4101
|
+
|
4102
|
+
|
4103
|
+
@lazy_cython
|
4104
|
+
def maxRstat(Z, R, i):
|
4105
|
+
"""
|
4106
|
+
Return the maximum statistic for each non-singleton cluster and its
|
4107
|
+
children.
|
4108
|
+
|
4109
|
+
Parameters
|
4110
|
+
----------
|
4111
|
+
Z : array_like
|
4112
|
+
The hierarchical clustering encoded as a matrix. See `linkage` for more
|
4113
|
+
information.
|
4114
|
+
R : array_like
|
4115
|
+
The inconsistency matrix.
|
4116
|
+
i : int
|
4117
|
+
The column of `R` to use as the statistic.
|
4118
|
+
|
4119
|
+
Returns
|
4120
|
+
-------
|
4121
|
+
MR : ndarray
|
4122
|
+
Calculates the maximum statistic for the i'th column of the
|
4123
|
+
inconsistency matrix `R` for each non-singleton cluster
|
4124
|
+
node. ``MR[j]`` is the maximum over ``R[Q(j)-n, i]``, where
|
4125
|
+
``Q(j)`` the set of all node ids corresponding to nodes below
|
4126
|
+
and including ``j``.
|
4127
|
+
|
4128
|
+
See Also
|
4129
|
+
--------
|
4130
|
+
linkage : for a description of what a linkage matrix is.
|
4131
|
+
inconsistent : for the creation of a inconsistency matrix.
|
4132
|
+
|
4133
|
+
Examples
|
4134
|
+
--------
|
4135
|
+
>>> from scipy.cluster.hierarchy import median, inconsistent, maxRstat
|
4136
|
+
>>> from scipy.spatial.distance import pdist
|
4137
|
+
|
4138
|
+
Given a data set ``X``, we can apply a clustering method to obtain a
|
4139
|
+
linkage matrix ``Z``. `scipy.cluster.hierarchy.inconsistent` can
|
4140
|
+
be also used to obtain the inconsistency matrix ``R`` associated to
|
4141
|
+
this clustering process:
|
4142
|
+
|
4143
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
4144
|
+
... [0, 4], [0, 3], [1, 4],
|
4145
|
+
... [4, 0], [3, 0], [4, 1],
|
4146
|
+
... [4, 4], [3, 4], [4, 3]]
|
4147
|
+
|
4148
|
+
>>> Z = median(pdist(X))
|
4149
|
+
>>> R = inconsistent(Z)
|
4150
|
+
>>> R
|
4151
|
+
array([[1. , 0. , 1. , 0. ],
|
4152
|
+
[1. , 0. , 1. , 0. ],
|
4153
|
+
[1. , 0. , 1. , 0. ],
|
4154
|
+
[1. , 0. , 1. , 0. ],
|
4155
|
+
[1.05901699, 0.08346263, 2. , 0.70710678],
|
4156
|
+
[1.05901699, 0.08346263, 2. , 0.70710678],
|
4157
|
+
[1.05901699, 0.08346263, 2. , 0.70710678],
|
4158
|
+
[1.05901699, 0.08346263, 2. , 0.70710678],
|
4159
|
+
[1.74535599, 1.08655358, 3. , 1.15470054],
|
4160
|
+
[1.91202266, 1.37522872, 3. , 1.15470054],
|
4161
|
+
[3.25 , 0.25 , 3. , 0. ]])
|
4162
|
+
|
4163
|
+
`scipy.cluster.hierarchy.maxRstat` can be used to compute
|
4164
|
+
the maximum value of each column of ``R``, for each non-singleton
|
4165
|
+
cluster and its children:
|
4166
|
+
|
4167
|
+
>>> maxRstat(Z, R, 0)
|
4168
|
+
array([1. , 1. , 1. , 1. , 1.05901699,
|
4169
|
+
1.05901699, 1.05901699, 1.05901699, 1.74535599, 1.91202266,
|
4170
|
+
3.25 ])
|
4171
|
+
>>> maxRstat(Z, R, 1)
|
4172
|
+
array([0. , 0. , 0. , 0. , 0.08346263,
|
4173
|
+
0.08346263, 0.08346263, 0.08346263, 1.08655358, 1.37522872,
|
4174
|
+
1.37522872])
|
4175
|
+
>>> maxRstat(Z, R, 3)
|
4176
|
+
array([0. , 0. , 0. , 0. , 0.70710678,
|
4177
|
+
0.70710678, 0.70710678, 0.70710678, 1.15470054, 1.15470054,
|
4178
|
+
1.15470054])
|
4179
|
+
|
4180
|
+
"""
|
4181
|
+
xp = array_namespace(Z, R)
|
4182
|
+
Z = _asarray(Z, order='C', dtype=xp.float64, xp=xp)
|
4183
|
+
R = _asarray(R, order='C', dtype=xp.float64, xp=xp)
|
4184
|
+
_is_valid_linkage(Z, throw=True, name='Z', xp=xp)
|
4185
|
+
_is_valid_im(R, throw=True, name='R', xp=xp)
|
4186
|
+
|
4187
|
+
if not isinstance(i, int):
|
4188
|
+
raise TypeError('The third argument must be an integer.')
|
4189
|
+
|
4190
|
+
if i < 0 or i > 3:
|
4191
|
+
raise ValueError('i must be an integer between 0 and 3 inclusive.')
|
4192
|
+
|
4193
|
+
if Z.shape[0] != R.shape[0]:
|
4194
|
+
raise ValueError("The inconsistency matrix and linkage matrix each "
|
4195
|
+
"have a different number of rows.")
|
4196
|
+
|
4197
|
+
def cy_maxRstat(Z, R, i, validate):
|
4198
|
+
if validate:
|
4199
|
+
_is_valid_linkage(Z, throw=True, name='Z', xp=np)
|
4200
|
+
_is_valid_im(R, throw=True, name='R', xp=np)
|
4201
|
+
MR = np.zeros((Z.shape[0],))
|
4202
|
+
n = Z.shape[0] + 1
|
4203
|
+
_hierarchy.get_max_Rfield_for_each_cluster(Z, R, MR, n, i)
|
4204
|
+
return MR
|
4205
|
+
|
4206
|
+
return xpx.lazy_apply(cy_maxRstat, Z, R, i=i, validate=is_lazy_array(Z),
|
4207
|
+
shape=(Z.shape[0], ), dtype=xp.float64,
|
4208
|
+
as_numpy=True, xp=xp)
|
4209
|
+
|
4210
|
+
|
4211
|
+
# Data-dependent output shape makes it impossible to use jax.jit
|
4212
|
+
@xp_capabilities(cpu_only=True, reason="Cython code", jax_jit=False,
|
4213
|
+
warnings=[("dask.array", "merges chunks")])
|
4214
|
+
def leaders(Z, T):
|
4215
|
+
"""
|
4216
|
+
Return the root nodes in a hierarchical clustering.
|
4217
|
+
|
4218
|
+
Returns the root nodes in a hierarchical clustering corresponding
|
4219
|
+
to a cut defined by a flat cluster assignment vector ``T``. See
|
4220
|
+
the ``fcluster`` function for more information on the format of ``T``.
|
4221
|
+
|
4222
|
+
For each flat cluster :math:`j` of the :math:`k` flat clusters
|
4223
|
+
represented in the n-sized flat cluster assignment vector ``T``,
|
4224
|
+
this function finds the lowest cluster node :math:`i` in the linkage
|
4225
|
+
tree Z, such that:
|
4226
|
+
|
4227
|
+
* leaf descendants belong only to flat cluster j
|
4228
|
+
(i.e., ``T[p]==j`` for all :math:`p` in :math:`S(i)`, where
|
4229
|
+
:math:`S(i)` is the set of leaf ids of descendant leaf nodes
|
4230
|
+
with cluster node :math:`i`)
|
4231
|
+
|
4232
|
+
* there does not exist a leaf that is not a descendant with
|
4233
|
+
:math:`i` that also belongs to cluster :math:`j`
|
4234
|
+
(i.e., ``T[q]!=j`` for all :math:`q` not in :math:`S(i)`). If
|
4235
|
+
this condition is violated, ``T`` is not a valid cluster
|
4236
|
+
assignment vector, and an exception will be thrown.
|
4237
|
+
|
4238
|
+
Parameters
|
4239
|
+
----------
|
4240
|
+
Z : ndarray
|
4241
|
+
The hierarchical clustering encoded as a matrix. See
|
4242
|
+
`linkage` for more information.
|
4243
|
+
T : ndarray
|
4244
|
+
The flat cluster assignment vector.
|
4245
|
+
|
4246
|
+
Returns
|
4247
|
+
-------
|
4248
|
+
L : ndarray
|
4249
|
+
The leader linkage node id's stored as a k-element 1-D array,
|
4250
|
+
where ``k`` is the number of flat clusters found in ``T``.
|
4251
|
+
|
4252
|
+
``L[j]=i`` is the linkage cluster node id that is the
|
4253
|
+
leader of flat cluster with id M[j]. If ``i < n``, ``i``
|
4254
|
+
corresponds to an original observation, otherwise it
|
4255
|
+
corresponds to a non-singleton cluster.
|
4256
|
+
M : ndarray
|
4257
|
+
The leader linkage node id's stored as a k-element 1-D array, where
|
4258
|
+
``k`` is the number of flat clusters found in ``T``. This allows the
|
4259
|
+
set of flat cluster ids to be any arbitrary set of ``k`` integers.
|
4260
|
+
|
4261
|
+
For example: if ``L[3]=2`` and ``M[3]=8``, the flat cluster with
|
4262
|
+
id 8's leader is linkage node 2.
|
4263
|
+
|
4264
|
+
See Also
|
4265
|
+
--------
|
4266
|
+
fcluster : for the creation of flat cluster assignments.
|
4267
|
+
|
4268
|
+
Examples
|
4269
|
+
--------
|
4270
|
+
>>> from scipy.cluster.hierarchy import ward, fcluster, leaders
|
4271
|
+
>>> from scipy.spatial.distance import pdist
|
4272
|
+
|
4273
|
+
Given a linkage matrix ``Z`` - obtained after apply a clustering method
|
4274
|
+
to a dataset ``X`` - and a flat cluster assignment array ``T``:
|
4275
|
+
|
4276
|
+
>>> X = [[0, 0], [0, 1], [1, 0],
|
4277
|
+
... [0, 4], [0, 3], [1, 4],
|
4278
|
+
... [4, 0], [3, 0], [4, 1],
|
4279
|
+
... [4, 4], [3, 4], [4, 3]]
|
4280
|
+
|
4281
|
+
>>> Z = ward(pdist(X))
|
4282
|
+
>>> Z
|
4283
|
+
array([[ 0. , 1. , 1. , 2. ],
|
4284
|
+
[ 3. , 4. , 1. , 2. ],
|
4285
|
+
[ 6. , 7. , 1. , 2. ],
|
4286
|
+
[ 9. , 10. , 1. , 2. ],
|
4287
|
+
[ 2. , 12. , 1.29099445, 3. ],
|
4288
|
+
[ 5. , 13. , 1.29099445, 3. ],
|
4289
|
+
[ 8. , 14. , 1.29099445, 3. ],
|
4290
|
+
[11. , 15. , 1.29099445, 3. ],
|
4291
|
+
[16. , 17. , 5.77350269, 6. ],
|
4292
|
+
[18. , 19. , 5.77350269, 6. ],
|
4293
|
+
[20. , 21. , 8.16496581, 12. ]])
|
4294
|
+
|
4295
|
+
>>> T = fcluster(Z, 3, criterion='distance')
|
4296
|
+
>>> T
|
4297
|
+
array([1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4], dtype=int32)
|
4298
|
+
|
4299
|
+
`scipy.cluster.hierarchy.leaders` returns the indices of the nodes
|
4300
|
+
in the dendrogram that are the leaders of each flat cluster:
|
4301
|
+
|
4302
|
+
>>> L, M = leaders(Z, T)
|
4303
|
+
>>> L
|
4304
|
+
array([16, 17, 18, 19], dtype=int32)
|
4305
|
+
|
4306
|
+
(remember that indices 0-11 point to the 12 data points in ``X``,
|
4307
|
+
whereas indices 12-22 point to the 11 rows of ``Z``)
|
4308
|
+
|
4309
|
+
`scipy.cluster.hierarchy.leaders` also returns the indices of
|
4310
|
+
the flat clusters in ``T``:
|
4311
|
+
|
4312
|
+
>>> M
|
4313
|
+
array([1, 2, 3, 4], dtype=int32)
|
4314
|
+
|
4315
|
+
Notes
|
4316
|
+
-----
|
4317
|
+
*Array API support (experimental):* This function returns arrays
|
4318
|
+
with data-dependent shape. In JAX, at the moment of writing this makes it
|
4319
|
+
impossible to execute it inside `@jax.jit`.
|
4320
|
+
"""
|
4321
|
+
xp = array_namespace(Z, T)
|
4322
|
+
Z = _asarray(Z, order='C', dtype=xp.float64, xp=xp)
|
4323
|
+
T = _asarray(T, order='C', xp=xp)
|
4324
|
+
_is_valid_linkage(Z, throw=True, name='Z', xp=xp)
|
4325
|
+
|
4326
|
+
if T.dtype != xp.int32:
|
4327
|
+
raise TypeError('T must be a 1-D array of dtype int32.')
|
4328
|
+
|
4329
|
+
if T.shape[0] != Z.shape[0] + 1:
|
4330
|
+
raise ValueError('Mismatch: len(T)!=Z.shape[0] + 1.')
|
4331
|
+
|
4332
|
+
n_obs = Z.shape[0] + 1
|
4333
|
+
|
4334
|
+
def cy_leaders(Z, T, validate):
|
4335
|
+
if validate:
|
4336
|
+
_is_valid_linkage(Z, throw=True, name='Z', xp=np)
|
4337
|
+
n_clusters = int(xpx.nunique(T))
|
4338
|
+
L = np.zeros(n_clusters, dtype=np.int32)
|
4339
|
+
M = np.zeros(n_clusters, dtype=np.int32)
|
4340
|
+
s = _hierarchy.leaders(Z, T, L, M, n_clusters, n_obs)
|
4341
|
+
if s >= 0:
|
4342
|
+
raise ValueError('T is not a valid assignment vector. Error found '
|
4343
|
+
f'when examining linkage node {s} (< 2n-1).')
|
4344
|
+
return L, M
|
4345
|
+
|
4346
|
+
return xpx.lazy_apply(cy_leaders, Z, T, validate=is_lazy_array(Z),
|
4347
|
+
shape=((None,), (None, )), dtype=(xp.int32, xp.int32),
|
4348
|
+
as_numpy=True, xp=xp)
|