scipy 1.16.2__cp312-cp312-win_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scipy/__config__.py +161 -0
- scipy/__init__.py +150 -0
- scipy/_cyutility.cp312-win_arm64.lib +0 -0
- scipy/_cyutility.cp312-win_arm64.pyd +0 -0
- scipy/_distributor_init.py +18 -0
- scipy/_lib/__init__.py +14 -0
- scipy/_lib/_array_api.py +931 -0
- scipy/_lib/_array_api_compat_vendor.py +9 -0
- scipy/_lib/_array_api_no_0d.py +103 -0
- scipy/_lib/_bunch.py +229 -0
- scipy/_lib/_ccallback.py +251 -0
- scipy/_lib/_ccallback_c.cp312-win_arm64.lib +0 -0
- scipy/_lib/_ccallback_c.cp312-win_arm64.pyd +0 -0
- scipy/_lib/_disjoint_set.py +254 -0
- scipy/_lib/_docscrape.py +761 -0
- scipy/_lib/_elementwise_iterative_method.py +346 -0
- scipy/_lib/_fpumode.cp312-win_arm64.lib +0 -0
- scipy/_lib/_fpumode.cp312-win_arm64.pyd +0 -0
- scipy/_lib/_gcutils.py +105 -0
- scipy/_lib/_pep440.py +487 -0
- scipy/_lib/_sparse.py +41 -0
- scipy/_lib/_test_ccallback.cp312-win_arm64.lib +0 -0
- scipy/_lib/_test_ccallback.cp312-win_arm64.pyd +0 -0
- scipy/_lib/_test_deprecation_call.cp312-win_arm64.lib +0 -0
- scipy/_lib/_test_deprecation_call.cp312-win_arm64.pyd +0 -0
- scipy/_lib/_test_deprecation_def.cp312-win_arm64.lib +0 -0
- scipy/_lib/_test_deprecation_def.cp312-win_arm64.pyd +0 -0
- scipy/_lib/_testutils.py +373 -0
- scipy/_lib/_threadsafety.py +58 -0
- scipy/_lib/_tmpdirs.py +86 -0
- scipy/_lib/_uarray/LICENSE +29 -0
- scipy/_lib/_uarray/__init__.py +116 -0
- scipy/_lib/_uarray/_backend.py +707 -0
- scipy/_lib/_uarray/_uarray.cp312-win_arm64.lib +0 -0
- scipy/_lib/_uarray/_uarray.cp312-win_arm64.pyd +0 -0
- scipy/_lib/_util.py +1283 -0
- scipy/_lib/array_api_compat/__init__.py +22 -0
- scipy/_lib/array_api_compat/_internal.py +59 -0
- scipy/_lib/array_api_compat/common/__init__.py +1 -0
- scipy/_lib/array_api_compat/common/_aliases.py +727 -0
- scipy/_lib/array_api_compat/common/_fft.py +213 -0
- scipy/_lib/array_api_compat/common/_helpers.py +1058 -0
- scipy/_lib/array_api_compat/common/_linalg.py +232 -0
- scipy/_lib/array_api_compat/common/_typing.py +192 -0
- scipy/_lib/array_api_compat/cupy/__init__.py +13 -0
- scipy/_lib/array_api_compat/cupy/_aliases.py +156 -0
- scipy/_lib/array_api_compat/cupy/_info.py +336 -0
- scipy/_lib/array_api_compat/cupy/_typing.py +31 -0
- scipy/_lib/array_api_compat/cupy/fft.py +36 -0
- scipy/_lib/array_api_compat/cupy/linalg.py +49 -0
- scipy/_lib/array_api_compat/dask/__init__.py +0 -0
- scipy/_lib/array_api_compat/dask/array/__init__.py +12 -0
- scipy/_lib/array_api_compat/dask/array/_aliases.py +376 -0
- scipy/_lib/array_api_compat/dask/array/_info.py +416 -0
- scipy/_lib/array_api_compat/dask/array/fft.py +21 -0
- scipy/_lib/array_api_compat/dask/array/linalg.py +72 -0
- scipy/_lib/array_api_compat/numpy/__init__.py +28 -0
- scipy/_lib/array_api_compat/numpy/_aliases.py +190 -0
- scipy/_lib/array_api_compat/numpy/_info.py +366 -0
- scipy/_lib/array_api_compat/numpy/_typing.py +30 -0
- scipy/_lib/array_api_compat/numpy/fft.py +35 -0
- scipy/_lib/array_api_compat/numpy/linalg.py +143 -0
- scipy/_lib/array_api_compat/torch/__init__.py +22 -0
- scipy/_lib/array_api_compat/torch/_aliases.py +855 -0
- scipy/_lib/array_api_compat/torch/_info.py +369 -0
- scipy/_lib/array_api_compat/torch/_typing.py +3 -0
- scipy/_lib/array_api_compat/torch/fft.py +85 -0
- scipy/_lib/array_api_compat/torch/linalg.py +121 -0
- scipy/_lib/array_api_extra/__init__.py +38 -0
- scipy/_lib/array_api_extra/_delegation.py +171 -0
- scipy/_lib/array_api_extra/_lib/__init__.py +1 -0
- scipy/_lib/array_api_extra/_lib/_at.py +463 -0
- scipy/_lib/array_api_extra/_lib/_backends.py +46 -0
- scipy/_lib/array_api_extra/_lib/_funcs.py +937 -0
- scipy/_lib/array_api_extra/_lib/_lazy.py +357 -0
- scipy/_lib/array_api_extra/_lib/_testing.py +278 -0
- scipy/_lib/array_api_extra/_lib/_utils/__init__.py +1 -0
- scipy/_lib/array_api_extra/_lib/_utils/_compat.py +74 -0
- scipy/_lib/array_api_extra/_lib/_utils/_compat.pyi +45 -0
- scipy/_lib/array_api_extra/_lib/_utils/_helpers.py +559 -0
- scipy/_lib/array_api_extra/_lib/_utils/_typing.py +10 -0
- scipy/_lib/array_api_extra/_lib/_utils/_typing.pyi +105 -0
- scipy/_lib/array_api_extra/testing.py +359 -0
- scipy/_lib/cobyqa/__init__.py +20 -0
- scipy/_lib/cobyqa/framework.py +1240 -0
- scipy/_lib/cobyqa/main.py +1506 -0
- scipy/_lib/cobyqa/models.py +1529 -0
- scipy/_lib/cobyqa/problem.py +1296 -0
- scipy/_lib/cobyqa/settings.py +132 -0
- scipy/_lib/cobyqa/subsolvers/__init__.py +14 -0
- scipy/_lib/cobyqa/subsolvers/geometry.py +387 -0
- scipy/_lib/cobyqa/subsolvers/optim.py +1203 -0
- scipy/_lib/cobyqa/utils/__init__.py +18 -0
- scipy/_lib/cobyqa/utils/exceptions.py +22 -0
- scipy/_lib/cobyqa/utils/math.py +77 -0
- scipy/_lib/cobyqa/utils/versions.py +67 -0
- scipy/_lib/decorator.py +399 -0
- scipy/_lib/deprecation.py +274 -0
- scipy/_lib/doccer.py +366 -0
- scipy/_lib/messagestream.cp312-win_arm64.lib +0 -0
- scipy/_lib/messagestream.cp312-win_arm64.pyd +0 -0
- scipy/_lib/pyprima/__init__.py +212 -0
- scipy/_lib/pyprima/cobyla/__init__.py +0 -0
- scipy/_lib/pyprima/cobyla/cobyla.py +559 -0
- scipy/_lib/pyprima/cobyla/cobylb.py +714 -0
- scipy/_lib/pyprima/cobyla/geometry.py +226 -0
- scipy/_lib/pyprima/cobyla/initialize.py +215 -0
- scipy/_lib/pyprima/cobyla/trustregion.py +492 -0
- scipy/_lib/pyprima/cobyla/update.py +289 -0
- scipy/_lib/pyprima/common/__init__.py +0 -0
- scipy/_lib/pyprima/common/_bounds.py +34 -0
- scipy/_lib/pyprima/common/_linear_constraints.py +46 -0
- scipy/_lib/pyprima/common/_nonlinear_constraints.py +54 -0
- scipy/_lib/pyprima/common/_project.py +173 -0
- scipy/_lib/pyprima/common/checkbreak.py +93 -0
- scipy/_lib/pyprima/common/consts.py +47 -0
- scipy/_lib/pyprima/common/evaluate.py +99 -0
- scipy/_lib/pyprima/common/history.py +38 -0
- scipy/_lib/pyprima/common/infos.py +30 -0
- scipy/_lib/pyprima/common/linalg.py +435 -0
- scipy/_lib/pyprima/common/message.py +290 -0
- scipy/_lib/pyprima/common/powalg.py +131 -0
- scipy/_lib/pyprima/common/preproc.py +277 -0
- scipy/_lib/pyprima/common/present.py +5 -0
- scipy/_lib/pyprima/common/ratio.py +54 -0
- scipy/_lib/pyprima/common/redrho.py +47 -0
- scipy/_lib/pyprima/common/selectx.py +296 -0
- scipy/_lib/tests/__init__.py +0 -0
- scipy/_lib/tests/test__gcutils.py +110 -0
- scipy/_lib/tests/test__pep440.py +67 -0
- scipy/_lib/tests/test__testutils.py +32 -0
- scipy/_lib/tests/test__threadsafety.py +51 -0
- scipy/_lib/tests/test__util.py +641 -0
- scipy/_lib/tests/test_array_api.py +322 -0
- scipy/_lib/tests/test_bunch.py +169 -0
- scipy/_lib/tests/test_ccallback.py +196 -0
- scipy/_lib/tests/test_config.py +45 -0
- scipy/_lib/tests/test_deprecation.py +10 -0
- scipy/_lib/tests/test_doccer.py +143 -0
- scipy/_lib/tests/test_import_cycles.py +18 -0
- scipy/_lib/tests/test_public_api.py +482 -0
- scipy/_lib/tests/test_scipy_version.py +28 -0
- scipy/_lib/tests/test_tmpdirs.py +48 -0
- scipy/_lib/tests/test_warnings.py +137 -0
- scipy/_lib/uarray.py +31 -0
- scipy/cluster/__init__.py +31 -0
- scipy/cluster/_hierarchy.cp312-win_arm64.lib +0 -0
- scipy/cluster/_hierarchy.cp312-win_arm64.pyd +0 -0
- scipy/cluster/_optimal_leaf_ordering.cp312-win_arm64.lib +0 -0
- scipy/cluster/_optimal_leaf_ordering.cp312-win_arm64.pyd +0 -0
- scipy/cluster/_vq.cp312-win_arm64.lib +0 -0
- scipy/cluster/_vq.cp312-win_arm64.pyd +0 -0
- scipy/cluster/hierarchy.py +4348 -0
- scipy/cluster/tests/__init__.py +0 -0
- scipy/cluster/tests/hierarchy_test_data.py +145 -0
- scipy/cluster/tests/test_disjoint_set.py +202 -0
- scipy/cluster/tests/test_hierarchy.py +1238 -0
- scipy/cluster/tests/test_vq.py +434 -0
- scipy/cluster/vq.py +832 -0
- scipy/conftest.py +683 -0
- scipy/constants/__init__.py +358 -0
- scipy/constants/_codata.py +2266 -0
- scipy/constants/_constants.py +369 -0
- scipy/constants/codata.py +21 -0
- scipy/constants/constants.py +53 -0
- scipy/constants/tests/__init__.py +0 -0
- scipy/constants/tests/test_codata.py +78 -0
- scipy/constants/tests/test_constants.py +83 -0
- scipy/datasets/__init__.py +90 -0
- scipy/datasets/_download_all.py +71 -0
- scipy/datasets/_fetchers.py +225 -0
- scipy/datasets/_registry.py +26 -0
- scipy/datasets/_utils.py +81 -0
- scipy/datasets/tests/__init__.py +0 -0
- scipy/datasets/tests/test_data.py +128 -0
- scipy/differentiate/__init__.py +27 -0
- scipy/differentiate/_differentiate.py +1129 -0
- scipy/differentiate/tests/__init__.py +0 -0
- scipy/differentiate/tests/test_differentiate.py +694 -0
- scipy/fft/__init__.py +114 -0
- scipy/fft/_backend.py +196 -0
- scipy/fft/_basic.py +1650 -0
- scipy/fft/_basic_backend.py +197 -0
- scipy/fft/_debug_backends.py +22 -0
- scipy/fft/_fftlog.py +223 -0
- scipy/fft/_fftlog_backend.py +200 -0
- scipy/fft/_helper.py +348 -0
- scipy/fft/_pocketfft/LICENSE.md +25 -0
- scipy/fft/_pocketfft/__init__.py +9 -0
- scipy/fft/_pocketfft/basic.py +251 -0
- scipy/fft/_pocketfft/helper.py +249 -0
- scipy/fft/_pocketfft/pypocketfft.cp312-win_arm64.lib +0 -0
- scipy/fft/_pocketfft/pypocketfft.cp312-win_arm64.pyd +0 -0
- scipy/fft/_pocketfft/realtransforms.py +109 -0
- scipy/fft/_pocketfft/tests/__init__.py +0 -0
- scipy/fft/_pocketfft/tests/test_basic.py +1011 -0
- scipy/fft/_pocketfft/tests/test_real_transforms.py +505 -0
- scipy/fft/_realtransforms.py +706 -0
- scipy/fft/_realtransforms_backend.py +63 -0
- scipy/fft/tests/__init__.py +0 -0
- scipy/fft/tests/mock_backend.py +96 -0
- scipy/fft/tests/test_backend.py +98 -0
- scipy/fft/tests/test_basic.py +504 -0
- scipy/fft/tests/test_fftlog.py +215 -0
- scipy/fft/tests/test_helper.py +558 -0
- scipy/fft/tests/test_multithreading.py +84 -0
- scipy/fft/tests/test_real_transforms.py +247 -0
- scipy/fftpack/__init__.py +103 -0
- scipy/fftpack/_basic.py +428 -0
- scipy/fftpack/_helper.py +115 -0
- scipy/fftpack/_pseudo_diffs.py +554 -0
- scipy/fftpack/_realtransforms.py +598 -0
- scipy/fftpack/basic.py +20 -0
- scipy/fftpack/convolve.cp312-win_arm64.lib +0 -0
- scipy/fftpack/convolve.cp312-win_arm64.pyd +0 -0
- scipy/fftpack/helper.py +19 -0
- scipy/fftpack/pseudo_diffs.py +22 -0
- scipy/fftpack/realtransforms.py +19 -0
- scipy/fftpack/tests/__init__.py +0 -0
- scipy/fftpack/tests/fftw_double_ref.npz +0 -0
- scipy/fftpack/tests/fftw_longdouble_ref.npz +0 -0
- scipy/fftpack/tests/fftw_single_ref.npz +0 -0
- scipy/fftpack/tests/test.npz +0 -0
- scipy/fftpack/tests/test_basic.py +877 -0
- scipy/fftpack/tests/test_helper.py +54 -0
- scipy/fftpack/tests/test_import.py +33 -0
- scipy/fftpack/tests/test_pseudo_diffs.py +388 -0
- scipy/fftpack/tests/test_real_transforms.py +836 -0
- scipy/integrate/__init__.py +122 -0
- scipy/integrate/_bvp.py +1160 -0
- scipy/integrate/_cubature.py +729 -0
- scipy/integrate/_dop.cp312-win_arm64.lib +0 -0
- scipy/integrate/_dop.cp312-win_arm64.pyd +0 -0
- scipy/integrate/_ivp/__init__.py +8 -0
- scipy/integrate/_ivp/base.py +290 -0
- scipy/integrate/_ivp/bdf.py +478 -0
- scipy/integrate/_ivp/common.py +451 -0
- scipy/integrate/_ivp/dop853_coefficients.py +193 -0
- scipy/integrate/_ivp/ivp.py +755 -0
- scipy/integrate/_ivp/lsoda.py +224 -0
- scipy/integrate/_ivp/radau.py +572 -0
- scipy/integrate/_ivp/rk.py +601 -0
- scipy/integrate/_ivp/tests/__init__.py +0 -0
- scipy/integrate/_ivp/tests/test_ivp.py +1287 -0
- scipy/integrate/_ivp/tests/test_rk.py +37 -0
- scipy/integrate/_lebedev.py +5450 -0
- scipy/integrate/_lsoda.cp312-win_arm64.lib +0 -0
- scipy/integrate/_lsoda.cp312-win_arm64.pyd +0 -0
- scipy/integrate/_ode.py +1395 -0
- scipy/integrate/_odepack.cp312-win_arm64.lib +0 -0
- scipy/integrate/_odepack.cp312-win_arm64.pyd +0 -0
- scipy/integrate/_odepack_py.py +273 -0
- scipy/integrate/_quad_vec.py +674 -0
- scipy/integrate/_quadpack.cp312-win_arm64.lib +0 -0
- scipy/integrate/_quadpack.cp312-win_arm64.pyd +0 -0
- scipy/integrate/_quadpack_py.py +1283 -0
- scipy/integrate/_quadrature.py +1336 -0
- scipy/integrate/_rules/__init__.py +12 -0
- scipy/integrate/_rules/_base.py +518 -0
- scipy/integrate/_rules/_gauss_kronrod.py +202 -0
- scipy/integrate/_rules/_gauss_legendre.py +62 -0
- scipy/integrate/_rules/_genz_malik.py +210 -0
- scipy/integrate/_tanhsinh.py +1385 -0
- scipy/integrate/_test_multivariate.cp312-win_arm64.lib +0 -0
- scipy/integrate/_test_multivariate.cp312-win_arm64.pyd +0 -0
- scipy/integrate/_test_odeint_banded.cp312-win_arm64.lib +0 -0
- scipy/integrate/_test_odeint_banded.cp312-win_arm64.pyd +0 -0
- scipy/integrate/_vode.cp312-win_arm64.lib +0 -0
- scipy/integrate/_vode.cp312-win_arm64.pyd +0 -0
- scipy/integrate/dop.py +15 -0
- scipy/integrate/lsoda.py +15 -0
- scipy/integrate/odepack.py +17 -0
- scipy/integrate/quadpack.py +23 -0
- scipy/integrate/tests/__init__.py +0 -0
- scipy/integrate/tests/test__quad_vec.py +211 -0
- scipy/integrate/tests/test_banded_ode_solvers.py +305 -0
- scipy/integrate/tests/test_bvp.py +714 -0
- scipy/integrate/tests/test_cubature.py +1375 -0
- scipy/integrate/tests/test_integrate.py +840 -0
- scipy/integrate/tests/test_odeint_jac.py +74 -0
- scipy/integrate/tests/test_quadpack.py +680 -0
- scipy/integrate/tests/test_quadrature.py +730 -0
- scipy/integrate/tests/test_tanhsinh.py +1171 -0
- scipy/integrate/vode.py +15 -0
- scipy/interpolate/__init__.py +228 -0
- scipy/interpolate/_bary_rational.py +715 -0
- scipy/interpolate/_bsplines.py +2469 -0
- scipy/interpolate/_cubic.py +973 -0
- scipy/interpolate/_dfitpack.cp312-win_arm64.lib +0 -0
- scipy/interpolate/_dfitpack.cp312-win_arm64.pyd +0 -0
- scipy/interpolate/_dierckx.cp312-win_arm64.lib +0 -0
- scipy/interpolate/_dierckx.cp312-win_arm64.pyd +0 -0
- scipy/interpolate/_fitpack.cp312-win_arm64.lib +0 -0
- scipy/interpolate/_fitpack.cp312-win_arm64.pyd +0 -0
- scipy/interpolate/_fitpack2.py +2397 -0
- scipy/interpolate/_fitpack_impl.py +811 -0
- scipy/interpolate/_fitpack_py.py +898 -0
- scipy/interpolate/_fitpack_repro.py +996 -0
- scipy/interpolate/_interpnd.cp312-win_arm64.lib +0 -0
- scipy/interpolate/_interpnd.cp312-win_arm64.pyd +0 -0
- scipy/interpolate/_interpolate.py +2266 -0
- scipy/interpolate/_ndbspline.py +415 -0
- scipy/interpolate/_ndgriddata.py +329 -0
- scipy/interpolate/_pade.py +67 -0
- scipy/interpolate/_polyint.py +1025 -0
- scipy/interpolate/_ppoly.cp312-win_arm64.lib +0 -0
- scipy/interpolate/_ppoly.cp312-win_arm64.pyd +0 -0
- scipy/interpolate/_rbf.py +290 -0
- scipy/interpolate/_rbfinterp.py +550 -0
- scipy/interpolate/_rbfinterp_pythran.cp312-win_arm64.lib +0 -0
- scipy/interpolate/_rbfinterp_pythran.cp312-win_arm64.pyd +0 -0
- scipy/interpolate/_rgi.py +764 -0
- scipy/interpolate/_rgi_cython.cp312-win_arm64.lib +0 -0
- scipy/interpolate/_rgi_cython.cp312-win_arm64.pyd +0 -0
- scipy/interpolate/dfitpack.py +24 -0
- scipy/interpolate/fitpack.py +31 -0
- scipy/interpolate/fitpack2.py +29 -0
- scipy/interpolate/interpnd.py +24 -0
- scipy/interpolate/interpolate.py +30 -0
- scipy/interpolate/ndgriddata.py +23 -0
- scipy/interpolate/polyint.py +24 -0
- scipy/interpolate/rbf.py +18 -0
- scipy/interpolate/tests/__init__.py +0 -0
- scipy/interpolate/tests/data/bug-1310.npz +0 -0
- scipy/interpolate/tests/data/estimate_gradients_hang.npy +0 -0
- scipy/interpolate/tests/data/gcvspl.npz +0 -0
- scipy/interpolate/tests/test_bary_rational.py +368 -0
- scipy/interpolate/tests/test_bsplines.py +3754 -0
- scipy/interpolate/tests/test_fitpack.py +519 -0
- scipy/interpolate/tests/test_fitpack2.py +1431 -0
- scipy/interpolate/tests/test_gil.py +64 -0
- scipy/interpolate/tests/test_interpnd.py +452 -0
- scipy/interpolate/tests/test_interpolate.py +2630 -0
- scipy/interpolate/tests/test_ndgriddata.py +308 -0
- scipy/interpolate/tests/test_pade.py +107 -0
- scipy/interpolate/tests/test_polyint.py +972 -0
- scipy/interpolate/tests/test_rbf.py +246 -0
- scipy/interpolate/tests/test_rbfinterp.py +534 -0
- scipy/interpolate/tests/test_rgi.py +1151 -0
- scipy/io/__init__.py +116 -0
- scipy/io/_fast_matrix_market/__init__.py +600 -0
- scipy/io/_fast_matrix_market/_fmm_core.cp312-win_arm64.lib +0 -0
- scipy/io/_fast_matrix_market/_fmm_core.cp312-win_arm64.pyd +0 -0
- scipy/io/_fortran.py +354 -0
- scipy/io/_harwell_boeing/__init__.py +7 -0
- scipy/io/_harwell_boeing/_fortran_format_parser.py +316 -0
- scipy/io/_harwell_boeing/hb.py +571 -0
- scipy/io/_harwell_boeing/tests/__init__.py +0 -0
- scipy/io/_harwell_boeing/tests/test_fortran_format.py +74 -0
- scipy/io/_harwell_boeing/tests/test_hb.py +70 -0
- scipy/io/_idl.py +917 -0
- scipy/io/_mmio.py +968 -0
- scipy/io/_netcdf.py +1104 -0
- scipy/io/_test_fortran.cp312-win_arm64.lib +0 -0
- scipy/io/_test_fortran.cp312-win_arm64.pyd +0 -0
- scipy/io/arff/__init__.py +28 -0
- scipy/io/arff/_arffread.py +873 -0
- scipy/io/arff/arffread.py +19 -0
- scipy/io/arff/tests/__init__.py +0 -0
- scipy/io/arff/tests/data/iris.arff +225 -0
- scipy/io/arff/tests/data/missing.arff +8 -0
- scipy/io/arff/tests/data/nodata.arff +11 -0
- scipy/io/arff/tests/data/quoted_nominal.arff +13 -0
- scipy/io/arff/tests/data/quoted_nominal_spaces.arff +13 -0
- scipy/io/arff/tests/data/test1.arff +10 -0
- scipy/io/arff/tests/data/test10.arff +8 -0
- scipy/io/arff/tests/data/test11.arff +11 -0
- scipy/io/arff/tests/data/test2.arff +15 -0
- scipy/io/arff/tests/data/test3.arff +6 -0
- scipy/io/arff/tests/data/test4.arff +11 -0
- scipy/io/arff/tests/data/test5.arff +26 -0
- scipy/io/arff/tests/data/test6.arff +12 -0
- scipy/io/arff/tests/data/test7.arff +15 -0
- scipy/io/arff/tests/data/test8.arff +12 -0
- scipy/io/arff/tests/data/test9.arff +14 -0
- scipy/io/arff/tests/test_arffread.py +421 -0
- scipy/io/harwell_boeing.py +17 -0
- scipy/io/idl.py +17 -0
- scipy/io/matlab/__init__.py +66 -0
- scipy/io/matlab/_byteordercodes.py +75 -0
- scipy/io/matlab/_mio.py +375 -0
- scipy/io/matlab/_mio4.py +632 -0
- scipy/io/matlab/_mio5.py +901 -0
- scipy/io/matlab/_mio5_params.py +281 -0
- scipy/io/matlab/_mio5_utils.cp312-win_arm64.lib +0 -0
- scipy/io/matlab/_mio5_utils.cp312-win_arm64.pyd +0 -0
- scipy/io/matlab/_mio_utils.cp312-win_arm64.lib +0 -0
- scipy/io/matlab/_mio_utils.cp312-win_arm64.pyd +0 -0
- scipy/io/matlab/_miobase.py +435 -0
- scipy/io/matlab/_streams.cp312-win_arm64.lib +0 -0
- scipy/io/matlab/_streams.cp312-win_arm64.pyd +0 -0
- scipy/io/matlab/byteordercodes.py +17 -0
- scipy/io/matlab/mio.py +16 -0
- scipy/io/matlab/mio4.py +17 -0
- scipy/io/matlab/mio5.py +19 -0
- scipy/io/matlab/mio5_params.py +18 -0
- scipy/io/matlab/mio5_utils.py +17 -0
- scipy/io/matlab/mio_utils.py +17 -0
- scipy/io/matlab/miobase.py +16 -0
- scipy/io/matlab/streams.py +16 -0
- scipy/io/matlab/tests/__init__.py +0 -0
- scipy/io/matlab/tests/data/bad_miuint32.mat +0 -0
- scipy/io/matlab/tests/data/bad_miutf8_array_name.mat +0 -0
- scipy/io/matlab/tests/data/big_endian.mat +0 -0
- scipy/io/matlab/tests/data/broken_utf8.mat +0 -0
- scipy/io/matlab/tests/data/corrupted_zlib_checksum.mat +0 -0
- scipy/io/matlab/tests/data/corrupted_zlib_data.mat +0 -0
- scipy/io/matlab/tests/data/debigged_m4.mat +0 -0
- scipy/io/matlab/tests/data/japanese_utf8.txt +5 -0
- scipy/io/matlab/tests/data/little_endian.mat +0 -0
- scipy/io/matlab/tests/data/logical_sparse.mat +0 -0
- scipy/io/matlab/tests/data/malformed1.mat +0 -0
- scipy/io/matlab/tests/data/miuint32_for_miint32.mat +0 -0
- scipy/io/matlab/tests/data/miutf8_array_name.mat +0 -0
- scipy/io/matlab/tests/data/nasty_duplicate_fieldnames.mat +0 -0
- scipy/io/matlab/tests/data/one_by_zero_char.mat +0 -0
- scipy/io/matlab/tests/data/parabola.mat +0 -0
- scipy/io/matlab/tests/data/single_empty_string.mat +0 -0
- scipy/io/matlab/tests/data/some_functions.mat +0 -0
- scipy/io/matlab/tests/data/sqr.mat +0 -0
- scipy/io/matlab/tests/data/test3dmatrix_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/test3dmatrix_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/test3dmatrix_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/test3dmatrix_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/test_empty_struct.mat +0 -0
- scipy/io/matlab/tests/data/test_mat4_le_floats.mat +0 -0
- scipy/io/matlab/tests/data/test_skip_variable.mat +0 -0
- scipy/io/matlab/tests/data/testbool_8_WIN64.mat +0 -0
- scipy/io/matlab/tests/data/testcell_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testcell_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testcell_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testcell_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testcellnest_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testcellnest_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testcellnest_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testcellnest_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testcomplex_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testcomplex_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testcomplex_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testcomplex_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testcomplex_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testdouble_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testdouble_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testdouble_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testdouble_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testdouble_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testemptycell_5.3_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testemptycell_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testemptycell_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testemptycell_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testfunc_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testhdf5_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testmatrix_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testmatrix_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testmatrix_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testmatrix_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testmatrix_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testminus_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testminus_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testminus_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testminus_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testminus_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testmulti_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testmulti_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testmulti_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testobject_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testobject_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testobject_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testobject_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testonechar_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testonechar_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testonechar_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testonechar_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testonechar_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testscalarcell_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testsimplecell.mat +0 -0
- scipy/io/matlab/tests/data/testsparse_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testsparse_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testsparse_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testsparse_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testsparse_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testsparsecomplex_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testsparsecomplex_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/testsparsecomplex_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testsparsecomplex_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testsparsecomplex_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testsparsefloat_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststring_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/teststring_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/teststring_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststring_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststring_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststringarray_4.2c_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/teststringarray_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/teststringarray_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststringarray_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststringarray_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststruct_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/teststruct_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststruct_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststruct_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststructarr_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/teststructarr_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststructarr_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststructarr_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststructnest_6.1_SOL2.mat +0 -0
- scipy/io/matlab/tests/data/teststructnest_6.5.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststructnest_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/teststructnest_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testunicode_7.1_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testunicode_7.4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/data/testvec_4_GLNX86.mat +0 -0
- scipy/io/matlab/tests/test_byteordercodes.py +29 -0
- scipy/io/matlab/tests/test_mio.py +1399 -0
- scipy/io/matlab/tests/test_mio5_utils.py +179 -0
- scipy/io/matlab/tests/test_mio_funcs.py +51 -0
- scipy/io/matlab/tests/test_mio_utils.py +45 -0
- scipy/io/matlab/tests/test_miobase.py +32 -0
- scipy/io/matlab/tests/test_pathological.py +33 -0
- scipy/io/matlab/tests/test_streams.py +241 -0
- scipy/io/mmio.py +17 -0
- scipy/io/netcdf.py +17 -0
- scipy/io/tests/__init__.py +0 -0
- scipy/io/tests/data/Transparent Busy.ani +0 -0
- scipy/io/tests/data/array_float32_1d.sav +0 -0
- scipy/io/tests/data/array_float32_2d.sav +0 -0
- scipy/io/tests/data/array_float32_3d.sav +0 -0
- scipy/io/tests/data/array_float32_4d.sav +0 -0
- scipy/io/tests/data/array_float32_5d.sav +0 -0
- scipy/io/tests/data/array_float32_6d.sav +0 -0
- scipy/io/tests/data/array_float32_7d.sav +0 -0
- scipy/io/tests/data/array_float32_8d.sav +0 -0
- scipy/io/tests/data/array_float32_pointer_1d.sav +0 -0
- scipy/io/tests/data/array_float32_pointer_2d.sav +0 -0
- scipy/io/tests/data/array_float32_pointer_3d.sav +0 -0
- scipy/io/tests/data/array_float32_pointer_4d.sav +0 -0
- scipy/io/tests/data/array_float32_pointer_5d.sav +0 -0
- scipy/io/tests/data/array_float32_pointer_6d.sav +0 -0
- scipy/io/tests/data/array_float32_pointer_7d.sav +0 -0
- scipy/io/tests/data/array_float32_pointer_8d.sav +0 -0
- scipy/io/tests/data/example_1.nc +0 -0
- scipy/io/tests/data/example_2.nc +0 -0
- scipy/io/tests/data/example_3_maskedvals.nc +0 -0
- scipy/io/tests/data/fortran-3x3d-2i.dat +0 -0
- scipy/io/tests/data/fortran-mixed.dat +0 -0
- scipy/io/tests/data/fortran-sf8-11x1x10.dat +0 -0
- scipy/io/tests/data/fortran-sf8-15x10x22.dat +0 -0
- scipy/io/tests/data/fortran-sf8-1x1x1.dat +0 -0
- scipy/io/tests/data/fortran-sf8-1x1x5.dat +0 -0
- scipy/io/tests/data/fortran-sf8-1x1x7.dat +0 -0
- scipy/io/tests/data/fortran-sf8-1x3x5.dat +0 -0
- scipy/io/tests/data/fortran-si4-11x1x10.dat +0 -0
- scipy/io/tests/data/fortran-si4-15x10x22.dat +0 -0
- scipy/io/tests/data/fortran-si4-1x1x1.dat +0 -0
- scipy/io/tests/data/fortran-si4-1x1x5.dat +0 -0
- scipy/io/tests/data/fortran-si4-1x1x7.dat +0 -0
- scipy/io/tests/data/fortran-si4-1x3x5.dat +0 -0
- scipy/io/tests/data/invalid_pointer.sav +0 -0
- scipy/io/tests/data/null_pointer.sav +0 -0
- scipy/io/tests/data/scalar_byte.sav +0 -0
- scipy/io/tests/data/scalar_byte_descr.sav +0 -0
- scipy/io/tests/data/scalar_complex32.sav +0 -0
- scipy/io/tests/data/scalar_complex64.sav +0 -0
- scipy/io/tests/data/scalar_float32.sav +0 -0
- scipy/io/tests/data/scalar_float64.sav +0 -0
- scipy/io/tests/data/scalar_heap_pointer.sav +0 -0
- scipy/io/tests/data/scalar_int16.sav +0 -0
- scipy/io/tests/data/scalar_int32.sav +0 -0
- scipy/io/tests/data/scalar_int64.sav +0 -0
- scipy/io/tests/data/scalar_string.sav +0 -0
- scipy/io/tests/data/scalar_uint16.sav +0 -0
- scipy/io/tests/data/scalar_uint32.sav +0 -0
- scipy/io/tests/data/scalar_uint64.sav +0 -0
- scipy/io/tests/data/struct_arrays.sav +0 -0
- scipy/io/tests/data/struct_arrays_byte_idl80.sav +0 -0
- scipy/io/tests/data/struct_arrays_replicated.sav +0 -0
- scipy/io/tests/data/struct_arrays_replicated_3d.sav +0 -0
- scipy/io/tests/data/struct_inherit.sav +0 -0
- scipy/io/tests/data/struct_pointer_arrays.sav +0 -0
- scipy/io/tests/data/struct_pointer_arrays_replicated.sav +0 -0
- scipy/io/tests/data/struct_pointer_arrays_replicated_3d.sav +0 -0
- scipy/io/tests/data/struct_pointers.sav +0 -0
- scipy/io/tests/data/struct_pointers_replicated.sav +0 -0
- scipy/io/tests/data/struct_pointers_replicated_3d.sav +0 -0
- scipy/io/tests/data/struct_scalars.sav +0 -0
- scipy/io/tests/data/struct_scalars_replicated.sav +0 -0
- scipy/io/tests/data/struct_scalars_replicated_3d.sav +0 -0
- scipy/io/tests/data/test-1234Hz-le-1ch-10S-20bit-extra.wav +0 -0
- scipy/io/tests/data/test-44100Hz-2ch-32bit-float-be.wav +0 -0
- scipy/io/tests/data/test-44100Hz-2ch-32bit-float-le.wav +0 -0
- scipy/io/tests/data/test-44100Hz-be-1ch-4bytes.wav +0 -0
- scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-early-eof-no-data.wav +0 -0
- scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-early-eof.wav +0 -0
- scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-incomplete-chunk.wav +0 -0
- scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-rf64.wav +0 -0
- scipy/io/tests/data/test-44100Hz-le-1ch-4bytes.wav +0 -0
- scipy/io/tests/data/test-48000Hz-2ch-64bit-float-le-wavex.wav +0 -0
- scipy/io/tests/data/test-8000Hz-be-3ch-5S-24bit.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-1ch-1byte-ulaw.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-2ch-1byteu.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-3ch-5S-24bit-inconsistent.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-3ch-5S-24bit-rf64.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-3ch-5S-24bit.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-3ch-5S-36bit.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-3ch-5S-45bit.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-3ch-5S-53bit.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-3ch-5S-64bit.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-4ch-9S-12bit.wav +0 -0
- scipy/io/tests/data/test-8000Hz-le-5ch-9S-5bit.wav +0 -0
- scipy/io/tests/data/various_compressed.sav +0 -0
- scipy/io/tests/test_fortran.py +264 -0
- scipy/io/tests/test_idl.py +483 -0
- scipy/io/tests/test_mmio.py +831 -0
- scipy/io/tests/test_netcdf.py +550 -0
- scipy/io/tests/test_paths.py +93 -0
- scipy/io/tests/test_wavfile.py +501 -0
- scipy/io/wavfile.py +938 -0
- scipy/linalg/__init__.pxd +1 -0
- scipy/linalg/__init__.py +236 -0
- scipy/linalg/_basic.py +2146 -0
- scipy/linalg/_blas_subroutines.h +164 -0
- scipy/linalg/_cythonized_array_utils.cp312-win_arm64.lib +0 -0
- scipy/linalg/_cythonized_array_utils.cp312-win_arm64.pyd +0 -0
- scipy/linalg/_cythonized_array_utils.pxd +40 -0
- scipy/linalg/_cythonized_array_utils.pyi +16 -0
- scipy/linalg/_decomp.py +1645 -0
- scipy/linalg/_decomp_cholesky.py +413 -0
- scipy/linalg/_decomp_cossin.py +236 -0
- scipy/linalg/_decomp_interpolative.cp312-win_arm64.lib +0 -0
- scipy/linalg/_decomp_interpolative.cp312-win_arm64.pyd +0 -0
- scipy/linalg/_decomp_ldl.py +356 -0
- scipy/linalg/_decomp_lu.py +401 -0
- scipy/linalg/_decomp_lu_cython.cp312-win_arm64.lib +0 -0
- scipy/linalg/_decomp_lu_cython.cp312-win_arm64.pyd +0 -0
- scipy/linalg/_decomp_lu_cython.pyi +6 -0
- scipy/linalg/_decomp_polar.py +113 -0
- scipy/linalg/_decomp_qr.py +494 -0
- scipy/linalg/_decomp_qz.py +452 -0
- scipy/linalg/_decomp_schur.py +336 -0
- scipy/linalg/_decomp_svd.py +545 -0
- scipy/linalg/_decomp_update.cp312-win_arm64.lib +0 -0
- scipy/linalg/_decomp_update.cp312-win_arm64.pyd +0 -0
- scipy/linalg/_expm_frechet.py +417 -0
- scipy/linalg/_fblas.cp312-win_arm64.lib +0 -0
- scipy/linalg/_fblas.cp312-win_arm64.pyd +0 -0
- scipy/linalg/_flapack.cp312-win_arm64.lib +0 -0
- scipy/linalg/_flapack.cp312-win_arm64.pyd +0 -0
- scipy/linalg/_lapack_subroutines.h +1521 -0
- scipy/linalg/_linalg_pythran.cp312-win_arm64.lib +0 -0
- scipy/linalg/_linalg_pythran.cp312-win_arm64.pyd +0 -0
- scipy/linalg/_matfuncs.py +1050 -0
- scipy/linalg/_matfuncs_expm.cp312-win_arm64.lib +0 -0
- scipy/linalg/_matfuncs_expm.cp312-win_arm64.pyd +0 -0
- scipy/linalg/_matfuncs_expm.pyi +6 -0
- scipy/linalg/_matfuncs_inv_ssq.py +886 -0
- scipy/linalg/_matfuncs_schur_sqrtm.cp312-win_arm64.lib +0 -0
- scipy/linalg/_matfuncs_schur_sqrtm.cp312-win_arm64.pyd +0 -0
- scipy/linalg/_matfuncs_sqrtm.py +107 -0
- scipy/linalg/_matfuncs_sqrtm_triu.cp312-win_arm64.lib +0 -0
- scipy/linalg/_matfuncs_sqrtm_triu.cp312-win_arm64.pyd +0 -0
- scipy/linalg/_misc.py +191 -0
- scipy/linalg/_procrustes.py +113 -0
- scipy/linalg/_sketches.py +189 -0
- scipy/linalg/_solve_toeplitz.cp312-win_arm64.lib +0 -0
- scipy/linalg/_solve_toeplitz.cp312-win_arm64.pyd +0 -0
- scipy/linalg/_solvers.py +862 -0
- scipy/linalg/_special_matrices.py +1322 -0
- scipy/linalg/_testutils.py +65 -0
- scipy/linalg/basic.py +23 -0
- scipy/linalg/blas.py +495 -0
- scipy/linalg/cython_blas.cp312-win_arm64.lib +0 -0
- scipy/linalg/cython_blas.cp312-win_arm64.pyd +0 -0
- scipy/linalg/cython_blas.pxd +169 -0
- scipy/linalg/cython_blas.pyx +1432 -0
- scipy/linalg/cython_lapack.cp312-win_arm64.lib +0 -0
- scipy/linalg/cython_lapack.cp312-win_arm64.pyd +0 -0
- scipy/linalg/cython_lapack.pxd +1528 -0
- scipy/linalg/cython_lapack.pyx +12045 -0
- scipy/linalg/decomp.py +23 -0
- scipy/linalg/decomp_cholesky.py +21 -0
- scipy/linalg/decomp_lu.py +21 -0
- scipy/linalg/decomp_qr.py +20 -0
- scipy/linalg/decomp_schur.py +21 -0
- scipy/linalg/decomp_svd.py +21 -0
- scipy/linalg/interpolative.py +989 -0
- scipy/linalg/lapack.py +1081 -0
- scipy/linalg/matfuncs.py +23 -0
- scipy/linalg/misc.py +21 -0
- scipy/linalg/special_matrices.py +22 -0
- scipy/linalg/tests/__init__.py +0 -0
- scipy/linalg/tests/_cython_examples/extending.pyx +23 -0
- scipy/linalg/tests/_cython_examples/meson.build +34 -0
- scipy/linalg/tests/data/carex_15_data.npz +0 -0
- scipy/linalg/tests/data/carex_18_data.npz +0 -0
- scipy/linalg/tests/data/carex_19_data.npz +0 -0
- scipy/linalg/tests/data/carex_20_data.npz +0 -0
- scipy/linalg/tests/data/carex_6_data.npz +0 -0
- scipy/linalg/tests/data/gendare_20170120_data.npz +0 -0
- scipy/linalg/tests/test_basic.py +2074 -0
- scipy/linalg/tests/test_batch.py +588 -0
- scipy/linalg/tests/test_blas.py +1127 -0
- scipy/linalg/tests/test_cython_blas.py +118 -0
- scipy/linalg/tests/test_cython_lapack.py +22 -0
- scipy/linalg/tests/test_cythonized_array_utils.py +130 -0
- scipy/linalg/tests/test_decomp.py +3189 -0
- scipy/linalg/tests/test_decomp_cholesky.py +268 -0
- scipy/linalg/tests/test_decomp_cossin.py +314 -0
- scipy/linalg/tests/test_decomp_ldl.py +137 -0
- scipy/linalg/tests/test_decomp_lu.py +308 -0
- scipy/linalg/tests/test_decomp_polar.py +110 -0
- scipy/linalg/tests/test_decomp_update.py +1701 -0
- scipy/linalg/tests/test_extending.py +46 -0
- scipy/linalg/tests/test_fblas.py +607 -0
- scipy/linalg/tests/test_interpolative.py +232 -0
- scipy/linalg/tests/test_lapack.py +3620 -0
- scipy/linalg/tests/test_matfuncs.py +1125 -0
- scipy/linalg/tests/test_matmul_toeplitz.py +136 -0
- scipy/linalg/tests/test_procrustes.py +214 -0
- scipy/linalg/tests/test_sketches.py +118 -0
- scipy/linalg/tests/test_solve_toeplitz.py +150 -0
- scipy/linalg/tests/test_solvers.py +844 -0
- scipy/linalg/tests/test_special_matrices.py +636 -0
- scipy/misc/__init__.py +6 -0
- scipy/misc/common.py +6 -0
- scipy/misc/doccer.py +6 -0
- scipy/ndimage/__init__.py +174 -0
- scipy/ndimage/_ctest.cp312-win_arm64.lib +0 -0
- scipy/ndimage/_ctest.cp312-win_arm64.pyd +0 -0
- scipy/ndimage/_cytest.cp312-win_arm64.lib +0 -0
- scipy/ndimage/_cytest.cp312-win_arm64.pyd +0 -0
- scipy/ndimage/_delegators.py +303 -0
- scipy/ndimage/_filters.py +2422 -0
- scipy/ndimage/_fourier.py +306 -0
- scipy/ndimage/_interpolation.py +1033 -0
- scipy/ndimage/_measurements.py +1689 -0
- scipy/ndimage/_morphology.py +2634 -0
- scipy/ndimage/_nd_image.cp312-win_arm64.lib +0 -0
- scipy/ndimage/_nd_image.cp312-win_arm64.pyd +0 -0
- scipy/ndimage/_ndimage_api.py +16 -0
- scipy/ndimage/_ni_docstrings.py +214 -0
- scipy/ndimage/_ni_label.cp312-win_arm64.lib +0 -0
- scipy/ndimage/_ni_label.cp312-win_arm64.pyd +0 -0
- scipy/ndimage/_ni_support.py +139 -0
- scipy/ndimage/_rank_filter_1d.cp312-win_arm64.lib +0 -0
- scipy/ndimage/_rank_filter_1d.cp312-win_arm64.pyd +0 -0
- scipy/ndimage/_support_alternative_backends.py +84 -0
- scipy/ndimage/filters.py +27 -0
- scipy/ndimage/fourier.py +21 -0
- scipy/ndimage/interpolation.py +22 -0
- scipy/ndimage/measurements.py +24 -0
- scipy/ndimage/morphology.py +27 -0
- scipy/ndimage/tests/__init__.py +12 -0
- scipy/ndimage/tests/data/label_inputs.txt +21 -0
- scipy/ndimage/tests/data/label_results.txt +294 -0
- scipy/ndimage/tests/data/label_strels.txt +42 -0
- scipy/ndimage/tests/dots.png +0 -0
- scipy/ndimage/tests/test_c_api.py +102 -0
- scipy/ndimage/tests/test_datatypes.py +67 -0
- scipy/ndimage/tests/test_filters.py +3083 -0
- scipy/ndimage/tests/test_fourier.py +187 -0
- scipy/ndimage/tests/test_interpolation.py +1491 -0
- scipy/ndimage/tests/test_measurements.py +1592 -0
- scipy/ndimage/tests/test_morphology.py +2950 -0
- scipy/ndimage/tests/test_ni_support.py +78 -0
- scipy/ndimage/tests/test_splines.py +70 -0
- scipy/odr/__init__.py +131 -0
- scipy/odr/__odrpack.cp312-win_arm64.lib +0 -0
- scipy/odr/__odrpack.cp312-win_arm64.pyd +0 -0
- scipy/odr/_add_newdocs.py +34 -0
- scipy/odr/_models.py +315 -0
- scipy/odr/_odrpack.py +1154 -0
- scipy/odr/models.py +20 -0
- scipy/odr/odrpack.py +21 -0
- scipy/odr/tests/__init__.py +0 -0
- scipy/odr/tests/test_odr.py +607 -0
- scipy/optimize/__init__.pxd +1 -0
- scipy/optimize/__init__.py +460 -0
- scipy/optimize/_basinhopping.py +741 -0
- scipy/optimize/_bglu_dense.cp312-win_arm64.lib +0 -0
- scipy/optimize/_bglu_dense.cp312-win_arm64.pyd +0 -0
- scipy/optimize/_bracket.py +706 -0
- scipy/optimize/_chandrupatla.py +551 -0
- scipy/optimize/_cobyla_py.py +297 -0
- scipy/optimize/_cobyqa_py.py +72 -0
- scipy/optimize/_constraints.py +598 -0
- scipy/optimize/_dcsrch.py +728 -0
- scipy/optimize/_differentiable_functions.py +835 -0
- scipy/optimize/_differentialevolution.py +1970 -0
- scipy/optimize/_direct.cp312-win_arm64.lib +0 -0
- scipy/optimize/_direct.cp312-win_arm64.pyd +0 -0
- scipy/optimize/_direct_py.py +280 -0
- scipy/optimize/_dual_annealing.py +732 -0
- scipy/optimize/_elementwise.py +798 -0
- scipy/optimize/_group_columns.cp312-win_arm64.lib +0 -0
- scipy/optimize/_group_columns.cp312-win_arm64.pyd +0 -0
- scipy/optimize/_hessian_update_strategy.py +479 -0
- scipy/optimize/_highspy/__init__.py +0 -0
- scipy/optimize/_highspy/_core.cp312-win_arm64.lib +0 -0
- scipy/optimize/_highspy/_core.cp312-win_arm64.pyd +0 -0
- scipy/optimize/_highspy/_highs_options.cp312-win_arm64.lib +0 -0
- scipy/optimize/_highspy/_highs_options.cp312-win_arm64.pyd +0 -0
- scipy/optimize/_highspy/_highs_wrapper.py +338 -0
- scipy/optimize/_isotonic.py +157 -0
- scipy/optimize/_lbfgsb.cp312-win_arm64.lib +0 -0
- scipy/optimize/_lbfgsb.cp312-win_arm64.pyd +0 -0
- scipy/optimize/_lbfgsb_py.py +634 -0
- scipy/optimize/_linesearch.py +896 -0
- scipy/optimize/_linprog.py +733 -0
- scipy/optimize/_linprog_doc.py +1434 -0
- scipy/optimize/_linprog_highs.py +422 -0
- scipy/optimize/_linprog_ip.py +1141 -0
- scipy/optimize/_linprog_rs.py +572 -0
- scipy/optimize/_linprog_simplex.py +663 -0
- scipy/optimize/_linprog_util.py +1521 -0
- scipy/optimize/_lsap.cp312-win_arm64.lib +0 -0
- scipy/optimize/_lsap.cp312-win_arm64.pyd +0 -0
- scipy/optimize/_lsq/__init__.py +5 -0
- scipy/optimize/_lsq/bvls.py +183 -0
- scipy/optimize/_lsq/common.py +731 -0
- scipy/optimize/_lsq/dogbox.py +345 -0
- scipy/optimize/_lsq/givens_elimination.cp312-win_arm64.lib +0 -0
- scipy/optimize/_lsq/givens_elimination.cp312-win_arm64.pyd +0 -0
- scipy/optimize/_lsq/least_squares.py +1044 -0
- scipy/optimize/_lsq/lsq_linear.py +361 -0
- scipy/optimize/_lsq/trf.py +587 -0
- scipy/optimize/_lsq/trf_linear.py +249 -0
- scipy/optimize/_milp.py +394 -0
- scipy/optimize/_minimize.py +1199 -0
- scipy/optimize/_minpack.cp312-win_arm64.lib +0 -0
- scipy/optimize/_minpack.cp312-win_arm64.pyd +0 -0
- scipy/optimize/_minpack_py.py +1178 -0
- scipy/optimize/_moduleTNC.cp312-win_arm64.lib +0 -0
- scipy/optimize/_moduleTNC.cp312-win_arm64.pyd +0 -0
- scipy/optimize/_nnls.py +96 -0
- scipy/optimize/_nonlin.py +1634 -0
- scipy/optimize/_numdiff.py +963 -0
- scipy/optimize/_optimize.py +4169 -0
- scipy/optimize/_pava_pybind.cp312-win_arm64.lib +0 -0
- scipy/optimize/_pava_pybind.cp312-win_arm64.pyd +0 -0
- scipy/optimize/_qap.py +760 -0
- scipy/optimize/_remove_redundancy.py +522 -0
- scipy/optimize/_root.py +732 -0
- scipy/optimize/_root_scalar.py +538 -0
- scipy/optimize/_shgo.py +1606 -0
- scipy/optimize/_shgo_lib/__init__.py +0 -0
- scipy/optimize/_shgo_lib/_complex.py +1225 -0
- scipy/optimize/_shgo_lib/_vertex.py +460 -0
- scipy/optimize/_slsqp_py.py +603 -0
- scipy/optimize/_slsqplib.cp312-win_arm64.lib +0 -0
- scipy/optimize/_slsqplib.cp312-win_arm64.pyd +0 -0
- scipy/optimize/_spectral.py +260 -0
- scipy/optimize/_tnc.py +438 -0
- scipy/optimize/_trlib/__init__.py +12 -0
- scipy/optimize/_trlib/_trlib.cp312-win_arm64.lib +0 -0
- scipy/optimize/_trlib/_trlib.cp312-win_arm64.pyd +0 -0
- scipy/optimize/_trustregion.py +318 -0
- scipy/optimize/_trustregion_constr/__init__.py +6 -0
- scipy/optimize/_trustregion_constr/canonical_constraint.py +390 -0
- scipy/optimize/_trustregion_constr/equality_constrained_sqp.py +231 -0
- scipy/optimize/_trustregion_constr/minimize_trustregion_constr.py +584 -0
- scipy/optimize/_trustregion_constr/projections.py +411 -0
- scipy/optimize/_trustregion_constr/qp_subproblem.py +637 -0
- scipy/optimize/_trustregion_constr/report.py +49 -0
- scipy/optimize/_trustregion_constr/tests/__init__.py +0 -0
- scipy/optimize/_trustregion_constr/tests/test_canonical_constraint.py +296 -0
- scipy/optimize/_trustregion_constr/tests/test_nested_minimize.py +39 -0
- scipy/optimize/_trustregion_constr/tests/test_projections.py +214 -0
- scipy/optimize/_trustregion_constr/tests/test_qp_subproblem.py +645 -0
- scipy/optimize/_trustregion_constr/tests/test_report.py +34 -0
- scipy/optimize/_trustregion_constr/tr_interior_point.py +361 -0
- scipy/optimize/_trustregion_dogleg.py +122 -0
- scipy/optimize/_trustregion_exact.py +437 -0
- scipy/optimize/_trustregion_krylov.py +65 -0
- scipy/optimize/_trustregion_ncg.py +126 -0
- scipy/optimize/_tstutils.py +972 -0
- scipy/optimize/_zeros.cp312-win_arm64.lib +0 -0
- scipy/optimize/_zeros.cp312-win_arm64.pyd +0 -0
- scipy/optimize/_zeros_py.py +1475 -0
- scipy/optimize/cobyla.py +19 -0
- scipy/optimize/cython_optimize/__init__.py +133 -0
- scipy/optimize/cython_optimize/_zeros.cp312-win_arm64.lib +0 -0
- scipy/optimize/cython_optimize/_zeros.cp312-win_arm64.pyd +0 -0
- scipy/optimize/cython_optimize/_zeros.pxd +33 -0
- scipy/optimize/cython_optimize/c_zeros.pxd +26 -0
- scipy/optimize/cython_optimize.pxd +11 -0
- scipy/optimize/elementwise.py +38 -0
- scipy/optimize/lbfgsb.py +23 -0
- scipy/optimize/linesearch.py +18 -0
- scipy/optimize/minpack.py +27 -0
- scipy/optimize/minpack2.py +17 -0
- scipy/optimize/moduleTNC.py +19 -0
- scipy/optimize/nonlin.py +29 -0
- scipy/optimize/optimize.py +40 -0
- scipy/optimize/slsqp.py +22 -0
- scipy/optimize/tests/__init__.py +0 -0
- scipy/optimize/tests/_cython_examples/extending.pyx +43 -0
- scipy/optimize/tests/_cython_examples/meson.build +32 -0
- scipy/optimize/tests/test__basinhopping.py +535 -0
- scipy/optimize/tests/test__differential_evolution.py +1703 -0
- scipy/optimize/tests/test__dual_annealing.py +416 -0
- scipy/optimize/tests/test__linprog_clean_inputs.py +312 -0
- scipy/optimize/tests/test__numdiff.py +885 -0
- scipy/optimize/tests/test__remove_redundancy.py +228 -0
- scipy/optimize/tests/test__root.py +124 -0
- scipy/optimize/tests/test__shgo.py +1164 -0
- scipy/optimize/tests/test__spectral.py +226 -0
- scipy/optimize/tests/test_bracket.py +896 -0
- scipy/optimize/tests/test_chandrupatla.py +982 -0
- scipy/optimize/tests/test_cobyla.py +195 -0
- scipy/optimize/tests/test_cobyqa.py +252 -0
- scipy/optimize/tests/test_constraint_conversion.py +286 -0
- scipy/optimize/tests/test_constraints.py +255 -0
- scipy/optimize/tests/test_cython_optimize.py +92 -0
- scipy/optimize/tests/test_differentiable_functions.py +1025 -0
- scipy/optimize/tests/test_direct.py +321 -0
- scipy/optimize/tests/test_extending.py +28 -0
- scipy/optimize/tests/test_hessian_update_strategy.py +300 -0
- scipy/optimize/tests/test_isotonic_regression.py +167 -0
- scipy/optimize/tests/test_lbfgsb_hessinv.py +65 -0
- scipy/optimize/tests/test_lbfgsb_setulb.py +122 -0
- scipy/optimize/tests/test_least_squares.py +986 -0
- scipy/optimize/tests/test_linear_assignment.py +116 -0
- scipy/optimize/tests/test_linesearch.py +328 -0
- scipy/optimize/tests/test_linprog.py +2577 -0
- scipy/optimize/tests/test_lsq_common.py +297 -0
- scipy/optimize/tests/test_lsq_linear.py +287 -0
- scipy/optimize/tests/test_milp.py +459 -0
- scipy/optimize/tests/test_minimize_constrained.py +845 -0
- scipy/optimize/tests/test_minpack.py +1194 -0
- scipy/optimize/tests/test_nnls.py +469 -0
- scipy/optimize/tests/test_nonlin.py +572 -0
- scipy/optimize/tests/test_optimize.py +3344 -0
- scipy/optimize/tests/test_quadratic_assignment.py +455 -0
- scipy/optimize/tests/test_regression.py +40 -0
- scipy/optimize/tests/test_slsqp.py +645 -0
- scipy/optimize/tests/test_tnc.py +345 -0
- scipy/optimize/tests/test_trustregion.py +110 -0
- scipy/optimize/tests/test_trustregion_exact.py +351 -0
- scipy/optimize/tests/test_trustregion_krylov.py +170 -0
- scipy/optimize/tests/test_zeros.py +998 -0
- scipy/optimize/tnc.py +22 -0
- scipy/optimize/zeros.py +26 -0
- scipy/signal/__init__.py +316 -0
- scipy/signal/_arraytools.py +264 -0
- scipy/signal/_czt.py +575 -0
- scipy/signal/_delegators.py +568 -0
- scipy/signal/_filter_design.py +5893 -0
- scipy/signal/_fir_filter_design.py +1458 -0
- scipy/signal/_lti_conversion.py +534 -0
- scipy/signal/_ltisys.py +3546 -0
- scipy/signal/_max_len_seq.py +139 -0
- scipy/signal/_max_len_seq_inner.cp312-win_arm64.lib +0 -0
- scipy/signal/_max_len_seq_inner.cp312-win_arm64.pyd +0 -0
- scipy/signal/_peak_finding.py +1310 -0
- scipy/signal/_peak_finding_utils.cp312-win_arm64.lib +0 -0
- scipy/signal/_peak_finding_utils.cp312-win_arm64.pyd +0 -0
- scipy/signal/_polyutils.py +172 -0
- scipy/signal/_savitzky_golay.py +357 -0
- scipy/signal/_short_time_fft.py +2228 -0
- scipy/signal/_signal_api.py +30 -0
- scipy/signal/_signaltools.py +5309 -0
- scipy/signal/_sigtools.cp312-win_arm64.lib +0 -0
- scipy/signal/_sigtools.cp312-win_arm64.pyd +0 -0
- scipy/signal/_sosfilt.cp312-win_arm64.lib +0 -0
- scipy/signal/_sosfilt.cp312-win_arm64.pyd +0 -0
- scipy/signal/_spectral_py.py +2471 -0
- scipy/signal/_spline.cp312-win_arm64.lib +0 -0
- scipy/signal/_spline.cp312-win_arm64.pyd +0 -0
- scipy/signal/_spline.pyi +34 -0
- scipy/signal/_spline_filters.py +848 -0
- scipy/signal/_support_alternative_backends.py +73 -0
- scipy/signal/_upfirdn.py +219 -0
- scipy/signal/_upfirdn_apply.cp312-win_arm64.lib +0 -0
- scipy/signal/_upfirdn_apply.cp312-win_arm64.pyd +0 -0
- scipy/signal/_waveforms.py +687 -0
- scipy/signal/_wavelets.py +29 -0
- scipy/signal/bsplines.py +21 -0
- scipy/signal/filter_design.py +28 -0
- scipy/signal/fir_filter_design.py +21 -0
- scipy/signal/lti_conversion.py +20 -0
- scipy/signal/ltisys.py +25 -0
- scipy/signal/signaltools.py +27 -0
- scipy/signal/spectral.py +21 -0
- scipy/signal/spline.py +18 -0
- scipy/signal/tests/__init__.py +0 -0
- scipy/signal/tests/_scipy_spectral_test_shim.py +311 -0
- scipy/signal/tests/mpsig.py +122 -0
- scipy/signal/tests/test_array_tools.py +111 -0
- scipy/signal/tests/test_bsplines.py +365 -0
- scipy/signal/tests/test_cont2discrete.py +424 -0
- scipy/signal/tests/test_czt.py +221 -0
- scipy/signal/tests/test_dltisys.py +599 -0
- scipy/signal/tests/test_filter_design.py +4744 -0
- scipy/signal/tests/test_fir_filter_design.py +851 -0
- scipy/signal/tests/test_ltisys.py +1225 -0
- scipy/signal/tests/test_max_len_seq.py +71 -0
- scipy/signal/tests/test_peak_finding.py +915 -0
- scipy/signal/tests/test_result_type.py +51 -0
- scipy/signal/tests/test_savitzky_golay.py +363 -0
- scipy/signal/tests/test_short_time_fft.py +1107 -0
- scipy/signal/tests/test_signaltools.py +4735 -0
- scipy/signal/tests/test_spectral.py +2141 -0
- scipy/signal/tests/test_splines.py +427 -0
- scipy/signal/tests/test_upfirdn.py +322 -0
- scipy/signal/tests/test_waveforms.py +400 -0
- scipy/signal/tests/test_wavelets.py +59 -0
- scipy/signal/tests/test_windows.py +987 -0
- scipy/signal/waveforms.py +20 -0
- scipy/signal/wavelets.py +17 -0
- scipy/signal/windows/__init__.py +52 -0
- scipy/signal/windows/_windows.py +2513 -0
- scipy/signal/windows/windows.py +23 -0
- scipy/sparse/__init__.py +350 -0
- scipy/sparse/_base.py +1613 -0
- scipy/sparse/_bsr.py +880 -0
- scipy/sparse/_compressed.py +1328 -0
- scipy/sparse/_construct.py +1454 -0
- scipy/sparse/_coo.py +1581 -0
- scipy/sparse/_csc.py +367 -0
- scipy/sparse/_csparsetools.cp312-win_arm64.lib +0 -0
- scipy/sparse/_csparsetools.cp312-win_arm64.pyd +0 -0
- scipy/sparse/_csr.py +558 -0
- scipy/sparse/_data.py +569 -0
- scipy/sparse/_dia.py +677 -0
- scipy/sparse/_dok.py +669 -0
- scipy/sparse/_extract.py +178 -0
- scipy/sparse/_index.py +444 -0
- scipy/sparse/_lil.py +632 -0
- scipy/sparse/_matrix.py +169 -0
- scipy/sparse/_matrix_io.py +167 -0
- scipy/sparse/_sparsetools.cp312-win_arm64.lib +0 -0
- scipy/sparse/_sparsetools.cp312-win_arm64.pyd +0 -0
- scipy/sparse/_spfuncs.py +76 -0
- scipy/sparse/_sputils.py +632 -0
- scipy/sparse/base.py +24 -0
- scipy/sparse/bsr.py +22 -0
- scipy/sparse/compressed.py +20 -0
- scipy/sparse/construct.py +38 -0
- scipy/sparse/coo.py +23 -0
- scipy/sparse/csc.py +22 -0
- scipy/sparse/csgraph/__init__.py +210 -0
- scipy/sparse/csgraph/_flow.cp312-win_arm64.lib +0 -0
- scipy/sparse/csgraph/_flow.cp312-win_arm64.pyd +0 -0
- scipy/sparse/csgraph/_laplacian.py +563 -0
- scipy/sparse/csgraph/_matching.cp312-win_arm64.lib +0 -0
- scipy/sparse/csgraph/_matching.cp312-win_arm64.pyd +0 -0
- scipy/sparse/csgraph/_min_spanning_tree.cp312-win_arm64.lib +0 -0
- scipy/sparse/csgraph/_min_spanning_tree.cp312-win_arm64.pyd +0 -0
- scipy/sparse/csgraph/_reordering.cp312-win_arm64.lib +0 -0
- scipy/sparse/csgraph/_reordering.cp312-win_arm64.pyd +0 -0
- scipy/sparse/csgraph/_shortest_path.cp312-win_arm64.lib +0 -0
- scipy/sparse/csgraph/_shortest_path.cp312-win_arm64.pyd +0 -0
- scipy/sparse/csgraph/_tools.cp312-win_arm64.lib +0 -0
- scipy/sparse/csgraph/_tools.cp312-win_arm64.pyd +0 -0
- scipy/sparse/csgraph/_traversal.cp312-win_arm64.lib +0 -0
- scipy/sparse/csgraph/_traversal.cp312-win_arm64.pyd +0 -0
- scipy/sparse/csgraph/_validation.py +66 -0
- scipy/sparse/csgraph/tests/__init__.py +0 -0
- scipy/sparse/csgraph/tests/test_connected_components.py +119 -0
- scipy/sparse/csgraph/tests/test_conversions.py +61 -0
- scipy/sparse/csgraph/tests/test_flow.py +209 -0
- scipy/sparse/csgraph/tests/test_graph_laplacian.py +368 -0
- scipy/sparse/csgraph/tests/test_matching.py +307 -0
- scipy/sparse/csgraph/tests/test_pydata_sparse.py +197 -0
- scipy/sparse/csgraph/tests/test_reordering.py +70 -0
- scipy/sparse/csgraph/tests/test_shortest_path.py +540 -0
- scipy/sparse/csgraph/tests/test_spanning_tree.py +66 -0
- scipy/sparse/csgraph/tests/test_traversal.py +148 -0
- scipy/sparse/csr.py +22 -0
- scipy/sparse/data.py +18 -0
- scipy/sparse/dia.py +22 -0
- scipy/sparse/dok.py +22 -0
- scipy/sparse/extract.py +23 -0
- scipy/sparse/lil.py +22 -0
- scipy/sparse/linalg/__init__.py +148 -0
- scipy/sparse/linalg/_dsolve/__init__.py +71 -0
- scipy/sparse/linalg/_dsolve/_add_newdocs.py +147 -0
- scipy/sparse/linalg/_dsolve/_superlu.cp312-win_arm64.lib +0 -0
- scipy/sparse/linalg/_dsolve/_superlu.cp312-win_arm64.pyd +0 -0
- scipy/sparse/linalg/_dsolve/linsolve.py +882 -0
- scipy/sparse/linalg/_dsolve/tests/__init__.py +0 -0
- scipy/sparse/linalg/_dsolve/tests/test_linsolve.py +928 -0
- scipy/sparse/linalg/_eigen/__init__.py +22 -0
- scipy/sparse/linalg/_eigen/_svds.py +540 -0
- scipy/sparse/linalg/_eigen/_svds_doc.py +382 -0
- scipy/sparse/linalg/_eigen/arpack/COPYING +45 -0
- scipy/sparse/linalg/_eigen/arpack/__init__.py +20 -0
- scipy/sparse/linalg/_eigen/arpack/_arpack.cp312-win_arm64.lib +0 -0
- scipy/sparse/linalg/_eigen/arpack/_arpack.cp312-win_arm64.pyd +0 -0
- scipy/sparse/linalg/_eigen/arpack/arpack.py +1706 -0
- scipy/sparse/linalg/_eigen/arpack/tests/__init__.py +0 -0
- scipy/sparse/linalg/_eigen/arpack/tests/test_arpack.py +717 -0
- scipy/sparse/linalg/_eigen/lobpcg/__init__.py +16 -0
- scipy/sparse/linalg/_eigen/lobpcg/lobpcg.py +1110 -0
- scipy/sparse/linalg/_eigen/lobpcg/tests/__init__.py +0 -0
- scipy/sparse/linalg/_eigen/lobpcg/tests/test_lobpcg.py +725 -0
- scipy/sparse/linalg/_eigen/tests/__init__.py +0 -0
- scipy/sparse/linalg/_eigen/tests/test_svds.py +886 -0
- scipy/sparse/linalg/_expm_multiply.py +816 -0
- scipy/sparse/linalg/_interface.py +920 -0
- scipy/sparse/linalg/_isolve/__init__.py +20 -0
- scipy/sparse/linalg/_isolve/_gcrotmk.py +503 -0
- scipy/sparse/linalg/_isolve/iterative.py +1051 -0
- scipy/sparse/linalg/_isolve/lgmres.py +230 -0
- scipy/sparse/linalg/_isolve/lsmr.py +486 -0
- scipy/sparse/linalg/_isolve/lsqr.py +589 -0
- scipy/sparse/linalg/_isolve/minres.py +372 -0
- scipy/sparse/linalg/_isolve/tests/__init__.py +0 -0
- scipy/sparse/linalg/_isolve/tests/test_gcrotmk.py +183 -0
- scipy/sparse/linalg/_isolve/tests/test_iterative.py +809 -0
- scipy/sparse/linalg/_isolve/tests/test_lgmres.py +225 -0
- scipy/sparse/linalg/_isolve/tests/test_lsmr.py +185 -0
- scipy/sparse/linalg/_isolve/tests/test_lsqr.py +120 -0
- scipy/sparse/linalg/_isolve/tests/test_minres.py +97 -0
- scipy/sparse/linalg/_isolve/tests/test_utils.py +9 -0
- scipy/sparse/linalg/_isolve/tfqmr.py +179 -0
- scipy/sparse/linalg/_isolve/utils.py +121 -0
- scipy/sparse/linalg/_matfuncs.py +940 -0
- scipy/sparse/linalg/_norm.py +195 -0
- scipy/sparse/linalg/_onenormest.py +467 -0
- scipy/sparse/linalg/_propack/_cpropack.cp312-win_arm64.lib +0 -0
- scipy/sparse/linalg/_propack/_cpropack.cp312-win_arm64.pyd +0 -0
- scipy/sparse/linalg/_propack/_dpropack.cp312-win_arm64.lib +0 -0
- scipy/sparse/linalg/_propack/_dpropack.cp312-win_arm64.pyd +0 -0
- scipy/sparse/linalg/_propack/_spropack.cp312-win_arm64.lib +0 -0
- scipy/sparse/linalg/_propack/_spropack.cp312-win_arm64.pyd +0 -0
- scipy/sparse/linalg/_propack/_zpropack.cp312-win_arm64.lib +0 -0
- scipy/sparse/linalg/_propack/_zpropack.cp312-win_arm64.pyd +0 -0
- scipy/sparse/linalg/_special_sparse_arrays.py +949 -0
- scipy/sparse/linalg/_svdp.py +309 -0
- scipy/sparse/linalg/dsolve.py +22 -0
- scipy/sparse/linalg/eigen.py +21 -0
- scipy/sparse/linalg/interface.py +20 -0
- scipy/sparse/linalg/isolve.py +22 -0
- scipy/sparse/linalg/matfuncs.py +18 -0
- scipy/sparse/linalg/tests/__init__.py +0 -0
- scipy/sparse/linalg/tests/propack_test_data.npz +0 -0
- scipy/sparse/linalg/tests/test_expm_multiply.py +367 -0
- scipy/sparse/linalg/tests/test_interface.py +561 -0
- scipy/sparse/linalg/tests/test_matfuncs.py +592 -0
- scipy/sparse/linalg/tests/test_norm.py +154 -0
- scipy/sparse/linalg/tests/test_onenormest.py +252 -0
- scipy/sparse/linalg/tests/test_propack.py +165 -0
- scipy/sparse/linalg/tests/test_pydata_sparse.py +272 -0
- scipy/sparse/linalg/tests/test_special_sparse_arrays.py +337 -0
- scipy/sparse/sparsetools.py +17 -0
- scipy/sparse/spfuncs.py +17 -0
- scipy/sparse/sputils.py +17 -0
- scipy/sparse/tests/__init__.py +0 -0
- scipy/sparse/tests/data/csc_py2.npz +0 -0
- scipy/sparse/tests/data/csc_py3.npz +0 -0
- scipy/sparse/tests/test_arithmetic1d.py +341 -0
- scipy/sparse/tests/test_array_api.py +561 -0
- scipy/sparse/tests/test_base.py +5870 -0
- scipy/sparse/tests/test_common1d.py +447 -0
- scipy/sparse/tests/test_construct.py +872 -0
- scipy/sparse/tests/test_coo.py +1119 -0
- scipy/sparse/tests/test_csc.py +98 -0
- scipy/sparse/tests/test_csr.py +214 -0
- scipy/sparse/tests/test_dok.py +209 -0
- scipy/sparse/tests/test_extract.py +51 -0
- scipy/sparse/tests/test_indexing1d.py +603 -0
- scipy/sparse/tests/test_matrix_io.py +109 -0
- scipy/sparse/tests/test_minmax1d.py +128 -0
- scipy/sparse/tests/test_sparsetools.py +344 -0
- scipy/sparse/tests/test_spfuncs.py +97 -0
- scipy/sparse/tests/test_sputils.py +424 -0
- scipy/spatial/__init__.py +129 -0
- scipy/spatial/_ckdtree.cp312-win_arm64.lib +0 -0
- scipy/spatial/_ckdtree.cp312-win_arm64.pyd +0 -0
- scipy/spatial/_distance_pybind.cp312-win_arm64.lib +0 -0
- scipy/spatial/_distance_pybind.cp312-win_arm64.pyd +0 -0
- scipy/spatial/_distance_wrap.cp312-win_arm64.lib +0 -0
- scipy/spatial/_distance_wrap.cp312-win_arm64.pyd +0 -0
- scipy/spatial/_geometric_slerp.py +238 -0
- scipy/spatial/_hausdorff.cp312-win_arm64.lib +0 -0
- scipy/spatial/_hausdorff.cp312-win_arm64.pyd +0 -0
- scipy/spatial/_kdtree.py +920 -0
- scipy/spatial/_plotutils.py +274 -0
- scipy/spatial/_procrustes.py +132 -0
- scipy/spatial/_qhull.cp312-win_arm64.lib +0 -0
- scipy/spatial/_qhull.cp312-win_arm64.pyd +0 -0
- scipy/spatial/_qhull.pyi +213 -0
- scipy/spatial/_spherical_voronoi.py +341 -0
- scipy/spatial/_voronoi.cp312-win_arm64.lib +0 -0
- scipy/spatial/_voronoi.cp312-win_arm64.pyd +0 -0
- scipy/spatial/_voronoi.pyi +4 -0
- scipy/spatial/ckdtree.py +18 -0
- scipy/spatial/distance.py +3147 -0
- scipy/spatial/distance.pyi +210 -0
- scipy/spatial/kdtree.py +25 -0
- scipy/spatial/qhull.py +25 -0
- scipy/spatial/qhull_src/COPYING_QHULL.txt +39 -0
- scipy/spatial/tests/__init__.py +0 -0
- scipy/spatial/tests/data/cdist-X1.txt +10 -0
- scipy/spatial/tests/data/cdist-X2.txt +20 -0
- scipy/spatial/tests/data/degenerate_pointset.npz +0 -0
- scipy/spatial/tests/data/iris.txt +150 -0
- scipy/spatial/tests/data/pdist-boolean-inp.txt +20 -0
- scipy/spatial/tests/data/pdist-chebyshev-ml-iris.txt +1 -0
- scipy/spatial/tests/data/pdist-chebyshev-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-cityblock-ml-iris.txt +1 -0
- scipy/spatial/tests/data/pdist-cityblock-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-correlation-ml-iris.txt +1 -0
- scipy/spatial/tests/data/pdist-correlation-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-cosine-ml-iris.txt +1 -0
- scipy/spatial/tests/data/pdist-cosine-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-double-inp.txt +20 -0
- scipy/spatial/tests/data/pdist-euclidean-ml-iris.txt +1 -0
- scipy/spatial/tests/data/pdist-euclidean-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-hamming-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-jaccard-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-jensenshannon-ml-iris.txt +1 -0
- scipy/spatial/tests/data/pdist-jensenshannon-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-minkowski-3.2-ml-iris.txt +1 -0
- scipy/spatial/tests/data/pdist-minkowski-3.2-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-minkowski-5.8-ml-iris.txt +1 -0
- scipy/spatial/tests/data/pdist-seuclidean-ml-iris.txt +1 -0
- scipy/spatial/tests/data/pdist-seuclidean-ml.txt +1 -0
- scipy/spatial/tests/data/pdist-spearman-ml.txt +1 -0
- scipy/spatial/tests/data/random-bool-data.txt +100 -0
- scipy/spatial/tests/data/random-double-data.txt +100 -0
- scipy/spatial/tests/data/random-int-data.txt +100 -0
- scipy/spatial/tests/data/random-uint-data.txt +100 -0
- scipy/spatial/tests/data/selfdual-4d-polytope.txt +27 -0
- scipy/spatial/tests/test__plotutils.py +91 -0
- scipy/spatial/tests/test__procrustes.py +116 -0
- scipy/spatial/tests/test_distance.py +2389 -0
- scipy/spatial/tests/test_hausdorff.py +199 -0
- scipy/spatial/tests/test_kdtree.py +1536 -0
- scipy/spatial/tests/test_qhull.py +1313 -0
- scipy/spatial/tests/test_slerp.py +417 -0
- scipy/spatial/tests/test_spherical_voronoi.py +358 -0
- scipy/spatial/transform/__init__.py +31 -0
- scipy/spatial/transform/_rigid_transform.cp312-win_arm64.lib +0 -0
- scipy/spatial/transform/_rigid_transform.cp312-win_arm64.pyd +0 -0
- scipy/spatial/transform/_rotation.cp312-win_arm64.lib +0 -0
- scipy/spatial/transform/_rotation.cp312-win_arm64.pyd +0 -0
- scipy/spatial/transform/_rotation_groups.py +140 -0
- scipy/spatial/transform/_rotation_spline.py +460 -0
- scipy/spatial/transform/rotation.py +21 -0
- scipy/spatial/transform/tests/__init__.py +0 -0
- scipy/spatial/transform/tests/test_rigid_transform.py +1221 -0
- scipy/spatial/transform/tests/test_rotation.py +2569 -0
- scipy/spatial/transform/tests/test_rotation_groups.py +169 -0
- scipy/spatial/transform/tests/test_rotation_spline.py +183 -0
- scipy/special/__init__.pxd +1 -0
- scipy/special/__init__.py +841 -0
- scipy/special/_add_newdocs.py +9961 -0
- scipy/special/_basic.py +3576 -0
- scipy/special/_comb.cp312-win_arm64.lib +0 -0
- scipy/special/_comb.cp312-win_arm64.pyd +0 -0
- scipy/special/_ellip_harm.py +214 -0
- scipy/special/_ellip_harm_2.cp312-win_arm64.lib +0 -0
- scipy/special/_ellip_harm_2.cp312-win_arm64.pyd +0 -0
- scipy/special/_gufuncs.cp312-win_arm64.lib +0 -0
- scipy/special/_gufuncs.cp312-win_arm64.pyd +0 -0
- scipy/special/_input_validation.py +17 -0
- scipy/special/_lambertw.py +149 -0
- scipy/special/_logsumexp.py +426 -0
- scipy/special/_mptestutils.py +453 -0
- scipy/special/_multiufuncs.py +610 -0
- scipy/special/_orthogonal.py +2592 -0
- scipy/special/_orthogonal.pyi +330 -0
- scipy/special/_precompute/__init__.py +0 -0
- scipy/special/_precompute/cosine_cdf.py +17 -0
- scipy/special/_precompute/expn_asy.py +54 -0
- scipy/special/_precompute/gammainc_asy.py +116 -0
- scipy/special/_precompute/gammainc_data.py +124 -0
- scipy/special/_precompute/hyp2f1_data.py +484 -0
- scipy/special/_precompute/lambertw.py +68 -0
- scipy/special/_precompute/loggamma.py +43 -0
- scipy/special/_precompute/struve_convergence.py +131 -0
- scipy/special/_precompute/utils.py +38 -0
- scipy/special/_precompute/wright_bessel.py +342 -0
- scipy/special/_precompute/wright_bessel_data.py +152 -0
- scipy/special/_precompute/wrightomega.py +41 -0
- scipy/special/_precompute/zetac.py +27 -0
- scipy/special/_sf_error.py +15 -0
- scipy/special/_specfun.cp312-win_arm64.lib +0 -0
- scipy/special/_specfun.cp312-win_arm64.pyd +0 -0
- scipy/special/_special_ufuncs.cp312-win_arm64.lib +0 -0
- scipy/special/_special_ufuncs.cp312-win_arm64.pyd +0 -0
- scipy/special/_spfun_stats.py +106 -0
- scipy/special/_spherical_bessel.py +397 -0
- scipy/special/_support_alternative_backends.py +295 -0
- scipy/special/_test_internal.cp312-win_arm64.lib +0 -0
- scipy/special/_test_internal.cp312-win_arm64.pyd +0 -0
- scipy/special/_test_internal.pyi +9 -0
- scipy/special/_testutils.py +321 -0
- scipy/special/_ufuncs.cp312-win_arm64.lib +0 -0
- scipy/special/_ufuncs.cp312-win_arm64.pyd +0 -0
- scipy/special/_ufuncs.pyi +522 -0
- scipy/special/_ufuncs.pyx +13173 -0
- scipy/special/_ufuncs_cxx.cp312-win_arm64.lib +0 -0
- scipy/special/_ufuncs_cxx.cp312-win_arm64.pyd +0 -0
- scipy/special/_ufuncs_cxx.pxd +142 -0
- scipy/special/_ufuncs_cxx.pyx +427 -0
- scipy/special/_ufuncs_cxx_defs.h +147 -0
- scipy/special/_ufuncs_defs.h +57 -0
- scipy/special/add_newdocs.py +15 -0
- scipy/special/basic.py +87 -0
- scipy/special/cython_special.cp312-win_arm64.lib +0 -0
- scipy/special/cython_special.cp312-win_arm64.pyd +0 -0
- scipy/special/cython_special.pxd +259 -0
- scipy/special/cython_special.pyi +3 -0
- scipy/special/orthogonal.py +45 -0
- scipy/special/sf_error.py +20 -0
- scipy/special/specfun.py +24 -0
- scipy/special/spfun_stats.py +17 -0
- scipy/special/tests/__init__.py +0 -0
- scipy/special/tests/_cython_examples/extending.pyx +12 -0
- scipy/special/tests/_cython_examples/meson.build +34 -0
- scipy/special/tests/data/__init__.py +0 -0
- scipy/special/tests/data/boost.npz +0 -0
- scipy/special/tests/data/gsl.npz +0 -0
- scipy/special/tests/data/local.npz +0 -0
- scipy/special/tests/test_basic.py +4815 -0
- scipy/special/tests/test_bdtr.py +112 -0
- scipy/special/tests/test_boost_ufuncs.py +64 -0
- scipy/special/tests/test_boxcox.py +125 -0
- scipy/special/tests/test_cdflib.py +712 -0
- scipy/special/tests/test_cdft_asymptotic.py +49 -0
- scipy/special/tests/test_cephes_intp_cast.py +29 -0
- scipy/special/tests/test_cosine_distr.py +83 -0
- scipy/special/tests/test_cython_special.py +363 -0
- scipy/special/tests/test_data.py +719 -0
- scipy/special/tests/test_dd.py +42 -0
- scipy/special/tests/test_digamma.py +45 -0
- scipy/special/tests/test_ellip_harm.py +278 -0
- scipy/special/tests/test_erfinv.py +89 -0
- scipy/special/tests/test_exponential_integrals.py +118 -0
- scipy/special/tests/test_extending.py +28 -0
- scipy/special/tests/test_faddeeva.py +85 -0
- scipy/special/tests/test_gamma.py +12 -0
- scipy/special/tests/test_gammainc.py +152 -0
- scipy/special/tests/test_hyp2f1.py +2566 -0
- scipy/special/tests/test_hypergeometric.py +234 -0
- scipy/special/tests/test_iv_ratio.py +249 -0
- scipy/special/tests/test_kolmogorov.py +491 -0
- scipy/special/tests/test_lambertw.py +109 -0
- scipy/special/tests/test_legendre.py +1518 -0
- scipy/special/tests/test_log1mexp.py +85 -0
- scipy/special/tests/test_loggamma.py +70 -0
- scipy/special/tests/test_logit.py +162 -0
- scipy/special/tests/test_logsumexp.py +469 -0
- scipy/special/tests/test_mpmath.py +2293 -0
- scipy/special/tests/test_nan_inputs.py +65 -0
- scipy/special/tests/test_ndtr.py +77 -0
- scipy/special/tests/test_ndtri_exp.py +94 -0
- scipy/special/tests/test_orthogonal.py +821 -0
- scipy/special/tests/test_orthogonal_eval.py +275 -0
- scipy/special/tests/test_owens_t.py +53 -0
- scipy/special/tests/test_pcf.py +24 -0
- scipy/special/tests/test_pdtr.py +48 -0
- scipy/special/tests/test_powm1.py +65 -0
- scipy/special/tests/test_precompute_expn_asy.py +24 -0
- scipy/special/tests/test_precompute_gammainc.py +108 -0
- scipy/special/tests/test_precompute_utils.py +36 -0
- scipy/special/tests/test_round.py +18 -0
- scipy/special/tests/test_sf_error.py +146 -0
- scipy/special/tests/test_sici.py +36 -0
- scipy/special/tests/test_specfun.py +48 -0
- scipy/special/tests/test_spence.py +32 -0
- scipy/special/tests/test_spfun_stats.py +61 -0
- scipy/special/tests/test_sph_harm.py +85 -0
- scipy/special/tests/test_spherical_bessel.py +400 -0
- scipy/special/tests/test_support_alternative_backends.py +248 -0
- scipy/special/tests/test_trig.py +72 -0
- scipy/special/tests/test_ufunc_signatures.py +46 -0
- scipy/special/tests/test_wright_bessel.py +205 -0
- scipy/special/tests/test_wrightomega.py +117 -0
- scipy/special/tests/test_zeta.py +301 -0
- scipy/stats/__init__.py +670 -0
- scipy/stats/_ansari_swilk_statistics.cp312-win_arm64.lib +0 -0
- scipy/stats/_ansari_swilk_statistics.cp312-win_arm64.pyd +0 -0
- scipy/stats/_axis_nan_policy.py +692 -0
- scipy/stats/_biasedurn.cp312-win_arm64.lib +0 -0
- scipy/stats/_biasedurn.cp312-win_arm64.pyd +0 -0
- scipy/stats/_biasedurn.pxd +27 -0
- scipy/stats/_binned_statistic.py +795 -0
- scipy/stats/_binomtest.py +375 -0
- scipy/stats/_bws_test.py +177 -0
- scipy/stats/_censored_data.py +459 -0
- scipy/stats/_common.py +5 -0
- scipy/stats/_constants.py +42 -0
- scipy/stats/_continued_fraction.py +387 -0
- scipy/stats/_continuous_distns.py +12486 -0
- scipy/stats/_correlation.py +210 -0
- scipy/stats/_covariance.py +636 -0
- scipy/stats/_crosstab.py +204 -0
- scipy/stats/_discrete_distns.py +2098 -0
- scipy/stats/_distn_infrastructure.py +4201 -0
- scipy/stats/_distr_params.py +299 -0
- scipy/stats/_distribution_infrastructure.py +5750 -0
- scipy/stats/_entropy.py +428 -0
- scipy/stats/_finite_differences.py +145 -0
- scipy/stats/_fit.py +1351 -0
- scipy/stats/_hypotests.py +2060 -0
- scipy/stats/_kde.py +732 -0
- scipy/stats/_ksstats.py +600 -0
- scipy/stats/_levy_stable/__init__.py +1231 -0
- scipy/stats/_levy_stable/levyst.cp312-win_arm64.lib +0 -0
- scipy/stats/_levy_stable/levyst.cp312-win_arm64.pyd +0 -0
- scipy/stats/_mannwhitneyu.py +492 -0
- scipy/stats/_mgc.py +550 -0
- scipy/stats/_morestats.py +4626 -0
- scipy/stats/_mstats_basic.py +3658 -0
- scipy/stats/_mstats_extras.py +521 -0
- scipy/stats/_multicomp.py +449 -0
- scipy/stats/_multivariate.py +7281 -0
- scipy/stats/_new_distributions.py +452 -0
- scipy/stats/_odds_ratio.py +466 -0
- scipy/stats/_page_trend_test.py +486 -0
- scipy/stats/_probability_distribution.py +1964 -0
- scipy/stats/_qmc.py +2956 -0
- scipy/stats/_qmc_cy.cp312-win_arm64.lib +0 -0
- scipy/stats/_qmc_cy.cp312-win_arm64.pyd +0 -0
- scipy/stats/_qmc_cy.pyi +54 -0
- scipy/stats/_qmvnt.py +454 -0
- scipy/stats/_qmvnt_cy.cp312-win_arm64.lib +0 -0
- scipy/stats/_qmvnt_cy.cp312-win_arm64.pyd +0 -0
- scipy/stats/_quantile.py +335 -0
- scipy/stats/_rcont/__init__.py +4 -0
- scipy/stats/_rcont/rcont.cp312-win_arm64.lib +0 -0
- scipy/stats/_rcont/rcont.cp312-win_arm64.pyd +0 -0
- scipy/stats/_relative_risk.py +263 -0
- scipy/stats/_resampling.py +2352 -0
- scipy/stats/_result_classes.py +40 -0
- scipy/stats/_sampling.py +1314 -0
- scipy/stats/_sensitivity_analysis.py +713 -0
- scipy/stats/_sobol.cp312-win_arm64.lib +0 -0
- scipy/stats/_sobol.cp312-win_arm64.pyd +0 -0
- scipy/stats/_sobol.pyi +54 -0
- scipy/stats/_sobol_direction_numbers.npz +0 -0
- scipy/stats/_stats.cp312-win_arm64.lib +0 -0
- scipy/stats/_stats.cp312-win_arm64.pyd +0 -0
- scipy/stats/_stats.pxd +10 -0
- scipy/stats/_stats_mstats_common.py +322 -0
- scipy/stats/_stats_py.py +11089 -0
- scipy/stats/_stats_pythran.cp312-win_arm64.lib +0 -0
- scipy/stats/_stats_pythran.cp312-win_arm64.pyd +0 -0
- scipy/stats/_survival.py +683 -0
- scipy/stats/_tukeylambda_stats.py +199 -0
- scipy/stats/_unuran/__init__.py +0 -0
- scipy/stats/_unuran/unuran_wrapper.cp312-win_arm64.lib +0 -0
- scipy/stats/_unuran/unuran_wrapper.cp312-win_arm64.pyd +0 -0
- scipy/stats/_unuran/unuran_wrapper.pyi +179 -0
- scipy/stats/_variation.py +126 -0
- scipy/stats/_warnings_errors.py +38 -0
- scipy/stats/_wilcoxon.py +265 -0
- scipy/stats/biasedurn.py +16 -0
- scipy/stats/contingency.py +521 -0
- scipy/stats/distributions.py +24 -0
- scipy/stats/kde.py +18 -0
- scipy/stats/morestats.py +27 -0
- scipy/stats/mstats.py +140 -0
- scipy/stats/mstats_basic.py +42 -0
- scipy/stats/mstats_extras.py +25 -0
- scipy/stats/mvn.py +17 -0
- scipy/stats/qmc.py +236 -0
- scipy/stats/sampling.py +73 -0
- scipy/stats/stats.py +41 -0
- scipy/stats/tests/__init__.py +0 -0
- scipy/stats/tests/common_tests.py +356 -0
- scipy/stats/tests/data/_mvt.py +171 -0
- scipy/stats/tests/data/fisher_exact_results_from_r.py +607 -0
- scipy/stats/tests/data/jf_skew_t_gamlss_pdf_data.npy +0 -0
- scipy/stats/tests/data/levy_stable/stable-Z1-cdf-sample-data.npy +0 -0
- scipy/stats/tests/data/levy_stable/stable-Z1-pdf-sample-data.npy +0 -0
- scipy/stats/tests/data/levy_stable/stable-loc-scale-sample-data.npy +0 -0
- scipy/stats/tests/data/nist_anova/AtmWtAg.dat +108 -0
- scipy/stats/tests/data/nist_anova/SiRstv.dat +85 -0
- scipy/stats/tests/data/nist_anova/SmLs01.dat +249 -0
- scipy/stats/tests/data/nist_anova/SmLs02.dat +1869 -0
- scipy/stats/tests/data/nist_anova/SmLs03.dat +18069 -0
- scipy/stats/tests/data/nist_anova/SmLs04.dat +249 -0
- scipy/stats/tests/data/nist_anova/SmLs05.dat +1869 -0
- scipy/stats/tests/data/nist_anova/SmLs06.dat +18069 -0
- scipy/stats/tests/data/nist_anova/SmLs07.dat +249 -0
- scipy/stats/tests/data/nist_anova/SmLs08.dat +1869 -0
- scipy/stats/tests/data/nist_anova/SmLs09.dat +18069 -0
- scipy/stats/tests/data/nist_linregress/Norris.dat +97 -0
- scipy/stats/tests/data/rel_breitwigner_pdf_sample_data_ROOT.npy +0 -0
- scipy/stats/tests/data/studentized_range_mpmath_ref.json +1499 -0
- scipy/stats/tests/test_axis_nan_policy.py +1388 -0
- scipy/stats/tests/test_binned_statistic.py +568 -0
- scipy/stats/tests/test_censored_data.py +152 -0
- scipy/stats/tests/test_contingency.py +294 -0
- scipy/stats/tests/test_continued_fraction.py +173 -0
- scipy/stats/tests/test_continuous.py +2198 -0
- scipy/stats/tests/test_continuous_basic.py +1053 -0
- scipy/stats/tests/test_continuous_fit_censored.py +683 -0
- scipy/stats/tests/test_correlation.py +80 -0
- scipy/stats/tests/test_crosstab.py +115 -0
- scipy/stats/tests/test_discrete_basic.py +580 -0
- scipy/stats/tests/test_discrete_distns.py +700 -0
- scipy/stats/tests/test_distributions.py +10413 -0
- scipy/stats/tests/test_entropy.py +322 -0
- scipy/stats/tests/test_fast_gen_inversion.py +435 -0
- scipy/stats/tests/test_fit.py +1090 -0
- scipy/stats/tests/test_hypotests.py +1991 -0
- scipy/stats/tests/test_kdeoth.py +676 -0
- scipy/stats/tests/test_marray.py +289 -0
- scipy/stats/tests/test_mgc.py +217 -0
- scipy/stats/tests/test_morestats.py +3259 -0
- scipy/stats/tests/test_mstats_basic.py +2071 -0
- scipy/stats/tests/test_mstats_extras.py +172 -0
- scipy/stats/tests/test_multicomp.py +405 -0
- scipy/stats/tests/test_multivariate.py +4381 -0
- scipy/stats/tests/test_odds_ratio.py +148 -0
- scipy/stats/tests/test_qmc.py +1492 -0
- scipy/stats/tests/test_quantile.py +199 -0
- scipy/stats/tests/test_rank.py +345 -0
- scipy/stats/tests/test_relative_risk.py +95 -0
- scipy/stats/tests/test_resampling.py +2000 -0
- scipy/stats/tests/test_sampling.py +1450 -0
- scipy/stats/tests/test_sensitivity_analysis.py +310 -0
- scipy/stats/tests/test_stats.py +9707 -0
- scipy/stats/tests/test_survival.py +466 -0
- scipy/stats/tests/test_tukeylambda_stats.py +85 -0
- scipy/stats/tests/test_variation.py +216 -0
- scipy/version.py +12 -0
- scipy-1.16.2.dist-info/DELVEWHEEL +2 -0
- scipy-1.16.2.dist-info/LICENSE.txt +912 -0
- scipy-1.16.2.dist-info/METADATA +1061 -0
- scipy-1.16.2.dist-info/RECORD +1530 -0
- scipy-1.16.2.dist-info/WHEEL +4 -0
- scipy.libs/msvcp140-5f1c5dd31916990d94181e07bc3afb32.dll +0 -0
- scipy.libs/scipy_openblas-f3ac85b1f412f7e86514c923dc4058d1.dll +0 -0
@@ -0,0 +1,2228 @@
|
|
1
|
+
"""Implementation of an FFT-based Short-time Fourier Transform. """
|
2
|
+
|
3
|
+
# Implementation Notes for this file (as of 2025-08)
|
4
|
+
# --------------------------------------------------
|
5
|
+
# * Since the method `stft` and `istft` have identical names as the legacy
|
6
|
+
# functions in the signal module, referencing them as HTML link in the
|
7
|
+
# docstrings has to be done by an explicit `~ShortTimeFFT.stft` instead of an
|
8
|
+
# ambiguous `stft` (The ``~`` hides the class / module name).
|
9
|
+
# * The HTML documentation currently renders each method/property on a separate
|
10
|
+
# page without reference to the parent class. Thus, a link to `ShortTimeFFT`
|
11
|
+
# was added to the "See Also" section of each method/property. These links
|
12
|
+
# can be removed, when SciPy updates ``pydata-sphinx-theme`` to >= 0.13.3
|
13
|
+
# (currently 0.9). Consult Issue 18512 and PR 16660 for further details.
|
14
|
+
|
15
|
+
|
16
|
+
# Linter does not allow to import ``Generator`` from ``typing`` module:
|
17
|
+
from collections.abc import Generator, Callable
|
18
|
+
from functools import partial, cached_property
|
19
|
+
from typing import get_args, Literal
|
20
|
+
|
21
|
+
import numpy as np
|
22
|
+
|
23
|
+
import scipy.fft as fft_lib
|
24
|
+
from scipy.signal._signaltools import detrend
|
25
|
+
from scipy.signal.windows import get_window
|
26
|
+
|
27
|
+
__all__ = ['closest_STFT_dual_window', 'ShortTimeFFT']
|
28
|
+
|
29
|
+
|
30
|
+
#: Allowed values for parameter `padding` of method `ShortTimeFFT.stft()`:
|
31
|
+
PAD_TYPE = Literal['zeros', 'edge', 'even', 'odd']
|
32
|
+
|
33
|
+
#: Allowed values for property `ShortTimeFFT.fft_mode`:
|
34
|
+
FFT_MODE_TYPE = Literal['twosided', 'centered', 'onesided', 'onesided2X']
|
35
|
+
|
36
|
+
|
37
|
+
def _calc_dual_canonical_window(win: np.ndarray, hop: int) -> np.ndarray:
|
38
|
+
"""Calculate canonical dual window for 1d window `win` and a time step
|
39
|
+
of `hop` samples.
|
40
|
+
|
41
|
+
A ``ValueError`` is raised if the inversion fails.
|
42
|
+
|
43
|
+
This is a separate function not a method, since it is also used in the
|
44
|
+
class method ``ShortTimeFFT.from_dual()``.
|
45
|
+
"""
|
46
|
+
if hop > len(win):
|
47
|
+
raise ValueError(f"{hop=} is larger than window length of {len(win)}" +
|
48
|
+
" => STFT not invertible!")
|
49
|
+
if issubclass(win.dtype.type, np.integer):
|
50
|
+
raise ValueError("Parameter 'win' cannot be of integer type, but " +
|
51
|
+
f"{win.dtype=} => STFT not invertible!")
|
52
|
+
# The calculation of `relative_resolution` does not work for ints.
|
53
|
+
# Furthermore, `win / DD` casts the integers away, thus an implicit
|
54
|
+
# cast is avoided, which can always cause confusion when using 32-Bit
|
55
|
+
# floats.
|
56
|
+
|
57
|
+
w2 = win.real**2 + win.imag**2 # win*win.conj() does not ensure w2 is real
|
58
|
+
DD = w2.copy()
|
59
|
+
for k_ in range(hop, len(win), hop):
|
60
|
+
DD[k_:] += w2[:-k_]
|
61
|
+
DD[:-k_] += w2[k_:]
|
62
|
+
|
63
|
+
# check DD > 0:
|
64
|
+
relative_resolution = np.finfo(win.dtype).resolution * max(DD)
|
65
|
+
if not np.all(DD >= relative_resolution):
|
66
|
+
raise ValueError("Short-time Fourier Transform not invertible!")
|
67
|
+
|
68
|
+
return win / DD
|
69
|
+
|
70
|
+
|
71
|
+
def closest_STFT_dual_window(win: np.ndarray, hop: int,
|
72
|
+
desired_dual: np.ndarray | None = None, *,
|
73
|
+
scaled: bool = True) \
|
74
|
+
-> tuple[np.ndarray, float | complex]:
|
75
|
+
r"""Calculate the STFT dual window of a given window closest to a desired dual
|
76
|
+
window.
|
77
|
+
|
78
|
+
For a given short-time Fourier transform window `win` incremented by `hop`
|
79
|
+
samples, the dual window is calculated, which minimizes
|
80
|
+
``abs(dual_win - desired_dual)**2`` when `scaled` is ``False``. For `scaled`
|
81
|
+
set to ``True``, ``abs(alpha*dual_win - desired_dual)**2`` is minimized with
|
82
|
+
`alpha` being the optimal scaling factor.
|
83
|
+
A ``ValueError`` is raised if no valid dual window can be determined.
|
84
|
+
|
85
|
+
|
86
|
+
Parameters
|
87
|
+
----------
|
88
|
+
win : np.ndarray
|
89
|
+
The window must be a real- or complex-valued 1d array.
|
90
|
+
hop : int
|
91
|
+
The increment in samples by which the window is shifted in each step.
|
92
|
+
desired_dual: np.ndarray | None
|
93
|
+
The desired dual window must be a 1d array of the same length as `win`.
|
94
|
+
If set to ``None`` (default), then `desired_dual` is assumed to be the
|
95
|
+
rectangular window, i.e., ``np.ones_like(win)``.
|
96
|
+
scaled : bool
|
97
|
+
If set (default), the closest scaled version instead of closest dual window
|
98
|
+
is calculated.
|
99
|
+
|
100
|
+
Returns
|
101
|
+
-------
|
102
|
+
dual_win : np.ndarray
|
103
|
+
A dual window of ``alpha*win`` (with hop interval `hop`), which is closest
|
104
|
+
to `desired_dual`. Note that the dual window of `win` is `dual_win/alpha`
|
105
|
+
and that the dual window of `dual_win` is `alpha*win`.
|
106
|
+
`dual_win` has the same shape as `win` and `desired_win`.
|
107
|
+
alpha : float | complex
|
108
|
+
Scale factor for `win`. It is always one if `scaled` is set to ``False``.
|
109
|
+
|
110
|
+
Notes
|
111
|
+
-----
|
112
|
+
For a given window and `hop` interval, all possible dual windows are expressed
|
113
|
+
by the `hop` linear conditions of Eq. :math:numref:`eq_STFT_AllDualWinsCond` in
|
114
|
+
the :ref:`tutorial_stft` section of the :ref:`user_guide`. Hence, decreasing
|
115
|
+
`hop`, increases the number of degrees of freedom of the set of all possible
|
116
|
+
dual windows, improving the ability to better approximate a `desired_dual`.
|
117
|
+
|
118
|
+
This function can also be used to determine windows which fulfill the
|
119
|
+
so-called "Constant OverLap Add" (COLA) condition [1]_. It states that summing
|
120
|
+
all touching window values at any given sample position results in the same
|
121
|
+
constant :math:`\alpha`. Eq. :math:numref:`eq_STFT_AllDualWinsCond` shows that
|
122
|
+
this is equal to having a rectangular dual window, i.e., the dual being
|
123
|
+
``alpha*np.ones(m)``.
|
124
|
+
|
125
|
+
Some examples of windows that satisfy COLA (taken from [2]_):
|
126
|
+
|
127
|
+
- Rectangular window at overlap of 0, 1/2, 2/3, 3/4, ...
|
128
|
+
- Bartlett window at overlap of 1/2, 3/4, 5/6, ...
|
129
|
+
- Hann window at 1/2, 2/3, 3/4, ...
|
130
|
+
- Any Blackman family window at 2/3 overlap
|
131
|
+
- Any window with ``hop=1``
|
132
|
+
|
133
|
+
References
|
134
|
+
----------
|
135
|
+
.. [1] Julius O. Smith III, "Spectral Audio Signal Processing",
|
136
|
+
online book, 2011, https://www.dsprelated.com/freebooks/sasp/
|
137
|
+
.. [2] G. Heinzel, A. Ruediger and R. Schilling, "Spectrum and spectral density
|
138
|
+
estimation by the Discrete Fourier transform (DFT), including a
|
139
|
+
comprehensive list of window functions and some new at-top windows",
|
140
|
+
2002, http://hdl.handle.net/11858/00-001M-0000-0013-557A-5
|
141
|
+
|
142
|
+
Examples
|
143
|
+
--------
|
144
|
+
Let's show that a Bartlett window with 75% overlap fulfills the COLA condition:
|
145
|
+
|
146
|
+
>>> import matplotlib.pyplot as plt
|
147
|
+
>>> import numpy as np
|
148
|
+
>>> from scipy.signal import closest_STFT_dual_window, windows
|
149
|
+
...
|
150
|
+
>>> m = 24
|
151
|
+
>>> win, w_rect = windows.bartlett(m, sym=False), np.ones(m)
|
152
|
+
>>> d_win, alpha = closest_STFT_dual_window(win, m//4, w_rect, scaled=True)
|
153
|
+
>>> print(f"COLA: {np.allclose(d_win, w_rect*alpha)}, {alpha = :g}")
|
154
|
+
COLA: True, alpha = 0.5
|
155
|
+
|
156
|
+
We can also determine for which hop intervals the COLA condition is fulfilled:
|
157
|
+
|
158
|
+
>>> hops, deviations, alphas = np.arange(1, 16, dtype=int), [], []
|
159
|
+
>>> for h_ in hops:
|
160
|
+
... w_cola, alpha = closest_STFT_dual_window(w_rect, h_, win, scaled=True)
|
161
|
+
... deviations.append(np.linalg.norm(w_cola - win*alpha))
|
162
|
+
... alphas.append(alpha)
|
163
|
+
...
|
164
|
+
>>> fg0, (ax0, ax1) = plt.subplots(2, 1, sharex='all', tight_layout=True)
|
165
|
+
>>> ax0.set_title(f"COLA Window closest to a {m}-Sample Bartlett Window")
|
166
|
+
>>> ax0.set(ylabel=r"$||w_\text{cola}-\alpha w||$", xlim=(0, hops[-1]-.5))
|
167
|
+
>>> ax1.set(xlabel="Hop Interval", ylabel=r"Scaling factor $\alpha$",
|
168
|
+
... ylim=(0, 1.25))
|
169
|
+
>>> ax0.plot(hops, deviations, 'C0.-')
|
170
|
+
>>> ax1.plot(hops, alphas, 'C1.-')
|
171
|
+
>>> for ax_ in (ax0, ax1):
|
172
|
+
... ax_.grid()
|
173
|
+
>>> plt.show()
|
174
|
+
|
175
|
+
The lower plot shows the calculated scaling factor :math:`\alpha` for different
|
176
|
+
`hops` whereas the upper displays the :math:`L^2`-norm of the difference
|
177
|
+
between the scaled Bartlett window and the calculated window. Since for `hops`
|
178
|
+
1 to 4 as well as for 6 and 12 the :math:`L^2`-norm of the difference is
|
179
|
+
practically zero, the COLA condition is fulfilled for those.
|
180
|
+
|
181
|
+
See Also
|
182
|
+
--------
|
183
|
+
ShortTimeFFT: Short-time Fourier transform which is able to utilize a dual
|
184
|
+
window for calculating the inverse.
|
185
|
+
ShortTimeFFT.from_win_equals_dual: Create instance where the window and its
|
186
|
+
dual are equal.
|
187
|
+
|
188
|
+
"""
|
189
|
+
if desired_dual is None: # default is rectangular window
|
190
|
+
desired_dual = np.ones_like(win)
|
191
|
+
if not (win.ndim == 1 and win.shape == desired_dual.shape):
|
192
|
+
raise ValueError("Parameters `win` and `desired_dual` are not 1d arrays of " +
|
193
|
+
f"equal length ({win.shape=}, {desired_dual.shape=})!")
|
194
|
+
if not all(np.isfinite(win)):
|
195
|
+
raise ValueError("Parameter win must have finite entries!")
|
196
|
+
if not all(np.isfinite(desired_dual)):
|
197
|
+
raise ValueError("Parameter desired_dual must have finite entries!")
|
198
|
+
if not (1 <= hop <= len(win) and isinstance(hop, int | np.integer)):
|
199
|
+
raise ValueError(f"Parameter {hop=} is not an integer between 1 and " +
|
200
|
+
f"{len(win)=}!")
|
201
|
+
|
202
|
+
w_d = _calc_dual_canonical_window(win, hop)
|
203
|
+
wdd = win.conjugate() * desired_dual
|
204
|
+
q_d = wdd.copy()
|
205
|
+
for k_ in range(hop, len(win), hop):
|
206
|
+
q_d[k_:] += wdd[:-k_]
|
207
|
+
q_d[:-k_] += wdd[k_:]
|
208
|
+
q_d = w_d * q_d
|
209
|
+
|
210
|
+
if not scaled:
|
211
|
+
return w_d + desired_dual - q_d, 1.
|
212
|
+
|
213
|
+
numerator = q_d.conjugate().T @ w_d
|
214
|
+
denominator = q_d.T.real @ q_d.real + q_d.T.imag @ q_d.imag # always >= 0
|
215
|
+
if not (abs(numerator) > 0 and denominator > np.finfo(w_d.dtype).resolution):
|
216
|
+
raise ValueError(
|
217
|
+
"Unable to calculate scaled closest dual window due to numerically " +
|
218
|
+
"unstable scaling factor! Try setting parameter `scaled` to False.")
|
219
|
+
alpha = numerator / denominator
|
220
|
+
return w_d + alpha * (desired_dual - q_d), alpha
|
221
|
+
|
222
|
+
|
223
|
+
# noinspection PyShadowingNames
|
224
|
+
class ShortTimeFFT:
|
225
|
+
r"""Provide a parametrized discrete Short-time Fourier transform (stft)
|
226
|
+
and its inverse (istft).
|
227
|
+
|
228
|
+
.. currentmodule:: scipy.signal.ShortTimeFFT
|
229
|
+
|
230
|
+
The `~ShortTimeFFT.stft` calculates sequential FFTs by sliding a
|
231
|
+
window (`win`) over an input signal by `hop` increments. It can be used to
|
232
|
+
quantify the change of the spectrum over time.
|
233
|
+
|
234
|
+
The `~ShortTimeFFT.stft` is represented by a complex-valued matrix S[q,p]
|
235
|
+
where the p-th column represents an FFT with the window centered at the
|
236
|
+
time t[p] = p * `delta_t` = p * `hop` * `T` where `T` is the sampling
|
237
|
+
interval of the input signal. The q-th row represents the values at the
|
238
|
+
frequency f[q] = q * `delta_f` with `delta_f` = 1 / (`mfft` * `T`) being
|
239
|
+
the bin width of the FFT.
|
240
|
+
|
241
|
+
The inverse STFT `~ShortTimeFFT.istft` is calculated by reversing the steps
|
242
|
+
of the STFT: Take the IFFT of the p-th slice of S[q,p] and multiply the
|
243
|
+
result with the so-called dual window (see `dual_win`). Shift the result by
|
244
|
+
p * `delta_t` and add the result to previous shifted results to reconstruct
|
245
|
+
the signal. If only the dual window is known and the STFT is invertible,
|
246
|
+
`from_dual` can be used to instantiate this class.
|
247
|
+
|
248
|
+
By default, the so-called canonical dual window is used. It is the window with
|
249
|
+
minimal energy among all possible dual windows. `from_win_equals_dual` and
|
250
|
+
`~scipy.signal.closest_STFT_dual_window` provide means for utilizing alterantive
|
251
|
+
dual windows. Note that `win` is also always a dual window of `dual_win`.
|
252
|
+
|
253
|
+
Due to the convention of time t = 0 being at the first sample of the input
|
254
|
+
signal, the STFT values typically have negative time slots. Hence,
|
255
|
+
negative indexes like `p_min` or `k_min` do not indicate counting
|
256
|
+
backwards from an array's end like in standard Python indexing but being
|
257
|
+
left of t = 0.
|
258
|
+
|
259
|
+
More detailed information can be found in the :ref:`tutorial_stft`
|
260
|
+
section of the :ref:`user_guide`.
|
261
|
+
|
262
|
+
Note that all parameters of the initializer, except `scale_to` (which uses
|
263
|
+
`scaling`) have identical named attributes.
|
264
|
+
|
265
|
+
Parameters
|
266
|
+
----------
|
267
|
+
win : np.ndarray
|
268
|
+
The window must be a real- or complex-valued 1d array.
|
269
|
+
hop : int
|
270
|
+
The increment in samples, by which the window is shifted in each step.
|
271
|
+
fs : float
|
272
|
+
Sampling frequency of input signal and window. Its relation to the
|
273
|
+
sampling interval `T` is ``T = 1 / fs``.
|
274
|
+
fft_mode : 'twosided', 'centered', 'onesided', 'onesided2X'
|
275
|
+
Mode of FFT to be used (default 'onesided').
|
276
|
+
See property `fft_mode` for details.
|
277
|
+
mfft: int | None
|
278
|
+
Length of the FFT used, if a zero padded FFT is desired.
|
279
|
+
If ``None`` (default), the length of the window `win` is used.
|
280
|
+
dual_win : np.ndarray | None
|
281
|
+
The dual window of `win`. If set to ``None``, it is calculated if
|
282
|
+
needed.
|
283
|
+
scale_to : 'magnitude', 'psd' | None
|
284
|
+
If not ``None`` (default) the window function is scaled, so each STFT
|
285
|
+
column represents either a 'magnitude' or a power spectral density
|
286
|
+
('psd') spectrum. This parameter sets the property `scaling` to the
|
287
|
+
same value. See method `scale_to` for details.
|
288
|
+
phase_shift : int | None
|
289
|
+
If set, add a linear phase `phase_shift` / `mfft` * `f` to each
|
290
|
+
frequency `f`. The default value of 0 ensures that there is no phase shift
|
291
|
+
on the zeroth slice (in which t=0 is centered). See property
|
292
|
+
`phase_shift` for more details.
|
293
|
+
|
294
|
+
Notes
|
295
|
+
-----
|
296
|
+
A typical STFT application is the creation of various types of time-frequency
|
297
|
+
plots, often subsumed under the term "spectrogram". Note that this term is also
|
298
|
+
used to explecitly refer to the absolute square of a STFT [11]_, as done in
|
299
|
+
:meth:`spectrogram`.
|
300
|
+
|
301
|
+
The STFT can also be used for filtering and filter banks as discussed in [12]_.
|
302
|
+
|
303
|
+
|
304
|
+
References
|
305
|
+
----------
|
306
|
+
.. [11] Karlheinz Gröchenig: "Foundations of Time-Frequency Analysis",
|
307
|
+
Birkhäuser Boston 2001, `10.1007/978-1-4612-0003-1`
|
308
|
+
.. [12] Julius O. Smith III, "Spectral Audio Signal Processing", online book, 2011,
|
309
|
+
https://www.dsprelated.com/freebooks/sasp/
|
310
|
+
|
311
|
+
|
312
|
+
Examples
|
313
|
+
--------
|
314
|
+
The following example shows the magnitude of the STFT of a sine with
|
315
|
+
varying frequency :math:`f_i(t)` (marked by a red dashed line in the plot):
|
316
|
+
|
317
|
+
>>> import numpy as np
|
318
|
+
>>> import matplotlib.pyplot as plt
|
319
|
+
>>> from scipy.signal import ShortTimeFFT
|
320
|
+
>>> from scipy.signal.windows import gaussian
|
321
|
+
...
|
322
|
+
>>> T_x, N = 1 / 20, 1000 # 20 Hz sampling rate for 50 s signal
|
323
|
+
>>> t_x = np.arange(N) * T_x # time indexes for signal
|
324
|
+
>>> f_i = 1 * np.arctan((t_x - t_x[N // 2]) / 2) + 5 # varying frequency
|
325
|
+
>>> x = np.sin(2*np.pi*np.cumsum(f_i)*T_x) # the signal
|
326
|
+
|
327
|
+
The utilized Gaussian window is 50 samples or 2.5 s long. The parameter
|
328
|
+
``mfft=200`` in `ShortTimeFFT` causes the spectrum to be oversampled
|
329
|
+
by a factor of 4:
|
330
|
+
|
331
|
+
>>> g_std = 8 # standard deviation for Gaussian window in samples
|
332
|
+
>>> w = gaussian(50, std=g_std, sym=True) # symmetric Gaussian window
|
333
|
+
>>> SFT = ShortTimeFFT(w, hop=10, fs=1/T_x, mfft=200, scale_to='magnitude')
|
334
|
+
>>> Sx = SFT.stft(x) # perform the STFT
|
335
|
+
|
336
|
+
In the plot, the time extent of the signal `x` is marked by vertical dashed
|
337
|
+
lines. Note that the SFT produces values outside the time range of `x`. The
|
338
|
+
shaded areas on the left and the right indicate border effects caused
|
339
|
+
by the window slices in that area not fully being inside time range of
|
340
|
+
`x`:
|
341
|
+
|
342
|
+
>>> fig1, ax1 = plt.subplots(figsize=(6., 4.)) # enlarge plot a bit
|
343
|
+
>>> t_lo, t_hi = SFT.extent(N)[:2] # time range of plot
|
344
|
+
>>> ax1.set_title(rf"STFT ({SFT.m_num*SFT.T:g}$\,s$ Gaussian window, " +
|
345
|
+
... rf"$\sigma_t={g_std*SFT.T}\,$s)")
|
346
|
+
>>> ax1.set(xlabel=f"Time $t$ in seconds ({SFT.p_num(N)} slices, " +
|
347
|
+
... rf"$\Delta t = {SFT.delta_t:g}\,$s)",
|
348
|
+
... ylabel=f"Freq. $f$ in Hz ({SFT.f_pts} bins, " +
|
349
|
+
... rf"$\Delta f = {SFT.delta_f:g}\,$Hz)",
|
350
|
+
... xlim=(t_lo, t_hi))
|
351
|
+
...
|
352
|
+
>>> im1 = ax1.imshow(abs(Sx), origin='lower', aspect='auto',
|
353
|
+
... extent=SFT.extent(N), cmap='viridis')
|
354
|
+
>>> ax1.plot(t_x, f_i, 'r--', alpha=.5, label='$f_i(t)$')
|
355
|
+
>>> fig1.colorbar(im1, label="Magnitude $|S_x(t, f)|$")
|
356
|
+
...
|
357
|
+
>>> # Shade areas where window slices stick out to the side:
|
358
|
+
>>> for t0_, t1_ in [(t_lo, SFT.lower_border_end[0] * SFT.T),
|
359
|
+
... (SFT.upper_border_begin(N)[0] * SFT.T, t_hi)]:
|
360
|
+
... ax1.axvspan(t0_, t1_, color='w', linewidth=0, alpha=.2)
|
361
|
+
>>> for t_ in [0, N * SFT.T]: # mark signal borders with vertical line:
|
362
|
+
... ax1.axvline(t_, color='y', linestyle='--', alpha=0.5)
|
363
|
+
>>> ax1.legend()
|
364
|
+
>>> fig1.tight_layout()
|
365
|
+
>>> plt.show()
|
366
|
+
|
367
|
+
Reconstructing the signal with the `~ShortTimeFFT.istft` is
|
368
|
+
straightforward, but note that the length of `x1` should be specified,
|
369
|
+
since the STFT length increases in `hop` steps:
|
370
|
+
|
371
|
+
>>> SFT.invertible # check if invertible
|
372
|
+
True
|
373
|
+
>>> x1 = SFT.istft(Sx, k1=N)
|
374
|
+
>>> np.allclose(x, x1)
|
375
|
+
True
|
376
|
+
|
377
|
+
It is possible to calculate the STFT of signal parts:
|
378
|
+
|
379
|
+
>>> N2 = SFT.nearest_k_p(N // 2)
|
380
|
+
>>> Sx0 = SFT.stft(x[:N2])
|
381
|
+
>>> Sx1 = SFT.stft(x[N2:])
|
382
|
+
|
383
|
+
When assembling sequential STFT parts together, the overlap needs to be
|
384
|
+
considered:
|
385
|
+
|
386
|
+
>>> p0_ub = SFT.upper_border_begin(N2)[1] - SFT.p_min
|
387
|
+
>>> p1_le = SFT.lower_border_end[1] - SFT.p_min
|
388
|
+
>>> Sx01 = np.hstack((Sx0[:, :p0_ub],
|
389
|
+
... Sx0[:, p0_ub:] + Sx1[:, :p1_le],
|
390
|
+
... Sx1[:, p1_le:]))
|
391
|
+
>>> np.allclose(Sx01, Sx) # Compare with SFT of complete signal
|
392
|
+
True
|
393
|
+
|
394
|
+
It is also possible to calculate the `itsft` for signal parts:
|
395
|
+
|
396
|
+
>>> y_p = SFT.istft(Sx, N//3, N//2)
|
397
|
+
>>> np.allclose(y_p, x[N//3:N//2])
|
398
|
+
True
|
399
|
+
|
400
|
+
"""
|
401
|
+
# immutable attributes (only have getters but no setters):
|
402
|
+
_win: np.ndarray # window
|
403
|
+
_dual_win: np.ndarray | None = None # canonical dual window
|
404
|
+
_hop: int # Step of STFT in number of samples
|
405
|
+
|
406
|
+
# mutable attributes:
|
407
|
+
_fs: float # sampling frequency of input signal and window
|
408
|
+
_fft_mode: FFT_MODE_TYPE = 'onesided' # Mode of FFT to use
|
409
|
+
_mfft: int # length of FFT used - defaults to len(win)
|
410
|
+
_scaling: Literal['magnitude', 'psd', 'unitary'] | None = None # Scaling of _win
|
411
|
+
_phase_shift: int | None # amount to shift phase of FFT in samples
|
412
|
+
|
413
|
+
# attributes for caching calculated values:
|
414
|
+
_fac_mag: float | None = None
|
415
|
+
_fac_psd: float | None = None
|
416
|
+
_lower_border_end: tuple[int, int] | None = None
|
417
|
+
# The following tuples store parameter(s) and return value(s) of methods for caching
|
418
|
+
# (initialized with invalid parameters; should only be accessed by atomic
|
419
|
+
# read/writes to alleviate potential multithreading issues):
|
420
|
+
_cache_post_padding: tuple[int, tuple[int, int]] = -1, (0, 0)
|
421
|
+
_cache_upper_border_begin: tuple[int, tuple[int, int]] = -1, (0, 0)
|
422
|
+
_cache_t: tuple[tuple[int, int | None, int | None, int, float], np.ndarray] = \
|
423
|
+
(-1, None, None, 0, 0.), np.ndarray([])
|
424
|
+
_cache_f: tuple[tuple[FFT_MODE_TYPE, int, float], np.ndarray] = \
|
425
|
+
('onesided', -1, 1.), np.ndarray([])
|
426
|
+
|
427
|
+
def __init__(self, win: np.ndarray, hop: int, fs: float, *,
|
428
|
+
fft_mode: FFT_MODE_TYPE = 'onesided',
|
429
|
+
mfft: int | None = None,
|
430
|
+
dual_win: np.ndarray | None = None,
|
431
|
+
scale_to: Literal['magnitude', 'psd'] | None = None,
|
432
|
+
phase_shift: int | None = 0):
|
433
|
+
if not (win.ndim == 1 and win.size > 0):
|
434
|
+
raise ValueError(f"Parameter win must be 1d, but {win.shape=}!")
|
435
|
+
if not all(np.isfinite(win)):
|
436
|
+
raise ValueError("Parameter win must have finite entries!")
|
437
|
+
if not (hop >= 1 and isinstance(hop, int | np.integer)):
|
438
|
+
raise ValueError(f"Parameter {hop=} is not an integer >= 1!")
|
439
|
+
|
440
|
+
self._win, self._hop, self.fs = win, hop, fs
|
441
|
+
self.win.setflags(write=False)
|
442
|
+
self.mfft = len(win) if mfft is None else mfft
|
443
|
+
|
444
|
+
if dual_win is not None:
|
445
|
+
if dual_win.shape != win.shape:
|
446
|
+
raise ValueError(f"{dual_win.shape=} must equal {win.shape=}!")
|
447
|
+
if not all(np.isfinite(dual_win)):
|
448
|
+
raise ValueError("Parameter dual_win must be a finite array!")
|
449
|
+
dual_win.setflags(write=False)
|
450
|
+
self._dual_win = dual_win # needs to be set before scaling
|
451
|
+
|
452
|
+
if scale_to is not None: # needs to be set before fft_mode
|
453
|
+
self.scale_to(scale_to)
|
454
|
+
|
455
|
+
self.fft_mode, self.phase_shift = fft_mode, phase_shift
|
456
|
+
|
457
|
+
@classmethod
|
458
|
+
def from_dual(cls, dual_win: np.ndarray, hop: int, fs: float, *,
|
459
|
+
fft_mode: FFT_MODE_TYPE = 'onesided',
|
460
|
+
mfft: int | None = None,
|
461
|
+
scale_to: Literal['magnitude', 'psd'] | None = None,
|
462
|
+
phase_shift: int | None = 0):
|
463
|
+
r"""Instantiate a `ShortTimeFFT` by only providing a dual window.
|
464
|
+
|
465
|
+
If an STFT is invertible, it is possible to calculate the window `win`
|
466
|
+
from a given dual window `dual_win`. All other parameters have the
|
467
|
+
same meaning as in the initializer of `ShortTimeFFT`.
|
468
|
+
|
469
|
+
As explained in the :ref:`tutorial_stft` section of the
|
470
|
+
:ref:`user_guide`, an invertible STFT can be interpreted as series
|
471
|
+
expansion of time-shifted and frequency modulated dual windows. E.g.,
|
472
|
+
the series coefficient S[q,p] belongs to the term, which shifted
|
473
|
+
`dual_win` by p * `delta_t` and multiplied it by
|
474
|
+
exp( 2 * j * pi * t * q * `delta_f`).
|
475
|
+
|
476
|
+
|
477
|
+
Examples
|
478
|
+
--------
|
479
|
+
The following example discusses decomposing a signal into time- and
|
480
|
+
frequency-shifted Gaussians. A Gaussian with standard deviation of
|
481
|
+
one made up of 51 samples will be used:
|
482
|
+
|
483
|
+
>>> import numpy as np
|
484
|
+
>>> import matplotlib.pyplot as plt
|
485
|
+
>>> from scipy.signal import ShortTimeFFT
|
486
|
+
>>> from scipy.signal.windows import gaussian
|
487
|
+
...
|
488
|
+
>>> T, N = 0.1, 51
|
489
|
+
>>> d_win = gaussian(N, std=1/T, sym=True) # symmetric Gaussian window
|
490
|
+
>>> t = T * (np.arange(N) - N//2)
|
491
|
+
...
|
492
|
+
>>> fg1, ax1 = plt.subplots()
|
493
|
+
>>> ax1.set_title(r"Dual Window: Gaussian with $\sigma_t=1$")
|
494
|
+
>>> ax1.set(xlabel=f"Time $t$ in seconds ({N} samples, $T={T}$ s)",
|
495
|
+
... xlim=(t[0], t[-1]), ylim=(0, 1.1*np.max(d_win)))
|
496
|
+
>>> ax1.plot(t, d_win, 'C0-')
|
497
|
+
|
498
|
+
The following plot with the overlap of 41, 11 and 2 samples show how
|
499
|
+
the `hop` interval affects the shape of the window `win`:
|
500
|
+
|
501
|
+
>>> fig2, axx = plt.subplots(3, 1, sharex='all')
|
502
|
+
...
|
503
|
+
>>> axx[0].set_title(r"Windows for hop$\in\{10, 40, 49\}$")
|
504
|
+
>>> for c_, h_ in enumerate([10, 40, 49]):
|
505
|
+
... SFT = ShortTimeFFT.from_dual(d_win, h_, 1/T)
|
506
|
+
... axx[c_].plot(t + h_ * T, SFT.win, 'k--', alpha=.3, label=None)
|
507
|
+
... axx[c_].plot(t - h_ * T, SFT.win, 'k:', alpha=.3, label=None)
|
508
|
+
... axx[c_].plot(t, SFT.win, f'C{c_+1}',
|
509
|
+
... label=r"$\Delta t=%0.1f\,$s" % SFT.delta_t)
|
510
|
+
... axx[c_].set_ylim(0, 1.1*max(SFT.win))
|
511
|
+
... axx[c_].legend(loc='center')
|
512
|
+
>>> axx[-1].set(xlabel=f"Time $t$ in seconds ({N} samples, $T={T}$ s)",
|
513
|
+
... xlim=(t[0], t[-1]))
|
514
|
+
>>> plt.show()
|
515
|
+
|
516
|
+
Beside the window `win` centered at t = 0 the previous (t = -`delta_t`)
|
517
|
+
and following window (t = `delta_t`) are depicted. It can be seen that
|
518
|
+
for small `hop` intervals, the window is compact and smooth, having a
|
519
|
+
good time-frequency concentration in the STFT. For the large `hop`
|
520
|
+
interval of 4.9 s, the window has small values around t = 0, which are
|
521
|
+
not covered by the overlap of the adjacent windows, which could lead to
|
522
|
+
numeric inaccuracies. Furthermore, the peaky shape at the beginning and
|
523
|
+
the end of the window points to a higher bandwidth, resulting in a
|
524
|
+
poorer time-frequency resolution of the STFT.
|
525
|
+
Hence, the choice of the `hop` interval will be a compromise between
|
526
|
+
a time-frequency resolution and memory requirements demanded by small
|
527
|
+
`hop` sizes.
|
528
|
+
|
529
|
+
See Also
|
530
|
+
--------
|
531
|
+
from_window: Create instance by wrapping `get_window`.
|
532
|
+
ShortTimeFFT: Create instance using standard initializer.
|
533
|
+
"""
|
534
|
+
win = _calc_dual_canonical_window(dual_win, hop)
|
535
|
+
return cls(win=win, hop=hop, fs=fs, fft_mode=fft_mode, mfft=mfft,
|
536
|
+
dual_win=dual_win, scale_to=scale_to,
|
537
|
+
phase_shift=phase_shift)
|
538
|
+
|
539
|
+
@classmethod
|
540
|
+
def from_window(cls, win_param: str | tuple | float,
|
541
|
+
fs: float, nperseg: int, noverlap: int, *,
|
542
|
+
symmetric_win: bool = False,
|
543
|
+
fft_mode: FFT_MODE_TYPE = 'onesided',
|
544
|
+
mfft: int | None = None,
|
545
|
+
scale_to: Literal['magnitude', 'psd'] | None = None,
|
546
|
+
phase_shift: int | None = 0):
|
547
|
+
"""Instantiate `ShortTimeFFT` by using `get_window`.
|
548
|
+
|
549
|
+
The method `get_window` is used to create a window of length
|
550
|
+
`nperseg`. The parameter names `noverlap`, and `nperseg` are used here,
|
551
|
+
since they more inline with other classical STFT libraries.
|
552
|
+
|
553
|
+
Parameters
|
554
|
+
----------
|
555
|
+
win_param: Union[str, tuple, float],
|
556
|
+
Parameters passed to `get_window`. For windows with no parameters,
|
557
|
+
it may be a string (e.g., ``'hann'``), for parametrized windows a
|
558
|
+
tuple, (e.g., ``('gaussian', 2.)``) or a single float specifying
|
559
|
+
the shape parameter of a kaiser window (i.e. ``4.`` and
|
560
|
+
``('kaiser', 4.)`` are equal. See `get_window` for more details.
|
561
|
+
fs : float
|
562
|
+
Sampling frequency of input signal. Its relation to the
|
563
|
+
sampling interval `T` is ``T = 1 / fs``.
|
564
|
+
nperseg: int
|
565
|
+
Window length in samples, which corresponds to the `m_num`.
|
566
|
+
noverlap: int
|
567
|
+
Window overlap in samples. It relates to the `hop` increment by
|
568
|
+
``hop = npsereg - noverlap``.
|
569
|
+
symmetric_win: bool
|
570
|
+
If ``True`` then a symmetric window is generated, else a periodic
|
571
|
+
window is generated (default). Though symmetric windows seem for
|
572
|
+
most applications to be more sensible, the default of a periodic
|
573
|
+
windows was chosen to correspond to the default of `get_window`.
|
574
|
+
fft_mode : 'twosided', 'centered', 'onesided', 'onesided2X'
|
575
|
+
Mode of FFT to be used (default 'onesided').
|
576
|
+
See property `fft_mode` for details.
|
577
|
+
mfft: int | None
|
578
|
+
Length of the FFT used, if a zero padded FFT is desired.
|
579
|
+
If ``None`` (default), the length of the window `win` is used.
|
580
|
+
scale_to : 'magnitude', 'psd' | None
|
581
|
+
If not ``None`` (default) the window function is scaled, so each
|
582
|
+
STFT column represents either a 'magnitude' or a power spectral
|
583
|
+
density ('psd') spectrum. This parameter sets the property
|
584
|
+
`scaling` to the same value. See method `scale_to` for details.
|
585
|
+
phase_shift : int | None
|
586
|
+
If set, add a linear phase `phase_shift` / `mfft` * `f` to each
|
587
|
+
frequency `f`. The default value 0 ensures that there is no phase
|
588
|
+
shift on the zeroth slice (in which t=0 is centered). See property
|
589
|
+
`phase_shift` for more details.
|
590
|
+
|
591
|
+
Examples
|
592
|
+
--------
|
593
|
+
The following instances ``SFT0`` and ``SFT1`` are equivalent:
|
594
|
+
|
595
|
+
>>> from scipy.signal import ShortTimeFFT, get_window
|
596
|
+
>>> nperseg = 9 # window length
|
597
|
+
>>> w = get_window(('gaussian', 2.), nperseg)
|
598
|
+
>>> fs = 128 # sampling frequency
|
599
|
+
>>> hop = 3 # increment of STFT time slice
|
600
|
+
>>> SFT0 = ShortTimeFFT(w, hop, fs=fs)
|
601
|
+
>>> SFT1 = ShortTimeFFT.from_window(('gaussian', 2.), fs, nperseg,
|
602
|
+
... noverlap=nperseg-hop)
|
603
|
+
|
604
|
+
See Also
|
605
|
+
--------
|
606
|
+
scipy.signal.get_window: Return a window of a given length and type.
|
607
|
+
from_dual: Create instance using dual window.
|
608
|
+
ShortTimeFFT: Create instance using standard initializer.
|
609
|
+
"""
|
610
|
+
win = get_window(win_param, nperseg, fftbins=not symmetric_win)
|
611
|
+
return cls(win, hop=nperseg-noverlap, fs=fs, fft_mode=fft_mode,
|
612
|
+
mfft=mfft, scale_to=scale_to, phase_shift=phase_shift)
|
613
|
+
|
614
|
+
@classmethod
|
615
|
+
def from_win_equals_dual(
|
616
|
+
cls, desired_win: np.ndarray, hop: int, fs: float, *,
|
617
|
+
fft_mode: FFT_MODE_TYPE = 'onesided', mfft: int | None = None,
|
618
|
+
scale_to: Literal['magnitude', 'psd', 'unitary'] | None = None,
|
619
|
+
phase_shift: int | None = 0):
|
620
|
+
r"""Create instance where the window and its dual are equal up to a
|
621
|
+
scaling factor.
|
622
|
+
|
623
|
+
An instance is created were window and dual window are equal as well as being
|
624
|
+
closest to the parameter `desired_win` in the least-squares sense, i.e.,
|
625
|
+
minimizing ``abs(win-desired_win)**2``. Hence, `win` has the same length as
|
626
|
+
`desired_win`. Then a scaling factor is applied accoring to the `scale_to`
|
627
|
+
parameter.
|
628
|
+
|
629
|
+
All other parameters have the identical meaning as in the initializer.
|
630
|
+
|
631
|
+
To be able to calculate a valid window, `desired_win` needs to have a valid
|
632
|
+
dual STFT window for the given `hop` interval.
|
633
|
+
If this is not the case, a ``ValueError`` is raised.
|
634
|
+
|
635
|
+
Parameters
|
636
|
+
----------
|
637
|
+
desired_win : np.ndarray
|
638
|
+
A real-valued or complex-valued 1d array containing the sample of the
|
639
|
+
desired window.
|
640
|
+
hop : int
|
641
|
+
The increment in samples, by which the window is shifted in each step.
|
642
|
+
fs : float
|
643
|
+
Sampling frequency of input signal and window. Its relation to the
|
644
|
+
sampling interval `T` is ``T = 1 / fs``.
|
645
|
+
fft_mode : 'twosided', 'centered', 'onesided', 'onesided2X'
|
646
|
+
Mode of FFT to be used (default 'onesided').
|
647
|
+
See property `fft_mode` for details.
|
648
|
+
mfft: int | None
|
649
|
+
Length of the FFT used, if a zero padded FFT is desired.
|
650
|
+
If ``None`` (default), the length of the window `win` is used.
|
651
|
+
scale_to : 'magnitude' | 'psd' | 'unitary' | None
|
652
|
+
If not ``None`` (default) the window function is scaled, so each STFT
|
653
|
+
column represents either a 'magnitude' or a power spectral density ('psd')
|
654
|
+
spectrum, Alternatively, the STFT can be scaled to a`unitary` mapping,
|
655
|
+
i.e., dividing the window by ``np.sqrt(mfft)`` and multiplying the dual
|
656
|
+
window by the same amount.
|
657
|
+
phase_shift : int | None
|
658
|
+
If set, add a linear phase `phase_shift` / `mfft` * `f` to each
|
659
|
+
frequency `f`. The default value of 0 ensures that there is no phase shift
|
660
|
+
on the zeroth slice (in which t=0 is centered). See property
|
661
|
+
`phase_shift` for more details.
|
662
|
+
|
663
|
+
|
664
|
+
Notes
|
665
|
+
-----
|
666
|
+
The set of all possible windows with identical dual is defined by the set of
|
667
|
+
linear constraints of Eq. :math:numref:`eq_STFT_AllDualWinsCond` in the
|
668
|
+
:ref:`tutorial_stft` section of the :ref:`user_guide`. There it is also
|
669
|
+
derived that ``ShortTimeFFT.dual_win == ShortTimeFFT.m_pts * ShortTimeFFT.win``
|
670
|
+
needs to hold for an STFT to be a unitary mapping.
|
671
|
+
|
672
|
+
A unitary mapping preserves the value of the scalar product, i.e.,
|
673
|
+
|
674
|
+
.. math::
|
675
|
+
|
676
|
+
\langle x, y\rangle = \sum_k x[k]\, \overline{y[k]}
|
677
|
+
\stackrel{\stackrel{\text{unitary}}{\downarrow}}{=}
|
678
|
+
\sum_{q,p} S_x[q,p]\, \overline{S_y[q,p]}
|
679
|
+
= \langle S_x[q,p], S_y[q,p]\rangle\ ,
|
680
|
+
|
681
|
+
with :math:`S_{x,y}` being the STFT of :math:`x,y`. Hence, the energy
|
682
|
+
:math:`E_x=T\sum_k |x[k]|^2` of a signal is also preserved. This is also
|
683
|
+
illustrated in the example below.
|
684
|
+
|
685
|
+
Thie reason of distinguishing between no scaling (i.e., parameter `scale_to` is
|
686
|
+
``None``) and unitary scaling (i.e., ``scale_to = 'unitary'``) is due to the
|
687
|
+
utilized FFT function not being unitary (i.e., using the default value
|
688
|
+
``'backward'`` for the `~scipy.fft.fft` parameter `norm`).
|
689
|
+
|
690
|
+
|
691
|
+
See Also
|
692
|
+
--------
|
693
|
+
closest_STFT_dual_window: Calculate the STFT dual window of a given window
|
694
|
+
closest to a desired dual window.
|
695
|
+
ShortTimeFFT.spectrogram: Calculate squared STFTs
|
696
|
+
ShortTimeFFT: Class this property belongs to.
|
697
|
+
|
698
|
+
Examples
|
699
|
+
--------
|
700
|
+
The following example shows that an STFT can be indeed unitary:
|
701
|
+
|
702
|
+
>>> import matplotlib.pyplot as plt
|
703
|
+
>>> import numpy as np
|
704
|
+
>>> from scipy.signal import ShortTimeFFT, windows
|
705
|
+
...
|
706
|
+
>>> m, hop, std = 36, 8, 5
|
707
|
+
>>> desired_win = windows.gaussian(m, std, sym=True)
|
708
|
+
>>> SFT = ShortTimeFFT.from_win_equals_dual(desired_win, hop, fs=1/m,
|
709
|
+
... fft_mode='twosided',
|
710
|
+
... scale_to='unitary')
|
711
|
+
>>> np.allclose(SFT.dual_win, SFT.win * SFT.m_num) # check if STFT is unitary
|
712
|
+
True
|
713
|
+
>>> x1, x2 = np.tile([-1, -1, 1, 1], 5), np.tile([1, -1, -1, 1], 5)
|
714
|
+
>>> np.sum(x1*x2) # scalar product is zero -> orthogonal signals
|
715
|
+
0
|
716
|
+
>>> np.sum(x1**2) # scalar product of x1 with itself
|
717
|
+
20
|
718
|
+
>>> Sx11, Sx12 = SFT.spectrogram(x1), SFT.spectrogram(x1, x2)
|
719
|
+
>>> np.sum(Sx12) # STFT scalar product is also zero
|
720
|
+
-4.163336342344337e-16+0j # may vary
|
721
|
+
>>> np.sum(Sx11) # == np.sum(x1**2)
|
722
|
+
19.999999999999996 # may vary
|
723
|
+
...
|
724
|
+
... # Do the plotting:
|
725
|
+
>>> fg1, (ax11, ax12) = plt.subplots(1, 2, tight_layout=True, figsize=(8, 4))
|
726
|
+
>>> s_fac = np.sqrt(SFT.mfft)
|
727
|
+
>>> _ = fg1.suptitle(f"Scaled Unitary Window of {m} Sample Gaussian with " +
|
728
|
+
... rf"{hop=}, $\sigma={std}$, Scale factor: {s_fac}")
|
729
|
+
>>> ax11.set(ylabel="Amplitude", xlabel="Samples", xlim=(0, m))
|
730
|
+
>>> ax12.set(xlabel="Frequency Bins", ylabel="Magnitude Spectrum",
|
731
|
+
... xlim=(0, 15), ylim=(1e-5, 1.5))
|
732
|
+
>>> u_win_str = rf"Unitary $\times{s_fac:g}$"
|
733
|
+
>>> for x_, n_ in zip((desired_win, SFT.win*s_fac), ('Desired', u_win_str)):
|
734
|
+
... ax11.plot(x_, '.-', alpha=0.5, label=n_)
|
735
|
+
... X_ = np.fft.rfft(x_) / np.sum(abs(x_))
|
736
|
+
... ax12.semilogy(abs(X_), '.-', alpha=0.5, label=n_)
|
737
|
+
>>> for ax_ in (ax11, ax12):
|
738
|
+
... ax_.grid(True)
|
739
|
+
... ax_.legend()
|
740
|
+
>>> plt.show()
|
741
|
+
|
742
|
+
Note that ``fftmode='twosided'`` is used, since we need sum over the complete
|
743
|
+
time frequency plane. Due to passing ``scale_to='unitary'`` the window
|
744
|
+
``SFT.win`` is scaled by ``1/np.sqrt(SFT.mfft)``. Hence, ``SFT.win`` needs to
|
745
|
+
be scaled by `s_fac` in the plot above.
|
746
|
+
"""
|
747
|
+
if not (desired_win.ndim == 1 and desired_win.size > 0):
|
748
|
+
raise ValueError(f"Parameter desired_win is not 1d, but "
|
749
|
+
f"{desired_win.shape=}!")
|
750
|
+
if issubclass(desired_win.dtype.type, np.integer):
|
751
|
+
raise ValueError("Parameter desired_win cannot be of integer type, " +
|
752
|
+
f"but {desired_win.dtype=} => cast to float | complex ")
|
753
|
+
if not all(np.isfinite(desired_win)):
|
754
|
+
raise ValueError("Parameter desired_win must have finite entries!")
|
755
|
+
if not (1 <= hop <= len(desired_win) and isinstance(hop, int | np.integer)):
|
756
|
+
raise ValueError(f"Parameter {hop=} is not an integer between 1 and " +
|
757
|
+
f"{len(desired_win)=}!")
|
758
|
+
if scale_to not in ['magnitude', 'psd', 'unitary', None]:
|
759
|
+
raise ValueError(f"Parameter {scale_to=} not in " +
|
760
|
+
"['magnitude', 'psd', 'unitary', None]!")
|
761
|
+
|
762
|
+
mfft = len(desired_win) if mfft is None else mfft
|
763
|
+
s_fac = np.sqrt(mfft) if scale_to == 'unitary' else 1
|
764
|
+
|
765
|
+
win = desired_win.copy() # we do not want to modify input parameters
|
766
|
+
relative_resolution = np.finfo(win.dtype).resolution * max(win)
|
767
|
+
for m in range(hop):
|
768
|
+
a = np.linalg.norm(desired_win[m::hop])
|
769
|
+
if not (a > relative_resolution):
|
770
|
+
raise ValueError("Parameter desired_win does not have valid STFT dual "
|
771
|
+
f"window for {hop=}!")
|
772
|
+
win[m::hop] /= a
|
773
|
+
|
774
|
+
SFT = cls(win=win/s_fac, hop=hop, fs=fs, fft_mode=fft_mode, mfft=mfft,
|
775
|
+
dual_win=win*s_fac, phase_shift=phase_shift,
|
776
|
+
scale_to=None if scale_to=='unitary' else scale_to)
|
777
|
+
|
778
|
+
if scale_to == 'unitary':
|
779
|
+
SFT._scaling = scale_to
|
780
|
+
return SFT
|
781
|
+
|
782
|
+
|
783
|
+
@property
|
784
|
+
def win(self) -> np.ndarray:
|
785
|
+
"""Window function as real- or complex-valued 1d array.
|
786
|
+
|
787
|
+
This attribute is read-only, since `dual_win` depends on it.
|
788
|
+
To make this array immutable, its WRITEABLE flag is set to ``FALSE``.
|
789
|
+
|
790
|
+
See Also
|
791
|
+
--------
|
792
|
+
dual_win: Dual window.
|
793
|
+
m_num: Number of samples in window `win`.
|
794
|
+
m_num_mid: Center index of window `win`.
|
795
|
+
mfft: Length of input for the FFT used - may be larger than `m_num`.
|
796
|
+
hop: ime increment in signal samples for sliding window.
|
797
|
+
win: Window function as real- or complex-valued 1d array.
|
798
|
+
numpy.ndarray.setflags: Modify array flags.
|
799
|
+
ShortTimeFFT: Class this property belongs to.
|
800
|
+
"""
|
801
|
+
return self._win
|
802
|
+
|
803
|
+
@property
|
804
|
+
def hop(self) -> int:
|
805
|
+
"""Time increment in signal samples for sliding window.
|
806
|
+
|
807
|
+
This attribute is read only, since `dual_win` depends on it.
|
808
|
+
|
809
|
+
See Also
|
810
|
+
--------
|
811
|
+
delta_t: Time increment of STFT (``hop*T``)
|
812
|
+
m_num: Number of samples in window `win`.
|
813
|
+
m_num_mid: Center index of window `win`.
|
814
|
+
mfft: Length of input for the FFT used - may be larger than `m_num`.
|
815
|
+
T: Sampling interval of input signal and of the window.
|
816
|
+
win: Window function as real- or complex-valued 1d array.
|
817
|
+
ShortTimeFFT: Class this property belongs to.
|
818
|
+
"""
|
819
|
+
return self._hop
|
820
|
+
|
821
|
+
@property
|
822
|
+
def T(self) -> float:
|
823
|
+
"""Sampling interval of input signal and of the window.
|
824
|
+
|
825
|
+
A ``ValueError`` is raised if it is set to a non-positive value.
|
826
|
+
|
827
|
+
See Also
|
828
|
+
--------
|
829
|
+
delta_t: Time increment of STFT (``hop*T``)
|
830
|
+
hop: Time increment in signal samples for sliding window.
|
831
|
+
fs: Sampling frequency (being ``1/T``)
|
832
|
+
t: Times of STFT for an input signal with `n` samples.
|
833
|
+
ShortTimeFFT: Class this property belongs to.
|
834
|
+
"""
|
835
|
+
return 1 / self._fs
|
836
|
+
|
837
|
+
@T.setter
|
838
|
+
def T(self, v: float):
|
839
|
+
"""Sampling interval of input signal and of the window.
|
840
|
+
|
841
|
+
A ``ValueError`` is raised if it is set to a non-positive value.
|
842
|
+
"""
|
843
|
+
if not (v > 0):
|
844
|
+
raise ValueError(f"Sampling interval T={v} must be positive!")
|
845
|
+
self._fs = 1 / v
|
846
|
+
|
847
|
+
@property
|
848
|
+
def fs(self) -> float:
|
849
|
+
"""Sampling frequency of input signal and of the window.
|
850
|
+
|
851
|
+
The sampling frequency is the inverse of the sampling interval `T`.
|
852
|
+
A ``ValueError`` is raised if it is set to a non-positive value.
|
853
|
+
|
854
|
+
See Also
|
855
|
+
--------
|
856
|
+
delta_t: Time increment of STFT (``hop*T``)
|
857
|
+
hop: Time increment in signal samples for sliding window.
|
858
|
+
T: Sampling interval of input signal and of the window (``1/fs``).
|
859
|
+
ShortTimeFFT: Class this property belongs to.
|
860
|
+
"""
|
861
|
+
return self._fs
|
862
|
+
|
863
|
+
@fs.setter
|
864
|
+
def fs(self, v: float):
|
865
|
+
"""Sampling frequency of input signal and of the window.
|
866
|
+
|
867
|
+
The sampling frequency is the inverse of the sampling interval `T`.
|
868
|
+
A ``ValueError`` is raised if it is set to a non-positive value.
|
869
|
+
"""
|
870
|
+
if not (v > 0):
|
871
|
+
raise ValueError(f"Sampling frequency fs={v} must be positive!")
|
872
|
+
self._fs = v
|
873
|
+
|
874
|
+
@property
|
875
|
+
def fft_mode(self) -> FFT_MODE_TYPE:
|
876
|
+
"""Mode of utilized FFT ('twosided', 'centered', 'onesided' or
|
877
|
+
'onesided2X').
|
878
|
+
|
879
|
+
It can have the following values:
|
880
|
+
|
881
|
+
'twosided':
|
882
|
+
Two-sided FFT, where values for the negative frequencies are in
|
883
|
+
upper half of the array. Corresponds to :func:`~scipy.fft.fft()`.
|
884
|
+
'centered':
|
885
|
+
Two-sided FFT with the values being ordered along monotonically
|
886
|
+
increasing frequencies. Corresponds to applying
|
887
|
+
:func:`~scipy.fft.fftshift()` to :func:`~scipy.fft.fft()`.
|
888
|
+
'onesided':
|
889
|
+
Calculates only values for non-negative frequency values.
|
890
|
+
Corresponds to :func:`~scipy.fft.rfft()`.
|
891
|
+
'onesided2X':
|
892
|
+
Like `onesided`, but the non-zero frequencies are doubled if
|
893
|
+
`scaling` is set to 'magnitude' or multiplied by ``sqrt(2)`` if
|
894
|
+
set to 'psd'. If `scaling` is ``None``, setting `fft_mode` to
|
895
|
+
`onesided2X` is not allowed.
|
896
|
+
If the FFT length `mfft` is even, the last FFT value is not paired,
|
897
|
+
and thus it is not scaled.
|
898
|
+
|
899
|
+
Note that `onesided` and `onesided2X` do not work for complex-valued signals or
|
900
|
+
complex-valued windows. Furthermore, the frequency values can be obtained by
|
901
|
+
reading the `f` property, and the number of samples by accessing the `f_pts`
|
902
|
+
property.
|
903
|
+
|
904
|
+
See Also
|
905
|
+
--------
|
906
|
+
delta_f: Width of the frequency bins of the STFT.
|
907
|
+
f: Frequencies values of the STFT.
|
908
|
+
f_pts: Width of the frequency bins of the STFT.
|
909
|
+
onesided_fft: True if a one-sided FFT is used.
|
910
|
+
scaling: Normalization applied to the window function
|
911
|
+
ShortTimeFFT: Class this property belongs to.
|
912
|
+
"""
|
913
|
+
return self._fft_mode
|
914
|
+
|
915
|
+
@fft_mode.setter
|
916
|
+
def fft_mode(self, t: FFT_MODE_TYPE):
|
917
|
+
"""Set mode of FFT.
|
918
|
+
|
919
|
+
Allowed values are 'twosided', 'centered', 'onesided', 'onesided2X'.
|
920
|
+
See the property `fft_mode` for more details.
|
921
|
+
"""
|
922
|
+
if t not in (fft_mode_types := get_args(FFT_MODE_TYPE)):
|
923
|
+
raise ValueError(f"fft_mode='{t}' not in {fft_mode_types}!")
|
924
|
+
|
925
|
+
if t in {'onesided', 'onesided2X'} and np.iscomplexobj(self.win):
|
926
|
+
raise ValueError(f"One-sided spectra, i.e., fft_mode='{t}', " +
|
927
|
+
"are not allowed for complex-valued windows!")
|
928
|
+
|
929
|
+
if t == 'onesided2X' and self.scaling is None:
|
930
|
+
raise ValueError(f"For scaling is None, fft_mode='{t}' is invalid!"
|
931
|
+
"Do scale_to('psd') or scale_to('magnitude')!")
|
932
|
+
self._fft_mode = t
|
933
|
+
|
934
|
+
@property
|
935
|
+
def mfft(self) -> int:
|
936
|
+
"""Length of input for the FFT used - may be larger than window
|
937
|
+
length `m_num`.
|
938
|
+
|
939
|
+
If not set, `mfft` defaults to the window length `m_num`.
|
940
|
+
|
941
|
+
See Also
|
942
|
+
--------
|
943
|
+
f_pts: Number of points along the frequency axis.
|
944
|
+
f: Frequencies values of the STFT.
|
945
|
+
m_num: Number of samples in window `win`.
|
946
|
+
ShortTimeFFT: Class this property belongs to.
|
947
|
+
"""
|
948
|
+
return self._mfft
|
949
|
+
|
950
|
+
@mfft.setter
|
951
|
+
def mfft(self, n_: int):
|
952
|
+
"""Setter for the length of FFT utilized.
|
953
|
+
|
954
|
+
See the property `mfft` for further details.
|
955
|
+
"""
|
956
|
+
if not (n_ >= self.m_num):
|
957
|
+
raise ValueError(f"Attribute mfft={n_} needs to be at least the " +
|
958
|
+
f"window length m_num={self.m_num}!")
|
959
|
+
self._mfft = n_
|
960
|
+
|
961
|
+
@property
|
962
|
+
def scaling(self) -> Literal['magnitude', 'psd', 'unitary'] | None:
|
963
|
+
"""Normalization applied to the window function
|
964
|
+
('magnitude', 'psd', 'unitary', or ``None``).
|
965
|
+
|
966
|
+
If not ``None``, the FFT slices may be either interpreted as a `magnitude` or
|
967
|
+
a power spectral density spectrum (`psd`). If set to `unitary`, the STFT may be
|
968
|
+
interpreted as a unitary mapping, i.e., preserving the value of the scalar
|
969
|
+
product.
|
970
|
+
|
971
|
+
The window function can be scaled by calling the `scale_to` method,
|
972
|
+
or it is set by the initializer parameter ``scale_to``. Note that a
|
973
|
+
window cannot to be scaled to be `unitary`. Use `from_win_equals_dual`
|
974
|
+
to create a unitary `ShortTimeFFT` instance.
|
975
|
+
|
976
|
+
See Also
|
977
|
+
--------
|
978
|
+
fac_magnitude: Scaling factor for to a magnitude spectrum.
|
979
|
+
fac_psd: Scaling factor for to a power spectral density spectrum.
|
980
|
+
fft_mode: Mode of utilized FFT
|
981
|
+
scale_to: Scale window to obtain 'magnitude' or 'psd' scaling.
|
982
|
+
from_win_equals_dual: Class-method for creating a unitary instance.
|
983
|
+
ShortTimeFFT: Class this property belongs to.
|
984
|
+
"""
|
985
|
+
return self._scaling
|
986
|
+
|
987
|
+
def scale_to(self, scaling: Literal['magnitude', 'psd']):
|
988
|
+
"""Scale window to obtain 'magnitude' or 'psd' scaling for the STFT.
|
989
|
+
|
990
|
+
The window of a 'magnitude' spectrum has an integral of one, i.e., unit
|
991
|
+
area for non-negative windows. This ensures that absolute the values of
|
992
|
+
spectrum does not change if the length of the window changes (given
|
993
|
+
the input signal is stationary).
|
994
|
+
|
995
|
+
To represent the power spectral density ('psd') for varying length
|
996
|
+
windows the area of the absolute square of the window needs to be
|
997
|
+
unity.
|
998
|
+
|
999
|
+
The `scaling` property shows the current scaling. The properties
|
1000
|
+
`fac_magnitude` and `fac_psd` show the scaling factors required to
|
1001
|
+
scale the STFT values to a magnitude or a psd spectrum.
|
1002
|
+
|
1003
|
+
Note that a window cannot to be scaled to be `unitary`. Use
|
1004
|
+
`from_win_equals_dual` to create a unitary `ShortTimeFFT` instance.
|
1005
|
+
|
1006
|
+
This method is called, if the initializer parameter `scale_to` is set.
|
1007
|
+
|
1008
|
+
See Also
|
1009
|
+
--------
|
1010
|
+
fac_magnitude: Scaling factor for to a magnitude spectrum.
|
1011
|
+
fac_psd: Scaling factor for to a power spectral density spectrum.
|
1012
|
+
fft_mode: Mode of utilized FFT
|
1013
|
+
scaling: Normalization applied to the window function.
|
1014
|
+
ShortTimeFFT: Class this method belongs to.
|
1015
|
+
"""
|
1016
|
+
if scaling not in (scaling_values := {'magnitude', 'psd'}):
|
1017
|
+
raise ValueError(f"{scaling=} not in {scaling_values}!")
|
1018
|
+
if self._scaling == scaling: # do nothing
|
1019
|
+
return
|
1020
|
+
|
1021
|
+
s_fac = self.fac_psd if scaling == 'psd' else self.fac_magnitude
|
1022
|
+
self._win = self._win * s_fac
|
1023
|
+
self.win.setflags(write=False)
|
1024
|
+
if self._dual_win is not None:
|
1025
|
+
self._dual_win = self._dual_win / s_fac
|
1026
|
+
self.dual_win.setflags(write=False)
|
1027
|
+
self._fac_mag, self._fac_psd = None, None # reset scaling factors
|
1028
|
+
self._scaling = scaling
|
1029
|
+
|
1030
|
+
@property
|
1031
|
+
def phase_shift(self) -> int | None:
|
1032
|
+
"""If set, add linear phase `phase_shift` / `mfft` * `f` to each FFT
|
1033
|
+
slice of frequency `f`.
|
1034
|
+
|
1035
|
+
Shifting (more precisely `rolling`) an `mfft`-point FFT input by
|
1036
|
+
`phase_shift` samples results in a multiplication of the output by
|
1037
|
+
``np.exp(2j*np.pi*q*phase_shift/mfft)`` at the frequency q * `delta_f`.
|
1038
|
+
|
1039
|
+
The default value 0 ensures that there is no phase shift on the
|
1040
|
+
zeroth slice (in which t=0 is centered).
|
1041
|
+
No phase shift (``phase_shift is None``) is equivalent to
|
1042
|
+
``phase_shift = -mfft//2``. In this case slices are not shifted
|
1043
|
+
before calculating the FFT.
|
1044
|
+
|
1045
|
+
The absolute value of `phase_shift` is limited to be less than `mfft`.
|
1046
|
+
|
1047
|
+
See Also
|
1048
|
+
--------
|
1049
|
+
delta_f: Width of the frequency bins of the STFT.
|
1050
|
+
f: Frequencies values of the STFT.
|
1051
|
+
mfft: Length of input for the FFT used
|
1052
|
+
ShortTimeFFT: Class this property belongs to.
|
1053
|
+
"""
|
1054
|
+
return self._phase_shift
|
1055
|
+
|
1056
|
+
@phase_shift.setter
|
1057
|
+
def phase_shift(self, v: int | None):
|
1058
|
+
"""The absolute value of the phase shift needs to be less than mfft
|
1059
|
+
samples.
|
1060
|
+
|
1061
|
+
See the `phase_shift` getter method for more details.
|
1062
|
+
"""
|
1063
|
+
if v is None:
|
1064
|
+
self._phase_shift = v
|
1065
|
+
return
|
1066
|
+
if not isinstance(v, int | np.integer):
|
1067
|
+
raise ValueError(f"phase_shift={v} has the unit samples. Hence " +
|
1068
|
+
"it needs to be an int or it may be None!")
|
1069
|
+
if not (-self.mfft < v < self.mfft):
|
1070
|
+
raise ValueError("-mfft < phase_shift < mfft does not hold " +
|
1071
|
+
f"for mfft={self.mfft}, phase_shift={v}!")
|
1072
|
+
self._phase_shift = v
|
1073
|
+
|
1074
|
+
def _x_slices(self, x: np.ndarray, k_off: int, p0: int, p1: int,
|
1075
|
+
padding: PAD_TYPE) -> Generator[np.ndarray, None, None]:
|
1076
|
+
"""Generate signal slices along last axis of `x`.
|
1077
|
+
|
1078
|
+
This method is only used by `stft_detrend`. The parameters are
|
1079
|
+
described in `~ShortTimeFFT.stft`.
|
1080
|
+
"""
|
1081
|
+
if padding not in (padding_types := get_args(PAD_TYPE)):
|
1082
|
+
raise ValueError(f"Parameter {padding=} not in {padding_types}!")
|
1083
|
+
pad_kws: dict[str, dict] = { # possible keywords to pass to np.pad:
|
1084
|
+
'zeros': dict(mode='constant', constant_values=(0, 0)),
|
1085
|
+
'edge': dict(mode='edge'),
|
1086
|
+
'even': dict(mode='reflect', reflect_type='even'),
|
1087
|
+
'odd': dict(mode='reflect', reflect_type='odd'),
|
1088
|
+
} # typing of pad_kws is needed to make mypy happy
|
1089
|
+
|
1090
|
+
n, n1 = x.shape[-1], (p1 - p0) * self.hop
|
1091
|
+
k0 = p0 * self.hop - self.m_num_mid + k_off # start sample
|
1092
|
+
k1 = k0 + n1 + self.m_num # end sample
|
1093
|
+
|
1094
|
+
i0, i1 = max(k0, 0), min(k1, n) # indexes to shorten x
|
1095
|
+
# dimensions for padding x:
|
1096
|
+
pad_width = [(0, 0)] * (x.ndim-1) + [(-min(k0, 0), max(k1 - n, 0))]
|
1097
|
+
|
1098
|
+
x1 = np.pad(x[..., i0:i1], pad_width, **pad_kws[padding])
|
1099
|
+
for k_ in range(0, n1, self.hop):
|
1100
|
+
yield x1[..., k_:k_ + self.m_num]
|
1101
|
+
|
1102
|
+
def stft(self, x: np.ndarray, p0: int | None = None,
|
1103
|
+
p1: int | None = None, *, k_offset: int = 0,
|
1104
|
+
padding: PAD_TYPE = 'zeros', axis: int = -1) \
|
1105
|
+
-> np.ndarray:
|
1106
|
+
"""Perform the short-time Fourier transform.
|
1107
|
+
|
1108
|
+
A two-dimensional matrix with ``p1-p0`` columns is calculated.
|
1109
|
+
The `f_pts` rows represent value at the frequencies `f`. The q-th
|
1110
|
+
column of the windowed FFT with the window `win` is centered at t[q].
|
1111
|
+
The columns represent the values at the frequencies `f`.
|
1112
|
+
|
1113
|
+
Parameters
|
1114
|
+
----------
|
1115
|
+
x : np.ndarray
|
1116
|
+
The input signal as real or complex valued array. For complex values, the
|
1117
|
+
property `fft_mode` must be set to 'twosided' or 'centered'.
|
1118
|
+
p0 : int | None
|
1119
|
+
The first element of the range of slices to calculate. If ``None``
|
1120
|
+
then it is set to :attr:`p_min`, which is the smallest possible
|
1121
|
+
slice.
|
1122
|
+
p1 : int | None
|
1123
|
+
The end of the array. If ``None`` then `p_max(n)` is used.
|
1124
|
+
k_offset : int
|
1125
|
+
Index of first sample (t = 0) in `x`.
|
1126
|
+
padding : 'zeros' | 'edge' | 'even' | 'odd'
|
1127
|
+
Kind of values which are added, when the sliding window sticks out
|
1128
|
+
on either the lower or upper end of the input `x`. Zeros are added
|
1129
|
+
if the default 'zeros' is set. For 'edge' either the first or the
|
1130
|
+
last value of `x` is used. 'even' pads by reflecting the
|
1131
|
+
signal on the first or last sample and 'odd' additionally
|
1132
|
+
multiplies it with -1.
|
1133
|
+
axis : int
|
1134
|
+
The axis of `x` over which to compute the STFT.
|
1135
|
+
If not given, the last axis is used.
|
1136
|
+
|
1137
|
+
Returns
|
1138
|
+
-------
|
1139
|
+
S : np.ndarray
|
1140
|
+
A complex array is returned with the dimension always being larger
|
1141
|
+
by one than of `x`. The last axis always represents the time slices
|
1142
|
+
of the STFT. `axis` defines the frequency axis (default second to
|
1143
|
+
last). E.g., for a one-dimensional `x`, a complex 2d array is
|
1144
|
+
returned, with axis 0 representing frequency and axis 1 the time
|
1145
|
+
slices.
|
1146
|
+
|
1147
|
+
See Also
|
1148
|
+
--------
|
1149
|
+
delta_f: Width of the frequency bins of the STFT.
|
1150
|
+
delta_t: Time increment of STFT
|
1151
|
+
f: Frequencies values of the STFT.
|
1152
|
+
invertible: Check if STFT is invertible.
|
1153
|
+
:meth:`~ShortTimeFFT.istft`: Inverse short-time Fourier transform.
|
1154
|
+
p_range: Determine and validate slice index range.
|
1155
|
+
stft_detrend: STFT with detrended segments.
|
1156
|
+
t: Times of STFT for an input signal with `n` samples.
|
1157
|
+
:class:`scipy.signal.ShortTimeFFT`: Class this method belongs to.
|
1158
|
+
"""
|
1159
|
+
return self.stft_detrend(x, None, p0, p1, k_offset=k_offset,
|
1160
|
+
padding=padding, axis=axis)
|
1161
|
+
|
1162
|
+
def stft_detrend(self, x: np.ndarray,
|
1163
|
+
detr: Callable[[np.ndarray], np.ndarray] | Literal['linear', 'constant'] | None, # noqa: E501
|
1164
|
+
p0: int | None = None, p1: int | None = None, *,
|
1165
|
+
k_offset: int = 0, padding: PAD_TYPE = 'zeros',
|
1166
|
+
axis: int = -1) \
|
1167
|
+
-> np.ndarray:
|
1168
|
+
"""Calculate short-time Fourier transform with a trend being subtracted from
|
1169
|
+
each segment beforehand.
|
1170
|
+
|
1171
|
+
When the parameter `detr` is ``None``, this method's behavior is identical to
|
1172
|
+
the `~ShortTimeFFT.stft` method. Note that due to the detrending, the original
|
1173
|
+
signal cannot be reconstructed by the `~ShortTimeFFT.istft`.
|
1174
|
+
|
1175
|
+
Parameters
|
1176
|
+
----------
|
1177
|
+
x : np.ndarray
|
1178
|
+
The input signal as real or complex valued array. For complex values, the
|
1179
|
+
property `fft_mode` must be set to 'twosided' or 'centered'.
|
1180
|
+
detr : 'linear' | 'constant' | Callable[[np.ndarray], np.ndarray] | None
|
1181
|
+
If 'constant', the mean is subtracted, if set to "linear", the linear
|
1182
|
+
trend is removed from each segment. This is achieved by calling
|
1183
|
+
`~scipy.signal.detrend`. If `detr` is a function with one parameter, `detr`
|
1184
|
+
is applied to each segment.
|
1185
|
+
p0 : int | None
|
1186
|
+
The first element of the range of slices to calculate. If ``None``
|
1187
|
+
then it is set to :attr:`p_min`, which is the smallest possible
|
1188
|
+
slice.
|
1189
|
+
p1 : int | None
|
1190
|
+
The end of the array. If ``None`` then `p_max(n)` is used.
|
1191
|
+
k_offset : int
|
1192
|
+
Index of first sample (t = 0) in `x`.
|
1193
|
+
padding : 'zeros' | 'edge' | 'even' | 'odd'
|
1194
|
+
Kind of values which are added, when the sliding window sticks out
|
1195
|
+
on either the lower or upper end of the input `x`. Zeros are added
|
1196
|
+
if the default 'zeros' is set. For 'edge' either the first or the
|
1197
|
+
last value of `x` is used. 'even' pads by reflecting the
|
1198
|
+
signal on the first or last sample and 'odd' additionally
|
1199
|
+
multiplies it with -1.
|
1200
|
+
axis: int
|
1201
|
+
The axis of `x` over which to compute the STFT.
|
1202
|
+
If not given, the last axis is used.
|
1203
|
+
|
1204
|
+
Returns
|
1205
|
+
-------
|
1206
|
+
S : np.ndarray
|
1207
|
+
A complex array is returned with the dimension always being larger
|
1208
|
+
by one than of `x`. The last axis always represents the time slices
|
1209
|
+
of the STFT. `axis` defines the frequency axis (default second to
|
1210
|
+
last). E.g., for a one-dimensional `x`, a complex 2d array is
|
1211
|
+
returned, with axis 0 representing frequency and axis 1 the time
|
1212
|
+
slices.
|
1213
|
+
|
1214
|
+
See Also
|
1215
|
+
--------
|
1216
|
+
invertible: Check if STFT is invertible.
|
1217
|
+
:meth:`~ShortTimeFFT.istft`: Inverse short-time Fourier transform.
|
1218
|
+
:meth:`~ShortTimeFFT.stft`: Short-time Fourier transform
|
1219
|
+
(without detrending).
|
1220
|
+
:class:`scipy.signal.ShortTimeFFT`: Class this method belongs to.
|
1221
|
+
"""
|
1222
|
+
if self.onesided_fft and np.iscomplexobj(x):
|
1223
|
+
raise ValueError(f"Complex-valued `x` not allowed for {self.fft_mode=}'! "
|
1224
|
+
"Set property `fft_mode` to 'twosided' or 'centered'.")
|
1225
|
+
if isinstance(detr, str):
|
1226
|
+
detr = partial(detrend, type=detr)
|
1227
|
+
elif not (detr is None or callable(detr)):
|
1228
|
+
raise ValueError(f"Parameter {detr=} is not a str, function or " +
|
1229
|
+
"None!")
|
1230
|
+
n = x.shape[axis]
|
1231
|
+
if not (n >= (m2p := self.m_num-self.m_num_mid)):
|
1232
|
+
e_str = f'{len(x)=}' if x.ndim == 1 else f'of {axis=} of {x.shape}'
|
1233
|
+
raise ValueError(f"{e_str} must be >= ceil(m_num/2) = {m2p}!")
|
1234
|
+
|
1235
|
+
if x.ndim > 1: # motivated by the NumPy broadcasting mechanisms:
|
1236
|
+
x = np.moveaxis(x, axis, -1)
|
1237
|
+
# determine slice index range:
|
1238
|
+
p0, p1 = self.p_range(n, p0, p1)
|
1239
|
+
S_shape_1d = (self.f_pts, p1 - p0)
|
1240
|
+
S_shape = x.shape[:-1] + S_shape_1d if x.ndim > 1 else S_shape_1d
|
1241
|
+
S = np.zeros(S_shape, dtype=complex)
|
1242
|
+
for p_, x_ in enumerate(self._x_slices(x, k_offset, p0, p1, padding)):
|
1243
|
+
if detr is not None:
|
1244
|
+
x_ = detr(x_)
|
1245
|
+
S[..., :, p_] = self._fft_func(x_ * self.win.conj())
|
1246
|
+
if x.ndim > 1:
|
1247
|
+
return np.moveaxis(S, -2, axis if axis >= 0 else axis-1)
|
1248
|
+
return S
|
1249
|
+
|
1250
|
+
def spectrogram(self, x: np.ndarray, y: np.ndarray | None = None,
|
1251
|
+
detr: Callable[[np.ndarray], np.ndarray] | Literal['linear', 'constant'] | None = None, # noqa: E501
|
1252
|
+
*,
|
1253
|
+
p0: int | None = None, p1: int | None = None,
|
1254
|
+
k_offset: int = 0, padding: PAD_TYPE = 'zeros',
|
1255
|
+
axis: int = -1) \
|
1256
|
+
-> np.ndarray:
|
1257
|
+
r"""Calculate spectrogram or cross-spectrogram.
|
1258
|
+
|
1259
|
+
The spectrogram is the absolute square of the STFT, i.e., it is
|
1260
|
+
``abs(S[q,p])**2`` for given ``S[q,p]`` and thus is always
|
1261
|
+
non-negative.
|
1262
|
+
For two STFTs ``Sx[q,p], Sy[q,p]``, the cross-spectrogram is defined
|
1263
|
+
as ``Sx[q,p] * np.conj(Sy[q,p])`` and is complex-valued.
|
1264
|
+
This is a convenience function for calling `~ShortTimeFFT.stft` /
|
1265
|
+
`stft_detrend`, hence all parameters are discussed there.
|
1266
|
+
|
1267
|
+
Parameters
|
1268
|
+
----------
|
1269
|
+
x : np.ndarray
|
1270
|
+
The input signal as real or complex valued array. For complex values, the
|
1271
|
+
property `fft_mode` must be set to 'twosided' or 'centered'.
|
1272
|
+
y : np.ndarray
|
1273
|
+
The second input signal of the same shape as `x`. If ``None``, it is
|
1274
|
+
assumed to be `x`. For complex values, the property `fft_mode` must be
|
1275
|
+
set to 'twosided' or 'centered'.
|
1276
|
+
detr : 'linear' | 'constant' | Callable[[np.ndarray], np.ndarray] | None
|
1277
|
+
If 'constant', the mean is subtracted, if set to "linear", the linear
|
1278
|
+
trend is removed from each segment. This is achieved by calling
|
1279
|
+
`~scipy.signal.detrend`. If `detr` is a function with one parameter, `detr`
|
1280
|
+
is applied to each segment. For ``None`` (default), no trends are removed.
|
1281
|
+
p0 : int | None
|
1282
|
+
The first element of the range of slices to calculate. If ``None``
|
1283
|
+
then it is set to :attr:`p_min`, which is the smallest possible
|
1284
|
+
slice.
|
1285
|
+
p1 : int | None
|
1286
|
+
The end of the array. If ``None`` then `p_max(n)` is used.
|
1287
|
+
k_offset : int
|
1288
|
+
Index of first sample (t = 0) in `x`.
|
1289
|
+
padding : 'zeros' | 'edge' | 'even' | 'odd'
|
1290
|
+
Kind of values which are added, when the sliding window sticks out
|
1291
|
+
on either the lower or upper end of the input `x`. Zeros are added
|
1292
|
+
if the default 'zeros' is set. For 'edge' either the first or the
|
1293
|
+
last value of `x` is used. 'even' pads by reflecting the
|
1294
|
+
signal on the first or last sample and 'odd' additionally
|
1295
|
+
multiplies it with -1.
|
1296
|
+
axis : int
|
1297
|
+
The axis of `x` over which to compute the STFT.
|
1298
|
+
If not given, the last axis is used.
|
1299
|
+
|
1300
|
+
Returns
|
1301
|
+
-------
|
1302
|
+
S_xy : np.ndarray
|
1303
|
+
A real-valued array with non-negative values is returned, if ``x is y`` or
|
1304
|
+
`y` is ``None``. The dimension is always by one larger than of `x`. The
|
1305
|
+
last axis always represents the time slices of the spectrogram. `axis`
|
1306
|
+
defines the frequency axis (default second to last). E.g., for a
|
1307
|
+
one-dimensional `x`, a complex 2d array is returned, with axis 0
|
1308
|
+
representing frequency and axis 1 the time slices.
|
1309
|
+
|
1310
|
+
Notes
|
1311
|
+
-----
|
1312
|
+
The cross-spectrogram may be interpreted as the time-frequency analogon of the
|
1313
|
+
cross-spectral density (consult `csd`). The absolute square `|Sxy|²` of a
|
1314
|
+
cross-spectrogram `Sxy` divided by the spectrograms `Sxx` and `Syy` can be
|
1315
|
+
interpreted as a coherence spectrogram ``Cxy := abs(Sxy)**2 / (Sxx*Syy)``,
|
1316
|
+
which is the time-frequency analogon to `~coherence`.
|
1317
|
+
|
1318
|
+
If the STFT is parametrized to be a unitary transform, i.e., utilitzing
|
1319
|
+
`~from_win_equals_dual`, then the value of the scalar product, hence also the
|
1320
|
+
energy, is preserved.
|
1321
|
+
|
1322
|
+
Examples
|
1323
|
+
--------
|
1324
|
+
The following example shows the spectrogram of a square wave with varying
|
1325
|
+
frequency :math:`f_i(t)` (marked by a green dashed line in the plot) sampled
|
1326
|
+
with 20 Hz. The utilized Gaussian window is 50 samples or 2.5 s long. For the
|
1327
|
+
`ShortTimeFFT`, the parameter ``mfft=800`` (oversampling factor 16) and the
|
1328
|
+
`hop` interval of 2 in was chosen to produce a sufficient number of points.
|
1329
|
+
|
1330
|
+
The plot's colormap is logarithmically scaled as the power spectral
|
1331
|
+
density is in dB. The time extent of the signal `x` is marked by
|
1332
|
+
vertical dashed lines, and the shaded areas mark the presence of border
|
1333
|
+
effects.
|
1334
|
+
|
1335
|
+
>>> import matplotlib.pyplot as plt
|
1336
|
+
>>> import numpy as np
|
1337
|
+
>>> from scipy.signal import square, ShortTimeFFT
|
1338
|
+
>>> from scipy.signal.windows import gaussian
|
1339
|
+
...
|
1340
|
+
>>> T_x, N = 1 / 20, 1000 # 20 Hz sampling rate for 50 s signal
|
1341
|
+
>>> t_x = np.arange(N) * T_x # time indexes for signal
|
1342
|
+
>>> f_i = 5e-3*(t_x - t_x[N // 3])**2 + 1 # varying frequency
|
1343
|
+
>>> x = square(2*np.pi*np.cumsum(f_i)*T_x) # the signal
|
1344
|
+
...
|
1345
|
+
>>> g_std = 12 # standard deviation for Gaussian window in samples
|
1346
|
+
>>> win = gaussian(50, std=g_std, sym=True) # symmetric Gaussian wind.
|
1347
|
+
>>> SFT = ShortTimeFFT(win, hop=2, fs=1/T_x, mfft=800, scale_to='psd')
|
1348
|
+
>>> Sx2 = SFT.spectrogram(x) # calculate absolute square of STFT
|
1349
|
+
...
|
1350
|
+
>>> fig1, ax1 = plt.subplots(figsize=(6., 4.)) # enlarge plot a bit
|
1351
|
+
>>> t_lo, t_hi = SFT.extent(N)[:2] # time range of plot
|
1352
|
+
>>> ax1.set_title(rf"Spectrogram ({SFT.m_num*SFT.T:g}$\,s$ Gaussian " +
|
1353
|
+
... rf"window, $\sigma_t={g_std*SFT.T:g}\,$s)")
|
1354
|
+
>>> ax1.set(xlabel=f"Time $t$ in seconds ({SFT.p_num(N)} slices, " +
|
1355
|
+
... rf"$\Delta t = {SFT.delta_t:g}\,$s)",
|
1356
|
+
... ylabel=f"Freq. $f$ in Hz ({SFT.f_pts} bins, " +
|
1357
|
+
... rf"$\Delta f = {SFT.delta_f:g}\,$Hz)",
|
1358
|
+
... xlim=(t_lo, t_hi))
|
1359
|
+
>>> Sx_dB = 10 * np.log10(np.fmax(Sx2, 1e-4)) # limit range to -40 dB
|
1360
|
+
>>> im1 = ax1.imshow(Sx_dB, origin='lower', aspect='auto',
|
1361
|
+
... extent=SFT.extent(N), cmap='magma')
|
1362
|
+
>>> ax1.plot(t_x, f_i, 'g--', alpha=.5, label='$f_i(t)$')
|
1363
|
+
>>> fig1.colorbar(im1, label='Power Spectral Density ' +
|
1364
|
+
... r"$20\,\log_{10}|S_x(t, f)|$ in dB")
|
1365
|
+
...
|
1366
|
+
>>> # Shade areas where window slices stick out to the side:
|
1367
|
+
>>> for t0_, t1_ in [(t_lo, SFT.lower_border_end[0] * SFT.T),
|
1368
|
+
... (SFT.upper_border_begin(N)[0] * SFT.T, t_hi)]:
|
1369
|
+
... ax1.axvspan(t0_, t1_, color='w', linewidth=0, alpha=.3)
|
1370
|
+
>>> for t_ in [0, N * SFT.T]: # mark signal borders with vertical line
|
1371
|
+
... ax1.axvline(t_, color='c', linestyle='--', alpha=0.5)
|
1372
|
+
>>> ax1.legend()
|
1373
|
+
>>> fig1.tight_layout()
|
1374
|
+
>>> plt.show()
|
1375
|
+
|
1376
|
+
The logarithmic scaling reveals the odd harmonics of the square wave,
|
1377
|
+
which are reflected at the Nyquist frequency of 10 Hz. This aliasing
|
1378
|
+
is also the main source of the noise artifacts in the plot.
|
1379
|
+
|
1380
|
+
See Also
|
1381
|
+
--------
|
1382
|
+
:meth:`~ShortTimeFFT.stft`: Perform the short-time Fourier transform.
|
1383
|
+
stft_detrend: STFT with a trend subtracted from each segment.
|
1384
|
+
:class:`scipy.signal.ShortTimeFFT`: Class this method belongs to.
|
1385
|
+
"""
|
1386
|
+
Sx = self.stft_detrend(x, detr, p0, p1, k_offset=k_offset,
|
1387
|
+
padding=padding, axis=axis)
|
1388
|
+
if y is None or y is x: # do spectrogram:
|
1389
|
+
return Sx.real**2 + Sx.imag**2
|
1390
|
+
# Cross-spectrogram:
|
1391
|
+
Sy = self.stft_detrend(y, detr, p0, p1, k_offset=k_offset,
|
1392
|
+
padding=padding, axis=axis)
|
1393
|
+
return Sx * Sy.conj()
|
1394
|
+
|
1395
|
+
@property
|
1396
|
+
def dual_win(self) -> np.ndarray:
|
1397
|
+
"""Dual window (canonical dual window by default).
|
1398
|
+
|
1399
|
+
A STFT can be interpreted as the input signal being expressed as a
|
1400
|
+
weighted sum of modulated and time-shifted dual windows. If no dual window is
|
1401
|
+
given on instantiation, the canonical dual window, i.e., the window with the
|
1402
|
+
minimal energy (i.e., minimal L²-norm) is calculated. Alternative means for
|
1403
|
+
determining dual windows are provided by `closest_STFT_dual_window` and the
|
1404
|
+
`from_win_equals_dual` class-method. Note that `win` is also always a
|
1405
|
+
dual window of `dual_win`.
|
1406
|
+
|
1407
|
+
`dual_win` has same length as `win`, namely `m_num` samples.
|
1408
|
+
|
1409
|
+
If the dual window cannot be calculated a ``ValueError`` is raised.
|
1410
|
+
This attribute is read only and calculated lazily.
|
1411
|
+
To make this array immutable, its WRITEABLE flag is set to ``FALSE``.
|
1412
|
+
|
1413
|
+
See Also
|
1414
|
+
--------
|
1415
|
+
m_num: Number of samples in window `win` and `dual_win`.
|
1416
|
+
win: Window function as real- or complex-valued 1d array.
|
1417
|
+
from_win_equals_dual: Create instance where `win` and `dual_win` are equal.
|
1418
|
+
closest_STFT_dual_window: Calculate dual window closest to a desired window.
|
1419
|
+
numpy.ndarray.setflags: Modify array flags.
|
1420
|
+
ShortTimeFFT: Class this property belongs to.
|
1421
|
+
"""
|
1422
|
+
if self._dual_win is None:
|
1423
|
+
self._dual_win = _calc_dual_canonical_window(self.win, self.hop)
|
1424
|
+
self.dual_win.setflags(write=False)
|
1425
|
+
return self._dual_win
|
1426
|
+
|
1427
|
+
@property
|
1428
|
+
def invertible(self) -> bool:
|
1429
|
+
"""Check if STFT is invertible.
|
1430
|
+
|
1431
|
+
This is achieved by trying to calculate the canonical dual window.
|
1432
|
+
|
1433
|
+
See Also
|
1434
|
+
--------
|
1435
|
+
:meth:`~ShortTimeFFT.istft`: Inverse short-time Fourier transform.
|
1436
|
+
m_num: Number of samples in window `win` and `dual_win`.
|
1437
|
+
dual_win: Dual window.
|
1438
|
+
win: Window for STFT.
|
1439
|
+
ShortTimeFFT: Class this property belongs to.
|
1440
|
+
"""
|
1441
|
+
try:
|
1442
|
+
return len(self.dual_win) > 0 # call self.dual_win()
|
1443
|
+
except ValueError:
|
1444
|
+
return False
|
1445
|
+
|
1446
|
+
def istft(self, S: np.ndarray, k0: int = 0, k1: int | None = None, *,
|
1447
|
+
f_axis: int = -2, t_axis: int = -1) \
|
1448
|
+
-> np.ndarray:
|
1449
|
+
"""Inverse short-time Fourier transform.
|
1450
|
+
|
1451
|
+
It returns an array of dimension ``S.ndim - 1`` which is real
|
1452
|
+
if `onesided_fft` is set, else complex. If the STFT is not
|
1453
|
+
`invertible`, or the parameters are out of bounds a ``ValueError`` is
|
1454
|
+
raised.
|
1455
|
+
|
1456
|
+
Parameters
|
1457
|
+
----------
|
1458
|
+
S
|
1459
|
+
A complex valued array where `f_axis` denotes the frequency
|
1460
|
+
values and the `t-axis` dimension the temporal values of the
|
1461
|
+
STFT values.
|
1462
|
+
k0, k1
|
1463
|
+
The start and the end index of the reconstructed signal. The
|
1464
|
+
default (``k0 = 0``, ``k1 = None``) assumes that the maximum length
|
1465
|
+
signal should be reconstructed.
|
1466
|
+
f_axis, t_axis
|
1467
|
+
The axes in `S` denoting the frequency and the time dimension.
|
1468
|
+
|
1469
|
+
Notes
|
1470
|
+
-----
|
1471
|
+
It is required that `S` has `f_pts` entries along the `f_axis`. For
|
1472
|
+
the `t_axis` it is assumed that the first entry corresponds to
|
1473
|
+
`p_min` * `delta_t` (being <= 0). The length of `t_axis` needs to be
|
1474
|
+
compatible with `k1`. I.e., ``S.shape[t_axis] >= self.p_max(k1)`` must
|
1475
|
+
hold, if `k1` is not ``None``. Else `k1` is set to `k_max` with::
|
1476
|
+
|
1477
|
+
q_max = S.shape[t_range] + self.p_min
|
1478
|
+
k_max = (q_max - 1) * self.hop + self.m_num - self.m_num_mid
|
1479
|
+
|
1480
|
+
The :ref:`tutorial_stft` section of the :ref:`user_guide` discussed the
|
1481
|
+
slicing behavior by means of an example.
|
1482
|
+
|
1483
|
+
See Also
|
1484
|
+
--------
|
1485
|
+
invertible: Check if STFT is invertible.
|
1486
|
+
:meth:`~ShortTimeFFT.stft`: Perform Short-time Fourier transform.
|
1487
|
+
:class:`scipy.signal.ShortTimeFFT`: Class this method belongs to.
|
1488
|
+
"""
|
1489
|
+
if f_axis == t_axis:
|
1490
|
+
raise ValueError(f"{f_axis=} may not be equal to {t_axis=}!")
|
1491
|
+
if S.shape[f_axis] != self.f_pts:
|
1492
|
+
raise ValueError(f"{S.shape[f_axis]=} must be equal to " +
|
1493
|
+
f"{self.f_pts=} ({S.shape=})!")
|
1494
|
+
n_min = self.m_num-self.m_num_mid # minimum signal length
|
1495
|
+
if not (S.shape[t_axis] >= (q_num := self.p_num(n_min))):
|
1496
|
+
raise ValueError(f"{S.shape[t_axis]=} needs to have at least " +
|
1497
|
+
f"{q_num} slices ({S.shape=})!")
|
1498
|
+
if t_axis != S.ndim - 1 or f_axis != S.ndim - 2:
|
1499
|
+
t_axis = S.ndim + t_axis if t_axis < 0 else t_axis
|
1500
|
+
f_axis = S.ndim + f_axis if f_axis < 0 else f_axis
|
1501
|
+
S = np.moveaxis(S, (f_axis, t_axis), (-2, -1))
|
1502
|
+
|
1503
|
+
q_max = S.shape[-1] + self.p_min
|
1504
|
+
k_max = (q_max - 1) * self.hop + self.m_num - self.m_num_mid
|
1505
|
+
|
1506
|
+
k1 = k_max if k1 is None else k1
|
1507
|
+
if not (self.k_min <= k0 < k1 <= k_max):
|
1508
|
+
raise ValueError(f"({self.k_min=}) <= ({k0=}) < ({k1=}) <= " +
|
1509
|
+
f"({k_max=}) is false!")
|
1510
|
+
if not (num_pts := k1 - k0) >= n_min:
|
1511
|
+
raise ValueError(f"({k1=}) - ({k0=}) = {num_pts} has to be at " +
|
1512
|
+
f"least the half the window length {n_min}!")
|
1513
|
+
|
1514
|
+
q0 = (k0 // self.hop + self.p_min if k0 >= 0 else # p_min always <= 0
|
1515
|
+
k0 // self.hop)
|
1516
|
+
q1 = min(self.p_max(k1), q_max)
|
1517
|
+
k_q0, k_q1 = self.nearest_k_p(k0), self.nearest_k_p(k1, left=False)
|
1518
|
+
n_pts = k_q1 - k_q0 + self.m_num - self.m_num_mid
|
1519
|
+
x = np.zeros(S.shape[:-2] + (n_pts,),
|
1520
|
+
dtype=float if self.onesided_fft else complex)
|
1521
|
+
for q_ in range(q0, q1):
|
1522
|
+
xs = self._ifft_func(S[..., :, q_ - self.p_min]) * self.dual_win
|
1523
|
+
i0 = q_ * self.hop - self.m_num_mid
|
1524
|
+
i1 = min(i0 + self.m_num, n_pts+k0)
|
1525
|
+
j0, j1 = 0, i1 - i0
|
1526
|
+
if i0 < k0: # xs sticks out to the left on x:
|
1527
|
+
j0 += k0 - i0
|
1528
|
+
i0 = k0
|
1529
|
+
x[..., i0-k0:i1-k0] += xs[..., j0:j1]
|
1530
|
+
x = x[..., :k1-k0]
|
1531
|
+
if x.ndim > 1:
|
1532
|
+
x = np.moveaxis(x, -1, f_axis if f_axis < x.ndim else t_axis)
|
1533
|
+
return x
|
1534
|
+
|
1535
|
+
@property
|
1536
|
+
def fac_magnitude(self) -> float:
|
1537
|
+
"""Factor to multiply the STFT values by to scale each frequency slice
|
1538
|
+
to a magnitude spectrum.
|
1539
|
+
|
1540
|
+
It is 1 if attribute ``scaling == 'magnitude'``.
|
1541
|
+
The window can be scaled to a magnitude spectrum by using the method
|
1542
|
+
`scale_to`.
|
1543
|
+
|
1544
|
+
See Also
|
1545
|
+
--------
|
1546
|
+
fac_psd: Scaling factor for to a power spectral density spectrum.
|
1547
|
+
scale_to: Scale window to obtain 'magnitude' or 'psd' scaling.
|
1548
|
+
scaling: Normalization applied to the window function.
|
1549
|
+
ShortTimeFFT: Class this property belongs to.
|
1550
|
+
"""
|
1551
|
+
if self.scaling == 'magnitude':
|
1552
|
+
return 1
|
1553
|
+
if self._fac_mag is None:
|
1554
|
+
self._fac_mag = 1 / abs(sum(self.win))
|
1555
|
+
return self._fac_mag
|
1556
|
+
|
1557
|
+
@property
|
1558
|
+
def fac_psd(self) -> float:
|
1559
|
+
"""Factor to multiply the STFT values by to scale each frequency slice
|
1560
|
+
to a power spectral density (PSD).
|
1561
|
+
|
1562
|
+
It is 1 if attribute ``scaling == 'psd'``.
|
1563
|
+
The window can be scaled to a psd spectrum by using the method
|
1564
|
+
`scale_to`.
|
1565
|
+
|
1566
|
+
See Also
|
1567
|
+
--------
|
1568
|
+
fac_magnitude: Scaling factor for to a magnitude spectrum.
|
1569
|
+
scale_to: Scale window to obtain 'magnitude' or 'psd' scaling.
|
1570
|
+
scaling: Normalization applied to the window function.
|
1571
|
+
ShortTimeFFT: Class this property belongs to.
|
1572
|
+
"""
|
1573
|
+
if self.scaling == 'psd':
|
1574
|
+
return 1
|
1575
|
+
if self._fac_psd is None:
|
1576
|
+
self._fac_psd = 1 / np.sqrt(
|
1577
|
+
sum(self.win.real**2+self.win.imag**2) / self.T)
|
1578
|
+
return self._fac_psd
|
1579
|
+
|
1580
|
+
@property
|
1581
|
+
def m_num(self) -> int:
|
1582
|
+
"""Number of samples in window `win`.
|
1583
|
+
|
1584
|
+
Note that the FFT can be oversampled by zero-padding. This is achieved
|
1585
|
+
by setting the `mfft` property.
|
1586
|
+
|
1587
|
+
See Also
|
1588
|
+
--------
|
1589
|
+
m_num_mid: Center index of window `win`.
|
1590
|
+
mfft: Length of input for the FFT used - may be larger than `m_num`.
|
1591
|
+
hop: Time increment in signal samples for sliding window.
|
1592
|
+
win: Window function as real- or complex-valued 1d array.
|
1593
|
+
ShortTimeFFT: Class this property belongs to.
|
1594
|
+
"""
|
1595
|
+
return len(self.win)
|
1596
|
+
|
1597
|
+
@property
|
1598
|
+
def m_num_mid(self) -> int:
|
1599
|
+
"""Center index of window `win`.
|
1600
|
+
|
1601
|
+
For odd `m_num`, ``(m_num - 1) / 2`` is returned and
|
1602
|
+
for even `m_num` (per definition) ``m_num / 2`` is returned.
|
1603
|
+
|
1604
|
+
See Also
|
1605
|
+
--------
|
1606
|
+
m_num: Number of samples in window `win`.
|
1607
|
+
mfft: Length of input for the FFT used - may be larger than `m_num`.
|
1608
|
+
hop: ime increment in signal samples for sliding window.
|
1609
|
+
win: Window function as real- or complex-valued 1d array.
|
1610
|
+
ShortTimeFFT: Class this property belongs to.
|
1611
|
+
"""
|
1612
|
+
return self.m_num // 2
|
1613
|
+
|
1614
|
+
@cached_property
|
1615
|
+
def _pre_padding(self) -> tuple[int, int]:
|
1616
|
+
"""Smallest signal index and slice index due to padding.
|
1617
|
+
|
1618
|
+
Since, per convention, for time t=0, n,q is zero, the returned values
|
1619
|
+
are negative or zero.
|
1620
|
+
"""
|
1621
|
+
w2 = self.win.real**2 + self.win.imag**2
|
1622
|
+
# move window to the left until the overlap with t >= 0 vanishes:
|
1623
|
+
n0 = -self.m_num_mid
|
1624
|
+
for p_, n_ in enumerate(range(n0, n0-self.m_num-1, -self.hop)):
|
1625
|
+
n_next = n_ - self.hop
|
1626
|
+
if n_next + self.m_num <= 0 or all(w2[n_next:] == 0):
|
1627
|
+
return n_, -p_
|
1628
|
+
# Make the linter happy:
|
1629
|
+
raise RuntimeError("This code line should never run! Please file a bug.")
|
1630
|
+
|
1631
|
+
@property
|
1632
|
+
def k_min(self) -> int:
|
1633
|
+
"""The smallest possible signal index of the STFT.
|
1634
|
+
|
1635
|
+
`k_min` is the index of the left-most non-zero value of the lowest
|
1636
|
+
slice `p_min`. Since the zeroth slice is centered over the zeroth
|
1637
|
+
sample of the input signal, `k_min` is never positive.
|
1638
|
+
A detailed example is provided in the :ref:`tutorial_stft_sliding_win`
|
1639
|
+
section of the :ref:`user_guide`.
|
1640
|
+
|
1641
|
+
See Also
|
1642
|
+
--------
|
1643
|
+
k_max: First sample index after signal end not touched by a time slice.
|
1644
|
+
lower_border_end: Where pre-padding effects end.
|
1645
|
+
p_min: The smallest possible slice index.
|
1646
|
+
p_max: Index of first non-overlapping upper time slice.
|
1647
|
+
p_num: Number of time slices, i.e., `p_max` - `p_min`.
|
1648
|
+
p_range: Determine and validate slice index range.
|
1649
|
+
upper_border_begin: Where post-padding effects start.
|
1650
|
+
ShortTimeFFT: Class this property belongs to.
|
1651
|
+
"""
|
1652
|
+
return self._pre_padding[0]
|
1653
|
+
|
1654
|
+
@property
|
1655
|
+
def p_min(self) -> int:
|
1656
|
+
"""The smallest possible slice index.
|
1657
|
+
|
1658
|
+
`p_min` is the index of the left-most slice, where the window still
|
1659
|
+
sticks into the signal, i.e., has non-zero part for t >= 0.
|
1660
|
+
`k_min` is the smallest index where the window function of the slice
|
1661
|
+
`p_min` is non-zero.
|
1662
|
+
|
1663
|
+
Since, per convention the zeroth slice is centered at t=0,
|
1664
|
+
`p_min` <= 0 always holds.
|
1665
|
+
A detailed example is provided in the :ref:`tutorial_stft_sliding_win`
|
1666
|
+
section of the :ref:`user_guide`.
|
1667
|
+
|
1668
|
+
See Also
|
1669
|
+
--------
|
1670
|
+
k_min: The smallest possible signal index.
|
1671
|
+
k_max: First sample index after signal end not touched by a time slice.
|
1672
|
+
p_max: Index of first non-overlapping upper time slice.
|
1673
|
+
p_num: Number of time slices, i.e., `p_max` - `p_min`.
|
1674
|
+
p_range: Determine and validate slice index range.
|
1675
|
+
ShortTimeFFT: Class this property belongs to.
|
1676
|
+
"""
|
1677
|
+
return self._pre_padding[1]
|
1678
|
+
|
1679
|
+
def _post_padding(self, n: int) -> tuple[int, int]:
|
1680
|
+
"""Largest signal index and slice index due to padding.
|
1681
|
+
|
1682
|
+
Parameters
|
1683
|
+
----------
|
1684
|
+
n : int
|
1685
|
+
Number of samples of input signal (must be ≥ half of the window length).
|
1686
|
+
|
1687
|
+
Notes
|
1688
|
+
-----
|
1689
|
+
Note that the return values are cached together with the parameter `n` to avoid
|
1690
|
+
unnecessary recalculations.
|
1691
|
+
"""
|
1692
|
+
if not (n >= (m2p := self.m_num - self.m_num_mid)):
|
1693
|
+
raise ValueError(f"Parameter n must be >= ceil(m_num/2) = {m2p}!")
|
1694
|
+
last_arg, last_return_value = self._cache_post_padding
|
1695
|
+
if n == last_arg: # use cached value:
|
1696
|
+
return last_return_value
|
1697
|
+
w2 = self.win.real**2 + self.win.imag**2
|
1698
|
+
# move window to the right until the overlap for t < t[n] vanishes:
|
1699
|
+
q1 = n // self.hop # last slice index with t[p1] <= t[n]
|
1700
|
+
k1 = q1 * self.hop - self.m_num_mid
|
1701
|
+
for q_, k_ in enumerate(range(k1, n+self.m_num, self.hop), start=q1):
|
1702
|
+
n_next = k_ + self.hop
|
1703
|
+
if n_next >= n or all(w2[:n-n_next] == 0):
|
1704
|
+
return_value = k_ + self.m_num, q_ + 1
|
1705
|
+
self._cache_post_padding = n, return_value
|
1706
|
+
return return_value
|
1707
|
+
raise RuntimeError("This code line should never run! Please file a bug.")
|
1708
|
+
# If this case is reached, it probably means the last slice should be
|
1709
|
+
# returned, i.e.: return k1 + self.m_num - self.m_num_mid, q1 + 1
|
1710
|
+
|
1711
|
+
def k_max(self, n: int) -> int:
|
1712
|
+
"""First sample index after signal end not touched by a time slice.
|
1713
|
+
|
1714
|
+
`k_max` - 1 is the largest sample index of the slice `p_max` - 1 for a
|
1715
|
+
given input signal of `n` samples.
|
1716
|
+
A detailed example is provided in the :ref:`tutorial_stft_sliding_win`
|
1717
|
+
section of the :ref:`user_guide`.
|
1718
|
+
|
1719
|
+
Parameters
|
1720
|
+
----------
|
1721
|
+
n : int
|
1722
|
+
Number of samples of input signal (must be ≥ half of the window length).
|
1723
|
+
|
1724
|
+
See Also
|
1725
|
+
--------
|
1726
|
+
k_min: The smallest possible signal index.
|
1727
|
+
p_min: The smallest possible slice index.
|
1728
|
+
p_max: Index of first non-overlapping upper time slice.
|
1729
|
+
p_num: Number of time slices, i.e., `p_max` - `p_min`.
|
1730
|
+
p_range: Determine and validate slice index range.
|
1731
|
+
ShortTimeFFT: Class this method belongs to.
|
1732
|
+
"""
|
1733
|
+
return self._post_padding(n)[0]
|
1734
|
+
|
1735
|
+
def p_max(self, n: int) -> int:
|
1736
|
+
"""Index of first non-overlapping upper time slice for `n` sample
|
1737
|
+
input.
|
1738
|
+
|
1739
|
+
Note that center point t[p_max] = (p_max(n)-1) * `delta_t` is typically
|
1740
|
+
larger than last time index t[n-1] == (`n`-1) * `T`. The upper border
|
1741
|
+
of samples indexes covered by the window slices is given by `k_max`.
|
1742
|
+
Furthermore, `p_max` does not denote the number of slices `p_num` since
|
1743
|
+
`p_min` is typically less than zero.
|
1744
|
+
A detailed example is provided in the :ref:`tutorial_stft_sliding_win`
|
1745
|
+
section of the :ref:`user_guide`.
|
1746
|
+
|
1747
|
+
See Also
|
1748
|
+
--------
|
1749
|
+
k_min: The smallest possible signal index.
|
1750
|
+
k_max: First sample index after signal end not touched by a time slice.
|
1751
|
+
p_min: The smallest possible slice index.
|
1752
|
+
p_num: Number of time slices, i.e., `p_max` - `p_min`.
|
1753
|
+
p_range: Determine and validate slice index range.
|
1754
|
+
ShortTimeFFT: Class this method belongs to.
|
1755
|
+
"""
|
1756
|
+
return self._post_padding(n)[1]
|
1757
|
+
|
1758
|
+
def p_num(self, n: int) -> int:
|
1759
|
+
"""Number of time slices for an input signal with `n` samples.
|
1760
|
+
|
1761
|
+
It is given by `p_num` = `p_max` - `p_min` with `p_min` typically
|
1762
|
+
being negative.
|
1763
|
+
A detailed example is provided in the :ref:`tutorial_stft_sliding_win`
|
1764
|
+
section of the :ref:`user_guide`.
|
1765
|
+
|
1766
|
+
See Also
|
1767
|
+
--------
|
1768
|
+
k_min: The smallest possible signal index.
|
1769
|
+
k_max: First sample index after signal end not touched by a time slice.
|
1770
|
+
lower_border_end: Where pre-padding effects end.
|
1771
|
+
p_min: The smallest possible slice index.
|
1772
|
+
p_max: Index of first non-overlapping upper time slice.
|
1773
|
+
p_range: Determine and validate slice index range.
|
1774
|
+
upper_border_begin: Where post-padding effects start.
|
1775
|
+
ShortTimeFFT: Class this method belongs to.
|
1776
|
+
"""
|
1777
|
+
return self.p_max(n) - self.p_min
|
1778
|
+
|
1779
|
+
@property
|
1780
|
+
def lower_border_end(self) -> tuple[int, int]:
|
1781
|
+
"""First signal index and first slice index unaffected by pre-padding.
|
1782
|
+
|
1783
|
+
Describes the point where the window does not stick out to the left
|
1784
|
+
of the signal domain.
|
1785
|
+
A detailed example is provided in the :ref:`tutorial_stft_sliding_win`
|
1786
|
+
section of the :ref:`user_guide`.
|
1787
|
+
|
1788
|
+
See Also
|
1789
|
+
--------
|
1790
|
+
k_min: The smallest possible signal index.
|
1791
|
+
k_max: First sample index after signal end not touched by a time slice.
|
1792
|
+
lower_border_end: Where pre-padding effects end.
|
1793
|
+
p_min: The smallest possible slice index.
|
1794
|
+
p_max: Index of first non-overlapping upper time slice.
|
1795
|
+
p_num: Number of time slices, i.e., `p_max` - `p_min`.
|
1796
|
+
p_range: Determine and validate slice index range.
|
1797
|
+
upper_border_begin: Where post-padding effects start.
|
1798
|
+
ShortTimeFFT: Class this property belongs to.
|
1799
|
+
"""
|
1800
|
+
if self._lower_border_end is not None:
|
1801
|
+
return self._lower_border_end
|
1802
|
+
|
1803
|
+
# first non-zero element in self.win:
|
1804
|
+
m0 = np.flatnonzero(self.win.real**2 + self.win.imag**2)[0]
|
1805
|
+
|
1806
|
+
# move window to the right until does not stick out to the left:
|
1807
|
+
k0 = -self.m_num_mid + m0
|
1808
|
+
for q_, k_ in enumerate(range(k0, self.hop + 1, self.hop)):
|
1809
|
+
if k_ + self.hop >= 0: # next entry does not stick out anymore
|
1810
|
+
self._lower_border_end = (k_ + self.m_num, q_ + 1)
|
1811
|
+
return self._lower_border_end
|
1812
|
+
self._lower_border_end = (0, max(self.p_min, 0)) # ends at first slice
|
1813
|
+
return self._lower_border_end
|
1814
|
+
|
1815
|
+
def upper_border_begin(self, n: int) -> tuple[int, int]:
|
1816
|
+
"""First signal index and first slice index affected by post-padding.
|
1817
|
+
|
1818
|
+
Describes the point where the window does begin stick out to the right
|
1819
|
+
of the signal domain.
|
1820
|
+
A detailed example is given :ref:`tutorial_stft_sliding_win` section
|
1821
|
+
of the :ref:`user_guide`.
|
1822
|
+
|
1823
|
+
Parameters
|
1824
|
+
----------
|
1825
|
+
n : int
|
1826
|
+
Number of samples of input signal (must be ≥ half of the window length).
|
1827
|
+
|
1828
|
+
Returns
|
1829
|
+
-------
|
1830
|
+
k_ub : int
|
1831
|
+
Lowest signal index, where a touching time slice sticks out past the
|
1832
|
+
signal end.
|
1833
|
+
p_ub : int
|
1834
|
+
Lowest index of time slice of which the end sticks out past the signal end.
|
1835
|
+
|
1836
|
+
Notes
|
1837
|
+
-----
|
1838
|
+
Note that the return values are cached together with the parameter `n` to avoid
|
1839
|
+
unnecessary recalculations.
|
1840
|
+
|
1841
|
+
See Also
|
1842
|
+
--------
|
1843
|
+
k_min: The smallest possible signal index.
|
1844
|
+
k_max: First sample index after signal end not touched by a time slice.
|
1845
|
+
lower_border_end: Where pre-padding effects end.
|
1846
|
+
p_min: The smallest possible slice index.
|
1847
|
+
p_max: Index of first non-overlapping upper time slice.
|
1848
|
+
p_num: Number of time slices, i.e., `p_max` - `p_min`.
|
1849
|
+
p_range: Determine and validate slice index range.
|
1850
|
+
ShortTimeFFT: Class this method belongs to.
|
1851
|
+
"""
|
1852
|
+
if not (n >= (m2p := self.m_num - self.m_num_mid)):
|
1853
|
+
raise ValueError(f"Parameter n must be >= ceil(m_num/2) = {m2p}!")
|
1854
|
+
last_arg, last_return_value = self._cache_upper_border_begin
|
1855
|
+
if n == last_arg: # use cached value:
|
1856
|
+
return last_return_value
|
1857
|
+
w2 = self.win.real**2 + self.win.imag**2
|
1858
|
+
q2 = n // self.hop + 1 # first t[q] >= t[n]
|
1859
|
+
q1 = max((n-self.m_num) // self.hop - 1, -1)
|
1860
|
+
# move window left until does not stick out to the right:
|
1861
|
+
for q_ in range(q2, q1, -1):
|
1862
|
+
k_ = q_ * self.hop + (self.m_num - self.m_num_mid)
|
1863
|
+
if k_ <= n or all(w2[n-k_:] == 0):
|
1864
|
+
return_value = (q_ + 1) * self.hop - self.m_num_mid, q_ + 1
|
1865
|
+
self. _cache_upper_border_begin = n, return_value
|
1866
|
+
return return_value
|
1867
|
+
# make linter happy:
|
1868
|
+
raise RuntimeError("This code line should never run! Please file a bug.")
|
1869
|
+
|
1870
|
+
@property
|
1871
|
+
def delta_t(self) -> float:
|
1872
|
+
"""Time increment of STFT.
|
1873
|
+
|
1874
|
+
The time increment `delta_t` = `T` * `hop` represents the sample
|
1875
|
+
increment `hop` converted to time based on the sampling interval `T`.
|
1876
|
+
|
1877
|
+
See Also
|
1878
|
+
--------
|
1879
|
+
delta_f: Width of the frequency bins of the STFT.
|
1880
|
+
hop: Hop size in signal samples for sliding window.
|
1881
|
+
t: Times of STFT for an input signal with `n` samples.
|
1882
|
+
T: Sampling interval of input signal and window `win`.
|
1883
|
+
ShortTimeFFT: Class this property belongs to
|
1884
|
+
"""
|
1885
|
+
return self.T * self.hop
|
1886
|
+
|
1887
|
+
def p_range(self, n: int, p0: int | None = None,
|
1888
|
+
p1: int | None = None) -> tuple[int, int]:
|
1889
|
+
"""Determine and validate slice index range.
|
1890
|
+
|
1891
|
+
Parameters
|
1892
|
+
----------
|
1893
|
+
n : int
|
1894
|
+
Number of samples of input signal, assuming t[0] = 0.
|
1895
|
+
p0 : int | None
|
1896
|
+
First slice index. If 0 then the first slice is centered at t = 0.
|
1897
|
+
If ``None`` then `p_min` is used. Note that p0 may be < 0 if
|
1898
|
+
slices are left of t = 0.
|
1899
|
+
p1 : int | None
|
1900
|
+
End of interval (last value is p1-1).
|
1901
|
+
If ``None`` then `p_max(n)` is used.
|
1902
|
+
|
1903
|
+
|
1904
|
+
Returns
|
1905
|
+
-------
|
1906
|
+
p0_ : int
|
1907
|
+
The fist slice index
|
1908
|
+
p1_ : int
|
1909
|
+
End of interval (last value is p1-1).
|
1910
|
+
|
1911
|
+
Notes
|
1912
|
+
-----
|
1913
|
+
A ``ValueError`` is raised if ``p_min <= p0 < p1 <= p_max(n)`` does not
|
1914
|
+
hold.
|
1915
|
+
|
1916
|
+
See Also
|
1917
|
+
--------
|
1918
|
+
k_min: The smallest possible signal index.
|
1919
|
+
k_max: First sample index after signal end not touched by a time slice.
|
1920
|
+
lower_border_end: Where pre-padding effects end.
|
1921
|
+
p_min: The smallest possible slice index.
|
1922
|
+
p_max: Index of first non-overlapping upper time slice.
|
1923
|
+
p_num: Number of time slices, i.e., `p_max` - `p_min`.
|
1924
|
+
upper_border_begin: Where post-padding effects start.
|
1925
|
+
ShortTimeFFT: Class this property belongs to.
|
1926
|
+
"""
|
1927
|
+
p_max = self.p_max(n) # shorthand
|
1928
|
+
p0_ = self.p_min if p0 is None else p0
|
1929
|
+
p1_ = p_max if p1 is None else p1
|
1930
|
+
if not (self.p_min <= p0_ < p1_ <= p_max):
|
1931
|
+
raise ValueError(f"Invalid Parameter {p0=}, {p1=}, i.e., " +
|
1932
|
+
f"{self.p_min=} <= p0 < p1 <= {p_max=} " +
|
1933
|
+
f"does not hold for signal length {n=}!")
|
1934
|
+
return p0_, p1_
|
1935
|
+
|
1936
|
+
def t(self, n: int, p0: int | None = None, p1: int | None = None,
|
1937
|
+
k_offset: int = 0) -> np.ndarray:
|
1938
|
+
"""Times of STFT for an input signal with `n` samples.
|
1939
|
+
|
1940
|
+
Returns a 1d array with times of the `~ShortTimeFFT.stft` values with
|
1941
|
+
the same parametrization. Note that the slices are
|
1942
|
+
``delta_t = hop * T`` time units apart.
|
1943
|
+
|
1944
|
+
Parameters
|
1945
|
+
----------
|
1946
|
+
n
|
1947
|
+
Number of sample of the input signal.
|
1948
|
+
p0
|
1949
|
+
The first element of the range of slices to calculate. If ``None``
|
1950
|
+
then it is set to :attr:`p_min`, which is the smallest possible
|
1951
|
+
slice.
|
1952
|
+
p1
|
1953
|
+
The end of the array. If ``None`` then `p_max(n)` is used.
|
1954
|
+
k_offset
|
1955
|
+
Index of first sample (t = 0) in `x`.
|
1956
|
+
|
1957
|
+
Notes
|
1958
|
+
-----
|
1959
|
+
Note that the returned array is cached together with the method's call
|
1960
|
+
parameters to avoid unnecessary recalculations.
|
1961
|
+
|
1962
|
+
See Also
|
1963
|
+
--------
|
1964
|
+
delta_t: Time increment of STFT (``hop*T``)
|
1965
|
+
hop: Time increment in signal samples for sliding window.
|
1966
|
+
nearest_k_p: Nearest sample index k_p for which t[k_p] == t[p] holds.
|
1967
|
+
T: Sampling interval of input signal and of the window (``1/fs``).
|
1968
|
+
fs: Sampling frequency (being ``1/T``)
|
1969
|
+
ShortTimeFFT: Class this method belongs to.
|
1970
|
+
"""
|
1971
|
+
if not (n > 0 and isinstance(n, int | np.integer)):
|
1972
|
+
raise ValueError(f"Parameter {n=} is not a positive integer!")
|
1973
|
+
args = n, p0, p1, k_offset, self.T # since `self.T` is mutable, it's needed too
|
1974
|
+
last_args, last_return_value = self._cache_t
|
1975
|
+
if args == last_args: # use cached value:
|
1976
|
+
return last_return_value
|
1977
|
+
|
1978
|
+
p0, p1 = self.p_range(n, p0, p1)
|
1979
|
+
return_value = np.arange(p0, p1) * self.delta_t + k_offset * self.T
|
1980
|
+
|
1981
|
+
self._cache_t = args, return_value
|
1982
|
+
return return_value
|
1983
|
+
|
1984
|
+
def nearest_k_p(self, k: int, left: bool = True) -> int:
|
1985
|
+
"""Return nearest sample index k_p for which t[k_p] == t[p] holds.
|
1986
|
+
|
1987
|
+
The nearest next smaller time sample p (where t[p] is the center
|
1988
|
+
position of the window of the p-th slice) is p_k = k // `hop`.
|
1989
|
+
If `hop` is a divisor of `k` then `k` is returned.
|
1990
|
+
If `left` is set then p_k * `hop` is returned else (p_k+1) * `hop`.
|
1991
|
+
|
1992
|
+
This method can be used to slice an input signal into chunks for
|
1993
|
+
calculating the STFT and iSTFT incrementally.
|
1994
|
+
|
1995
|
+
See Also
|
1996
|
+
--------
|
1997
|
+
delta_t: Time increment of STFT (``hop*T``)
|
1998
|
+
hop: Time increment in signal samples for sliding window.
|
1999
|
+
T: Sampling interval of input signal and of the window (``1/fs``).
|
2000
|
+
fs: Sampling frequency (being ``1/T``)
|
2001
|
+
t: Times of STFT for an input signal with `n` samples.
|
2002
|
+
ShortTimeFFT: Class this method belongs to.
|
2003
|
+
"""
|
2004
|
+
p_q, remainder = divmod(k, self.hop)
|
2005
|
+
if remainder == 0:
|
2006
|
+
return k
|
2007
|
+
return p_q * self.hop if left else (p_q + 1) * self.hop
|
2008
|
+
|
2009
|
+
@property
|
2010
|
+
def delta_f(self) -> float:
|
2011
|
+
"""Width of the frequency bins of the STFT.
|
2012
|
+
|
2013
|
+
Return the frequency interval `delta_f` = 1 / (`mfft` * `T`).
|
2014
|
+
|
2015
|
+
See Also
|
2016
|
+
--------
|
2017
|
+
delta_t: Time increment of STFT.
|
2018
|
+
f_pts: Number of points along the frequency axis.
|
2019
|
+
f: Frequencies values of the STFT.
|
2020
|
+
mfft: Length of the input for FFT used.
|
2021
|
+
T: Sampling interval.
|
2022
|
+
t: Times of STFT for an input signal with `n` samples.
|
2023
|
+
ShortTimeFFT: Class this property belongs to.
|
2024
|
+
"""
|
2025
|
+
return 1 / (self.mfft * self.T)
|
2026
|
+
|
2027
|
+
@property
|
2028
|
+
def f_pts(self) -> int:
|
2029
|
+
"""Number of points along the frequency axis.
|
2030
|
+
|
2031
|
+
See Also
|
2032
|
+
--------
|
2033
|
+
delta_f: Width of the frequency bins of the STFT.
|
2034
|
+
f: Frequencies values of the STFT.
|
2035
|
+
mfft: Length of the input for FFT used.
|
2036
|
+
ShortTimeFFT: Class this property belongs to.
|
2037
|
+
"""
|
2038
|
+
return self.mfft // 2 + 1 if self.onesided_fft else self.mfft
|
2039
|
+
|
2040
|
+
@property
|
2041
|
+
def onesided_fft(self) -> bool:
|
2042
|
+
"""Return True if a one-sided FFT is used.
|
2043
|
+
|
2044
|
+
Returns ``True`` if `fft_mode` is either 'onesided' or 'onesided2X'.
|
2045
|
+
|
2046
|
+
See Also
|
2047
|
+
--------
|
2048
|
+
fft_mode: Utilized FFT ('twosided', 'centered', 'onesided' or
|
2049
|
+
'onesided2X')
|
2050
|
+
ShortTimeFFT: Class this property belongs to.
|
2051
|
+
"""
|
2052
|
+
return self.fft_mode in {'onesided', 'onesided2X'}
|
2053
|
+
|
2054
|
+
@property
|
2055
|
+
def f(self) -> np.ndarray:
|
2056
|
+
"""Frequencies values of the STFT.
|
2057
|
+
|
2058
|
+
A 1d array of length `f_pts` with `delta_f` spaced entries is returned.
|
2059
|
+
This array is calculated lazily.
|
2060
|
+
|
2061
|
+
See Also
|
2062
|
+
--------
|
2063
|
+
delta_f: Width of the frequency bins of the STFT.
|
2064
|
+
f_pts: Number of points along the frequency axis.
|
2065
|
+
mfft: Length of the input for FFT used.
|
2066
|
+
ShortTimeFFT: Class this property belongs to.
|
2067
|
+
"""
|
2068
|
+
last_state, last_return_value = self._cache_f
|
2069
|
+
current_state = self.fft_mode, self.mfft, self.T
|
2070
|
+
if current_state == last_state: # use cached value:
|
2071
|
+
return last_return_value
|
2072
|
+
|
2073
|
+
if self.fft_mode in {'onesided', 'onesided2X'}:
|
2074
|
+
return_value = fft_lib.rfftfreq(self.mfft, self.T)
|
2075
|
+
elif self.fft_mode == 'twosided':
|
2076
|
+
return_value = fft_lib.fftfreq(self.mfft, self.T)
|
2077
|
+
elif self.fft_mode == 'centered':
|
2078
|
+
return_value = fft_lib.fftshift(fft_lib.fftfreq(self.mfft, self.T))
|
2079
|
+
else: # This should never happen but makes the Linters happy:
|
2080
|
+
fft_modes = get_args(FFT_MODE_TYPE)
|
2081
|
+
raise RuntimeError(f"{self.fft_mode=} not in {fft_modes}!")
|
2082
|
+
self._cache_f = current_state, return_value
|
2083
|
+
return return_value
|
2084
|
+
|
2085
|
+
def _fft_func(self, x: np.ndarray) -> np.ndarray:
|
2086
|
+
"""FFT based on the `fft_mode`, `mfft`, `scaling` and `phase_shift`
|
2087
|
+
attributes.
|
2088
|
+
|
2089
|
+
For multidimensional arrays the transformation is carried out on the
|
2090
|
+
last axis.
|
2091
|
+
"""
|
2092
|
+
if self.phase_shift is not None:
|
2093
|
+
if x.shape[-1] < self.mfft: # zero pad if needed
|
2094
|
+
z_shape = list(x.shape)
|
2095
|
+
z_shape[-1] = self.mfft - x.shape[-1]
|
2096
|
+
x = np.hstack((x, np.zeros(z_shape, dtype=x.dtype)))
|
2097
|
+
p_s = (self.phase_shift + self.m_num_mid) % self.m_num
|
2098
|
+
x = np.roll(x, -p_s, axis=-1)
|
2099
|
+
|
2100
|
+
if self.fft_mode == 'twosided':
|
2101
|
+
return fft_lib.fft(x, n=self.mfft, axis=-1)
|
2102
|
+
if self.fft_mode == 'centered':
|
2103
|
+
return fft_lib.fftshift(fft_lib.fft(x, self.mfft, axis=-1), axes=-1)
|
2104
|
+
if self.fft_mode == 'onesided':
|
2105
|
+
return fft_lib.rfft(x, n=self.mfft, axis=-1)
|
2106
|
+
if self.fft_mode == 'onesided2X':
|
2107
|
+
X = fft_lib.rfft(x, n=self.mfft, axis=-1)
|
2108
|
+
# Either squared magnitude (psd) or magnitude is doubled:
|
2109
|
+
fac = np.sqrt(2) if self.scaling == 'psd' else 2
|
2110
|
+
# For even input length, the last entry is unpaired:
|
2111
|
+
X[..., 1: -1 if self.mfft % 2 == 0 else None] *= fac
|
2112
|
+
return X
|
2113
|
+
# This should never happen but makes the Linter happy:
|
2114
|
+
fft_modes = get_args(FFT_MODE_TYPE)
|
2115
|
+
raise RuntimeError(f"{self.fft_mode=} not in {fft_modes}!")
|
2116
|
+
|
2117
|
+
def _ifft_func(self, X: np.ndarray) -> np.ndarray:
|
2118
|
+
"""Inverse to `_fft_func`.
|
2119
|
+
|
2120
|
+
Returned is an array of length `m_num`. If the FFT is `onesided`
|
2121
|
+
then a float array is returned else a complex array is returned.
|
2122
|
+
For multidimensional arrays the transformation is carried out on the
|
2123
|
+
last axis.
|
2124
|
+
"""
|
2125
|
+
if self.fft_mode == 'twosided':
|
2126
|
+
x = fft_lib.ifft(X, n=self.mfft, axis=-1)
|
2127
|
+
elif self.fft_mode == 'centered':
|
2128
|
+
x = fft_lib.ifft(fft_lib.ifftshift(X, axes=-1), n=self.mfft, axis=-1)
|
2129
|
+
elif self.fft_mode == 'onesided':
|
2130
|
+
x = fft_lib.irfft(X, n=self.mfft, axis=-1)
|
2131
|
+
elif self.fft_mode == 'onesided2X':
|
2132
|
+
Xc = X.copy() # we do not want to modify function parameters
|
2133
|
+
fac = np.sqrt(2) if self.scaling == 'psd' else 2
|
2134
|
+
# For even length X the last value is not paired with a negative
|
2135
|
+
# value on the two-sided FFT:
|
2136
|
+
q1 = -1 if self.mfft % 2 == 0 else None
|
2137
|
+
Xc[..., 1:q1] /= fac
|
2138
|
+
x = fft_lib.irfft(Xc, n=self.mfft, axis=-1)
|
2139
|
+
else: # This should never happen but makes the Linter happy:
|
2140
|
+
raise RuntimeError(f"{self.fft_mode=} not in {get_args(FFT_MODE_TYPE)}!")
|
2141
|
+
|
2142
|
+
if self.phase_shift is None:
|
2143
|
+
return x[..., :self.m_num]
|
2144
|
+
p_s = (self.phase_shift + self.m_num_mid) % self.m_num
|
2145
|
+
return np.roll(x, p_s, axis=-1)[..., :self.m_num]
|
2146
|
+
|
2147
|
+
def extent(self, n: int, axes_seq: Literal['tf', 'ft'] = 'tf',
|
2148
|
+
center_bins: bool = False) -> tuple[float, float, float, float]:
|
2149
|
+
"""Return minimum and maximum values time-frequency values.
|
2150
|
+
|
2151
|
+
A tuple with four floats ``(t0, t1, f0, f1)`` for 'tf' and
|
2152
|
+
``(f0, f1, t0, t1)`` for 'ft' is returned describing the corners
|
2153
|
+
of the time-frequency domain of the `~ShortTimeFFT.stft`.
|
2154
|
+
That tuple can be passed to `matplotlib.pyplot.imshow` as a parameter
|
2155
|
+
with the same name.
|
2156
|
+
|
2157
|
+
Parameters
|
2158
|
+
----------
|
2159
|
+
n : int
|
2160
|
+
Number of samples in input signal.
|
2161
|
+
axes_seq : {'tf', 'ft'}
|
2162
|
+
Return time extent first and then frequency extent or vice versa.
|
2163
|
+
center_bins: bool
|
2164
|
+
If set (default ``False``), the values of the time slots and
|
2165
|
+
frequency bins are moved from the side the middle. This is useful,
|
2166
|
+
when plotting the `~ShortTimeFFT.stft` values as step functions,
|
2167
|
+
i.e., with no interpolation.
|
2168
|
+
|
2169
|
+
See Also
|
2170
|
+
--------
|
2171
|
+
:func:`matplotlib.pyplot.imshow`: Display data as an image.
|
2172
|
+
:class:`scipy.signal.ShortTimeFFT`: Class this method belongs to.
|
2173
|
+
|
2174
|
+
Examples
|
2175
|
+
--------
|
2176
|
+
The following two plots illustrate the effect of the parameter `center_bins`:
|
2177
|
+
The grid lines represent the three time and the four frequency values of the
|
2178
|
+
STFT.
|
2179
|
+
The left plot, where ``(t0, t1, f0, f1) = (0, 3, 0, 4)`` is passed as parameter
|
2180
|
+
``extent`` to `~matplotlib.pyplot.imshow`, shows the standard behavior of the
|
2181
|
+
time and frequency values being at the lower edge of the corrsponding bin.
|
2182
|
+
The right plot, with ``(t0, t1, f0, f1) = (-0.5, 2.5, -0.5, 3.5)``, shows that
|
2183
|
+
the bins are centered over the respective values when passing
|
2184
|
+
``center_bins=True``.
|
2185
|
+
|
2186
|
+
>>> import matplotlib.pyplot as plt
|
2187
|
+
>>> import numpy as np
|
2188
|
+
>>> from scipy.signal import ShortTimeFFT
|
2189
|
+
...
|
2190
|
+
>>> n, m = 12, 6
|
2191
|
+
>>> SFT = ShortTimeFFT.from_window('hann', fs=m, nperseg=m, noverlap=0)
|
2192
|
+
>>> Sxx = SFT.stft(np.cos(np.arange(n))) # produces a colorful plot
|
2193
|
+
...
|
2194
|
+
>>> fig, axx = plt.subplots(1, 2, tight_layout=True, figsize=(6., 4.))
|
2195
|
+
>>> for ax_, center_bins in zip(axx, (False, True)):
|
2196
|
+
... ax_.imshow(abs(Sxx), origin='lower', interpolation=None, aspect='equal',
|
2197
|
+
... cmap='viridis', extent=SFT.extent(n, 'tf', center_bins))
|
2198
|
+
... ax_.set_title(f"{center_bins=}")
|
2199
|
+
... ax_.set_xlabel(f"Time ({SFT.p_num(n)} points, Δt={SFT.delta_t})")
|
2200
|
+
... ax_.set_ylabel(f"Frequency ({SFT.f_pts} points, Δf={SFT.delta_f})")
|
2201
|
+
... ax_.set_xticks(SFT.t(n)) # vertical grid line are timestamps
|
2202
|
+
... ax_.set_yticks(SFT.f) # horizontal grid line are frequency values
|
2203
|
+
... ax_.grid(True)
|
2204
|
+
>>> plt.show()
|
2205
|
+
|
2206
|
+
Note that the step-like behavior with the constant colors is caused by passing
|
2207
|
+
``interpolation=None`` to `~matplotlib.pyplot.imshow`.
|
2208
|
+
"""
|
2209
|
+
if axes_seq not in ('tf', 'ft'):
|
2210
|
+
raise ValueError(f"Parameter {axes_seq=} not in ['tf', 'ft']!")
|
2211
|
+
|
2212
|
+
if self.onesided_fft:
|
2213
|
+
q0, q1 = 0, self.f_pts
|
2214
|
+
elif self.fft_mode == 'centered':
|
2215
|
+
q0 = -(self.mfft // 2)
|
2216
|
+
q1 = self.mfft // 2 if self.mfft % 2 == 0 else self.mfft // 2 + 1
|
2217
|
+
else:
|
2218
|
+
raise ValueError(f"Attribute fft_mode={self.fft_mode} must be " +
|
2219
|
+
"in ['centered', 'onesided', 'onesided2X']")
|
2220
|
+
|
2221
|
+
p0, p1 = self.p_min, self.p_max(n) # shorthand
|
2222
|
+
if center_bins:
|
2223
|
+
t0, t1 = self.delta_t * (p0 - 0.5), self.delta_t * (p1 - 0.5)
|
2224
|
+
f0, f1 = self.delta_f * (q0 - 0.5), self.delta_f * (q1 - 0.5)
|
2225
|
+
else:
|
2226
|
+
t0, t1 = self.delta_t * p0, self.delta_t * p1
|
2227
|
+
f0, f1 = self.delta_f * q0, self.delta_f * q1
|
2228
|
+
return (t0, t1, f0, f1) if axes_seq == 'tf' else (f0, f1, t0, t1)
|