scipy 1.16.2__cp313-cp313t-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.
Files changed (1530) hide show
  1. scipy/__config__.py +161 -0
  2. scipy/__init__.py +150 -0
  3. scipy/_cyutility.cp313t-win_arm64.lib +0 -0
  4. scipy/_cyutility.cp313t-win_arm64.pyd +0 -0
  5. scipy/_distributor_init.py +18 -0
  6. scipy/_lib/__init__.py +14 -0
  7. scipy/_lib/_array_api.py +931 -0
  8. scipy/_lib/_array_api_compat_vendor.py +9 -0
  9. scipy/_lib/_array_api_no_0d.py +103 -0
  10. scipy/_lib/_bunch.py +229 -0
  11. scipy/_lib/_ccallback.py +251 -0
  12. scipy/_lib/_ccallback_c.cp313t-win_arm64.lib +0 -0
  13. scipy/_lib/_ccallback_c.cp313t-win_arm64.pyd +0 -0
  14. scipy/_lib/_disjoint_set.py +254 -0
  15. scipy/_lib/_docscrape.py +761 -0
  16. scipy/_lib/_elementwise_iterative_method.py +346 -0
  17. scipy/_lib/_fpumode.cp313t-win_arm64.lib +0 -0
  18. scipy/_lib/_fpumode.cp313t-win_arm64.pyd +0 -0
  19. scipy/_lib/_gcutils.py +105 -0
  20. scipy/_lib/_pep440.py +487 -0
  21. scipy/_lib/_sparse.py +41 -0
  22. scipy/_lib/_test_ccallback.cp313t-win_arm64.lib +0 -0
  23. scipy/_lib/_test_ccallback.cp313t-win_arm64.pyd +0 -0
  24. scipy/_lib/_test_deprecation_call.cp313t-win_arm64.lib +0 -0
  25. scipy/_lib/_test_deprecation_call.cp313t-win_arm64.pyd +0 -0
  26. scipy/_lib/_test_deprecation_def.cp313t-win_arm64.lib +0 -0
  27. scipy/_lib/_test_deprecation_def.cp313t-win_arm64.pyd +0 -0
  28. scipy/_lib/_testutils.py +373 -0
  29. scipy/_lib/_threadsafety.py +58 -0
  30. scipy/_lib/_tmpdirs.py +86 -0
  31. scipy/_lib/_uarray/LICENSE +29 -0
  32. scipy/_lib/_uarray/__init__.py +116 -0
  33. scipy/_lib/_uarray/_backend.py +707 -0
  34. scipy/_lib/_uarray/_uarray.cp313t-win_arm64.lib +0 -0
  35. scipy/_lib/_uarray/_uarray.cp313t-win_arm64.pyd +0 -0
  36. scipy/_lib/_util.py +1283 -0
  37. scipy/_lib/array_api_compat/__init__.py +22 -0
  38. scipy/_lib/array_api_compat/_internal.py +59 -0
  39. scipy/_lib/array_api_compat/common/__init__.py +1 -0
  40. scipy/_lib/array_api_compat/common/_aliases.py +727 -0
  41. scipy/_lib/array_api_compat/common/_fft.py +213 -0
  42. scipy/_lib/array_api_compat/common/_helpers.py +1058 -0
  43. scipy/_lib/array_api_compat/common/_linalg.py +232 -0
  44. scipy/_lib/array_api_compat/common/_typing.py +192 -0
  45. scipy/_lib/array_api_compat/cupy/__init__.py +13 -0
  46. scipy/_lib/array_api_compat/cupy/_aliases.py +156 -0
  47. scipy/_lib/array_api_compat/cupy/_info.py +336 -0
  48. scipy/_lib/array_api_compat/cupy/_typing.py +31 -0
  49. scipy/_lib/array_api_compat/cupy/fft.py +36 -0
  50. scipy/_lib/array_api_compat/cupy/linalg.py +49 -0
  51. scipy/_lib/array_api_compat/dask/__init__.py +0 -0
  52. scipy/_lib/array_api_compat/dask/array/__init__.py +12 -0
  53. scipy/_lib/array_api_compat/dask/array/_aliases.py +376 -0
  54. scipy/_lib/array_api_compat/dask/array/_info.py +416 -0
  55. scipy/_lib/array_api_compat/dask/array/fft.py +21 -0
  56. scipy/_lib/array_api_compat/dask/array/linalg.py +72 -0
  57. scipy/_lib/array_api_compat/numpy/__init__.py +28 -0
  58. scipy/_lib/array_api_compat/numpy/_aliases.py +190 -0
  59. scipy/_lib/array_api_compat/numpy/_info.py +366 -0
  60. scipy/_lib/array_api_compat/numpy/_typing.py +30 -0
  61. scipy/_lib/array_api_compat/numpy/fft.py +35 -0
  62. scipy/_lib/array_api_compat/numpy/linalg.py +143 -0
  63. scipy/_lib/array_api_compat/torch/__init__.py +22 -0
  64. scipy/_lib/array_api_compat/torch/_aliases.py +855 -0
  65. scipy/_lib/array_api_compat/torch/_info.py +369 -0
  66. scipy/_lib/array_api_compat/torch/_typing.py +3 -0
  67. scipy/_lib/array_api_compat/torch/fft.py +85 -0
  68. scipy/_lib/array_api_compat/torch/linalg.py +121 -0
  69. scipy/_lib/array_api_extra/__init__.py +38 -0
  70. scipy/_lib/array_api_extra/_delegation.py +171 -0
  71. scipy/_lib/array_api_extra/_lib/__init__.py +1 -0
  72. scipy/_lib/array_api_extra/_lib/_at.py +463 -0
  73. scipy/_lib/array_api_extra/_lib/_backends.py +46 -0
  74. scipy/_lib/array_api_extra/_lib/_funcs.py +937 -0
  75. scipy/_lib/array_api_extra/_lib/_lazy.py +357 -0
  76. scipy/_lib/array_api_extra/_lib/_testing.py +278 -0
  77. scipy/_lib/array_api_extra/_lib/_utils/__init__.py +1 -0
  78. scipy/_lib/array_api_extra/_lib/_utils/_compat.py +74 -0
  79. scipy/_lib/array_api_extra/_lib/_utils/_compat.pyi +45 -0
  80. scipy/_lib/array_api_extra/_lib/_utils/_helpers.py +559 -0
  81. scipy/_lib/array_api_extra/_lib/_utils/_typing.py +10 -0
  82. scipy/_lib/array_api_extra/_lib/_utils/_typing.pyi +105 -0
  83. scipy/_lib/array_api_extra/testing.py +359 -0
  84. scipy/_lib/cobyqa/__init__.py +20 -0
  85. scipy/_lib/cobyqa/framework.py +1240 -0
  86. scipy/_lib/cobyqa/main.py +1506 -0
  87. scipy/_lib/cobyqa/models.py +1529 -0
  88. scipy/_lib/cobyqa/problem.py +1296 -0
  89. scipy/_lib/cobyqa/settings.py +132 -0
  90. scipy/_lib/cobyqa/subsolvers/__init__.py +14 -0
  91. scipy/_lib/cobyqa/subsolvers/geometry.py +387 -0
  92. scipy/_lib/cobyqa/subsolvers/optim.py +1203 -0
  93. scipy/_lib/cobyqa/utils/__init__.py +18 -0
  94. scipy/_lib/cobyqa/utils/exceptions.py +22 -0
  95. scipy/_lib/cobyqa/utils/math.py +77 -0
  96. scipy/_lib/cobyqa/utils/versions.py +67 -0
  97. scipy/_lib/decorator.py +399 -0
  98. scipy/_lib/deprecation.py +274 -0
  99. scipy/_lib/doccer.py +366 -0
  100. scipy/_lib/messagestream.cp313t-win_arm64.lib +0 -0
  101. scipy/_lib/messagestream.cp313t-win_arm64.pyd +0 -0
  102. scipy/_lib/pyprima/__init__.py +212 -0
  103. scipy/_lib/pyprima/cobyla/__init__.py +0 -0
  104. scipy/_lib/pyprima/cobyla/cobyla.py +559 -0
  105. scipy/_lib/pyprima/cobyla/cobylb.py +714 -0
  106. scipy/_lib/pyprima/cobyla/geometry.py +226 -0
  107. scipy/_lib/pyprima/cobyla/initialize.py +215 -0
  108. scipy/_lib/pyprima/cobyla/trustregion.py +492 -0
  109. scipy/_lib/pyprima/cobyla/update.py +289 -0
  110. scipy/_lib/pyprima/common/__init__.py +0 -0
  111. scipy/_lib/pyprima/common/_bounds.py +34 -0
  112. scipy/_lib/pyprima/common/_linear_constraints.py +46 -0
  113. scipy/_lib/pyprima/common/_nonlinear_constraints.py +54 -0
  114. scipy/_lib/pyprima/common/_project.py +173 -0
  115. scipy/_lib/pyprima/common/checkbreak.py +93 -0
  116. scipy/_lib/pyprima/common/consts.py +47 -0
  117. scipy/_lib/pyprima/common/evaluate.py +99 -0
  118. scipy/_lib/pyprima/common/history.py +38 -0
  119. scipy/_lib/pyprima/common/infos.py +30 -0
  120. scipy/_lib/pyprima/common/linalg.py +435 -0
  121. scipy/_lib/pyprima/common/message.py +290 -0
  122. scipy/_lib/pyprima/common/powalg.py +131 -0
  123. scipy/_lib/pyprima/common/preproc.py +277 -0
  124. scipy/_lib/pyprima/common/present.py +5 -0
  125. scipy/_lib/pyprima/common/ratio.py +54 -0
  126. scipy/_lib/pyprima/common/redrho.py +47 -0
  127. scipy/_lib/pyprima/common/selectx.py +296 -0
  128. scipy/_lib/tests/__init__.py +0 -0
  129. scipy/_lib/tests/test__gcutils.py +110 -0
  130. scipy/_lib/tests/test__pep440.py +67 -0
  131. scipy/_lib/tests/test__testutils.py +32 -0
  132. scipy/_lib/tests/test__threadsafety.py +51 -0
  133. scipy/_lib/tests/test__util.py +641 -0
  134. scipy/_lib/tests/test_array_api.py +322 -0
  135. scipy/_lib/tests/test_bunch.py +169 -0
  136. scipy/_lib/tests/test_ccallback.py +196 -0
  137. scipy/_lib/tests/test_config.py +45 -0
  138. scipy/_lib/tests/test_deprecation.py +10 -0
  139. scipy/_lib/tests/test_doccer.py +143 -0
  140. scipy/_lib/tests/test_import_cycles.py +18 -0
  141. scipy/_lib/tests/test_public_api.py +482 -0
  142. scipy/_lib/tests/test_scipy_version.py +28 -0
  143. scipy/_lib/tests/test_tmpdirs.py +48 -0
  144. scipy/_lib/tests/test_warnings.py +137 -0
  145. scipy/_lib/uarray.py +31 -0
  146. scipy/cluster/__init__.py +31 -0
  147. scipy/cluster/_hierarchy.cp313t-win_arm64.lib +0 -0
  148. scipy/cluster/_hierarchy.cp313t-win_arm64.pyd +0 -0
  149. scipy/cluster/_optimal_leaf_ordering.cp313t-win_arm64.lib +0 -0
  150. scipy/cluster/_optimal_leaf_ordering.cp313t-win_arm64.pyd +0 -0
  151. scipy/cluster/_vq.cp313t-win_arm64.lib +0 -0
  152. scipy/cluster/_vq.cp313t-win_arm64.pyd +0 -0
  153. scipy/cluster/hierarchy.py +4348 -0
  154. scipy/cluster/tests/__init__.py +0 -0
  155. scipy/cluster/tests/hierarchy_test_data.py +145 -0
  156. scipy/cluster/tests/test_disjoint_set.py +202 -0
  157. scipy/cluster/tests/test_hierarchy.py +1238 -0
  158. scipy/cluster/tests/test_vq.py +434 -0
  159. scipy/cluster/vq.py +832 -0
  160. scipy/conftest.py +683 -0
  161. scipy/constants/__init__.py +358 -0
  162. scipy/constants/_codata.py +2266 -0
  163. scipy/constants/_constants.py +369 -0
  164. scipy/constants/codata.py +21 -0
  165. scipy/constants/constants.py +53 -0
  166. scipy/constants/tests/__init__.py +0 -0
  167. scipy/constants/tests/test_codata.py +78 -0
  168. scipy/constants/tests/test_constants.py +83 -0
  169. scipy/datasets/__init__.py +90 -0
  170. scipy/datasets/_download_all.py +71 -0
  171. scipy/datasets/_fetchers.py +225 -0
  172. scipy/datasets/_registry.py +26 -0
  173. scipy/datasets/_utils.py +81 -0
  174. scipy/datasets/tests/__init__.py +0 -0
  175. scipy/datasets/tests/test_data.py +128 -0
  176. scipy/differentiate/__init__.py +27 -0
  177. scipy/differentiate/_differentiate.py +1129 -0
  178. scipy/differentiate/tests/__init__.py +0 -0
  179. scipy/differentiate/tests/test_differentiate.py +694 -0
  180. scipy/fft/__init__.py +114 -0
  181. scipy/fft/_backend.py +196 -0
  182. scipy/fft/_basic.py +1650 -0
  183. scipy/fft/_basic_backend.py +197 -0
  184. scipy/fft/_debug_backends.py +22 -0
  185. scipy/fft/_fftlog.py +223 -0
  186. scipy/fft/_fftlog_backend.py +200 -0
  187. scipy/fft/_helper.py +348 -0
  188. scipy/fft/_pocketfft/LICENSE.md +25 -0
  189. scipy/fft/_pocketfft/__init__.py +9 -0
  190. scipy/fft/_pocketfft/basic.py +251 -0
  191. scipy/fft/_pocketfft/helper.py +249 -0
  192. scipy/fft/_pocketfft/pypocketfft.cp313t-win_arm64.lib +0 -0
  193. scipy/fft/_pocketfft/pypocketfft.cp313t-win_arm64.pyd +0 -0
  194. scipy/fft/_pocketfft/realtransforms.py +109 -0
  195. scipy/fft/_pocketfft/tests/__init__.py +0 -0
  196. scipy/fft/_pocketfft/tests/test_basic.py +1011 -0
  197. scipy/fft/_pocketfft/tests/test_real_transforms.py +505 -0
  198. scipy/fft/_realtransforms.py +706 -0
  199. scipy/fft/_realtransforms_backend.py +63 -0
  200. scipy/fft/tests/__init__.py +0 -0
  201. scipy/fft/tests/mock_backend.py +96 -0
  202. scipy/fft/tests/test_backend.py +98 -0
  203. scipy/fft/tests/test_basic.py +504 -0
  204. scipy/fft/tests/test_fftlog.py +215 -0
  205. scipy/fft/tests/test_helper.py +558 -0
  206. scipy/fft/tests/test_multithreading.py +84 -0
  207. scipy/fft/tests/test_real_transforms.py +247 -0
  208. scipy/fftpack/__init__.py +103 -0
  209. scipy/fftpack/_basic.py +428 -0
  210. scipy/fftpack/_helper.py +115 -0
  211. scipy/fftpack/_pseudo_diffs.py +554 -0
  212. scipy/fftpack/_realtransforms.py +598 -0
  213. scipy/fftpack/basic.py +20 -0
  214. scipy/fftpack/convolve.cp313t-win_arm64.lib +0 -0
  215. scipy/fftpack/convolve.cp313t-win_arm64.pyd +0 -0
  216. scipy/fftpack/helper.py +19 -0
  217. scipy/fftpack/pseudo_diffs.py +22 -0
  218. scipy/fftpack/realtransforms.py +19 -0
  219. scipy/fftpack/tests/__init__.py +0 -0
  220. scipy/fftpack/tests/fftw_double_ref.npz +0 -0
  221. scipy/fftpack/tests/fftw_longdouble_ref.npz +0 -0
  222. scipy/fftpack/tests/fftw_single_ref.npz +0 -0
  223. scipy/fftpack/tests/test.npz +0 -0
  224. scipy/fftpack/tests/test_basic.py +877 -0
  225. scipy/fftpack/tests/test_helper.py +54 -0
  226. scipy/fftpack/tests/test_import.py +33 -0
  227. scipy/fftpack/tests/test_pseudo_diffs.py +388 -0
  228. scipy/fftpack/tests/test_real_transforms.py +836 -0
  229. scipy/integrate/__init__.py +122 -0
  230. scipy/integrate/_bvp.py +1160 -0
  231. scipy/integrate/_cubature.py +729 -0
  232. scipy/integrate/_dop.cp313t-win_arm64.lib +0 -0
  233. scipy/integrate/_dop.cp313t-win_arm64.pyd +0 -0
  234. scipy/integrate/_ivp/__init__.py +8 -0
  235. scipy/integrate/_ivp/base.py +290 -0
  236. scipy/integrate/_ivp/bdf.py +478 -0
  237. scipy/integrate/_ivp/common.py +451 -0
  238. scipy/integrate/_ivp/dop853_coefficients.py +193 -0
  239. scipy/integrate/_ivp/ivp.py +755 -0
  240. scipy/integrate/_ivp/lsoda.py +224 -0
  241. scipy/integrate/_ivp/radau.py +572 -0
  242. scipy/integrate/_ivp/rk.py +601 -0
  243. scipy/integrate/_ivp/tests/__init__.py +0 -0
  244. scipy/integrate/_ivp/tests/test_ivp.py +1287 -0
  245. scipy/integrate/_ivp/tests/test_rk.py +37 -0
  246. scipy/integrate/_lebedev.py +5450 -0
  247. scipy/integrate/_lsoda.cp313t-win_arm64.lib +0 -0
  248. scipy/integrate/_lsoda.cp313t-win_arm64.pyd +0 -0
  249. scipy/integrate/_ode.py +1395 -0
  250. scipy/integrate/_odepack.cp313t-win_arm64.lib +0 -0
  251. scipy/integrate/_odepack.cp313t-win_arm64.pyd +0 -0
  252. scipy/integrate/_odepack_py.py +273 -0
  253. scipy/integrate/_quad_vec.py +674 -0
  254. scipy/integrate/_quadpack.cp313t-win_arm64.lib +0 -0
  255. scipy/integrate/_quadpack.cp313t-win_arm64.pyd +0 -0
  256. scipy/integrate/_quadpack_py.py +1283 -0
  257. scipy/integrate/_quadrature.py +1336 -0
  258. scipy/integrate/_rules/__init__.py +12 -0
  259. scipy/integrate/_rules/_base.py +518 -0
  260. scipy/integrate/_rules/_gauss_kronrod.py +202 -0
  261. scipy/integrate/_rules/_gauss_legendre.py +62 -0
  262. scipy/integrate/_rules/_genz_malik.py +210 -0
  263. scipy/integrate/_tanhsinh.py +1385 -0
  264. scipy/integrate/_test_multivariate.cp313t-win_arm64.lib +0 -0
  265. scipy/integrate/_test_multivariate.cp313t-win_arm64.pyd +0 -0
  266. scipy/integrate/_test_odeint_banded.cp313t-win_arm64.lib +0 -0
  267. scipy/integrate/_test_odeint_banded.cp313t-win_arm64.pyd +0 -0
  268. scipy/integrate/_vode.cp313t-win_arm64.lib +0 -0
  269. scipy/integrate/_vode.cp313t-win_arm64.pyd +0 -0
  270. scipy/integrate/dop.py +15 -0
  271. scipy/integrate/lsoda.py +15 -0
  272. scipy/integrate/odepack.py +17 -0
  273. scipy/integrate/quadpack.py +23 -0
  274. scipy/integrate/tests/__init__.py +0 -0
  275. scipy/integrate/tests/test__quad_vec.py +211 -0
  276. scipy/integrate/tests/test_banded_ode_solvers.py +305 -0
  277. scipy/integrate/tests/test_bvp.py +714 -0
  278. scipy/integrate/tests/test_cubature.py +1375 -0
  279. scipy/integrate/tests/test_integrate.py +840 -0
  280. scipy/integrate/tests/test_odeint_jac.py +74 -0
  281. scipy/integrate/tests/test_quadpack.py +680 -0
  282. scipy/integrate/tests/test_quadrature.py +730 -0
  283. scipy/integrate/tests/test_tanhsinh.py +1171 -0
  284. scipy/integrate/vode.py +15 -0
  285. scipy/interpolate/__init__.py +228 -0
  286. scipy/interpolate/_bary_rational.py +715 -0
  287. scipy/interpolate/_bsplines.py +2469 -0
  288. scipy/interpolate/_cubic.py +973 -0
  289. scipy/interpolate/_dfitpack.cp313t-win_arm64.lib +0 -0
  290. scipy/interpolate/_dfitpack.cp313t-win_arm64.pyd +0 -0
  291. scipy/interpolate/_dierckx.cp313t-win_arm64.lib +0 -0
  292. scipy/interpolate/_dierckx.cp313t-win_arm64.pyd +0 -0
  293. scipy/interpolate/_fitpack.cp313t-win_arm64.lib +0 -0
  294. scipy/interpolate/_fitpack.cp313t-win_arm64.pyd +0 -0
  295. scipy/interpolate/_fitpack2.py +2397 -0
  296. scipy/interpolate/_fitpack_impl.py +811 -0
  297. scipy/interpolate/_fitpack_py.py +898 -0
  298. scipy/interpolate/_fitpack_repro.py +996 -0
  299. scipy/interpolate/_interpnd.cp313t-win_arm64.lib +0 -0
  300. scipy/interpolate/_interpnd.cp313t-win_arm64.pyd +0 -0
  301. scipy/interpolate/_interpolate.py +2266 -0
  302. scipy/interpolate/_ndbspline.py +415 -0
  303. scipy/interpolate/_ndgriddata.py +329 -0
  304. scipy/interpolate/_pade.py +67 -0
  305. scipy/interpolate/_polyint.py +1025 -0
  306. scipy/interpolate/_ppoly.cp313t-win_arm64.lib +0 -0
  307. scipy/interpolate/_ppoly.cp313t-win_arm64.pyd +0 -0
  308. scipy/interpolate/_rbf.py +290 -0
  309. scipy/interpolate/_rbfinterp.py +550 -0
  310. scipy/interpolate/_rbfinterp_pythran.cp313t-win_arm64.lib +0 -0
  311. scipy/interpolate/_rbfinterp_pythran.cp313t-win_arm64.pyd +0 -0
  312. scipy/interpolate/_rgi.py +764 -0
  313. scipy/interpolate/_rgi_cython.cp313t-win_arm64.lib +0 -0
  314. scipy/interpolate/_rgi_cython.cp313t-win_arm64.pyd +0 -0
  315. scipy/interpolate/dfitpack.py +24 -0
  316. scipy/interpolate/fitpack.py +31 -0
  317. scipy/interpolate/fitpack2.py +29 -0
  318. scipy/interpolate/interpnd.py +24 -0
  319. scipy/interpolate/interpolate.py +30 -0
  320. scipy/interpolate/ndgriddata.py +23 -0
  321. scipy/interpolate/polyint.py +24 -0
  322. scipy/interpolate/rbf.py +18 -0
  323. scipy/interpolate/tests/__init__.py +0 -0
  324. scipy/interpolate/tests/data/bug-1310.npz +0 -0
  325. scipy/interpolate/tests/data/estimate_gradients_hang.npy +0 -0
  326. scipy/interpolate/tests/data/gcvspl.npz +0 -0
  327. scipy/interpolate/tests/test_bary_rational.py +368 -0
  328. scipy/interpolate/tests/test_bsplines.py +3754 -0
  329. scipy/interpolate/tests/test_fitpack.py +519 -0
  330. scipy/interpolate/tests/test_fitpack2.py +1431 -0
  331. scipy/interpolate/tests/test_gil.py +64 -0
  332. scipy/interpolate/tests/test_interpnd.py +452 -0
  333. scipy/interpolate/tests/test_interpolate.py +2630 -0
  334. scipy/interpolate/tests/test_ndgriddata.py +308 -0
  335. scipy/interpolate/tests/test_pade.py +107 -0
  336. scipy/interpolate/tests/test_polyint.py +972 -0
  337. scipy/interpolate/tests/test_rbf.py +246 -0
  338. scipy/interpolate/tests/test_rbfinterp.py +534 -0
  339. scipy/interpolate/tests/test_rgi.py +1151 -0
  340. scipy/io/__init__.py +116 -0
  341. scipy/io/_fast_matrix_market/__init__.py +600 -0
  342. scipy/io/_fast_matrix_market/_fmm_core.cp313t-win_arm64.lib +0 -0
  343. scipy/io/_fast_matrix_market/_fmm_core.cp313t-win_arm64.pyd +0 -0
  344. scipy/io/_fortran.py +354 -0
  345. scipy/io/_harwell_boeing/__init__.py +7 -0
  346. scipy/io/_harwell_boeing/_fortran_format_parser.py +316 -0
  347. scipy/io/_harwell_boeing/hb.py +571 -0
  348. scipy/io/_harwell_boeing/tests/__init__.py +0 -0
  349. scipy/io/_harwell_boeing/tests/test_fortran_format.py +74 -0
  350. scipy/io/_harwell_boeing/tests/test_hb.py +70 -0
  351. scipy/io/_idl.py +917 -0
  352. scipy/io/_mmio.py +968 -0
  353. scipy/io/_netcdf.py +1104 -0
  354. scipy/io/_test_fortran.cp313t-win_arm64.lib +0 -0
  355. scipy/io/_test_fortran.cp313t-win_arm64.pyd +0 -0
  356. scipy/io/arff/__init__.py +28 -0
  357. scipy/io/arff/_arffread.py +873 -0
  358. scipy/io/arff/arffread.py +19 -0
  359. scipy/io/arff/tests/__init__.py +0 -0
  360. scipy/io/arff/tests/data/iris.arff +225 -0
  361. scipy/io/arff/tests/data/missing.arff +8 -0
  362. scipy/io/arff/tests/data/nodata.arff +11 -0
  363. scipy/io/arff/tests/data/quoted_nominal.arff +13 -0
  364. scipy/io/arff/tests/data/quoted_nominal_spaces.arff +13 -0
  365. scipy/io/arff/tests/data/test1.arff +10 -0
  366. scipy/io/arff/tests/data/test10.arff +8 -0
  367. scipy/io/arff/tests/data/test11.arff +11 -0
  368. scipy/io/arff/tests/data/test2.arff +15 -0
  369. scipy/io/arff/tests/data/test3.arff +6 -0
  370. scipy/io/arff/tests/data/test4.arff +11 -0
  371. scipy/io/arff/tests/data/test5.arff +26 -0
  372. scipy/io/arff/tests/data/test6.arff +12 -0
  373. scipy/io/arff/tests/data/test7.arff +15 -0
  374. scipy/io/arff/tests/data/test8.arff +12 -0
  375. scipy/io/arff/tests/data/test9.arff +14 -0
  376. scipy/io/arff/tests/test_arffread.py +421 -0
  377. scipy/io/harwell_boeing.py +17 -0
  378. scipy/io/idl.py +17 -0
  379. scipy/io/matlab/__init__.py +66 -0
  380. scipy/io/matlab/_byteordercodes.py +75 -0
  381. scipy/io/matlab/_mio.py +375 -0
  382. scipy/io/matlab/_mio4.py +632 -0
  383. scipy/io/matlab/_mio5.py +901 -0
  384. scipy/io/matlab/_mio5_params.py +281 -0
  385. scipy/io/matlab/_mio5_utils.cp313t-win_arm64.lib +0 -0
  386. scipy/io/matlab/_mio5_utils.cp313t-win_arm64.pyd +0 -0
  387. scipy/io/matlab/_mio_utils.cp313t-win_arm64.lib +0 -0
  388. scipy/io/matlab/_mio_utils.cp313t-win_arm64.pyd +0 -0
  389. scipy/io/matlab/_miobase.py +435 -0
  390. scipy/io/matlab/_streams.cp313t-win_arm64.lib +0 -0
  391. scipy/io/matlab/_streams.cp313t-win_arm64.pyd +0 -0
  392. scipy/io/matlab/byteordercodes.py +17 -0
  393. scipy/io/matlab/mio.py +16 -0
  394. scipy/io/matlab/mio4.py +17 -0
  395. scipy/io/matlab/mio5.py +19 -0
  396. scipy/io/matlab/mio5_params.py +18 -0
  397. scipy/io/matlab/mio5_utils.py +17 -0
  398. scipy/io/matlab/mio_utils.py +17 -0
  399. scipy/io/matlab/miobase.py +16 -0
  400. scipy/io/matlab/streams.py +16 -0
  401. scipy/io/matlab/tests/__init__.py +0 -0
  402. scipy/io/matlab/tests/data/bad_miuint32.mat +0 -0
  403. scipy/io/matlab/tests/data/bad_miutf8_array_name.mat +0 -0
  404. scipy/io/matlab/tests/data/big_endian.mat +0 -0
  405. scipy/io/matlab/tests/data/broken_utf8.mat +0 -0
  406. scipy/io/matlab/tests/data/corrupted_zlib_checksum.mat +0 -0
  407. scipy/io/matlab/tests/data/corrupted_zlib_data.mat +0 -0
  408. scipy/io/matlab/tests/data/debigged_m4.mat +0 -0
  409. scipy/io/matlab/tests/data/japanese_utf8.txt +5 -0
  410. scipy/io/matlab/tests/data/little_endian.mat +0 -0
  411. scipy/io/matlab/tests/data/logical_sparse.mat +0 -0
  412. scipy/io/matlab/tests/data/malformed1.mat +0 -0
  413. scipy/io/matlab/tests/data/miuint32_for_miint32.mat +0 -0
  414. scipy/io/matlab/tests/data/miutf8_array_name.mat +0 -0
  415. scipy/io/matlab/tests/data/nasty_duplicate_fieldnames.mat +0 -0
  416. scipy/io/matlab/tests/data/one_by_zero_char.mat +0 -0
  417. scipy/io/matlab/tests/data/parabola.mat +0 -0
  418. scipy/io/matlab/tests/data/single_empty_string.mat +0 -0
  419. scipy/io/matlab/tests/data/some_functions.mat +0 -0
  420. scipy/io/matlab/tests/data/sqr.mat +0 -0
  421. scipy/io/matlab/tests/data/test3dmatrix_6.1_SOL2.mat +0 -0
  422. scipy/io/matlab/tests/data/test3dmatrix_6.5.1_GLNX86.mat +0 -0
  423. scipy/io/matlab/tests/data/test3dmatrix_7.1_GLNX86.mat +0 -0
  424. scipy/io/matlab/tests/data/test3dmatrix_7.4_GLNX86.mat +0 -0
  425. scipy/io/matlab/tests/data/test_empty_struct.mat +0 -0
  426. scipy/io/matlab/tests/data/test_mat4_le_floats.mat +0 -0
  427. scipy/io/matlab/tests/data/test_skip_variable.mat +0 -0
  428. scipy/io/matlab/tests/data/testbool_8_WIN64.mat +0 -0
  429. scipy/io/matlab/tests/data/testcell_6.1_SOL2.mat +0 -0
  430. scipy/io/matlab/tests/data/testcell_6.5.1_GLNX86.mat +0 -0
  431. scipy/io/matlab/tests/data/testcell_7.1_GLNX86.mat +0 -0
  432. scipy/io/matlab/tests/data/testcell_7.4_GLNX86.mat +0 -0
  433. scipy/io/matlab/tests/data/testcellnest_6.1_SOL2.mat +0 -0
  434. scipy/io/matlab/tests/data/testcellnest_6.5.1_GLNX86.mat +0 -0
  435. scipy/io/matlab/tests/data/testcellnest_7.1_GLNX86.mat +0 -0
  436. scipy/io/matlab/tests/data/testcellnest_7.4_GLNX86.mat +0 -0
  437. scipy/io/matlab/tests/data/testcomplex_4.2c_SOL2.mat +0 -0
  438. scipy/io/matlab/tests/data/testcomplex_6.1_SOL2.mat +0 -0
  439. scipy/io/matlab/tests/data/testcomplex_6.5.1_GLNX86.mat +0 -0
  440. scipy/io/matlab/tests/data/testcomplex_7.1_GLNX86.mat +0 -0
  441. scipy/io/matlab/tests/data/testcomplex_7.4_GLNX86.mat +0 -0
  442. scipy/io/matlab/tests/data/testdouble_4.2c_SOL2.mat +0 -0
  443. scipy/io/matlab/tests/data/testdouble_6.1_SOL2.mat +0 -0
  444. scipy/io/matlab/tests/data/testdouble_6.5.1_GLNX86.mat +0 -0
  445. scipy/io/matlab/tests/data/testdouble_7.1_GLNX86.mat +0 -0
  446. scipy/io/matlab/tests/data/testdouble_7.4_GLNX86.mat +0 -0
  447. scipy/io/matlab/tests/data/testemptycell_5.3_SOL2.mat +0 -0
  448. scipy/io/matlab/tests/data/testemptycell_6.5.1_GLNX86.mat +0 -0
  449. scipy/io/matlab/tests/data/testemptycell_7.1_GLNX86.mat +0 -0
  450. scipy/io/matlab/tests/data/testemptycell_7.4_GLNX86.mat +0 -0
  451. scipy/io/matlab/tests/data/testfunc_7.4_GLNX86.mat +0 -0
  452. scipy/io/matlab/tests/data/testhdf5_7.4_GLNX86.mat +0 -0
  453. scipy/io/matlab/tests/data/testmatrix_4.2c_SOL2.mat +0 -0
  454. scipy/io/matlab/tests/data/testmatrix_6.1_SOL2.mat +0 -0
  455. scipy/io/matlab/tests/data/testmatrix_6.5.1_GLNX86.mat +0 -0
  456. scipy/io/matlab/tests/data/testmatrix_7.1_GLNX86.mat +0 -0
  457. scipy/io/matlab/tests/data/testmatrix_7.4_GLNX86.mat +0 -0
  458. scipy/io/matlab/tests/data/testminus_4.2c_SOL2.mat +0 -0
  459. scipy/io/matlab/tests/data/testminus_6.1_SOL2.mat +0 -0
  460. scipy/io/matlab/tests/data/testminus_6.5.1_GLNX86.mat +0 -0
  461. scipy/io/matlab/tests/data/testminus_7.1_GLNX86.mat +0 -0
  462. scipy/io/matlab/tests/data/testminus_7.4_GLNX86.mat +0 -0
  463. scipy/io/matlab/tests/data/testmulti_4.2c_SOL2.mat +0 -0
  464. scipy/io/matlab/tests/data/testmulti_7.1_GLNX86.mat +0 -0
  465. scipy/io/matlab/tests/data/testmulti_7.4_GLNX86.mat +0 -0
  466. scipy/io/matlab/tests/data/testobject_6.1_SOL2.mat +0 -0
  467. scipy/io/matlab/tests/data/testobject_6.5.1_GLNX86.mat +0 -0
  468. scipy/io/matlab/tests/data/testobject_7.1_GLNX86.mat +0 -0
  469. scipy/io/matlab/tests/data/testobject_7.4_GLNX86.mat +0 -0
  470. scipy/io/matlab/tests/data/testonechar_4.2c_SOL2.mat +0 -0
  471. scipy/io/matlab/tests/data/testonechar_6.1_SOL2.mat +0 -0
  472. scipy/io/matlab/tests/data/testonechar_6.5.1_GLNX86.mat +0 -0
  473. scipy/io/matlab/tests/data/testonechar_7.1_GLNX86.mat +0 -0
  474. scipy/io/matlab/tests/data/testonechar_7.4_GLNX86.mat +0 -0
  475. scipy/io/matlab/tests/data/testscalarcell_7.4_GLNX86.mat +0 -0
  476. scipy/io/matlab/tests/data/testsimplecell.mat +0 -0
  477. scipy/io/matlab/tests/data/testsparse_4.2c_SOL2.mat +0 -0
  478. scipy/io/matlab/tests/data/testsparse_6.1_SOL2.mat +0 -0
  479. scipy/io/matlab/tests/data/testsparse_6.5.1_GLNX86.mat +0 -0
  480. scipy/io/matlab/tests/data/testsparse_7.1_GLNX86.mat +0 -0
  481. scipy/io/matlab/tests/data/testsparse_7.4_GLNX86.mat +0 -0
  482. scipy/io/matlab/tests/data/testsparsecomplex_4.2c_SOL2.mat +0 -0
  483. scipy/io/matlab/tests/data/testsparsecomplex_6.1_SOL2.mat +0 -0
  484. scipy/io/matlab/tests/data/testsparsecomplex_6.5.1_GLNX86.mat +0 -0
  485. scipy/io/matlab/tests/data/testsparsecomplex_7.1_GLNX86.mat +0 -0
  486. scipy/io/matlab/tests/data/testsparsecomplex_7.4_GLNX86.mat +0 -0
  487. scipy/io/matlab/tests/data/testsparsefloat_7.4_GLNX86.mat +0 -0
  488. scipy/io/matlab/tests/data/teststring_4.2c_SOL2.mat +0 -0
  489. scipy/io/matlab/tests/data/teststring_6.1_SOL2.mat +0 -0
  490. scipy/io/matlab/tests/data/teststring_6.5.1_GLNX86.mat +0 -0
  491. scipy/io/matlab/tests/data/teststring_7.1_GLNX86.mat +0 -0
  492. scipy/io/matlab/tests/data/teststring_7.4_GLNX86.mat +0 -0
  493. scipy/io/matlab/tests/data/teststringarray_4.2c_SOL2.mat +0 -0
  494. scipy/io/matlab/tests/data/teststringarray_6.1_SOL2.mat +0 -0
  495. scipy/io/matlab/tests/data/teststringarray_6.5.1_GLNX86.mat +0 -0
  496. scipy/io/matlab/tests/data/teststringarray_7.1_GLNX86.mat +0 -0
  497. scipy/io/matlab/tests/data/teststringarray_7.4_GLNX86.mat +0 -0
  498. scipy/io/matlab/tests/data/teststruct_6.1_SOL2.mat +0 -0
  499. scipy/io/matlab/tests/data/teststruct_6.5.1_GLNX86.mat +0 -0
  500. scipy/io/matlab/tests/data/teststruct_7.1_GLNX86.mat +0 -0
  501. scipy/io/matlab/tests/data/teststruct_7.4_GLNX86.mat +0 -0
  502. scipy/io/matlab/tests/data/teststructarr_6.1_SOL2.mat +0 -0
  503. scipy/io/matlab/tests/data/teststructarr_6.5.1_GLNX86.mat +0 -0
  504. scipy/io/matlab/tests/data/teststructarr_7.1_GLNX86.mat +0 -0
  505. scipy/io/matlab/tests/data/teststructarr_7.4_GLNX86.mat +0 -0
  506. scipy/io/matlab/tests/data/teststructnest_6.1_SOL2.mat +0 -0
  507. scipy/io/matlab/tests/data/teststructnest_6.5.1_GLNX86.mat +0 -0
  508. scipy/io/matlab/tests/data/teststructnest_7.1_GLNX86.mat +0 -0
  509. scipy/io/matlab/tests/data/teststructnest_7.4_GLNX86.mat +0 -0
  510. scipy/io/matlab/tests/data/testunicode_7.1_GLNX86.mat +0 -0
  511. scipy/io/matlab/tests/data/testunicode_7.4_GLNX86.mat +0 -0
  512. scipy/io/matlab/tests/data/testvec_4_GLNX86.mat +0 -0
  513. scipy/io/matlab/tests/test_byteordercodes.py +29 -0
  514. scipy/io/matlab/tests/test_mio.py +1399 -0
  515. scipy/io/matlab/tests/test_mio5_utils.py +179 -0
  516. scipy/io/matlab/tests/test_mio_funcs.py +51 -0
  517. scipy/io/matlab/tests/test_mio_utils.py +45 -0
  518. scipy/io/matlab/tests/test_miobase.py +32 -0
  519. scipy/io/matlab/tests/test_pathological.py +33 -0
  520. scipy/io/matlab/tests/test_streams.py +241 -0
  521. scipy/io/mmio.py +17 -0
  522. scipy/io/netcdf.py +17 -0
  523. scipy/io/tests/__init__.py +0 -0
  524. scipy/io/tests/data/Transparent Busy.ani +0 -0
  525. scipy/io/tests/data/array_float32_1d.sav +0 -0
  526. scipy/io/tests/data/array_float32_2d.sav +0 -0
  527. scipy/io/tests/data/array_float32_3d.sav +0 -0
  528. scipy/io/tests/data/array_float32_4d.sav +0 -0
  529. scipy/io/tests/data/array_float32_5d.sav +0 -0
  530. scipy/io/tests/data/array_float32_6d.sav +0 -0
  531. scipy/io/tests/data/array_float32_7d.sav +0 -0
  532. scipy/io/tests/data/array_float32_8d.sav +0 -0
  533. scipy/io/tests/data/array_float32_pointer_1d.sav +0 -0
  534. scipy/io/tests/data/array_float32_pointer_2d.sav +0 -0
  535. scipy/io/tests/data/array_float32_pointer_3d.sav +0 -0
  536. scipy/io/tests/data/array_float32_pointer_4d.sav +0 -0
  537. scipy/io/tests/data/array_float32_pointer_5d.sav +0 -0
  538. scipy/io/tests/data/array_float32_pointer_6d.sav +0 -0
  539. scipy/io/tests/data/array_float32_pointer_7d.sav +0 -0
  540. scipy/io/tests/data/array_float32_pointer_8d.sav +0 -0
  541. scipy/io/tests/data/example_1.nc +0 -0
  542. scipy/io/tests/data/example_2.nc +0 -0
  543. scipy/io/tests/data/example_3_maskedvals.nc +0 -0
  544. scipy/io/tests/data/fortran-3x3d-2i.dat +0 -0
  545. scipy/io/tests/data/fortran-mixed.dat +0 -0
  546. scipy/io/tests/data/fortran-sf8-11x1x10.dat +0 -0
  547. scipy/io/tests/data/fortran-sf8-15x10x22.dat +0 -0
  548. scipy/io/tests/data/fortran-sf8-1x1x1.dat +0 -0
  549. scipy/io/tests/data/fortran-sf8-1x1x5.dat +0 -0
  550. scipy/io/tests/data/fortran-sf8-1x1x7.dat +0 -0
  551. scipy/io/tests/data/fortran-sf8-1x3x5.dat +0 -0
  552. scipy/io/tests/data/fortran-si4-11x1x10.dat +0 -0
  553. scipy/io/tests/data/fortran-si4-15x10x22.dat +0 -0
  554. scipy/io/tests/data/fortran-si4-1x1x1.dat +0 -0
  555. scipy/io/tests/data/fortran-si4-1x1x5.dat +0 -0
  556. scipy/io/tests/data/fortran-si4-1x1x7.dat +0 -0
  557. scipy/io/tests/data/fortran-si4-1x3x5.dat +0 -0
  558. scipy/io/tests/data/invalid_pointer.sav +0 -0
  559. scipy/io/tests/data/null_pointer.sav +0 -0
  560. scipy/io/tests/data/scalar_byte.sav +0 -0
  561. scipy/io/tests/data/scalar_byte_descr.sav +0 -0
  562. scipy/io/tests/data/scalar_complex32.sav +0 -0
  563. scipy/io/tests/data/scalar_complex64.sav +0 -0
  564. scipy/io/tests/data/scalar_float32.sav +0 -0
  565. scipy/io/tests/data/scalar_float64.sav +0 -0
  566. scipy/io/tests/data/scalar_heap_pointer.sav +0 -0
  567. scipy/io/tests/data/scalar_int16.sav +0 -0
  568. scipy/io/tests/data/scalar_int32.sav +0 -0
  569. scipy/io/tests/data/scalar_int64.sav +0 -0
  570. scipy/io/tests/data/scalar_string.sav +0 -0
  571. scipy/io/tests/data/scalar_uint16.sav +0 -0
  572. scipy/io/tests/data/scalar_uint32.sav +0 -0
  573. scipy/io/tests/data/scalar_uint64.sav +0 -0
  574. scipy/io/tests/data/struct_arrays.sav +0 -0
  575. scipy/io/tests/data/struct_arrays_byte_idl80.sav +0 -0
  576. scipy/io/tests/data/struct_arrays_replicated.sav +0 -0
  577. scipy/io/tests/data/struct_arrays_replicated_3d.sav +0 -0
  578. scipy/io/tests/data/struct_inherit.sav +0 -0
  579. scipy/io/tests/data/struct_pointer_arrays.sav +0 -0
  580. scipy/io/tests/data/struct_pointer_arrays_replicated.sav +0 -0
  581. scipy/io/tests/data/struct_pointer_arrays_replicated_3d.sav +0 -0
  582. scipy/io/tests/data/struct_pointers.sav +0 -0
  583. scipy/io/tests/data/struct_pointers_replicated.sav +0 -0
  584. scipy/io/tests/data/struct_pointers_replicated_3d.sav +0 -0
  585. scipy/io/tests/data/struct_scalars.sav +0 -0
  586. scipy/io/tests/data/struct_scalars_replicated.sav +0 -0
  587. scipy/io/tests/data/struct_scalars_replicated_3d.sav +0 -0
  588. scipy/io/tests/data/test-1234Hz-le-1ch-10S-20bit-extra.wav +0 -0
  589. scipy/io/tests/data/test-44100Hz-2ch-32bit-float-be.wav +0 -0
  590. scipy/io/tests/data/test-44100Hz-2ch-32bit-float-le.wav +0 -0
  591. scipy/io/tests/data/test-44100Hz-be-1ch-4bytes.wav +0 -0
  592. scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-early-eof-no-data.wav +0 -0
  593. scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-early-eof.wav +0 -0
  594. scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-incomplete-chunk.wav +0 -0
  595. scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-rf64.wav +0 -0
  596. scipy/io/tests/data/test-44100Hz-le-1ch-4bytes.wav +0 -0
  597. scipy/io/tests/data/test-48000Hz-2ch-64bit-float-le-wavex.wav +0 -0
  598. scipy/io/tests/data/test-8000Hz-be-3ch-5S-24bit.wav +0 -0
  599. scipy/io/tests/data/test-8000Hz-le-1ch-1byte-ulaw.wav +0 -0
  600. scipy/io/tests/data/test-8000Hz-le-2ch-1byteu.wav +0 -0
  601. scipy/io/tests/data/test-8000Hz-le-3ch-5S-24bit-inconsistent.wav +0 -0
  602. scipy/io/tests/data/test-8000Hz-le-3ch-5S-24bit-rf64.wav +0 -0
  603. scipy/io/tests/data/test-8000Hz-le-3ch-5S-24bit.wav +0 -0
  604. scipy/io/tests/data/test-8000Hz-le-3ch-5S-36bit.wav +0 -0
  605. scipy/io/tests/data/test-8000Hz-le-3ch-5S-45bit.wav +0 -0
  606. scipy/io/tests/data/test-8000Hz-le-3ch-5S-53bit.wav +0 -0
  607. scipy/io/tests/data/test-8000Hz-le-3ch-5S-64bit.wav +0 -0
  608. scipy/io/tests/data/test-8000Hz-le-4ch-9S-12bit.wav +0 -0
  609. scipy/io/tests/data/test-8000Hz-le-5ch-9S-5bit.wav +0 -0
  610. scipy/io/tests/data/various_compressed.sav +0 -0
  611. scipy/io/tests/test_fortran.py +264 -0
  612. scipy/io/tests/test_idl.py +483 -0
  613. scipy/io/tests/test_mmio.py +831 -0
  614. scipy/io/tests/test_netcdf.py +550 -0
  615. scipy/io/tests/test_paths.py +93 -0
  616. scipy/io/tests/test_wavfile.py +501 -0
  617. scipy/io/wavfile.py +938 -0
  618. scipy/linalg/__init__.pxd +1 -0
  619. scipy/linalg/__init__.py +236 -0
  620. scipy/linalg/_basic.py +2146 -0
  621. scipy/linalg/_blas_subroutines.h +164 -0
  622. scipy/linalg/_cythonized_array_utils.cp313t-win_arm64.lib +0 -0
  623. scipy/linalg/_cythonized_array_utils.cp313t-win_arm64.pyd +0 -0
  624. scipy/linalg/_cythonized_array_utils.pxd +40 -0
  625. scipy/linalg/_cythonized_array_utils.pyi +16 -0
  626. scipy/linalg/_decomp.py +1645 -0
  627. scipy/linalg/_decomp_cholesky.py +413 -0
  628. scipy/linalg/_decomp_cossin.py +236 -0
  629. scipy/linalg/_decomp_interpolative.cp313t-win_arm64.lib +0 -0
  630. scipy/linalg/_decomp_interpolative.cp313t-win_arm64.pyd +0 -0
  631. scipy/linalg/_decomp_ldl.py +356 -0
  632. scipy/linalg/_decomp_lu.py +401 -0
  633. scipy/linalg/_decomp_lu_cython.cp313t-win_arm64.lib +0 -0
  634. scipy/linalg/_decomp_lu_cython.cp313t-win_arm64.pyd +0 -0
  635. scipy/linalg/_decomp_lu_cython.pyi +6 -0
  636. scipy/linalg/_decomp_polar.py +113 -0
  637. scipy/linalg/_decomp_qr.py +494 -0
  638. scipy/linalg/_decomp_qz.py +452 -0
  639. scipy/linalg/_decomp_schur.py +336 -0
  640. scipy/linalg/_decomp_svd.py +545 -0
  641. scipy/linalg/_decomp_update.cp313t-win_arm64.lib +0 -0
  642. scipy/linalg/_decomp_update.cp313t-win_arm64.pyd +0 -0
  643. scipy/linalg/_expm_frechet.py +417 -0
  644. scipy/linalg/_fblas.cp313t-win_arm64.lib +0 -0
  645. scipy/linalg/_fblas.cp313t-win_arm64.pyd +0 -0
  646. scipy/linalg/_flapack.cp313t-win_arm64.lib +0 -0
  647. scipy/linalg/_flapack.cp313t-win_arm64.pyd +0 -0
  648. scipy/linalg/_lapack_subroutines.h +1521 -0
  649. scipy/linalg/_linalg_pythran.cp313t-win_arm64.lib +0 -0
  650. scipy/linalg/_linalg_pythran.cp313t-win_arm64.pyd +0 -0
  651. scipy/linalg/_matfuncs.py +1050 -0
  652. scipy/linalg/_matfuncs_expm.cp313t-win_arm64.lib +0 -0
  653. scipy/linalg/_matfuncs_expm.cp313t-win_arm64.pyd +0 -0
  654. scipy/linalg/_matfuncs_expm.pyi +6 -0
  655. scipy/linalg/_matfuncs_inv_ssq.py +886 -0
  656. scipy/linalg/_matfuncs_schur_sqrtm.cp313t-win_arm64.lib +0 -0
  657. scipy/linalg/_matfuncs_schur_sqrtm.cp313t-win_arm64.pyd +0 -0
  658. scipy/linalg/_matfuncs_sqrtm.py +107 -0
  659. scipy/linalg/_matfuncs_sqrtm_triu.cp313t-win_arm64.lib +0 -0
  660. scipy/linalg/_matfuncs_sqrtm_triu.cp313t-win_arm64.pyd +0 -0
  661. scipy/linalg/_misc.py +191 -0
  662. scipy/linalg/_procrustes.py +113 -0
  663. scipy/linalg/_sketches.py +189 -0
  664. scipy/linalg/_solve_toeplitz.cp313t-win_arm64.lib +0 -0
  665. scipy/linalg/_solve_toeplitz.cp313t-win_arm64.pyd +0 -0
  666. scipy/linalg/_solvers.py +862 -0
  667. scipy/linalg/_special_matrices.py +1322 -0
  668. scipy/linalg/_testutils.py +65 -0
  669. scipy/linalg/basic.py +23 -0
  670. scipy/linalg/blas.py +495 -0
  671. scipy/linalg/cython_blas.cp313t-win_arm64.lib +0 -0
  672. scipy/linalg/cython_blas.cp313t-win_arm64.pyd +0 -0
  673. scipy/linalg/cython_blas.pxd +169 -0
  674. scipy/linalg/cython_blas.pyx +1432 -0
  675. scipy/linalg/cython_lapack.cp313t-win_arm64.lib +0 -0
  676. scipy/linalg/cython_lapack.cp313t-win_arm64.pyd +0 -0
  677. scipy/linalg/cython_lapack.pxd +1528 -0
  678. scipy/linalg/cython_lapack.pyx +12045 -0
  679. scipy/linalg/decomp.py +23 -0
  680. scipy/linalg/decomp_cholesky.py +21 -0
  681. scipy/linalg/decomp_lu.py +21 -0
  682. scipy/linalg/decomp_qr.py +20 -0
  683. scipy/linalg/decomp_schur.py +21 -0
  684. scipy/linalg/decomp_svd.py +21 -0
  685. scipy/linalg/interpolative.py +989 -0
  686. scipy/linalg/lapack.py +1081 -0
  687. scipy/linalg/matfuncs.py +23 -0
  688. scipy/linalg/misc.py +21 -0
  689. scipy/linalg/special_matrices.py +22 -0
  690. scipy/linalg/tests/__init__.py +0 -0
  691. scipy/linalg/tests/_cython_examples/extending.pyx +23 -0
  692. scipy/linalg/tests/_cython_examples/meson.build +34 -0
  693. scipy/linalg/tests/data/carex_15_data.npz +0 -0
  694. scipy/linalg/tests/data/carex_18_data.npz +0 -0
  695. scipy/linalg/tests/data/carex_19_data.npz +0 -0
  696. scipy/linalg/tests/data/carex_20_data.npz +0 -0
  697. scipy/linalg/tests/data/carex_6_data.npz +0 -0
  698. scipy/linalg/tests/data/gendare_20170120_data.npz +0 -0
  699. scipy/linalg/tests/test_basic.py +2074 -0
  700. scipy/linalg/tests/test_batch.py +588 -0
  701. scipy/linalg/tests/test_blas.py +1127 -0
  702. scipy/linalg/tests/test_cython_blas.py +118 -0
  703. scipy/linalg/tests/test_cython_lapack.py +22 -0
  704. scipy/linalg/tests/test_cythonized_array_utils.py +130 -0
  705. scipy/linalg/tests/test_decomp.py +3189 -0
  706. scipy/linalg/tests/test_decomp_cholesky.py +268 -0
  707. scipy/linalg/tests/test_decomp_cossin.py +314 -0
  708. scipy/linalg/tests/test_decomp_ldl.py +137 -0
  709. scipy/linalg/tests/test_decomp_lu.py +308 -0
  710. scipy/linalg/tests/test_decomp_polar.py +110 -0
  711. scipy/linalg/tests/test_decomp_update.py +1701 -0
  712. scipy/linalg/tests/test_extending.py +46 -0
  713. scipy/linalg/tests/test_fblas.py +607 -0
  714. scipy/linalg/tests/test_interpolative.py +232 -0
  715. scipy/linalg/tests/test_lapack.py +3620 -0
  716. scipy/linalg/tests/test_matfuncs.py +1125 -0
  717. scipy/linalg/tests/test_matmul_toeplitz.py +136 -0
  718. scipy/linalg/tests/test_procrustes.py +214 -0
  719. scipy/linalg/tests/test_sketches.py +118 -0
  720. scipy/linalg/tests/test_solve_toeplitz.py +150 -0
  721. scipy/linalg/tests/test_solvers.py +844 -0
  722. scipy/linalg/tests/test_special_matrices.py +636 -0
  723. scipy/misc/__init__.py +6 -0
  724. scipy/misc/common.py +6 -0
  725. scipy/misc/doccer.py +6 -0
  726. scipy/ndimage/__init__.py +174 -0
  727. scipy/ndimage/_ctest.cp313t-win_arm64.lib +0 -0
  728. scipy/ndimage/_ctest.cp313t-win_arm64.pyd +0 -0
  729. scipy/ndimage/_cytest.cp313t-win_arm64.lib +0 -0
  730. scipy/ndimage/_cytest.cp313t-win_arm64.pyd +0 -0
  731. scipy/ndimage/_delegators.py +303 -0
  732. scipy/ndimage/_filters.py +2422 -0
  733. scipy/ndimage/_fourier.py +306 -0
  734. scipy/ndimage/_interpolation.py +1033 -0
  735. scipy/ndimage/_measurements.py +1689 -0
  736. scipy/ndimage/_morphology.py +2634 -0
  737. scipy/ndimage/_nd_image.cp313t-win_arm64.lib +0 -0
  738. scipy/ndimage/_nd_image.cp313t-win_arm64.pyd +0 -0
  739. scipy/ndimage/_ndimage_api.py +16 -0
  740. scipy/ndimage/_ni_docstrings.py +214 -0
  741. scipy/ndimage/_ni_label.cp313t-win_arm64.lib +0 -0
  742. scipy/ndimage/_ni_label.cp313t-win_arm64.pyd +0 -0
  743. scipy/ndimage/_ni_support.py +139 -0
  744. scipy/ndimage/_rank_filter_1d.cp313t-win_arm64.lib +0 -0
  745. scipy/ndimage/_rank_filter_1d.cp313t-win_arm64.pyd +0 -0
  746. scipy/ndimage/_support_alternative_backends.py +84 -0
  747. scipy/ndimage/filters.py +27 -0
  748. scipy/ndimage/fourier.py +21 -0
  749. scipy/ndimage/interpolation.py +22 -0
  750. scipy/ndimage/measurements.py +24 -0
  751. scipy/ndimage/morphology.py +27 -0
  752. scipy/ndimage/tests/__init__.py +12 -0
  753. scipy/ndimage/tests/data/label_inputs.txt +21 -0
  754. scipy/ndimage/tests/data/label_results.txt +294 -0
  755. scipy/ndimage/tests/data/label_strels.txt +42 -0
  756. scipy/ndimage/tests/dots.png +0 -0
  757. scipy/ndimage/tests/test_c_api.py +102 -0
  758. scipy/ndimage/tests/test_datatypes.py +67 -0
  759. scipy/ndimage/tests/test_filters.py +3083 -0
  760. scipy/ndimage/tests/test_fourier.py +187 -0
  761. scipy/ndimage/tests/test_interpolation.py +1491 -0
  762. scipy/ndimage/tests/test_measurements.py +1592 -0
  763. scipy/ndimage/tests/test_morphology.py +2950 -0
  764. scipy/ndimage/tests/test_ni_support.py +78 -0
  765. scipy/ndimage/tests/test_splines.py +70 -0
  766. scipy/odr/__init__.py +131 -0
  767. scipy/odr/__odrpack.cp313t-win_arm64.lib +0 -0
  768. scipy/odr/__odrpack.cp313t-win_arm64.pyd +0 -0
  769. scipy/odr/_add_newdocs.py +34 -0
  770. scipy/odr/_models.py +315 -0
  771. scipy/odr/_odrpack.py +1154 -0
  772. scipy/odr/models.py +20 -0
  773. scipy/odr/odrpack.py +21 -0
  774. scipy/odr/tests/__init__.py +0 -0
  775. scipy/odr/tests/test_odr.py +607 -0
  776. scipy/optimize/__init__.pxd +1 -0
  777. scipy/optimize/__init__.py +460 -0
  778. scipy/optimize/_basinhopping.py +741 -0
  779. scipy/optimize/_bglu_dense.cp313t-win_arm64.lib +0 -0
  780. scipy/optimize/_bglu_dense.cp313t-win_arm64.pyd +0 -0
  781. scipy/optimize/_bracket.py +706 -0
  782. scipy/optimize/_chandrupatla.py +551 -0
  783. scipy/optimize/_cobyla_py.py +297 -0
  784. scipy/optimize/_cobyqa_py.py +72 -0
  785. scipy/optimize/_constraints.py +598 -0
  786. scipy/optimize/_dcsrch.py +728 -0
  787. scipy/optimize/_differentiable_functions.py +835 -0
  788. scipy/optimize/_differentialevolution.py +1970 -0
  789. scipy/optimize/_direct.cp313t-win_arm64.lib +0 -0
  790. scipy/optimize/_direct.cp313t-win_arm64.pyd +0 -0
  791. scipy/optimize/_direct_py.py +280 -0
  792. scipy/optimize/_dual_annealing.py +732 -0
  793. scipy/optimize/_elementwise.py +798 -0
  794. scipy/optimize/_group_columns.cp313t-win_arm64.lib +0 -0
  795. scipy/optimize/_group_columns.cp313t-win_arm64.pyd +0 -0
  796. scipy/optimize/_hessian_update_strategy.py +479 -0
  797. scipy/optimize/_highspy/__init__.py +0 -0
  798. scipy/optimize/_highspy/_core.cp313t-win_arm64.lib +0 -0
  799. scipy/optimize/_highspy/_core.cp313t-win_arm64.pyd +0 -0
  800. scipy/optimize/_highspy/_highs_options.cp313t-win_arm64.lib +0 -0
  801. scipy/optimize/_highspy/_highs_options.cp313t-win_arm64.pyd +0 -0
  802. scipy/optimize/_highspy/_highs_wrapper.py +338 -0
  803. scipy/optimize/_isotonic.py +157 -0
  804. scipy/optimize/_lbfgsb.cp313t-win_arm64.lib +0 -0
  805. scipy/optimize/_lbfgsb.cp313t-win_arm64.pyd +0 -0
  806. scipy/optimize/_lbfgsb_py.py +634 -0
  807. scipy/optimize/_linesearch.py +896 -0
  808. scipy/optimize/_linprog.py +733 -0
  809. scipy/optimize/_linprog_doc.py +1434 -0
  810. scipy/optimize/_linprog_highs.py +422 -0
  811. scipy/optimize/_linprog_ip.py +1141 -0
  812. scipy/optimize/_linprog_rs.py +572 -0
  813. scipy/optimize/_linprog_simplex.py +663 -0
  814. scipy/optimize/_linprog_util.py +1521 -0
  815. scipy/optimize/_lsap.cp313t-win_arm64.lib +0 -0
  816. scipy/optimize/_lsap.cp313t-win_arm64.pyd +0 -0
  817. scipy/optimize/_lsq/__init__.py +5 -0
  818. scipy/optimize/_lsq/bvls.py +183 -0
  819. scipy/optimize/_lsq/common.py +731 -0
  820. scipy/optimize/_lsq/dogbox.py +345 -0
  821. scipy/optimize/_lsq/givens_elimination.cp313t-win_arm64.lib +0 -0
  822. scipy/optimize/_lsq/givens_elimination.cp313t-win_arm64.pyd +0 -0
  823. scipy/optimize/_lsq/least_squares.py +1044 -0
  824. scipy/optimize/_lsq/lsq_linear.py +361 -0
  825. scipy/optimize/_lsq/trf.py +587 -0
  826. scipy/optimize/_lsq/trf_linear.py +249 -0
  827. scipy/optimize/_milp.py +394 -0
  828. scipy/optimize/_minimize.py +1199 -0
  829. scipy/optimize/_minpack.cp313t-win_arm64.lib +0 -0
  830. scipy/optimize/_minpack.cp313t-win_arm64.pyd +0 -0
  831. scipy/optimize/_minpack_py.py +1178 -0
  832. scipy/optimize/_moduleTNC.cp313t-win_arm64.lib +0 -0
  833. scipy/optimize/_moduleTNC.cp313t-win_arm64.pyd +0 -0
  834. scipy/optimize/_nnls.py +96 -0
  835. scipy/optimize/_nonlin.py +1634 -0
  836. scipy/optimize/_numdiff.py +963 -0
  837. scipy/optimize/_optimize.py +4169 -0
  838. scipy/optimize/_pava_pybind.cp313t-win_arm64.lib +0 -0
  839. scipy/optimize/_pava_pybind.cp313t-win_arm64.pyd +0 -0
  840. scipy/optimize/_qap.py +760 -0
  841. scipy/optimize/_remove_redundancy.py +522 -0
  842. scipy/optimize/_root.py +732 -0
  843. scipy/optimize/_root_scalar.py +538 -0
  844. scipy/optimize/_shgo.py +1606 -0
  845. scipy/optimize/_shgo_lib/__init__.py +0 -0
  846. scipy/optimize/_shgo_lib/_complex.py +1225 -0
  847. scipy/optimize/_shgo_lib/_vertex.py +460 -0
  848. scipy/optimize/_slsqp_py.py +603 -0
  849. scipy/optimize/_slsqplib.cp313t-win_arm64.lib +0 -0
  850. scipy/optimize/_slsqplib.cp313t-win_arm64.pyd +0 -0
  851. scipy/optimize/_spectral.py +260 -0
  852. scipy/optimize/_tnc.py +438 -0
  853. scipy/optimize/_trlib/__init__.py +12 -0
  854. scipy/optimize/_trlib/_trlib.cp313t-win_arm64.lib +0 -0
  855. scipy/optimize/_trlib/_trlib.cp313t-win_arm64.pyd +0 -0
  856. scipy/optimize/_trustregion.py +318 -0
  857. scipy/optimize/_trustregion_constr/__init__.py +6 -0
  858. scipy/optimize/_trustregion_constr/canonical_constraint.py +390 -0
  859. scipy/optimize/_trustregion_constr/equality_constrained_sqp.py +231 -0
  860. scipy/optimize/_trustregion_constr/minimize_trustregion_constr.py +584 -0
  861. scipy/optimize/_trustregion_constr/projections.py +411 -0
  862. scipy/optimize/_trustregion_constr/qp_subproblem.py +637 -0
  863. scipy/optimize/_trustregion_constr/report.py +49 -0
  864. scipy/optimize/_trustregion_constr/tests/__init__.py +0 -0
  865. scipy/optimize/_trustregion_constr/tests/test_canonical_constraint.py +296 -0
  866. scipy/optimize/_trustregion_constr/tests/test_nested_minimize.py +39 -0
  867. scipy/optimize/_trustregion_constr/tests/test_projections.py +214 -0
  868. scipy/optimize/_trustregion_constr/tests/test_qp_subproblem.py +645 -0
  869. scipy/optimize/_trustregion_constr/tests/test_report.py +34 -0
  870. scipy/optimize/_trustregion_constr/tr_interior_point.py +361 -0
  871. scipy/optimize/_trustregion_dogleg.py +122 -0
  872. scipy/optimize/_trustregion_exact.py +437 -0
  873. scipy/optimize/_trustregion_krylov.py +65 -0
  874. scipy/optimize/_trustregion_ncg.py +126 -0
  875. scipy/optimize/_tstutils.py +972 -0
  876. scipy/optimize/_zeros.cp313t-win_arm64.lib +0 -0
  877. scipy/optimize/_zeros.cp313t-win_arm64.pyd +0 -0
  878. scipy/optimize/_zeros_py.py +1475 -0
  879. scipy/optimize/cobyla.py +19 -0
  880. scipy/optimize/cython_optimize/__init__.py +133 -0
  881. scipy/optimize/cython_optimize/_zeros.cp313t-win_arm64.lib +0 -0
  882. scipy/optimize/cython_optimize/_zeros.cp313t-win_arm64.pyd +0 -0
  883. scipy/optimize/cython_optimize/_zeros.pxd +33 -0
  884. scipy/optimize/cython_optimize/c_zeros.pxd +26 -0
  885. scipy/optimize/cython_optimize.pxd +11 -0
  886. scipy/optimize/elementwise.py +38 -0
  887. scipy/optimize/lbfgsb.py +23 -0
  888. scipy/optimize/linesearch.py +18 -0
  889. scipy/optimize/minpack.py +27 -0
  890. scipy/optimize/minpack2.py +17 -0
  891. scipy/optimize/moduleTNC.py +19 -0
  892. scipy/optimize/nonlin.py +29 -0
  893. scipy/optimize/optimize.py +40 -0
  894. scipy/optimize/slsqp.py +22 -0
  895. scipy/optimize/tests/__init__.py +0 -0
  896. scipy/optimize/tests/_cython_examples/extending.pyx +43 -0
  897. scipy/optimize/tests/_cython_examples/meson.build +32 -0
  898. scipy/optimize/tests/test__basinhopping.py +535 -0
  899. scipy/optimize/tests/test__differential_evolution.py +1703 -0
  900. scipy/optimize/tests/test__dual_annealing.py +416 -0
  901. scipy/optimize/tests/test__linprog_clean_inputs.py +312 -0
  902. scipy/optimize/tests/test__numdiff.py +885 -0
  903. scipy/optimize/tests/test__remove_redundancy.py +228 -0
  904. scipy/optimize/tests/test__root.py +124 -0
  905. scipy/optimize/tests/test__shgo.py +1164 -0
  906. scipy/optimize/tests/test__spectral.py +226 -0
  907. scipy/optimize/tests/test_bracket.py +896 -0
  908. scipy/optimize/tests/test_chandrupatla.py +982 -0
  909. scipy/optimize/tests/test_cobyla.py +195 -0
  910. scipy/optimize/tests/test_cobyqa.py +252 -0
  911. scipy/optimize/tests/test_constraint_conversion.py +286 -0
  912. scipy/optimize/tests/test_constraints.py +255 -0
  913. scipy/optimize/tests/test_cython_optimize.py +92 -0
  914. scipy/optimize/tests/test_differentiable_functions.py +1025 -0
  915. scipy/optimize/tests/test_direct.py +321 -0
  916. scipy/optimize/tests/test_extending.py +28 -0
  917. scipy/optimize/tests/test_hessian_update_strategy.py +300 -0
  918. scipy/optimize/tests/test_isotonic_regression.py +167 -0
  919. scipy/optimize/tests/test_lbfgsb_hessinv.py +65 -0
  920. scipy/optimize/tests/test_lbfgsb_setulb.py +122 -0
  921. scipy/optimize/tests/test_least_squares.py +986 -0
  922. scipy/optimize/tests/test_linear_assignment.py +116 -0
  923. scipy/optimize/tests/test_linesearch.py +328 -0
  924. scipy/optimize/tests/test_linprog.py +2577 -0
  925. scipy/optimize/tests/test_lsq_common.py +297 -0
  926. scipy/optimize/tests/test_lsq_linear.py +287 -0
  927. scipy/optimize/tests/test_milp.py +459 -0
  928. scipy/optimize/tests/test_minimize_constrained.py +845 -0
  929. scipy/optimize/tests/test_minpack.py +1194 -0
  930. scipy/optimize/tests/test_nnls.py +469 -0
  931. scipy/optimize/tests/test_nonlin.py +572 -0
  932. scipy/optimize/tests/test_optimize.py +3344 -0
  933. scipy/optimize/tests/test_quadratic_assignment.py +455 -0
  934. scipy/optimize/tests/test_regression.py +40 -0
  935. scipy/optimize/tests/test_slsqp.py +645 -0
  936. scipy/optimize/tests/test_tnc.py +345 -0
  937. scipy/optimize/tests/test_trustregion.py +110 -0
  938. scipy/optimize/tests/test_trustregion_exact.py +351 -0
  939. scipy/optimize/tests/test_trustregion_krylov.py +170 -0
  940. scipy/optimize/tests/test_zeros.py +998 -0
  941. scipy/optimize/tnc.py +22 -0
  942. scipy/optimize/zeros.py +26 -0
  943. scipy/signal/__init__.py +316 -0
  944. scipy/signal/_arraytools.py +264 -0
  945. scipy/signal/_czt.py +575 -0
  946. scipy/signal/_delegators.py +568 -0
  947. scipy/signal/_filter_design.py +5893 -0
  948. scipy/signal/_fir_filter_design.py +1458 -0
  949. scipy/signal/_lti_conversion.py +534 -0
  950. scipy/signal/_ltisys.py +3546 -0
  951. scipy/signal/_max_len_seq.py +139 -0
  952. scipy/signal/_max_len_seq_inner.cp313t-win_arm64.lib +0 -0
  953. scipy/signal/_max_len_seq_inner.cp313t-win_arm64.pyd +0 -0
  954. scipy/signal/_peak_finding.py +1310 -0
  955. scipy/signal/_peak_finding_utils.cp313t-win_arm64.lib +0 -0
  956. scipy/signal/_peak_finding_utils.cp313t-win_arm64.pyd +0 -0
  957. scipy/signal/_polyutils.py +172 -0
  958. scipy/signal/_savitzky_golay.py +357 -0
  959. scipy/signal/_short_time_fft.py +2228 -0
  960. scipy/signal/_signal_api.py +30 -0
  961. scipy/signal/_signaltools.py +5309 -0
  962. scipy/signal/_sigtools.cp313t-win_arm64.lib +0 -0
  963. scipy/signal/_sigtools.cp313t-win_arm64.pyd +0 -0
  964. scipy/signal/_sosfilt.cp313t-win_arm64.lib +0 -0
  965. scipy/signal/_sosfilt.cp313t-win_arm64.pyd +0 -0
  966. scipy/signal/_spectral_py.py +2471 -0
  967. scipy/signal/_spline.cp313t-win_arm64.lib +0 -0
  968. scipy/signal/_spline.cp313t-win_arm64.pyd +0 -0
  969. scipy/signal/_spline.pyi +34 -0
  970. scipy/signal/_spline_filters.py +848 -0
  971. scipy/signal/_support_alternative_backends.py +73 -0
  972. scipy/signal/_upfirdn.py +219 -0
  973. scipy/signal/_upfirdn_apply.cp313t-win_arm64.lib +0 -0
  974. scipy/signal/_upfirdn_apply.cp313t-win_arm64.pyd +0 -0
  975. scipy/signal/_waveforms.py +687 -0
  976. scipy/signal/_wavelets.py +29 -0
  977. scipy/signal/bsplines.py +21 -0
  978. scipy/signal/filter_design.py +28 -0
  979. scipy/signal/fir_filter_design.py +21 -0
  980. scipy/signal/lti_conversion.py +20 -0
  981. scipy/signal/ltisys.py +25 -0
  982. scipy/signal/signaltools.py +27 -0
  983. scipy/signal/spectral.py +21 -0
  984. scipy/signal/spline.py +18 -0
  985. scipy/signal/tests/__init__.py +0 -0
  986. scipy/signal/tests/_scipy_spectral_test_shim.py +311 -0
  987. scipy/signal/tests/mpsig.py +122 -0
  988. scipy/signal/tests/test_array_tools.py +111 -0
  989. scipy/signal/tests/test_bsplines.py +365 -0
  990. scipy/signal/tests/test_cont2discrete.py +424 -0
  991. scipy/signal/tests/test_czt.py +221 -0
  992. scipy/signal/tests/test_dltisys.py +599 -0
  993. scipy/signal/tests/test_filter_design.py +4744 -0
  994. scipy/signal/tests/test_fir_filter_design.py +851 -0
  995. scipy/signal/tests/test_ltisys.py +1225 -0
  996. scipy/signal/tests/test_max_len_seq.py +71 -0
  997. scipy/signal/tests/test_peak_finding.py +915 -0
  998. scipy/signal/tests/test_result_type.py +51 -0
  999. scipy/signal/tests/test_savitzky_golay.py +363 -0
  1000. scipy/signal/tests/test_short_time_fft.py +1107 -0
  1001. scipy/signal/tests/test_signaltools.py +4735 -0
  1002. scipy/signal/tests/test_spectral.py +2141 -0
  1003. scipy/signal/tests/test_splines.py +427 -0
  1004. scipy/signal/tests/test_upfirdn.py +322 -0
  1005. scipy/signal/tests/test_waveforms.py +400 -0
  1006. scipy/signal/tests/test_wavelets.py +59 -0
  1007. scipy/signal/tests/test_windows.py +987 -0
  1008. scipy/signal/waveforms.py +20 -0
  1009. scipy/signal/wavelets.py +17 -0
  1010. scipy/signal/windows/__init__.py +52 -0
  1011. scipy/signal/windows/_windows.py +2513 -0
  1012. scipy/signal/windows/windows.py +23 -0
  1013. scipy/sparse/__init__.py +350 -0
  1014. scipy/sparse/_base.py +1613 -0
  1015. scipy/sparse/_bsr.py +880 -0
  1016. scipy/sparse/_compressed.py +1328 -0
  1017. scipy/sparse/_construct.py +1454 -0
  1018. scipy/sparse/_coo.py +1581 -0
  1019. scipy/sparse/_csc.py +367 -0
  1020. scipy/sparse/_csparsetools.cp313t-win_arm64.lib +0 -0
  1021. scipy/sparse/_csparsetools.cp313t-win_arm64.pyd +0 -0
  1022. scipy/sparse/_csr.py +558 -0
  1023. scipy/sparse/_data.py +569 -0
  1024. scipy/sparse/_dia.py +677 -0
  1025. scipy/sparse/_dok.py +669 -0
  1026. scipy/sparse/_extract.py +178 -0
  1027. scipy/sparse/_index.py +444 -0
  1028. scipy/sparse/_lil.py +632 -0
  1029. scipy/sparse/_matrix.py +169 -0
  1030. scipy/sparse/_matrix_io.py +167 -0
  1031. scipy/sparse/_sparsetools.cp313t-win_arm64.lib +0 -0
  1032. scipy/sparse/_sparsetools.cp313t-win_arm64.pyd +0 -0
  1033. scipy/sparse/_spfuncs.py +76 -0
  1034. scipy/sparse/_sputils.py +632 -0
  1035. scipy/sparse/base.py +24 -0
  1036. scipy/sparse/bsr.py +22 -0
  1037. scipy/sparse/compressed.py +20 -0
  1038. scipy/sparse/construct.py +38 -0
  1039. scipy/sparse/coo.py +23 -0
  1040. scipy/sparse/csc.py +22 -0
  1041. scipy/sparse/csgraph/__init__.py +210 -0
  1042. scipy/sparse/csgraph/_flow.cp313t-win_arm64.lib +0 -0
  1043. scipy/sparse/csgraph/_flow.cp313t-win_arm64.pyd +0 -0
  1044. scipy/sparse/csgraph/_laplacian.py +563 -0
  1045. scipy/sparse/csgraph/_matching.cp313t-win_arm64.lib +0 -0
  1046. scipy/sparse/csgraph/_matching.cp313t-win_arm64.pyd +0 -0
  1047. scipy/sparse/csgraph/_min_spanning_tree.cp313t-win_arm64.lib +0 -0
  1048. scipy/sparse/csgraph/_min_spanning_tree.cp313t-win_arm64.pyd +0 -0
  1049. scipy/sparse/csgraph/_reordering.cp313t-win_arm64.lib +0 -0
  1050. scipy/sparse/csgraph/_reordering.cp313t-win_arm64.pyd +0 -0
  1051. scipy/sparse/csgraph/_shortest_path.cp313t-win_arm64.lib +0 -0
  1052. scipy/sparse/csgraph/_shortest_path.cp313t-win_arm64.pyd +0 -0
  1053. scipy/sparse/csgraph/_tools.cp313t-win_arm64.lib +0 -0
  1054. scipy/sparse/csgraph/_tools.cp313t-win_arm64.pyd +0 -0
  1055. scipy/sparse/csgraph/_traversal.cp313t-win_arm64.lib +0 -0
  1056. scipy/sparse/csgraph/_traversal.cp313t-win_arm64.pyd +0 -0
  1057. scipy/sparse/csgraph/_validation.py +66 -0
  1058. scipy/sparse/csgraph/tests/__init__.py +0 -0
  1059. scipy/sparse/csgraph/tests/test_connected_components.py +119 -0
  1060. scipy/sparse/csgraph/tests/test_conversions.py +61 -0
  1061. scipy/sparse/csgraph/tests/test_flow.py +209 -0
  1062. scipy/sparse/csgraph/tests/test_graph_laplacian.py +368 -0
  1063. scipy/sparse/csgraph/tests/test_matching.py +307 -0
  1064. scipy/sparse/csgraph/tests/test_pydata_sparse.py +197 -0
  1065. scipy/sparse/csgraph/tests/test_reordering.py +70 -0
  1066. scipy/sparse/csgraph/tests/test_shortest_path.py +540 -0
  1067. scipy/sparse/csgraph/tests/test_spanning_tree.py +66 -0
  1068. scipy/sparse/csgraph/tests/test_traversal.py +148 -0
  1069. scipy/sparse/csr.py +22 -0
  1070. scipy/sparse/data.py +18 -0
  1071. scipy/sparse/dia.py +22 -0
  1072. scipy/sparse/dok.py +22 -0
  1073. scipy/sparse/extract.py +23 -0
  1074. scipy/sparse/lil.py +22 -0
  1075. scipy/sparse/linalg/__init__.py +148 -0
  1076. scipy/sparse/linalg/_dsolve/__init__.py +71 -0
  1077. scipy/sparse/linalg/_dsolve/_add_newdocs.py +147 -0
  1078. scipy/sparse/linalg/_dsolve/_superlu.cp313t-win_arm64.lib +0 -0
  1079. scipy/sparse/linalg/_dsolve/_superlu.cp313t-win_arm64.pyd +0 -0
  1080. scipy/sparse/linalg/_dsolve/linsolve.py +882 -0
  1081. scipy/sparse/linalg/_dsolve/tests/__init__.py +0 -0
  1082. scipy/sparse/linalg/_dsolve/tests/test_linsolve.py +928 -0
  1083. scipy/sparse/linalg/_eigen/__init__.py +22 -0
  1084. scipy/sparse/linalg/_eigen/_svds.py +540 -0
  1085. scipy/sparse/linalg/_eigen/_svds_doc.py +382 -0
  1086. scipy/sparse/linalg/_eigen/arpack/COPYING +45 -0
  1087. scipy/sparse/linalg/_eigen/arpack/__init__.py +20 -0
  1088. scipy/sparse/linalg/_eigen/arpack/_arpack.cp313t-win_arm64.lib +0 -0
  1089. scipy/sparse/linalg/_eigen/arpack/_arpack.cp313t-win_arm64.pyd +0 -0
  1090. scipy/sparse/linalg/_eigen/arpack/arpack.py +1706 -0
  1091. scipy/sparse/linalg/_eigen/arpack/tests/__init__.py +0 -0
  1092. scipy/sparse/linalg/_eigen/arpack/tests/test_arpack.py +717 -0
  1093. scipy/sparse/linalg/_eigen/lobpcg/__init__.py +16 -0
  1094. scipy/sparse/linalg/_eigen/lobpcg/lobpcg.py +1110 -0
  1095. scipy/sparse/linalg/_eigen/lobpcg/tests/__init__.py +0 -0
  1096. scipy/sparse/linalg/_eigen/lobpcg/tests/test_lobpcg.py +725 -0
  1097. scipy/sparse/linalg/_eigen/tests/__init__.py +0 -0
  1098. scipy/sparse/linalg/_eigen/tests/test_svds.py +886 -0
  1099. scipy/sparse/linalg/_expm_multiply.py +816 -0
  1100. scipy/sparse/linalg/_interface.py +920 -0
  1101. scipy/sparse/linalg/_isolve/__init__.py +20 -0
  1102. scipy/sparse/linalg/_isolve/_gcrotmk.py +503 -0
  1103. scipy/sparse/linalg/_isolve/iterative.py +1051 -0
  1104. scipy/sparse/linalg/_isolve/lgmres.py +230 -0
  1105. scipy/sparse/linalg/_isolve/lsmr.py +486 -0
  1106. scipy/sparse/linalg/_isolve/lsqr.py +589 -0
  1107. scipy/sparse/linalg/_isolve/minres.py +372 -0
  1108. scipy/sparse/linalg/_isolve/tests/__init__.py +0 -0
  1109. scipy/sparse/linalg/_isolve/tests/test_gcrotmk.py +183 -0
  1110. scipy/sparse/linalg/_isolve/tests/test_iterative.py +809 -0
  1111. scipy/sparse/linalg/_isolve/tests/test_lgmres.py +225 -0
  1112. scipy/sparse/linalg/_isolve/tests/test_lsmr.py +185 -0
  1113. scipy/sparse/linalg/_isolve/tests/test_lsqr.py +120 -0
  1114. scipy/sparse/linalg/_isolve/tests/test_minres.py +97 -0
  1115. scipy/sparse/linalg/_isolve/tests/test_utils.py +9 -0
  1116. scipy/sparse/linalg/_isolve/tfqmr.py +179 -0
  1117. scipy/sparse/linalg/_isolve/utils.py +121 -0
  1118. scipy/sparse/linalg/_matfuncs.py +940 -0
  1119. scipy/sparse/linalg/_norm.py +195 -0
  1120. scipy/sparse/linalg/_onenormest.py +467 -0
  1121. scipy/sparse/linalg/_propack/_cpropack.cp313t-win_arm64.lib +0 -0
  1122. scipy/sparse/linalg/_propack/_cpropack.cp313t-win_arm64.pyd +0 -0
  1123. scipy/sparse/linalg/_propack/_dpropack.cp313t-win_arm64.lib +0 -0
  1124. scipy/sparse/linalg/_propack/_dpropack.cp313t-win_arm64.pyd +0 -0
  1125. scipy/sparse/linalg/_propack/_spropack.cp313t-win_arm64.lib +0 -0
  1126. scipy/sparse/linalg/_propack/_spropack.cp313t-win_arm64.pyd +0 -0
  1127. scipy/sparse/linalg/_propack/_zpropack.cp313t-win_arm64.lib +0 -0
  1128. scipy/sparse/linalg/_propack/_zpropack.cp313t-win_arm64.pyd +0 -0
  1129. scipy/sparse/linalg/_special_sparse_arrays.py +949 -0
  1130. scipy/sparse/linalg/_svdp.py +309 -0
  1131. scipy/sparse/linalg/dsolve.py +22 -0
  1132. scipy/sparse/linalg/eigen.py +21 -0
  1133. scipy/sparse/linalg/interface.py +20 -0
  1134. scipy/sparse/linalg/isolve.py +22 -0
  1135. scipy/sparse/linalg/matfuncs.py +18 -0
  1136. scipy/sparse/linalg/tests/__init__.py +0 -0
  1137. scipy/sparse/linalg/tests/propack_test_data.npz +0 -0
  1138. scipy/sparse/linalg/tests/test_expm_multiply.py +367 -0
  1139. scipy/sparse/linalg/tests/test_interface.py +561 -0
  1140. scipy/sparse/linalg/tests/test_matfuncs.py +592 -0
  1141. scipy/sparse/linalg/tests/test_norm.py +154 -0
  1142. scipy/sparse/linalg/tests/test_onenormest.py +252 -0
  1143. scipy/sparse/linalg/tests/test_propack.py +165 -0
  1144. scipy/sparse/linalg/tests/test_pydata_sparse.py +272 -0
  1145. scipy/sparse/linalg/tests/test_special_sparse_arrays.py +337 -0
  1146. scipy/sparse/sparsetools.py +17 -0
  1147. scipy/sparse/spfuncs.py +17 -0
  1148. scipy/sparse/sputils.py +17 -0
  1149. scipy/sparse/tests/__init__.py +0 -0
  1150. scipy/sparse/tests/data/csc_py2.npz +0 -0
  1151. scipy/sparse/tests/data/csc_py3.npz +0 -0
  1152. scipy/sparse/tests/test_arithmetic1d.py +341 -0
  1153. scipy/sparse/tests/test_array_api.py +561 -0
  1154. scipy/sparse/tests/test_base.py +5870 -0
  1155. scipy/sparse/tests/test_common1d.py +447 -0
  1156. scipy/sparse/tests/test_construct.py +872 -0
  1157. scipy/sparse/tests/test_coo.py +1119 -0
  1158. scipy/sparse/tests/test_csc.py +98 -0
  1159. scipy/sparse/tests/test_csr.py +214 -0
  1160. scipy/sparse/tests/test_dok.py +209 -0
  1161. scipy/sparse/tests/test_extract.py +51 -0
  1162. scipy/sparse/tests/test_indexing1d.py +603 -0
  1163. scipy/sparse/tests/test_matrix_io.py +109 -0
  1164. scipy/sparse/tests/test_minmax1d.py +128 -0
  1165. scipy/sparse/tests/test_sparsetools.py +344 -0
  1166. scipy/sparse/tests/test_spfuncs.py +97 -0
  1167. scipy/sparse/tests/test_sputils.py +424 -0
  1168. scipy/spatial/__init__.py +129 -0
  1169. scipy/spatial/_ckdtree.cp313t-win_arm64.lib +0 -0
  1170. scipy/spatial/_ckdtree.cp313t-win_arm64.pyd +0 -0
  1171. scipy/spatial/_distance_pybind.cp313t-win_arm64.lib +0 -0
  1172. scipy/spatial/_distance_pybind.cp313t-win_arm64.pyd +0 -0
  1173. scipy/spatial/_distance_wrap.cp313t-win_arm64.lib +0 -0
  1174. scipy/spatial/_distance_wrap.cp313t-win_arm64.pyd +0 -0
  1175. scipy/spatial/_geometric_slerp.py +238 -0
  1176. scipy/spatial/_hausdorff.cp313t-win_arm64.lib +0 -0
  1177. scipy/spatial/_hausdorff.cp313t-win_arm64.pyd +0 -0
  1178. scipy/spatial/_kdtree.py +920 -0
  1179. scipy/spatial/_plotutils.py +274 -0
  1180. scipy/spatial/_procrustes.py +132 -0
  1181. scipy/spatial/_qhull.cp313t-win_arm64.lib +0 -0
  1182. scipy/spatial/_qhull.cp313t-win_arm64.pyd +0 -0
  1183. scipy/spatial/_qhull.pyi +213 -0
  1184. scipy/spatial/_spherical_voronoi.py +341 -0
  1185. scipy/spatial/_voronoi.cp313t-win_arm64.lib +0 -0
  1186. scipy/spatial/_voronoi.cp313t-win_arm64.pyd +0 -0
  1187. scipy/spatial/_voronoi.pyi +4 -0
  1188. scipy/spatial/ckdtree.py +18 -0
  1189. scipy/spatial/distance.py +3147 -0
  1190. scipy/spatial/distance.pyi +210 -0
  1191. scipy/spatial/kdtree.py +25 -0
  1192. scipy/spatial/qhull.py +25 -0
  1193. scipy/spatial/qhull_src/COPYING_QHULL.txt +39 -0
  1194. scipy/spatial/tests/__init__.py +0 -0
  1195. scipy/spatial/tests/data/cdist-X1.txt +10 -0
  1196. scipy/spatial/tests/data/cdist-X2.txt +20 -0
  1197. scipy/spatial/tests/data/degenerate_pointset.npz +0 -0
  1198. scipy/spatial/tests/data/iris.txt +150 -0
  1199. scipy/spatial/tests/data/pdist-boolean-inp.txt +20 -0
  1200. scipy/spatial/tests/data/pdist-chebyshev-ml-iris.txt +1 -0
  1201. scipy/spatial/tests/data/pdist-chebyshev-ml.txt +1 -0
  1202. scipy/spatial/tests/data/pdist-cityblock-ml-iris.txt +1 -0
  1203. scipy/spatial/tests/data/pdist-cityblock-ml.txt +1 -0
  1204. scipy/spatial/tests/data/pdist-correlation-ml-iris.txt +1 -0
  1205. scipy/spatial/tests/data/pdist-correlation-ml.txt +1 -0
  1206. scipy/spatial/tests/data/pdist-cosine-ml-iris.txt +1 -0
  1207. scipy/spatial/tests/data/pdist-cosine-ml.txt +1 -0
  1208. scipy/spatial/tests/data/pdist-double-inp.txt +20 -0
  1209. scipy/spatial/tests/data/pdist-euclidean-ml-iris.txt +1 -0
  1210. scipy/spatial/tests/data/pdist-euclidean-ml.txt +1 -0
  1211. scipy/spatial/tests/data/pdist-hamming-ml.txt +1 -0
  1212. scipy/spatial/tests/data/pdist-jaccard-ml.txt +1 -0
  1213. scipy/spatial/tests/data/pdist-jensenshannon-ml-iris.txt +1 -0
  1214. scipy/spatial/tests/data/pdist-jensenshannon-ml.txt +1 -0
  1215. scipy/spatial/tests/data/pdist-minkowski-3.2-ml-iris.txt +1 -0
  1216. scipy/spatial/tests/data/pdist-minkowski-3.2-ml.txt +1 -0
  1217. scipy/spatial/tests/data/pdist-minkowski-5.8-ml-iris.txt +1 -0
  1218. scipy/spatial/tests/data/pdist-seuclidean-ml-iris.txt +1 -0
  1219. scipy/spatial/tests/data/pdist-seuclidean-ml.txt +1 -0
  1220. scipy/spatial/tests/data/pdist-spearman-ml.txt +1 -0
  1221. scipy/spatial/tests/data/random-bool-data.txt +100 -0
  1222. scipy/spatial/tests/data/random-double-data.txt +100 -0
  1223. scipy/spatial/tests/data/random-int-data.txt +100 -0
  1224. scipy/spatial/tests/data/random-uint-data.txt +100 -0
  1225. scipy/spatial/tests/data/selfdual-4d-polytope.txt +27 -0
  1226. scipy/spatial/tests/test__plotutils.py +91 -0
  1227. scipy/spatial/tests/test__procrustes.py +116 -0
  1228. scipy/spatial/tests/test_distance.py +2389 -0
  1229. scipy/spatial/tests/test_hausdorff.py +199 -0
  1230. scipy/spatial/tests/test_kdtree.py +1536 -0
  1231. scipy/spatial/tests/test_qhull.py +1313 -0
  1232. scipy/spatial/tests/test_slerp.py +417 -0
  1233. scipy/spatial/tests/test_spherical_voronoi.py +358 -0
  1234. scipy/spatial/transform/__init__.py +31 -0
  1235. scipy/spatial/transform/_rigid_transform.cp313t-win_arm64.lib +0 -0
  1236. scipy/spatial/transform/_rigid_transform.cp313t-win_arm64.pyd +0 -0
  1237. scipy/spatial/transform/_rotation.cp313t-win_arm64.lib +0 -0
  1238. scipy/spatial/transform/_rotation.cp313t-win_arm64.pyd +0 -0
  1239. scipy/spatial/transform/_rotation_groups.py +140 -0
  1240. scipy/spatial/transform/_rotation_spline.py +460 -0
  1241. scipy/spatial/transform/rotation.py +21 -0
  1242. scipy/spatial/transform/tests/__init__.py +0 -0
  1243. scipy/spatial/transform/tests/test_rigid_transform.py +1221 -0
  1244. scipy/spatial/transform/tests/test_rotation.py +2569 -0
  1245. scipy/spatial/transform/tests/test_rotation_groups.py +169 -0
  1246. scipy/spatial/transform/tests/test_rotation_spline.py +183 -0
  1247. scipy/special/__init__.pxd +1 -0
  1248. scipy/special/__init__.py +841 -0
  1249. scipy/special/_add_newdocs.py +9961 -0
  1250. scipy/special/_basic.py +3576 -0
  1251. scipy/special/_comb.cp313t-win_arm64.lib +0 -0
  1252. scipy/special/_comb.cp313t-win_arm64.pyd +0 -0
  1253. scipy/special/_ellip_harm.py +214 -0
  1254. scipy/special/_ellip_harm_2.cp313t-win_arm64.lib +0 -0
  1255. scipy/special/_ellip_harm_2.cp313t-win_arm64.pyd +0 -0
  1256. scipy/special/_gufuncs.cp313t-win_arm64.lib +0 -0
  1257. scipy/special/_gufuncs.cp313t-win_arm64.pyd +0 -0
  1258. scipy/special/_input_validation.py +17 -0
  1259. scipy/special/_lambertw.py +149 -0
  1260. scipy/special/_logsumexp.py +426 -0
  1261. scipy/special/_mptestutils.py +453 -0
  1262. scipy/special/_multiufuncs.py +610 -0
  1263. scipy/special/_orthogonal.py +2592 -0
  1264. scipy/special/_orthogonal.pyi +330 -0
  1265. scipy/special/_precompute/__init__.py +0 -0
  1266. scipy/special/_precompute/cosine_cdf.py +17 -0
  1267. scipy/special/_precompute/expn_asy.py +54 -0
  1268. scipy/special/_precompute/gammainc_asy.py +116 -0
  1269. scipy/special/_precompute/gammainc_data.py +124 -0
  1270. scipy/special/_precompute/hyp2f1_data.py +484 -0
  1271. scipy/special/_precompute/lambertw.py +68 -0
  1272. scipy/special/_precompute/loggamma.py +43 -0
  1273. scipy/special/_precompute/struve_convergence.py +131 -0
  1274. scipy/special/_precompute/utils.py +38 -0
  1275. scipy/special/_precompute/wright_bessel.py +342 -0
  1276. scipy/special/_precompute/wright_bessel_data.py +152 -0
  1277. scipy/special/_precompute/wrightomega.py +41 -0
  1278. scipy/special/_precompute/zetac.py +27 -0
  1279. scipy/special/_sf_error.py +15 -0
  1280. scipy/special/_specfun.cp313t-win_arm64.lib +0 -0
  1281. scipy/special/_specfun.cp313t-win_arm64.pyd +0 -0
  1282. scipy/special/_special_ufuncs.cp313t-win_arm64.lib +0 -0
  1283. scipy/special/_special_ufuncs.cp313t-win_arm64.pyd +0 -0
  1284. scipy/special/_spfun_stats.py +106 -0
  1285. scipy/special/_spherical_bessel.py +397 -0
  1286. scipy/special/_support_alternative_backends.py +295 -0
  1287. scipy/special/_test_internal.cp313t-win_arm64.lib +0 -0
  1288. scipy/special/_test_internal.cp313t-win_arm64.pyd +0 -0
  1289. scipy/special/_test_internal.pyi +9 -0
  1290. scipy/special/_testutils.py +321 -0
  1291. scipy/special/_ufuncs.cp313t-win_arm64.lib +0 -0
  1292. scipy/special/_ufuncs.cp313t-win_arm64.pyd +0 -0
  1293. scipy/special/_ufuncs.pyi +522 -0
  1294. scipy/special/_ufuncs.pyx +13173 -0
  1295. scipy/special/_ufuncs_cxx.cp313t-win_arm64.lib +0 -0
  1296. scipy/special/_ufuncs_cxx.cp313t-win_arm64.pyd +0 -0
  1297. scipy/special/_ufuncs_cxx.pxd +142 -0
  1298. scipy/special/_ufuncs_cxx.pyx +427 -0
  1299. scipy/special/_ufuncs_cxx_defs.h +147 -0
  1300. scipy/special/_ufuncs_defs.h +57 -0
  1301. scipy/special/add_newdocs.py +15 -0
  1302. scipy/special/basic.py +87 -0
  1303. scipy/special/cython_special.cp313t-win_arm64.lib +0 -0
  1304. scipy/special/cython_special.cp313t-win_arm64.pyd +0 -0
  1305. scipy/special/cython_special.pxd +259 -0
  1306. scipy/special/cython_special.pyi +3 -0
  1307. scipy/special/orthogonal.py +45 -0
  1308. scipy/special/sf_error.py +20 -0
  1309. scipy/special/specfun.py +24 -0
  1310. scipy/special/spfun_stats.py +17 -0
  1311. scipy/special/tests/__init__.py +0 -0
  1312. scipy/special/tests/_cython_examples/extending.pyx +12 -0
  1313. scipy/special/tests/_cython_examples/meson.build +34 -0
  1314. scipy/special/tests/data/__init__.py +0 -0
  1315. scipy/special/tests/data/boost.npz +0 -0
  1316. scipy/special/tests/data/gsl.npz +0 -0
  1317. scipy/special/tests/data/local.npz +0 -0
  1318. scipy/special/tests/test_basic.py +4815 -0
  1319. scipy/special/tests/test_bdtr.py +112 -0
  1320. scipy/special/tests/test_boost_ufuncs.py +64 -0
  1321. scipy/special/tests/test_boxcox.py +125 -0
  1322. scipy/special/tests/test_cdflib.py +712 -0
  1323. scipy/special/tests/test_cdft_asymptotic.py +49 -0
  1324. scipy/special/tests/test_cephes_intp_cast.py +29 -0
  1325. scipy/special/tests/test_cosine_distr.py +83 -0
  1326. scipy/special/tests/test_cython_special.py +363 -0
  1327. scipy/special/tests/test_data.py +719 -0
  1328. scipy/special/tests/test_dd.py +42 -0
  1329. scipy/special/tests/test_digamma.py +45 -0
  1330. scipy/special/tests/test_ellip_harm.py +278 -0
  1331. scipy/special/tests/test_erfinv.py +89 -0
  1332. scipy/special/tests/test_exponential_integrals.py +118 -0
  1333. scipy/special/tests/test_extending.py +28 -0
  1334. scipy/special/tests/test_faddeeva.py +85 -0
  1335. scipy/special/tests/test_gamma.py +12 -0
  1336. scipy/special/tests/test_gammainc.py +152 -0
  1337. scipy/special/tests/test_hyp2f1.py +2566 -0
  1338. scipy/special/tests/test_hypergeometric.py +234 -0
  1339. scipy/special/tests/test_iv_ratio.py +249 -0
  1340. scipy/special/tests/test_kolmogorov.py +491 -0
  1341. scipy/special/tests/test_lambertw.py +109 -0
  1342. scipy/special/tests/test_legendre.py +1518 -0
  1343. scipy/special/tests/test_log1mexp.py +85 -0
  1344. scipy/special/tests/test_loggamma.py +70 -0
  1345. scipy/special/tests/test_logit.py +162 -0
  1346. scipy/special/tests/test_logsumexp.py +469 -0
  1347. scipy/special/tests/test_mpmath.py +2293 -0
  1348. scipy/special/tests/test_nan_inputs.py +65 -0
  1349. scipy/special/tests/test_ndtr.py +77 -0
  1350. scipy/special/tests/test_ndtri_exp.py +94 -0
  1351. scipy/special/tests/test_orthogonal.py +821 -0
  1352. scipy/special/tests/test_orthogonal_eval.py +275 -0
  1353. scipy/special/tests/test_owens_t.py +53 -0
  1354. scipy/special/tests/test_pcf.py +24 -0
  1355. scipy/special/tests/test_pdtr.py +48 -0
  1356. scipy/special/tests/test_powm1.py +65 -0
  1357. scipy/special/tests/test_precompute_expn_asy.py +24 -0
  1358. scipy/special/tests/test_precompute_gammainc.py +108 -0
  1359. scipy/special/tests/test_precompute_utils.py +36 -0
  1360. scipy/special/tests/test_round.py +18 -0
  1361. scipy/special/tests/test_sf_error.py +146 -0
  1362. scipy/special/tests/test_sici.py +36 -0
  1363. scipy/special/tests/test_specfun.py +48 -0
  1364. scipy/special/tests/test_spence.py +32 -0
  1365. scipy/special/tests/test_spfun_stats.py +61 -0
  1366. scipy/special/tests/test_sph_harm.py +85 -0
  1367. scipy/special/tests/test_spherical_bessel.py +400 -0
  1368. scipy/special/tests/test_support_alternative_backends.py +248 -0
  1369. scipy/special/tests/test_trig.py +72 -0
  1370. scipy/special/tests/test_ufunc_signatures.py +46 -0
  1371. scipy/special/tests/test_wright_bessel.py +205 -0
  1372. scipy/special/tests/test_wrightomega.py +117 -0
  1373. scipy/special/tests/test_zeta.py +301 -0
  1374. scipy/stats/__init__.py +670 -0
  1375. scipy/stats/_ansari_swilk_statistics.cp313t-win_arm64.lib +0 -0
  1376. scipy/stats/_ansari_swilk_statistics.cp313t-win_arm64.pyd +0 -0
  1377. scipy/stats/_axis_nan_policy.py +692 -0
  1378. scipy/stats/_biasedurn.cp313t-win_arm64.lib +0 -0
  1379. scipy/stats/_biasedurn.cp313t-win_arm64.pyd +0 -0
  1380. scipy/stats/_biasedurn.pxd +27 -0
  1381. scipy/stats/_binned_statistic.py +795 -0
  1382. scipy/stats/_binomtest.py +375 -0
  1383. scipy/stats/_bws_test.py +177 -0
  1384. scipy/stats/_censored_data.py +459 -0
  1385. scipy/stats/_common.py +5 -0
  1386. scipy/stats/_constants.py +42 -0
  1387. scipy/stats/_continued_fraction.py +387 -0
  1388. scipy/stats/_continuous_distns.py +12486 -0
  1389. scipy/stats/_correlation.py +210 -0
  1390. scipy/stats/_covariance.py +636 -0
  1391. scipy/stats/_crosstab.py +204 -0
  1392. scipy/stats/_discrete_distns.py +2098 -0
  1393. scipy/stats/_distn_infrastructure.py +4201 -0
  1394. scipy/stats/_distr_params.py +299 -0
  1395. scipy/stats/_distribution_infrastructure.py +5750 -0
  1396. scipy/stats/_entropy.py +428 -0
  1397. scipy/stats/_finite_differences.py +145 -0
  1398. scipy/stats/_fit.py +1351 -0
  1399. scipy/stats/_hypotests.py +2060 -0
  1400. scipy/stats/_kde.py +732 -0
  1401. scipy/stats/_ksstats.py +600 -0
  1402. scipy/stats/_levy_stable/__init__.py +1231 -0
  1403. scipy/stats/_levy_stable/levyst.cp313t-win_arm64.lib +0 -0
  1404. scipy/stats/_levy_stable/levyst.cp313t-win_arm64.pyd +0 -0
  1405. scipy/stats/_mannwhitneyu.py +492 -0
  1406. scipy/stats/_mgc.py +550 -0
  1407. scipy/stats/_morestats.py +4626 -0
  1408. scipy/stats/_mstats_basic.py +3658 -0
  1409. scipy/stats/_mstats_extras.py +521 -0
  1410. scipy/stats/_multicomp.py +449 -0
  1411. scipy/stats/_multivariate.py +7281 -0
  1412. scipy/stats/_new_distributions.py +452 -0
  1413. scipy/stats/_odds_ratio.py +466 -0
  1414. scipy/stats/_page_trend_test.py +486 -0
  1415. scipy/stats/_probability_distribution.py +1964 -0
  1416. scipy/stats/_qmc.py +2956 -0
  1417. scipy/stats/_qmc_cy.cp313t-win_arm64.lib +0 -0
  1418. scipy/stats/_qmc_cy.cp313t-win_arm64.pyd +0 -0
  1419. scipy/stats/_qmc_cy.pyi +54 -0
  1420. scipy/stats/_qmvnt.py +454 -0
  1421. scipy/stats/_qmvnt_cy.cp313t-win_arm64.lib +0 -0
  1422. scipy/stats/_qmvnt_cy.cp313t-win_arm64.pyd +0 -0
  1423. scipy/stats/_quantile.py +335 -0
  1424. scipy/stats/_rcont/__init__.py +4 -0
  1425. scipy/stats/_rcont/rcont.cp313t-win_arm64.lib +0 -0
  1426. scipy/stats/_rcont/rcont.cp313t-win_arm64.pyd +0 -0
  1427. scipy/stats/_relative_risk.py +263 -0
  1428. scipy/stats/_resampling.py +2352 -0
  1429. scipy/stats/_result_classes.py +40 -0
  1430. scipy/stats/_sampling.py +1314 -0
  1431. scipy/stats/_sensitivity_analysis.py +713 -0
  1432. scipy/stats/_sobol.cp313t-win_arm64.lib +0 -0
  1433. scipy/stats/_sobol.cp313t-win_arm64.pyd +0 -0
  1434. scipy/stats/_sobol.pyi +54 -0
  1435. scipy/stats/_sobol_direction_numbers.npz +0 -0
  1436. scipy/stats/_stats.cp313t-win_arm64.lib +0 -0
  1437. scipy/stats/_stats.cp313t-win_arm64.pyd +0 -0
  1438. scipy/stats/_stats.pxd +10 -0
  1439. scipy/stats/_stats_mstats_common.py +322 -0
  1440. scipy/stats/_stats_py.py +11089 -0
  1441. scipy/stats/_stats_pythran.cp313t-win_arm64.lib +0 -0
  1442. scipy/stats/_stats_pythran.cp313t-win_arm64.pyd +0 -0
  1443. scipy/stats/_survival.py +683 -0
  1444. scipy/stats/_tukeylambda_stats.py +199 -0
  1445. scipy/stats/_unuran/__init__.py +0 -0
  1446. scipy/stats/_unuran/unuran_wrapper.cp313t-win_arm64.lib +0 -0
  1447. scipy/stats/_unuran/unuran_wrapper.cp313t-win_arm64.pyd +0 -0
  1448. scipy/stats/_unuran/unuran_wrapper.pyi +179 -0
  1449. scipy/stats/_variation.py +126 -0
  1450. scipy/stats/_warnings_errors.py +38 -0
  1451. scipy/stats/_wilcoxon.py +265 -0
  1452. scipy/stats/biasedurn.py +16 -0
  1453. scipy/stats/contingency.py +521 -0
  1454. scipy/stats/distributions.py +24 -0
  1455. scipy/stats/kde.py +18 -0
  1456. scipy/stats/morestats.py +27 -0
  1457. scipy/stats/mstats.py +140 -0
  1458. scipy/stats/mstats_basic.py +42 -0
  1459. scipy/stats/mstats_extras.py +25 -0
  1460. scipy/stats/mvn.py +17 -0
  1461. scipy/stats/qmc.py +236 -0
  1462. scipy/stats/sampling.py +73 -0
  1463. scipy/stats/stats.py +41 -0
  1464. scipy/stats/tests/__init__.py +0 -0
  1465. scipy/stats/tests/common_tests.py +356 -0
  1466. scipy/stats/tests/data/_mvt.py +171 -0
  1467. scipy/stats/tests/data/fisher_exact_results_from_r.py +607 -0
  1468. scipy/stats/tests/data/jf_skew_t_gamlss_pdf_data.npy +0 -0
  1469. scipy/stats/tests/data/levy_stable/stable-Z1-cdf-sample-data.npy +0 -0
  1470. scipy/stats/tests/data/levy_stable/stable-Z1-pdf-sample-data.npy +0 -0
  1471. scipy/stats/tests/data/levy_stable/stable-loc-scale-sample-data.npy +0 -0
  1472. scipy/stats/tests/data/nist_anova/AtmWtAg.dat +108 -0
  1473. scipy/stats/tests/data/nist_anova/SiRstv.dat +85 -0
  1474. scipy/stats/tests/data/nist_anova/SmLs01.dat +249 -0
  1475. scipy/stats/tests/data/nist_anova/SmLs02.dat +1869 -0
  1476. scipy/stats/tests/data/nist_anova/SmLs03.dat +18069 -0
  1477. scipy/stats/tests/data/nist_anova/SmLs04.dat +249 -0
  1478. scipy/stats/tests/data/nist_anova/SmLs05.dat +1869 -0
  1479. scipy/stats/tests/data/nist_anova/SmLs06.dat +18069 -0
  1480. scipy/stats/tests/data/nist_anova/SmLs07.dat +249 -0
  1481. scipy/stats/tests/data/nist_anova/SmLs08.dat +1869 -0
  1482. scipy/stats/tests/data/nist_anova/SmLs09.dat +18069 -0
  1483. scipy/stats/tests/data/nist_linregress/Norris.dat +97 -0
  1484. scipy/stats/tests/data/rel_breitwigner_pdf_sample_data_ROOT.npy +0 -0
  1485. scipy/stats/tests/data/studentized_range_mpmath_ref.json +1499 -0
  1486. scipy/stats/tests/test_axis_nan_policy.py +1388 -0
  1487. scipy/stats/tests/test_binned_statistic.py +568 -0
  1488. scipy/stats/tests/test_censored_data.py +152 -0
  1489. scipy/stats/tests/test_contingency.py +294 -0
  1490. scipy/stats/tests/test_continued_fraction.py +173 -0
  1491. scipy/stats/tests/test_continuous.py +2198 -0
  1492. scipy/stats/tests/test_continuous_basic.py +1053 -0
  1493. scipy/stats/tests/test_continuous_fit_censored.py +683 -0
  1494. scipy/stats/tests/test_correlation.py +80 -0
  1495. scipy/stats/tests/test_crosstab.py +115 -0
  1496. scipy/stats/tests/test_discrete_basic.py +580 -0
  1497. scipy/stats/tests/test_discrete_distns.py +700 -0
  1498. scipy/stats/tests/test_distributions.py +10413 -0
  1499. scipy/stats/tests/test_entropy.py +322 -0
  1500. scipy/stats/tests/test_fast_gen_inversion.py +435 -0
  1501. scipy/stats/tests/test_fit.py +1090 -0
  1502. scipy/stats/tests/test_hypotests.py +1991 -0
  1503. scipy/stats/tests/test_kdeoth.py +676 -0
  1504. scipy/stats/tests/test_marray.py +289 -0
  1505. scipy/stats/tests/test_mgc.py +217 -0
  1506. scipy/stats/tests/test_morestats.py +3259 -0
  1507. scipy/stats/tests/test_mstats_basic.py +2071 -0
  1508. scipy/stats/tests/test_mstats_extras.py +172 -0
  1509. scipy/stats/tests/test_multicomp.py +405 -0
  1510. scipy/stats/tests/test_multivariate.py +4381 -0
  1511. scipy/stats/tests/test_odds_ratio.py +148 -0
  1512. scipy/stats/tests/test_qmc.py +1492 -0
  1513. scipy/stats/tests/test_quantile.py +199 -0
  1514. scipy/stats/tests/test_rank.py +345 -0
  1515. scipy/stats/tests/test_relative_risk.py +95 -0
  1516. scipy/stats/tests/test_resampling.py +2000 -0
  1517. scipy/stats/tests/test_sampling.py +1450 -0
  1518. scipy/stats/tests/test_sensitivity_analysis.py +310 -0
  1519. scipy/stats/tests/test_stats.py +9707 -0
  1520. scipy/stats/tests/test_survival.py +466 -0
  1521. scipy/stats/tests/test_tukeylambda_stats.py +85 -0
  1522. scipy/stats/tests/test_variation.py +216 -0
  1523. scipy/version.py +12 -0
  1524. scipy-1.16.2.dist-info/DELVEWHEEL +2 -0
  1525. scipy-1.16.2.dist-info/LICENSE.txt +912 -0
  1526. scipy-1.16.2.dist-info/METADATA +1061 -0
  1527. scipy-1.16.2.dist-info/RECORD +1530 -0
  1528. scipy-1.16.2.dist-info/WHEEL +4 -0
  1529. scipy.libs/msvcp140-5f1c5dd31916990d94181e07bc3afb32.dll +0 -0
  1530. 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)