Coverage for /usr/lib/python3/dist-packages/scipy/stats/_stats_py.py: 10%

2010 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1# Copyright 2002 Gary Strangman. All rights reserved 

2# Copyright 2002-2016 The SciPy Developers 

3# 

4# The original code from Gary Strangman was heavily adapted for 

5# use in SciPy by Travis Oliphant. The original code came with the 

6# following disclaimer: 

7# 

8# This software is provided "as-is". There are no expressed or implied 

9# warranties of any kind, including, but not limited to, the warranties 

10# of merchantability and fitness for a given application. In no event 

11# shall Gary Strangman be liable for any direct, indirect, incidental, 

12# special, exemplary or consequential damages (including, but not limited 

13# to, loss of use, data or profits, or business interruption) however 

14# caused and on any theory of liability, whether in contract, strict 

15# liability or tort (including negligence or otherwise) arising in any way 

16# out of the use of this software, even if advised of the possibility of 

17# such damage. 

18 

19""" 

20A collection of basic statistical functions for Python. 

21 

22References 

23---------- 

24.. [CRCProbStat2000] Zwillinger, D. and Kokoska, S. (2000). CRC Standard 

25 Probability and Statistics Tables and Formulae. Chapman & Hall: New 

26 York. 2000. 

27 

28""" 

29import warnings 

30import math 

31from math import gcd 

32from collections import namedtuple 

33 

34import numpy as np 

35from numpy import array, asarray, ma 

36from numpy.lib import NumpyVersion 

37from numpy.testing import suppress_warnings 

38 

39from scipy.spatial.distance import cdist 

40from scipy.ndimage import _measurements 

41from scipy._lib._util import (check_random_state, MapWrapper, _get_nan, 

42 rng_integers, _rename_parameter, _contains_nan) 

43 

44import scipy.special as special 

45from scipy import linalg 

46from . import distributions 

47from . import _mstats_basic as mstats_basic 

48from ._stats_mstats_common import (_find_repeats, linregress, theilslopes, 

49 siegelslopes) 

50from ._stats import (_kendall_dis, _toint64, _weightedrankedtau, 

51 _local_correlations) 

52from dataclasses import dataclass 

53from ._hypotests import _all_partitions 

54from ._stats_pythran import _compute_outer_prob_inside_method 

55from ._resampling import (MonteCarloMethod, PermutationMethod, BootstrapMethod, 

56 monte_carlo_test, permutation_test, bootstrap, 

57 _batch_generator) 

58from ._axis_nan_policy import (_axis_nan_policy_factory, 

59 _broadcast_concatenate) 

60from ._binomtest import _binary_search_for_binom_tst as _binary_search 

61from scipy._lib._bunch import _make_tuple_bunch 

62from scipy import stats 

63from scipy.optimize import root_scalar 

64 

65 

66# Functions/classes in other files should be added in `__init__.py`, not here 

67__all__ = ['find_repeats', 'gmean', 'hmean', 'pmean', 'mode', 'tmean', 'tvar', 

68 'tmin', 'tmax', 'tstd', 'tsem', 'moment', 

69 'skew', 'kurtosis', 'describe', 'skewtest', 'kurtosistest', 

70 'normaltest', 'jarque_bera', 

71 'scoreatpercentile', 'percentileofscore', 

72 'cumfreq', 'relfreq', 'obrientransform', 

73 'sem', 'zmap', 'zscore', 'gzscore', 'iqr', 'gstd', 

74 'median_abs_deviation', 

75 'sigmaclip', 'trimboth', 'trim1', 'trim_mean', 

76 'f_oneway', 'pearsonr', 'fisher_exact', 

77 'spearmanr', 'pointbiserialr', 

78 'kendalltau', 'weightedtau', 'multiscale_graphcorr', 

79 'linregress', 'siegelslopes', 'theilslopes', 'ttest_1samp', 

80 'ttest_ind', 'ttest_ind_from_stats', 'ttest_rel', 

81 'kstest', 'ks_1samp', 'ks_2samp', 

82 'chisquare', 'power_divergence', 

83 'tiecorrect', 'ranksums', 'kruskal', 'friedmanchisquare', 

84 'rankdata', 

85 'combine_pvalues', 'wasserstein_distance', 'energy_distance', 

86 'brunnermunzel', 'alexandergovern', 

87 'expectile', ] 

88 

89 

90def _chk_asarray(a, axis): 

91 if axis is None: 

92 a = np.ravel(a) 

93 outaxis = 0 

94 else: 

95 a = np.asarray(a) 

96 outaxis = axis 

97 

98 if a.ndim == 0: 

99 a = np.atleast_1d(a) 

100 

101 return a, outaxis 

102 

103 

104def _chk2_asarray(a, b, axis): 

105 if axis is None: 

106 a = np.ravel(a) 

107 b = np.ravel(b) 

108 outaxis = 0 

109 else: 

110 a = np.asarray(a) 

111 b = np.asarray(b) 

112 outaxis = axis 

113 

114 if a.ndim == 0: 

115 a = np.atleast_1d(a) 

116 if b.ndim == 0: 

117 b = np.atleast_1d(b) 

118 

119 return a, b, outaxis 

120 

121 

122SignificanceResult = _make_tuple_bunch('SignificanceResult', 

123 ['statistic', 'pvalue'], []) 

124 

125 

126# note that `weights` are paired with `x` 

127@_axis_nan_policy_factory( 

128 lambda x: x, n_samples=1, n_outputs=1, too_small=0, paired=True, 

129 result_to_tuple=lambda x: (x,), kwd_samples=['weights']) 

130def gmean(a, axis=0, dtype=None, weights=None): 

131 r"""Compute the weighted geometric mean along the specified axis. 

132 

133 The weighted geometric mean of the array :math:`a_i` associated to weights 

134 :math:`w_i` is: 

135 

136 .. math:: 

137 

138 \exp \left( \frac{ \sum_{i=1}^n w_i \ln a_i }{ \sum_{i=1}^n w_i } 

139 \right) \, , 

140 

141 and, with equal weights, it gives: 

142 

143 .. math:: 

144 

145 \sqrt[n]{ \prod_{i=1}^n a_i } \, . 

146 

147 Parameters 

148 ---------- 

149 a : array_like 

150 Input array or object that can be converted to an array. 

151 axis : int or None, optional 

152 Axis along which the geometric mean is computed. Default is 0. 

153 If None, compute over the whole array `a`. 

154 dtype : dtype, optional 

155 Type to which the input arrays are cast before the calculation is 

156 performed. 

157 weights : array_like, optional 

158 The `weights` array must be broadcastable to the same shape as `a`. 

159 Default is None, which gives each value a weight of 1.0. 

160 

161 Returns 

162 ------- 

163 gmean : ndarray 

164 See `dtype` parameter above. 

165 

166 See Also 

167 -------- 

168 numpy.mean : Arithmetic average 

169 numpy.average : Weighted average 

170 hmean : Harmonic mean 

171 

172 References 

173 ---------- 

174 .. [1] "Weighted Geometric Mean", *Wikipedia*, 

175 https://en.wikipedia.org/wiki/Weighted_geometric_mean. 

176 .. [2] Grossman, J., Grossman, M., Katz, R., "Averages: A New Approach", 

177 Archimedes Foundation, 1983 

178 

179 Examples 

180 -------- 

181 >>> from scipy.stats import gmean 

182 >>> gmean([1, 4]) 

183 2.0 

184 >>> gmean([1, 2, 3, 4, 5, 6, 7]) 

185 3.3800151591412964 

186 >>> gmean([1, 4, 7], weights=[3, 1, 3]) 

187 2.80668351922014 

188 

189 """ 

190 

191 a = np.asarray(a, dtype=dtype) 

192 

193 if weights is not None: 

194 weights = np.asarray(weights, dtype=dtype) 

195 

196 with np.errstate(divide='ignore'): 

197 log_a = np.log(a) 

198 

199 return np.exp(np.average(log_a, axis=axis, weights=weights)) 

200 

201 

202@_axis_nan_policy_factory( 

203 lambda x: x, n_samples=1, n_outputs=1, too_small=0, paired=True, 

204 result_to_tuple=lambda x: (x,), kwd_samples=['weights']) 

205def hmean(a, axis=0, dtype=None, *, weights=None): 

206 r"""Calculate the weighted harmonic mean along the specified axis. 

207 

208 The weighted harmonic mean of the array :math:`a_i` associated to weights 

209 :math:`w_i` is: 

210 

211 .. math:: 

212 

213 \frac{ \sum_{i=1}^n w_i }{ \sum_{i=1}^n \frac{w_i}{a_i} } \, , 

214 

215 and, with equal weights, it gives: 

216 

217 .. math:: 

218 

219 \frac{ n }{ \sum_{i=1}^n \frac{1}{a_i} } \, . 

220 

221 Parameters 

222 ---------- 

223 a : array_like 

224 Input array, masked array or object that can be converted to an array. 

225 axis : int or None, optional 

226 Axis along which the harmonic mean is computed. Default is 0. 

227 If None, compute over the whole array `a`. 

228 dtype : dtype, optional 

229 Type of the returned array and of the accumulator in which the 

230 elements are summed. If `dtype` is not specified, it defaults to the 

231 dtype of `a`, unless `a` has an integer `dtype` with a precision less 

232 than that of the default platform integer. In that case, the default 

233 platform integer is used. 

234 weights : array_like, optional 

235 The weights array can either be 1-D (in which case its length must be 

236 the size of `a` along the given `axis`) or of the same shape as `a`. 

237 Default is None, which gives each value a weight of 1.0. 

238 

239 .. versionadded:: 1.9 

240 

241 Returns 

242 ------- 

243 hmean : ndarray 

244 See `dtype` parameter above. 

245 

246 See Also 

247 -------- 

248 numpy.mean : Arithmetic average 

249 numpy.average : Weighted average 

250 gmean : Geometric mean 

251 

252 Notes 

253 ----- 

254 The harmonic mean is computed over a single dimension of the input 

255 array, axis=0 by default, or all values in the array if axis=None. 

256 float64 intermediate and return values are used for integer inputs. 

257 

258 References 

259 ---------- 

260 .. [1] "Weighted Harmonic Mean", *Wikipedia*, 

261 https://en.wikipedia.org/wiki/Harmonic_mean#Weighted_harmonic_mean 

262 .. [2] Ferger, F., "The nature and use of the harmonic mean", Journal of 

263 the American Statistical Association, vol. 26, pp. 36-40, 1931 

264 

265 Examples 

266 -------- 

267 >>> from scipy.stats import hmean 

268 >>> hmean([1, 4]) 

269 1.6000000000000001 

270 >>> hmean([1, 2, 3, 4, 5, 6, 7]) 

271 2.6997245179063363 

272 >>> hmean([1, 4, 7], weights=[3, 1, 3]) 

273 1.9029126213592233 

274 

275 """ 

276 if not isinstance(a, np.ndarray): 

277 a = np.array(a, dtype=dtype) 

278 elif dtype: 

279 # Must change the default dtype allowing array type 

280 if isinstance(a, np.ma.MaskedArray): 

281 a = np.ma.asarray(a, dtype=dtype) 

282 else: 

283 a = np.asarray(a, dtype=dtype) 

284 

285 if np.all(a >= 0): 

286 # Harmonic mean only defined if greater than or equal to zero. 

287 if weights is not None: 

288 weights = np.asanyarray(weights, dtype=dtype) 

289 

290 with np.errstate(divide='ignore'): 

291 return 1.0 / np.average(1.0 / a, axis=axis, weights=weights) 

292 else: 

293 raise ValueError("Harmonic mean only defined if all elements greater " 

294 "than or equal to zero") 

295 

296 

297@_axis_nan_policy_factory( 

298 lambda x: x, n_samples=1, n_outputs=1, too_small=0, paired=True, 

299 result_to_tuple=lambda x: (x,), kwd_samples=['weights']) 

300def pmean(a, p, *, axis=0, dtype=None, weights=None): 

301 r"""Calculate the weighted power mean along the specified axis. 

302 

303 The weighted power mean of the array :math:`a_i` associated to weights 

304 :math:`w_i` is: 

305 

306 .. math:: 

307 

308 \left( \frac{ \sum_{i=1}^n w_i a_i^p }{ \sum_{i=1}^n w_i } 

309 \right)^{ 1 / p } \, , 

310 

311 and, with equal weights, it gives: 

312 

313 .. math:: 

314 

315 \left( \frac{ 1 }{ n } \sum_{i=1}^n a_i^p \right)^{ 1 / p } \, . 

316 

317 When ``p=0``, it returns the geometric mean. 

318 

319 This mean is also called generalized mean or Hölder mean, and must not be 

320 confused with the Kolmogorov generalized mean, also called 

321 quasi-arithmetic mean or generalized f-mean [3]_. 

322 

323 Parameters 

324 ---------- 

325 a : array_like 

326 Input array, masked array or object that can be converted to an array. 

327 p : int or float 

328 Exponent. 

329 axis : int or None, optional 

330 Axis along which the power mean is computed. Default is 0. 

331 If None, compute over the whole array `a`. 

332 dtype : dtype, optional 

333 Type of the returned array and of the accumulator in which the 

334 elements are summed. If `dtype` is not specified, it defaults to the 

335 dtype of `a`, unless `a` has an integer `dtype` with a precision less 

336 than that of the default platform integer. In that case, the default 

337 platform integer is used. 

338 weights : array_like, optional 

339 The weights array can either be 1-D (in which case its length must be 

340 the size of `a` along the given `axis`) or of the same shape as `a`. 

341 Default is None, which gives each value a weight of 1.0. 

342 

343 Returns 

344 ------- 

345 pmean : ndarray, see `dtype` parameter above. 

346 Output array containing the power mean values. 

347 

348 See Also 

349 -------- 

350 numpy.average : Weighted average 

351 gmean : Geometric mean 

352 hmean : Harmonic mean 

353 

354 Notes 

355 ----- 

356 The power mean is computed over a single dimension of the input 

357 array, ``axis=0`` by default, or all values in the array if ``axis=None``. 

358 float64 intermediate and return values are used for integer inputs. 

359 

360 .. versionadded:: 1.9 

361 

362 References 

363 ---------- 

364 .. [1] "Generalized Mean", *Wikipedia*, 

365 https://en.wikipedia.org/wiki/Generalized_mean 

366 .. [2] Norris, N., "Convexity properties of generalized mean value 

367 functions", The Annals of Mathematical Statistics, vol. 8, 

368 pp. 118-120, 1937 

369 .. [3] Bullen, P.S., Handbook of Means and Their Inequalities, 2003 

370 

371 Examples 

372 -------- 

373 >>> from scipy.stats import pmean, hmean, gmean 

374 >>> pmean([1, 4], 1.3) 

375 2.639372938300652 

376 >>> pmean([1, 2, 3, 4, 5, 6, 7], 1.3) 

377 4.157111214492084 

378 >>> pmean([1, 4, 7], -2, weights=[3, 1, 3]) 

379 1.4969684896631954 

380 

381 For p=-1, power mean is equal to harmonic mean: 

382 

383 >>> pmean([1, 4, 7], -1, weights=[3, 1, 3]) 

384 1.9029126213592233 

385 >>> hmean([1, 4, 7], weights=[3, 1, 3]) 

386 1.9029126213592233 

387 

388 For p=0, power mean is defined as the geometric mean: 

389 

390 >>> pmean([1, 4, 7], 0, weights=[3, 1, 3]) 

391 2.80668351922014 

392 >>> gmean([1, 4, 7], weights=[3, 1, 3]) 

393 2.80668351922014 

394 

395 """ 

396 if not isinstance(p, (int, float)): 

397 raise ValueError("Power mean only defined for exponent of type int or " 

398 "float.") 

399 if p == 0: 

400 return gmean(a, axis=axis, dtype=dtype, weights=weights) 

401 

402 if not isinstance(a, np.ndarray): 

403 a = np.array(a, dtype=dtype) 

404 elif dtype: 

405 # Must change the default dtype allowing array type 

406 if isinstance(a, np.ma.MaskedArray): 

407 a = np.ma.asarray(a, dtype=dtype) 

408 else: 

409 a = np.asarray(a, dtype=dtype) 

410 

411 if np.all(a >= 0): 

412 # Power mean only defined if greater than or equal to zero 

413 if weights is not None: 

414 weights = np.asanyarray(weights, dtype=dtype) 

415 

416 with np.errstate(divide='ignore'): 

417 return np.float_power( 

418 np.average(np.float_power(a, p), axis=axis, weights=weights), 

419 1/p) 

420 else: 

421 raise ValueError("Power mean only defined if all elements greater " 

422 "than or equal to zero") 

423 

424 

425ModeResult = namedtuple('ModeResult', ('mode', 'count')) 

426 

427 

428def _mode_result(mode, count): 

429 # When a slice is empty, `_axis_nan_policy` automatically produces 

430 # NaN for `mode` and `count`. This is a reasonable convention for `mode`, 

431 # but `count` should not be NaN; it should be zero. 

432 i = np.isnan(count) 

433 if i.shape == (): 

434 count = count.dtype(0) if i else count 

435 else: 

436 count[i] = 0 

437 return ModeResult(mode, count) 

438 

439 

440@_axis_nan_policy_factory(_mode_result, override={'vectorization': True, 

441 'nan_propagation': False}) 

442def mode(a, axis=0, nan_policy='propagate', keepdims=False): 

443 r"""Return an array of the modal (most common) value in the passed array. 

444 

445 If there is more than one such value, only one is returned. 

446 The bin-count for the modal bins is also returned. 

447 

448 Parameters 

449 ---------- 

450 a : array_like 

451 Numeric, n-dimensional array of which to find mode(s). 

452 axis : int or None, optional 

453 Axis along which to operate. Default is 0. If None, compute over 

454 the whole array `a`. 

455 nan_policy : {'propagate', 'raise', 'omit'}, optional 

456 Defines how to handle when input contains nan. 

457 The following options are available (default is 'propagate'): 

458 

459 * 'propagate': treats nan as it would treat any other value 

460 * 'raise': throws an error 

461 * 'omit': performs the calculations ignoring nan values 

462 keepdims : bool, optional 

463 If set to ``False``, the `axis` over which the statistic is taken 

464 is consumed (eliminated from the output array). If set to ``True``, 

465 the `axis` is retained with size one, and the result will broadcast 

466 correctly against the input array. 

467 

468 Returns 

469 ------- 

470 mode : ndarray 

471 Array of modal values. 

472 count : ndarray 

473 Array of counts for each mode. 

474 

475 Notes 

476 ----- 

477 The mode is calculated using `numpy.unique`. 

478 In NumPy versions 1.21 and after, all NaNs - even those with different 

479 binary representations - are treated as equivalent and counted as separate 

480 instances of the same value. 

481 

482 By convention, the mode of an empty array is NaN, and the associated count 

483 is zero. 

484 

485 Examples 

486 -------- 

487 >>> import numpy as np 

488 >>> a = np.array([[3, 0, 3, 7], 

489 ... [3, 2, 6, 2], 

490 ... [1, 7, 2, 8], 

491 ... [3, 0, 6, 1], 

492 ... [3, 2, 5, 5]]) 

493 >>> from scipy import stats 

494 >>> stats.mode(a, keepdims=True) 

495 ModeResult(mode=array([[3, 0, 6, 1]]), count=array([[4, 2, 2, 1]])) 

496 

497 To get mode of whole array, specify ``axis=None``: 

498 

499 >>> stats.mode(a, axis=None, keepdims=True) 

500 ModeResult(mode=[[3]], count=[[5]]) 

501 >>> stats.mode(a, axis=None, keepdims=False) 

502 ModeResult(mode=3, count=5) 

503 

504 """ # noqa: E501 

505 # `axis`, `nan_policy`, and `keepdims` are handled by `_axis_nan_policy` 

506 if not np.issubdtype(a.dtype, np.number): 

507 message = ("Argument `a` is not recognized as numeric. " 

508 "Support for input that cannot be coerced to a numeric " 

509 "array was deprecated in SciPy 1.9.0 and removed in SciPy " 

510 "1.11.0. Please consider `np.unique`.") 

511 raise TypeError(message) 

512 

513 if a.size == 0: 

514 NaN = _get_nan(a) 

515 return ModeResult(*np.array([NaN, 0], dtype=NaN.dtype)) 

516 

517 vals, cnts = np.unique(a, return_counts=True) 

518 modes, counts = vals[cnts.argmax()], cnts.max() 

519 return ModeResult(modes[()], counts[()]) 

520 

521 

522def _mask_to_limits(a, limits, inclusive): 

523 """Mask an array for values outside of given limits. 

524 

525 This is primarily a utility function. 

526 

527 Parameters 

528 ---------- 

529 a : array 

530 limits : (float or None, float or None) 

531 A tuple consisting of the (lower limit, upper limit). Values in the 

532 input array less than the lower limit or greater than the upper limit 

533 will be masked out. None implies no limit. 

534 inclusive : (bool, bool) 

535 A tuple consisting of the (lower flag, upper flag). These flags 

536 determine whether values exactly equal to lower or upper are allowed. 

537 

538 Returns 

539 ------- 

540 A MaskedArray. 

541 

542 Raises 

543 ------ 

544 A ValueError if there are no values within the given limits. 

545 

546 """ 

547 lower_limit, upper_limit = limits 

548 lower_include, upper_include = inclusive 

549 am = ma.MaskedArray(a) 

550 if lower_limit is not None: 

551 if lower_include: 

552 am = ma.masked_less(am, lower_limit) 

553 else: 

554 am = ma.masked_less_equal(am, lower_limit) 

555 

556 if upper_limit is not None: 

557 if upper_include: 

558 am = ma.masked_greater(am, upper_limit) 

559 else: 

560 am = ma.masked_greater_equal(am, upper_limit) 

561 

562 if am.count() == 0: 

563 raise ValueError("No array values within given limits") 

564 

565 return am 

566 

567 

568def tmean(a, limits=None, inclusive=(True, True), axis=None): 

569 """Compute the trimmed mean. 

570 

571 This function finds the arithmetic mean of given values, ignoring values 

572 outside the given `limits`. 

573 

574 Parameters 

575 ---------- 

576 a : array_like 

577 Array of values. 

578 limits : None or (lower limit, upper limit), optional 

579 Values in the input array less than the lower limit or greater than the 

580 upper limit will be ignored. When limits is None (default), then all 

581 values are used. Either of the limit values in the tuple can also be 

582 None representing a half-open interval. 

583 inclusive : (bool, bool), optional 

584 A tuple consisting of the (lower flag, upper flag). These flags 

585 determine whether values exactly equal to the lower or upper limits 

586 are included. The default value is (True, True). 

587 axis : int or None, optional 

588 Axis along which to compute test. Default is None. 

589 

590 Returns 

591 ------- 

592 tmean : ndarray 

593 Trimmed mean. 

594 

595 See Also 

596 -------- 

597 trim_mean : Returns mean after trimming a proportion from both tails. 

598 

599 Examples 

600 -------- 

601 >>> import numpy as np 

602 >>> from scipy import stats 

603 >>> x = np.arange(20) 

604 >>> stats.tmean(x) 

605 9.5 

606 >>> stats.tmean(x, (3,17)) 

607 10.0 

608 

609 """ 

610 a = asarray(a) 

611 if limits is None: 

612 return np.mean(a, axis) 

613 am = _mask_to_limits(a, limits, inclusive) 

614 mean = np.ma.filled(am.mean(axis=axis), fill_value=np.nan) 

615 return mean if mean.ndim > 0 else mean.item() 

616 

617 

618def tvar(a, limits=None, inclusive=(True, True), axis=0, ddof=1): 

619 """Compute the trimmed variance. 

620 

621 This function computes the sample variance of an array of values, 

622 while ignoring values which are outside of given `limits`. 

623 

624 Parameters 

625 ---------- 

626 a : array_like 

627 Array of values. 

628 limits : None or (lower limit, upper limit), optional 

629 Values in the input array less than the lower limit or greater than the 

630 upper limit will be ignored. When limits is None, then all values are 

631 used. Either of the limit values in the tuple can also be None 

632 representing a half-open interval. The default value is None. 

633 inclusive : (bool, bool), optional 

634 A tuple consisting of the (lower flag, upper flag). These flags 

635 determine whether values exactly equal to the lower or upper limits 

636 are included. The default value is (True, True). 

637 axis : int or None, optional 

638 Axis along which to operate. Default is 0. If None, compute over the 

639 whole array `a`. 

640 ddof : int, optional 

641 Delta degrees of freedom. Default is 1. 

642 

643 Returns 

644 ------- 

645 tvar : float 

646 Trimmed variance. 

647 

648 Notes 

649 ----- 

650 `tvar` computes the unbiased sample variance, i.e. it uses a correction 

651 factor ``n / (n - 1)``. 

652 

653 Examples 

654 -------- 

655 >>> import numpy as np 

656 >>> from scipy import stats 

657 >>> x = np.arange(20) 

658 >>> stats.tvar(x) 

659 35.0 

660 >>> stats.tvar(x, (3,17)) 

661 20.0 

662 

663 """ 

664 a = asarray(a) 

665 a = a.astype(float) 

666 if limits is None: 

667 return a.var(ddof=ddof, axis=axis) 

668 am = _mask_to_limits(a, limits, inclusive) 

669 amnan = am.filled(fill_value=np.nan) 

670 return np.nanvar(amnan, ddof=ddof, axis=axis) 

671 

672 

673def tmin(a, lowerlimit=None, axis=0, inclusive=True, nan_policy='propagate'): 

674 """Compute the trimmed minimum. 

675 

676 This function finds the miminum value of an array `a` along the 

677 specified axis, but only considering values greater than a specified 

678 lower limit. 

679 

680 Parameters 

681 ---------- 

682 a : array_like 

683 Array of values. 

684 lowerlimit : None or float, optional 

685 Values in the input array less than the given limit will be ignored. 

686 When lowerlimit is None, then all values are used. The default value 

687 is None. 

688 axis : int or None, optional 

689 Axis along which to operate. Default is 0. If None, compute over the 

690 whole array `a`. 

691 inclusive : {True, False}, optional 

692 This flag determines whether values exactly equal to the lower limit 

693 are included. The default value is True. 

694 nan_policy : {'propagate', 'raise', 'omit'}, optional 

695 Defines how to handle when input contains nan. 

696 The following options are available (default is 'propagate'): 

697 

698 * 'propagate': returns nan 

699 * 'raise': throws an error 

700 * 'omit': performs the calculations ignoring nan values 

701 

702 Returns 

703 ------- 

704 tmin : float, int or ndarray 

705 Trimmed minimum. 

706 

707 Examples 

708 -------- 

709 >>> import numpy as np 

710 >>> from scipy import stats 

711 >>> x = np.arange(20) 

712 >>> stats.tmin(x) 

713 0 

714 

715 >>> stats.tmin(x, 13) 

716 13 

717 

718 >>> stats.tmin(x, 13, inclusive=False) 

719 14 

720 

721 """ 

722 a, axis = _chk_asarray(a, axis) 

723 am = _mask_to_limits(a, (lowerlimit, None), (inclusive, False)) 

724 

725 contains_nan, nan_policy = _contains_nan(am, nan_policy) 

726 

727 if contains_nan and nan_policy == 'omit': 

728 am = ma.masked_invalid(am) 

729 

730 res = ma.minimum.reduce(am, axis).data 

731 if res.ndim == 0: 

732 return res[()] 

733 return res 

734 

735 

736def tmax(a, upperlimit=None, axis=0, inclusive=True, nan_policy='propagate'): 

737 """Compute the trimmed maximum. 

738 

739 This function computes the maximum value of an array along a given axis, 

740 while ignoring values larger than a specified upper limit. 

741 

742 Parameters 

743 ---------- 

744 a : array_like 

745 Array of values. 

746 upperlimit : None or float, optional 

747 Values in the input array greater than the given limit will be ignored. 

748 When upperlimit is None, then all values are used. The default value 

749 is None. 

750 axis : int or None, optional 

751 Axis along which to operate. Default is 0. If None, compute over the 

752 whole array `a`. 

753 inclusive : {True, False}, optional 

754 This flag determines whether values exactly equal to the upper limit 

755 are included. The default value is True. 

756 nan_policy : {'propagate', 'raise', 'omit'}, optional 

757 Defines how to handle when input contains nan. 

758 The following options are available (default is 'propagate'): 

759 

760 * 'propagate': returns nan 

761 * 'raise': throws an error 

762 * 'omit': performs the calculations ignoring nan values 

763 

764 Returns 

765 ------- 

766 tmax : float, int or ndarray 

767 Trimmed maximum. 

768 

769 Examples 

770 -------- 

771 >>> import numpy as np 

772 >>> from scipy import stats 

773 >>> x = np.arange(20) 

774 >>> stats.tmax(x) 

775 19 

776 

777 >>> stats.tmax(x, 13) 

778 13 

779 

780 >>> stats.tmax(x, 13, inclusive=False) 

781 12 

782 

783 """ 

784 a, axis = _chk_asarray(a, axis) 

785 am = _mask_to_limits(a, (None, upperlimit), (False, inclusive)) 

786 

787 contains_nan, nan_policy = _contains_nan(am, nan_policy) 

788 

789 if contains_nan and nan_policy == 'omit': 

790 am = ma.masked_invalid(am) 

791 

792 res = ma.maximum.reduce(am, axis).data 

793 if res.ndim == 0: 

794 return res[()] 

795 return res 

796 

797 

798def tstd(a, limits=None, inclusive=(True, True), axis=0, ddof=1): 

799 """Compute the trimmed sample standard deviation. 

800 

801 This function finds the sample standard deviation of given values, 

802 ignoring values outside the given `limits`. 

803 

804 Parameters 

805 ---------- 

806 a : array_like 

807 Array of values. 

808 limits : None or (lower limit, upper limit), optional 

809 Values in the input array less than the lower limit or greater than the 

810 upper limit will be ignored. When limits is None, then all values are 

811 used. Either of the limit values in the tuple can also be None 

812 representing a half-open interval. The default value is None. 

813 inclusive : (bool, bool), optional 

814 A tuple consisting of the (lower flag, upper flag). These flags 

815 determine whether values exactly equal to the lower or upper limits 

816 are included. The default value is (True, True). 

817 axis : int or None, optional 

818 Axis along which to operate. Default is 0. If None, compute over the 

819 whole array `a`. 

820 ddof : int, optional 

821 Delta degrees of freedom. Default is 1. 

822 

823 Returns 

824 ------- 

825 tstd : float 

826 Trimmed sample standard deviation. 

827 

828 Notes 

829 ----- 

830 `tstd` computes the unbiased sample standard deviation, i.e. it uses a 

831 correction factor ``n / (n - 1)``. 

832 

833 Examples 

834 -------- 

835 >>> import numpy as np 

836 >>> from scipy import stats 

837 >>> x = np.arange(20) 

838 >>> stats.tstd(x) 

839 5.9160797830996161 

840 >>> stats.tstd(x, (3,17)) 

841 4.4721359549995796 

842 

843 """ 

844 return np.sqrt(tvar(a, limits, inclusive, axis, ddof)) 

845 

846 

847def tsem(a, limits=None, inclusive=(True, True), axis=0, ddof=1): 

848 """Compute the trimmed standard error of the mean. 

849 

850 This function finds the standard error of the mean for given 

851 values, ignoring values outside the given `limits`. 

852 

853 Parameters 

854 ---------- 

855 a : array_like 

856 Array of values. 

857 limits : None or (lower limit, upper limit), optional 

858 Values in the input array less than the lower limit or greater than the 

859 upper limit will be ignored. When limits is None, then all values are 

860 used. Either of the limit values in the tuple can also be None 

861 representing a half-open interval. The default value is None. 

862 inclusive : (bool, bool), optional 

863 A tuple consisting of the (lower flag, upper flag). These flags 

864 determine whether values exactly equal to the lower or upper limits 

865 are included. The default value is (True, True). 

866 axis : int or None, optional 

867 Axis along which to operate. Default is 0. If None, compute over the 

868 whole array `a`. 

869 ddof : int, optional 

870 Delta degrees of freedom. Default is 1. 

871 

872 Returns 

873 ------- 

874 tsem : float 

875 Trimmed standard error of the mean. 

876 

877 Notes 

878 ----- 

879 `tsem` uses unbiased sample standard deviation, i.e. it uses a 

880 correction factor ``n / (n - 1)``. 

881 

882 Examples 

883 -------- 

884 >>> import numpy as np 

885 >>> from scipy import stats 

886 >>> x = np.arange(20) 

887 >>> stats.tsem(x) 

888 1.3228756555322954 

889 >>> stats.tsem(x, (3,17)) 

890 1.1547005383792515 

891 

892 """ 

893 a = np.asarray(a).ravel() 

894 if limits is None: 

895 return a.std(ddof=ddof) / np.sqrt(a.size) 

896 

897 am = _mask_to_limits(a, limits, inclusive) 

898 sd = np.sqrt(np.ma.var(am, ddof=ddof, axis=axis)) 

899 return sd / np.sqrt(am.count()) 

900 

901 

902##################################### 

903# MOMENTS # 

904##################################### 

905 

906 

907def _moment_outputs(kwds): 

908 moment = np.atleast_1d(kwds.get('moment', 1)) 

909 if moment.size == 0: 

910 raise ValueError("'moment' must be a scalar or a non-empty 1D " 

911 "list/array.") 

912 return len(moment) 

913 

914 

915def _moment_result_object(*args): 

916 if len(args) == 1: 

917 return args[0] 

918 return np.asarray(args) 

919 

920# `moment` fits into the `_axis_nan_policy` pattern, but it is a bit unusual 

921# because the number of outputs is variable. Specifically, 

922# `result_to_tuple=lambda x: (x,)` may be surprising for a function that 

923# can produce more than one output, but it is intended here. 

924# When `moment is called to produce the output: 

925# - `result_to_tuple` packs the returned array into a single-element tuple, 

926# - `_moment_result_object` extracts and returns that single element. 

927# However, when the input array is empty, `moment` is never called. Instead, 

928# - `_check_empty_inputs` is used to produce an empty array with the 

929# appropriate dimensions. 

930# - A list comprehension creates the appropriate number of copies of this 

931# array, depending on `n_outputs`. 

932# - This list - which may have multiple elements - is passed into 

933# `_moment_result_object`. 

934# - If there is a single output, `_moment_result_object` extracts and returns 

935# the single output from the list. 

936# - If there are multiple outputs, and therefore multiple elements in the list, 

937# `_moment_result_object` converts the list of arrays to a single array and 

938# returns it. 

939# Currently this leads to a slight inconsistency: when the input array is 

940# empty, there is no distinction between the `moment` function being called 

941# with parameter `moments=1` and `moments=[1]`; the latter *should* produce 

942# the same as the former but with a singleton zeroth dimension. 

943@_axis_nan_policy_factory( # noqa: E302 

944 _moment_result_object, n_samples=1, result_to_tuple=lambda x: (x,), 

945 n_outputs=_moment_outputs 

946) 

947def moment(a, moment=1, axis=0, nan_policy='propagate', *, center=None): 

948 r"""Calculate the nth moment about the mean for a sample. 

949 

950 A moment is a specific quantitative measure of the shape of a set of 

951 points. It is often used to calculate coefficients of skewness and kurtosis 

952 due to its close relationship with them. 

953 

954 Parameters 

955 ---------- 

956 a : array_like 

957 Input array. 

958 moment : int or array_like of ints, optional 

959 Order of central moment that is returned. Default is 1. 

960 axis : int or None, optional 

961 Axis along which the central moment is computed. Default is 0. 

962 If None, compute over the whole array `a`. 

963 nan_policy : {'propagate', 'raise', 'omit'}, optional 

964 Defines how to handle when input contains nan. 

965 The following options are available (default is 'propagate'): 

966 

967 * 'propagate': returns nan 

968 * 'raise': throws an error 

969 * 'omit': performs the calculations ignoring nan values 

970 

971 center : float or None, optional 

972 The point about which moments are taken. This can be the sample mean, 

973 the origin, or any other be point. If `None` (default) compute the 

974 center as the sample mean. 

975 

976 Returns 

977 ------- 

978 n-th moment about the `center` : ndarray or float 

979 The appropriate moment along the given axis or over all values if axis 

980 is None. The denominator for the moment calculation is the number of 

981 observations, no degrees of freedom correction is done. 

982 

983 See Also 

984 -------- 

985 kurtosis, skew, describe 

986 

987 Notes 

988 ----- 

989 The k-th moment of a data sample is: 

990 

991 .. math:: 

992 

993 m_k = \frac{1}{n} \sum_{i = 1}^n (x_i - c)^k 

994 

995 Where `n` is the number of samples, and `c` is the center around which the 

996 moment is calculated. This function uses exponentiation by squares [1]_ for 

997 efficiency. 

998 

999 Note that, if `a` is an empty array (``a.size == 0``), array `moment` with 

1000 one element (`moment.size == 1`) is treated the same as scalar `moment` 

1001 (``np.isscalar(moment)``). This might produce arrays of unexpected shape. 

1002 

1003 References 

1004 ---------- 

1005 .. [1] https://eli.thegreenplace.net/2009/03/21/efficient-integer-exponentiation-algorithms 

1006 

1007 Examples 

1008 -------- 

1009 >>> from scipy.stats import moment 

1010 >>> moment([1, 2, 3, 4, 5], moment=1) 

1011 0.0 

1012 >>> moment([1, 2, 3, 4, 5], moment=2) 

1013 2.0 

1014 

1015 """ 

1016 a, axis = _chk_asarray(a, axis) 

1017 

1018 # for array_like moment input, return a value for each. 

1019 if not np.isscalar(moment): 

1020 # Calculated the mean once at most, and only if it will be used 

1021 calculate_mean = center is None and np.any(np.asarray(moment) > 1) 

1022 mean = a.mean(axis, keepdims=True) if calculate_mean else None 

1023 mmnt = [] 

1024 for i in moment: 

1025 if center is None and i > 1: 

1026 mmnt.append(_moment(a, i, axis, mean=mean)) 

1027 else: 

1028 mmnt.append(_moment(a, i, axis, mean=center)) 

1029 return np.array(mmnt) 

1030 else: 

1031 return _moment(a, moment, axis, mean=center) 

1032 

1033 

1034# Moment with optional pre-computed mean, equal to a.mean(axis, keepdims=True) 

1035def _moment(a, moment, axis, *, mean=None): 

1036 if np.abs(moment - np.round(moment)) > 0: 

1037 raise ValueError("All moment parameters must be integers") 

1038 

1039 # moment of empty array is the same regardless of order 

1040 if a.size == 0: 

1041 return np.mean(a, axis=axis) 

1042 

1043 dtype = a.dtype.type if a.dtype.kind in 'fc' else np.float64 

1044 

1045 if moment == 0 or (moment == 1 and mean is None): 

1046 # By definition the zeroth moment is always 1, and the first *central* 

1047 # moment is 0. 

1048 shape = list(a.shape) 

1049 del shape[axis] 

1050 

1051 if len(shape) == 0: 

1052 return dtype(1.0 if moment == 0 else 0.0) 

1053 else: 

1054 return (np.ones(shape, dtype=dtype) if moment == 0 

1055 else np.zeros(shape, dtype=dtype)) 

1056 else: 

1057 # Exponentiation by squares: form exponent sequence 

1058 n_list = [moment] 

1059 current_n = moment 

1060 while current_n > 2: 

1061 if current_n % 2: 

1062 current_n = (current_n - 1) / 2 

1063 else: 

1064 current_n /= 2 

1065 n_list.append(current_n) 

1066 

1067 # Starting point for exponentiation by squares 

1068 mean = (a.mean(axis, keepdims=True) if mean is None 

1069 else np.asarray(mean, dtype=dtype)[()]) 

1070 a_zero_mean = a - mean 

1071 

1072 eps = np.finfo(a_zero_mean.dtype).resolution * 10 

1073 with np.errstate(divide='ignore', invalid='ignore'): 

1074 rel_diff = np.max(np.abs(a_zero_mean), axis=axis, 

1075 keepdims=True) / np.abs(mean) 

1076 with np.errstate(invalid='ignore'): 

1077 precision_loss = np.any(rel_diff < eps) 

1078 n = a.shape[axis] if axis is not None else a.size 

1079 if precision_loss and n > 1: 

1080 message = ("Precision loss occurred in moment calculation due to " 

1081 "catastrophic cancellation. This occurs when the data " 

1082 "are nearly identical. Results may be unreliable.") 

1083 warnings.warn(message, RuntimeWarning, stacklevel=4) 

1084 

1085 if n_list[-1] == 1: 

1086 s = a_zero_mean.copy() 

1087 else: 

1088 s = a_zero_mean**2 

1089 

1090 # Perform multiplications 

1091 for n in n_list[-2::-1]: 

1092 s = s**2 

1093 if n % 2: 

1094 s *= a_zero_mean 

1095 return np.mean(s, axis) 

1096 

1097 

1098def _var(x, axis=0, ddof=0, mean=None): 

1099 # Calculate variance of sample, warning if precision is lost 

1100 var = _moment(x, 2, axis, mean=mean) 

1101 if ddof != 0: 

1102 n = x.shape[axis] if axis is not None else x.size 

1103 var *= np.divide(n, n-ddof) # to avoid error on division by zero 

1104 return var 

1105 

1106 

1107@_axis_nan_policy_factory( 

1108 lambda x: x, result_to_tuple=lambda x: (x,), n_outputs=1 

1109) 

1110def skew(a, axis=0, bias=True, nan_policy='propagate'): 

1111 r"""Compute the sample skewness of a data set. 

1112 

1113 For normally distributed data, the skewness should be about zero. For 

1114 unimodal continuous distributions, a skewness value greater than zero means 

1115 that there is more weight in the right tail of the distribution. The 

1116 function `skewtest` can be used to determine if the skewness value 

1117 is close enough to zero, statistically speaking. 

1118 

1119 Parameters 

1120 ---------- 

1121 a : ndarray 

1122 Input array. 

1123 axis : int or None, optional 

1124 Axis along which skewness is calculated. Default is 0. 

1125 If None, compute over the whole array `a`. 

1126 bias : bool, optional 

1127 If False, then the calculations are corrected for statistical bias. 

1128 nan_policy : {'propagate', 'raise', 'omit'}, optional 

1129 Defines how to handle when input contains nan. 

1130 The following options are available (default is 'propagate'): 

1131 

1132 * 'propagate': returns nan 

1133 * 'raise': throws an error 

1134 * 'omit': performs the calculations ignoring nan values 

1135 

1136 Returns 

1137 ------- 

1138 skewness : ndarray 

1139 The skewness of values along an axis, returning NaN where all values 

1140 are equal. 

1141 

1142 Notes 

1143 ----- 

1144 The sample skewness is computed as the Fisher-Pearson coefficient 

1145 of skewness, i.e. 

1146 

1147 .. math:: 

1148 

1149 g_1=\frac{m_3}{m_2^{3/2}} 

1150 

1151 where 

1152 

1153 .. math:: 

1154 

1155 m_i=\frac{1}{N}\sum_{n=1}^N(x[n]-\bar{x})^i 

1156 

1157 is the biased sample :math:`i\texttt{th}` central moment, and 

1158 :math:`\bar{x}` is 

1159 the sample mean. If ``bias`` is False, the calculations are 

1160 corrected for bias and the value computed is the adjusted 

1161 Fisher-Pearson standardized moment coefficient, i.e. 

1162 

1163 .. math:: 

1164 

1165 G_1=\frac{k_3}{k_2^{3/2}}= 

1166 \frac{\sqrt{N(N-1)}}{N-2}\frac{m_3}{m_2^{3/2}}. 

1167 

1168 References 

1169 ---------- 

1170 .. [1] Zwillinger, D. and Kokoska, S. (2000). CRC Standard 

1171 Probability and Statistics Tables and Formulae. Chapman & Hall: New 

1172 York. 2000. 

1173 Section 2.2.24.1 

1174 

1175 Examples 

1176 -------- 

1177 >>> from scipy.stats import skew 

1178 >>> skew([1, 2, 3, 4, 5]) 

1179 0.0 

1180 >>> skew([2, 8, 0, 4, 1, 9, 9, 0]) 

1181 0.2650554122698573 

1182 

1183 """ 

1184 a, axis = _chk_asarray(a, axis) 

1185 n = a.shape[axis] 

1186 

1187 contains_nan, nan_policy = _contains_nan(a, nan_policy) 

1188 

1189 if contains_nan and nan_policy == 'omit': 

1190 a = ma.masked_invalid(a) 

1191 return mstats_basic.skew(a, axis, bias) 

1192 

1193 mean = a.mean(axis, keepdims=True) 

1194 m2 = _moment(a, 2, axis, mean=mean) 

1195 m3 = _moment(a, 3, axis, mean=mean) 

1196 with np.errstate(all='ignore'): 

1197 zero = (m2 <= (np.finfo(m2.dtype).resolution * mean.squeeze(axis))**2) 

1198 vals = np.where(zero, np.nan, m3 / m2**1.5) 

1199 if not bias: 

1200 can_correct = ~zero & (n > 2) 

1201 if can_correct.any(): 

1202 m2 = np.extract(can_correct, m2) 

1203 m3 = np.extract(can_correct, m3) 

1204 nval = np.sqrt((n - 1.0) * n) / (n - 2.0) * m3 / m2**1.5 

1205 np.place(vals, can_correct, nval) 

1206 

1207 return vals[()] 

1208 

1209 

1210@_axis_nan_policy_factory( 

1211 lambda x: x, result_to_tuple=lambda x: (x,), n_outputs=1 

1212) 

1213def kurtosis(a, axis=0, fisher=True, bias=True, nan_policy='propagate'): 

1214 """Compute the kurtosis (Fisher or Pearson) of a dataset. 

1215 

1216 Kurtosis is the fourth central moment divided by the square of the 

1217 variance. If Fisher's definition is used, then 3.0 is subtracted from 

1218 the result to give 0.0 for a normal distribution. 

1219 

1220 If bias is False then the kurtosis is calculated using k statistics to 

1221 eliminate bias coming from biased moment estimators 

1222 

1223 Use `kurtosistest` to see if result is close enough to normal. 

1224 

1225 Parameters 

1226 ---------- 

1227 a : array 

1228 Data for which the kurtosis is calculated. 

1229 axis : int or None, optional 

1230 Axis along which the kurtosis is calculated. Default is 0. 

1231 If None, compute over the whole array `a`. 

1232 fisher : bool, optional 

1233 If True, Fisher's definition is used (normal ==> 0.0). If False, 

1234 Pearson's definition is used (normal ==> 3.0). 

1235 bias : bool, optional 

1236 If False, then the calculations are corrected for statistical bias. 

1237 nan_policy : {'propagate', 'raise', 'omit'}, optional 

1238 Defines how to handle when input contains nan. 'propagate' returns nan, 

1239 'raise' throws an error, 'omit' performs the calculations ignoring nan 

1240 values. Default is 'propagate'. 

1241 

1242 Returns 

1243 ------- 

1244 kurtosis : array 

1245 The kurtosis of values along an axis, returning NaN where all values 

1246 are equal. 

1247 

1248 References 

1249 ---------- 

1250 .. [1] Zwillinger, D. and Kokoska, S. (2000). CRC Standard 

1251 Probability and Statistics Tables and Formulae. Chapman & Hall: New 

1252 York. 2000. 

1253 

1254 Examples 

1255 -------- 

1256 In Fisher's definition, the kurtosis of the normal distribution is zero. 

1257 In the following example, the kurtosis is close to zero, because it was 

1258 calculated from the dataset, not from the continuous distribution. 

1259 

1260 >>> import numpy as np 

1261 >>> from scipy.stats import norm, kurtosis 

1262 >>> data = norm.rvs(size=1000, random_state=3) 

1263 >>> kurtosis(data) 

1264 -0.06928694200380558 

1265 

1266 The distribution with a higher kurtosis has a heavier tail. 

1267 The zero valued kurtosis of the normal distribution in Fisher's definition 

1268 can serve as a reference point. 

1269 

1270 >>> import matplotlib.pyplot as plt 

1271 >>> import scipy.stats as stats 

1272 >>> from scipy.stats import kurtosis 

1273 

1274 >>> x = np.linspace(-5, 5, 100) 

1275 >>> ax = plt.subplot() 

1276 >>> distnames = ['laplace', 'norm', 'uniform'] 

1277 

1278 >>> for distname in distnames: 

1279 ... if distname == 'uniform': 

1280 ... dist = getattr(stats, distname)(loc=-2, scale=4) 

1281 ... else: 

1282 ... dist = getattr(stats, distname) 

1283 ... data = dist.rvs(size=1000) 

1284 ... kur = kurtosis(data, fisher=True) 

1285 ... y = dist.pdf(x) 

1286 ... ax.plot(x, y, label="{}, {}".format(distname, round(kur, 3))) 

1287 ... ax.legend() 

1288 

1289 The Laplace distribution has a heavier tail than the normal distribution. 

1290 The uniform distribution (which has negative kurtosis) has the thinnest 

1291 tail. 

1292 

1293 """ 

1294 a, axis = _chk_asarray(a, axis) 

1295 

1296 contains_nan, nan_policy = _contains_nan(a, nan_policy) 

1297 

1298 if contains_nan and nan_policy == 'omit': 

1299 a = ma.masked_invalid(a) 

1300 return mstats_basic.kurtosis(a, axis, fisher, bias) 

1301 

1302 n = a.shape[axis] 

1303 mean = a.mean(axis, keepdims=True) 

1304 m2 = _moment(a, 2, axis, mean=mean) 

1305 m4 = _moment(a, 4, axis, mean=mean) 

1306 with np.errstate(all='ignore'): 

1307 zero = (m2 <= (np.finfo(m2.dtype).resolution * mean.squeeze(axis))**2) 

1308 vals = np.where(zero, np.nan, m4 / m2**2.0) 

1309 

1310 if not bias: 

1311 can_correct = ~zero & (n > 3) 

1312 if can_correct.any(): 

1313 m2 = np.extract(can_correct, m2) 

1314 m4 = np.extract(can_correct, m4) 

1315 nval = 1.0/(n-2)/(n-3) * ((n**2-1.0)*m4/m2**2.0 - 3*(n-1)**2.0) 

1316 np.place(vals, can_correct, nval + 3.0) 

1317 

1318 return vals[()] - 3 if fisher else vals[()] 

1319 

1320 

1321DescribeResult = namedtuple('DescribeResult', 

1322 ('nobs', 'minmax', 'mean', 'variance', 'skewness', 

1323 'kurtosis')) 

1324 

1325 

1326def describe(a, axis=0, ddof=1, bias=True, nan_policy='propagate'): 

1327 """Compute several descriptive statistics of the passed array. 

1328 

1329 Parameters 

1330 ---------- 

1331 a : array_like 

1332 Input data. 

1333 axis : int or None, optional 

1334 Axis along which statistics are calculated. Default is 0. 

1335 If None, compute over the whole array `a`. 

1336 ddof : int, optional 

1337 Delta degrees of freedom (only for variance). Default is 1. 

1338 bias : bool, optional 

1339 If False, then the skewness and kurtosis calculations are corrected 

1340 for statistical bias. 

1341 nan_policy : {'propagate', 'raise', 'omit'}, optional 

1342 Defines how to handle when input contains nan. 

1343 The following options are available (default is 'propagate'): 

1344 

1345 * 'propagate': returns nan 

1346 * 'raise': throws an error 

1347 * 'omit': performs the calculations ignoring nan values 

1348 

1349 Returns 

1350 ------- 

1351 nobs : int or ndarray of ints 

1352 Number of observations (length of data along `axis`). 

1353 When 'omit' is chosen as nan_policy, the length along each axis 

1354 slice is counted separately. 

1355 minmax: tuple of ndarrays or floats 

1356 Minimum and maximum value of `a` along the given axis. 

1357 mean : ndarray or float 

1358 Arithmetic mean of `a` along the given axis. 

1359 variance : ndarray or float 

1360 Unbiased variance of `a` along the given axis; denominator is number 

1361 of observations minus one. 

1362 skewness : ndarray or float 

1363 Skewness of `a` along the given axis, based on moment calculations 

1364 with denominator equal to the number of observations, i.e. no degrees 

1365 of freedom correction. 

1366 kurtosis : ndarray or float 

1367 Kurtosis (Fisher) of `a` along the given axis. The kurtosis is 

1368 normalized so that it is zero for the normal distribution. No 

1369 degrees of freedom are used. 

1370 

1371 See Also 

1372 -------- 

1373 skew, kurtosis 

1374 

1375 Examples 

1376 -------- 

1377 >>> import numpy as np 

1378 >>> from scipy import stats 

1379 >>> a = np.arange(10) 

1380 >>> stats.describe(a) 

1381 DescribeResult(nobs=10, minmax=(0, 9), mean=4.5, 

1382 variance=9.166666666666666, skewness=0.0, 

1383 kurtosis=-1.2242424242424244) 

1384 >>> b = [[1, 2], [3, 4]] 

1385 >>> stats.describe(b) 

1386 DescribeResult(nobs=2, minmax=(array([1, 2]), array([3, 4])), 

1387 mean=array([2., 3.]), variance=array([2., 2.]), 

1388 skewness=array([0., 0.]), kurtosis=array([-2., -2.])) 

1389 

1390 """ 

1391 a, axis = _chk_asarray(a, axis) 

1392 

1393 contains_nan, nan_policy = _contains_nan(a, nan_policy) 

1394 

1395 if contains_nan and nan_policy == 'omit': 

1396 a = ma.masked_invalid(a) 

1397 return mstats_basic.describe(a, axis, ddof, bias) 

1398 

1399 if a.size == 0: 

1400 raise ValueError("The input must not be empty.") 

1401 n = a.shape[axis] 

1402 mm = (np.min(a, axis=axis), np.max(a, axis=axis)) 

1403 m = np.mean(a, axis=axis) 

1404 v = _var(a, axis=axis, ddof=ddof) 

1405 sk = skew(a, axis, bias=bias) 

1406 kurt = kurtosis(a, axis, bias=bias) 

1407 

1408 return DescribeResult(n, mm, m, v, sk, kurt) 

1409 

1410##################################### 

1411# NORMALITY TESTS # 

1412##################################### 

1413 

1414 

1415def _normtest_finish(z, alternative): 

1416 """Common code between all the normality-test functions.""" 

1417 if alternative == 'less': 

1418 prob = distributions.norm.cdf(z) 

1419 elif alternative == 'greater': 

1420 prob = distributions.norm.sf(z) 

1421 elif alternative == 'two-sided': 

1422 prob = 2 * distributions.norm.sf(np.abs(z)) 

1423 else: 

1424 raise ValueError("alternative must be " 

1425 "'less', 'greater' or 'two-sided'") 

1426 

1427 if z.ndim == 0: 

1428 z = z[()] 

1429 

1430 return z, prob 

1431 

1432 

1433SkewtestResult = namedtuple('SkewtestResult', ('statistic', 'pvalue')) 

1434 

1435 

1436def skewtest(a, axis=0, nan_policy='propagate', alternative='two-sided'): 

1437 r"""Test whether the skew is different from the normal distribution. 

1438 

1439 This function tests the null hypothesis that the skewness of 

1440 the population that the sample was drawn from is the same 

1441 as that of a corresponding normal distribution. 

1442 

1443 Parameters 

1444 ---------- 

1445 a : array 

1446 The data to be tested. 

1447 axis : int or None, optional 

1448 Axis along which statistics are calculated. Default is 0. 

1449 If None, compute over the whole array `a`. 

1450 nan_policy : {'propagate', 'raise', 'omit'}, optional 

1451 Defines how to handle when input contains nan. 

1452 The following options are available (default is 'propagate'): 

1453 

1454 * 'propagate': returns nan 

1455 * 'raise': throws an error 

1456 * 'omit': performs the calculations ignoring nan values 

1457 

1458 alternative : {'two-sided', 'less', 'greater'}, optional 

1459 Defines the alternative hypothesis. Default is 'two-sided'. 

1460 The following options are available: 

1461 

1462 * 'two-sided': the skewness of the distribution underlying the sample 

1463 is different from that of the normal distribution (i.e. 0) 

1464 * 'less': the skewness of the distribution underlying the sample 

1465 is less than that of the normal distribution 

1466 * 'greater': the skewness of the distribution underlying the sample 

1467 is greater than that of the normal distribution 

1468 

1469 .. versionadded:: 1.7.0 

1470 

1471 Returns 

1472 ------- 

1473 statistic : float 

1474 The computed z-score for this test. 

1475 pvalue : float 

1476 The p-value for the hypothesis test. 

1477 

1478 Notes 

1479 ----- 

1480 The sample size must be at least 8. 

1481 

1482 References 

1483 ---------- 

1484 .. [1] R. B. D'Agostino, A. J. Belanger and R. B. D'Agostino Jr., 

1485 "A suggestion for using powerful and informative tests of 

1486 normality", American Statistician 44, pp. 316-321, 1990. 

1487 .. [2] Shapiro, S. S., & Wilk, M. B. (1965). An analysis of variance test 

1488 for normality (complete samples). Biometrika, 52(3/4), 591-611. 

1489 .. [3] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be 

1490 Zero: Calculating Exact P-values When Permutations Are Randomly 

1491 Drawn." Statistical Applications in Genetics and Molecular Biology 

1492 9.1 (2010). 

1493 

1494 Examples 

1495 -------- 

1496 Suppose we wish to infer from measurements whether the weights of adult 

1497 human males in a medical study are not normally distributed [2]_. 

1498 The weights (lbs) are recorded in the array ``x`` below. 

1499 

1500 >>> import numpy as np 

1501 >>> x = np.array([148, 154, 158, 160, 161, 162, 166, 170, 182, 195, 236]) 

1502 

1503 The skewness test from [1]_ begins by computing a statistic based on the 

1504 sample skewness. 

1505 

1506 >>> from scipy import stats 

1507 >>> res = stats.skewtest(x) 

1508 >>> res.statistic 

1509 2.7788579769903414 

1510 

1511 Because normal distributions have zero skewness, the magnitude of this 

1512 statistic tends to be low for samples drawn from a normal distribution. 

1513 

1514 The test is performed by comparing the observed value of the 

1515 statistic against the null distribution: the distribution of statistic 

1516 values derived under the null hypothesis that the weights were drawn from 

1517 a normal distribution. 

1518 

1519 For this test, the null distribution of the statistic for very large 

1520 samples is the standard normal distribution. 

1521 

1522 >>> import matplotlib.pyplot as plt 

1523 >>> dist = stats.norm() 

1524 >>> st_val = np.linspace(-5, 5, 100) 

1525 >>> pdf = dist.pdf(st_val) 

1526 >>> fig, ax = plt.subplots(figsize=(8, 5)) 

1527 >>> def st_plot(ax): # we'll re-use this 

1528 ... ax.plot(st_val, pdf) 

1529 ... ax.set_title("Skew Test Null Distribution") 

1530 ... ax.set_xlabel("statistic") 

1531 ... ax.set_ylabel("probability density") 

1532 >>> st_plot(ax) 

1533 >>> plt.show() 

1534 

1535 The comparison is quantified by the p-value: the proportion of values in 

1536 the null distribution as extreme or more extreme than the observed 

1537 value of the statistic. In a two-sided test, elements of the null 

1538 distribution greater than the observed statistic and elements of the null 

1539 distribution less than the negative of the observed statistic are both 

1540 considered "more extreme". 

1541 

1542 >>> fig, ax = plt.subplots(figsize=(8, 5)) 

1543 >>> st_plot(ax) 

1544 >>> pvalue = dist.cdf(-res.statistic) + dist.sf(res.statistic) 

1545 >>> annotation = (f'p-value={pvalue:.3f}\n(shaded area)') 

1546 >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8) 

1547 >>> _ = ax.annotate(annotation, (3, 0.005), (3.25, 0.02), arrowprops=props) 

1548 >>> i = st_val >= res.statistic 

1549 >>> ax.fill_between(st_val[i], y1=0, y2=pdf[i], color='C0') 

1550 >>> i = st_val <= -res.statistic 

1551 >>> ax.fill_between(st_val[i], y1=0, y2=pdf[i], color='C0') 

1552 >>> ax.set_xlim(-5, 5) 

1553 >>> ax.set_ylim(0, 0.1) 

1554 >>> plt.show() 

1555 >>> res.pvalue 

1556 0.005455036974740185 

1557 

1558 If the p-value is "small" - that is, if there is a low probability of 

1559 sampling data from a normally distributed population that produces such an 

1560 extreme value of the statistic - this may be taken as evidence against 

1561 the null hypothesis in favor of the alternative: the weights were not 

1562 drawn from a normal distribution. Note that: 

1563 

1564 - The inverse is not true; that is, the test is not used to provide 

1565 evidence for the null hypothesis. 

1566 - The threshold for values that will be considered "small" is a choice that 

1567 should be made before the data is analyzed [3]_ with consideration of the 

1568 risks of both false positives (incorrectly rejecting the null hypothesis) 

1569 and false negatives (failure to reject a false null hypothesis). 

1570 

1571 Note that the standard normal distribution provides an asymptotic 

1572 approximation of the null distribution; it is only accurate for samples 

1573 with many observations. For small samples like ours, 

1574 `scipy.stats.monte_carlo_test` may provide a more accurate, albeit 

1575 stochastic, approximation of the exact p-value. 

1576 

1577 >>> def statistic(x, axis): 

1578 ... # get just the skewtest statistic; ignore the p-value 

1579 ... return stats.skewtest(x, axis=axis).statistic 

1580 >>> res = stats.monte_carlo_test(x, stats.norm.rvs, statistic) 

1581 >>> fig, ax = plt.subplots(figsize=(8, 5)) 

1582 >>> st_plot(ax) 

1583 >>> ax.hist(res.null_distribution, np.linspace(-5, 5, 50), 

1584 ... density=True) 

1585 >>> ax.legend(['aymptotic approximation\n(many observations)', 

1586 ... 'Monte Carlo approximation\n(11 observations)']) 

1587 >>> plt.show() 

1588 >>> res.pvalue 

1589 0.0062 # may vary 

1590 

1591 In this case, the asymptotic approximation and Monte Carlo approximation 

1592 agree fairly closely, even for our small sample. 

1593 

1594 """ 

1595 a, axis = _chk_asarray(a, axis) 

1596 

1597 contains_nan, nan_policy = _contains_nan(a, nan_policy) 

1598 

1599 if contains_nan and nan_policy == 'omit': 

1600 a = ma.masked_invalid(a) 

1601 return mstats_basic.skewtest(a, axis, alternative) 

1602 

1603 if axis is None: 

1604 a = np.ravel(a) 

1605 axis = 0 

1606 b2 = skew(a, axis) 

1607 n = a.shape[axis] 

1608 if n < 8: 

1609 raise ValueError( 

1610 "skewtest is not valid with less than 8 samples; %i samples" 

1611 " were given." % int(n)) 

1612 y = b2 * math.sqrt(((n + 1) * (n + 3)) / (6.0 * (n - 2))) 

1613 beta2 = (3.0 * (n**2 + 27*n - 70) * (n+1) * (n+3) / 

1614 ((n-2.0) * (n+5) * (n+7) * (n+9))) 

1615 W2 = -1 + math.sqrt(2 * (beta2 - 1)) 

1616 delta = 1 / math.sqrt(0.5 * math.log(W2)) 

1617 alpha = math.sqrt(2.0 / (W2 - 1)) 

1618 y = np.where(y == 0, 1, y) 

1619 Z = delta * np.log(y / alpha + np.sqrt((y / alpha)**2 + 1)) 

1620 

1621 return SkewtestResult(*_normtest_finish(Z, alternative)) 

1622 

1623 

1624KurtosistestResult = namedtuple('KurtosistestResult', ('statistic', 'pvalue')) 

1625 

1626 

1627def kurtosistest(a, axis=0, nan_policy='propagate', alternative='two-sided'): 

1628 r"""Test whether a dataset has normal kurtosis. 

1629 

1630 This function tests the null hypothesis that the kurtosis 

1631 of the population from which the sample was drawn is that 

1632 of the normal distribution. 

1633 

1634 Parameters 

1635 ---------- 

1636 a : array 

1637 Array of the sample data. 

1638 axis : int or None, optional 

1639 Axis along which to compute test. Default is 0. If None, 

1640 compute over the whole array `a`. 

1641 nan_policy : {'propagate', 'raise', 'omit'}, optional 

1642 Defines how to handle when input contains nan. 

1643 The following options are available (default is 'propagate'): 

1644 

1645 * 'propagate': returns nan 

1646 * 'raise': throws an error 

1647 * 'omit': performs the calculations ignoring nan values 

1648 

1649 alternative : {'two-sided', 'less', 'greater'}, optional 

1650 Defines the alternative hypothesis. 

1651 The following options are available (default is 'two-sided'): 

1652 

1653 * 'two-sided': the kurtosis of the distribution underlying the sample 

1654 is different from that of the normal distribution 

1655 * 'less': the kurtosis of the distribution underlying the sample 

1656 is less than that of the normal distribution 

1657 * 'greater': the kurtosis of the distribution underlying the sample 

1658 is greater than that of the normal distribution 

1659 

1660 .. versionadded:: 1.7.0 

1661 

1662 Returns 

1663 ------- 

1664 statistic : float 

1665 The computed z-score for this test. 

1666 pvalue : float 

1667 The p-value for the hypothesis test. 

1668 

1669 Notes 

1670 ----- 

1671 Valid only for n>20. This function uses the method described in [1]_. 

1672 

1673 References 

1674 ---------- 

1675 .. [1] see e.g. F. J. Anscombe, W. J. Glynn, "Distribution of the kurtosis 

1676 statistic b2 for normal samples", Biometrika, vol. 70, pp. 227-234, 1983. 

1677 .. [2] Shapiro, S. S., & Wilk, M. B. (1965). An analysis of variance test 

1678 for normality (complete samples). Biometrika, 52(3/4), 591-611. 

1679 .. [3] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be 

1680 Zero: Calculating Exact P-values When Permutations Are Randomly 

1681 Drawn." Statistical Applications in Genetics and Molecular Biology 

1682 9.1 (2010). 

1683 .. [4] Panagiotakos, D. B. (2008). The value of p-value in biomedical 

1684 research. The open cardiovascular medicine journal, 2, 97. 

1685 

1686 Examples 

1687 -------- 

1688 Suppose we wish to infer from measurements whether the weights of adult 

1689 human males in a medical study are not normally distributed [2]_. 

1690 The weights (lbs) are recorded in the array ``x`` below. 

1691 

1692 >>> import numpy as np 

1693 >>> x = np.array([148, 154, 158, 160, 161, 162, 166, 170, 182, 195, 236]) 

1694 

1695 The kurtosis test from [1]_ begins by computing a statistic based on the 

1696 sample (excess/Fisher) kurtosis. 

1697 

1698 >>> from scipy import stats 

1699 >>> res = stats.kurtosistest(x) 

1700 >>> res.statistic 

1701 2.3048235214240873 

1702 

1703 (The test warns that our sample has too few observations to perform the 

1704 test. We'll return to this at the end of the example.) 

1705 Because normal distributions have zero excess kurtosis (by definition), 

1706 the magnitude of this statistic tends to be low for samples drawn from a 

1707 normal distribution. 

1708 

1709 The test is performed by comparing the observed value of the 

1710 statistic against the null distribution: the distribution of statistic 

1711 values derived under the null hypothesis that the weights were drawn from 

1712 a normal distribution. 

1713 

1714 For this test, the null distribution of the statistic for very large 

1715 samples is the standard normal distribution. 

1716 

1717 >>> import matplotlib.pyplot as plt 

1718 >>> dist = stats.norm() 

1719 >>> kt_val = np.linspace(-5, 5, 100) 

1720 >>> pdf = dist.pdf(kt_val) 

1721 >>> fig, ax = plt.subplots(figsize=(8, 5)) 

1722 >>> def kt_plot(ax): # we'll re-use this 

1723 ... ax.plot(kt_val, pdf) 

1724 ... ax.set_title("Kurtosis Test Null Distribution") 

1725 ... ax.set_xlabel("statistic") 

1726 ... ax.set_ylabel("probability density") 

1727 >>> kt_plot(ax) 

1728 >>> plt.show() 

1729 

1730 The comparison is quantified by the p-value: the proportion of values in 

1731 the null distribution as extreme or more extreme than the observed 

1732 value of the statistic. In a two-sided test in which the statistic is 

1733 positive, elements of the null distribution greater than the observed 

1734 statistic and elements of the null distribution less than the negative of 

1735 the observed statistic are both considered "more extreme". 

1736 

1737 >>> fig, ax = plt.subplots(figsize=(8, 5)) 

1738 >>> kt_plot(ax) 

1739 >>> pvalue = dist.cdf(-res.statistic) + dist.sf(res.statistic) 

1740 >>> annotation = (f'p-value={pvalue:.3f}\n(shaded area)') 

1741 >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8) 

1742 >>> _ = ax.annotate(annotation, (3, 0.005), (3.25, 0.02), arrowprops=props) 

1743 >>> i = kt_val >= res.statistic 

1744 >>> ax.fill_between(kt_val[i], y1=0, y2=pdf[i], color='C0') 

1745 >>> i = kt_val <= -res.statistic 

1746 >>> ax.fill_between(kt_val[i], y1=0, y2=pdf[i], color='C0') 

1747 >>> ax.set_xlim(-5, 5) 

1748 >>> ax.set_ylim(0, 0.1) 

1749 >>> plt.show() 

1750 >>> res.pvalue 

1751 0.0211764592113868 

1752 

1753 If the p-value is "small" - that is, if there is a low probability of 

1754 sampling data from a normally distributed population that produces such an 

1755 extreme value of the statistic - this may be taken as evidence against 

1756 the null hypothesis in favor of the alternative: the weights were not 

1757 drawn from a normal distribution. Note that: 

1758 

1759 - The inverse is not true; that is, the test is not used to provide 

1760 evidence for the null hypothesis. 

1761 - The threshold for values that will be considered "small" is a choice that 

1762 should be made before the data is analyzed [3]_ with consideration of the 

1763 risks of both false positives (incorrectly rejecting the null hypothesis) 

1764 and false negatives (failure to reject a false null hypothesis). 

1765 

1766 Note that the standard normal distribution provides an asymptotic 

1767 approximation of the null distribution; it is only accurate for samples 

1768 with many observations. This is the reason we received a warning at the 

1769 beginning of the example; our sample is quite small. In this case, 

1770 `scipy.stats.monte_carlo_test` may provide a more accurate, albeit 

1771 stochastic, approximation of the exact p-value. 

1772 

1773 >>> def statistic(x, axis): 

1774 ... # get just the skewtest statistic; ignore the p-value 

1775 ... return stats.kurtosistest(x, axis=axis).statistic 

1776 >>> res = stats.monte_carlo_test(x, stats.norm.rvs, statistic) 

1777 >>> fig, ax = plt.subplots(figsize=(8, 5)) 

1778 >>> kt_plot(ax) 

1779 >>> ax.hist(res.null_distribution, np.linspace(-5, 5, 50), 

1780 ... density=True) 

1781 >>> ax.legend(['aymptotic approximation\n(many observations)', 

1782 ... 'Monte Carlo approximation\n(11 observations)']) 

1783 >>> plt.show() 

1784 >>> res.pvalue 

1785 0.0272 # may vary 

1786 

1787 Furthermore, despite their stochastic nature, p-values computed in this way 

1788 can be used to exactly control the rate of false rejections of the null 

1789 hypothesis [4]_. 

1790 

1791 """ 

1792 a, axis = _chk_asarray(a, axis) 

1793 

1794 contains_nan, nan_policy = _contains_nan(a, nan_policy) 

1795 

1796 if contains_nan and nan_policy == 'omit': 

1797 a = ma.masked_invalid(a) 

1798 return mstats_basic.kurtosistest(a, axis, alternative) 

1799 

1800 n = a.shape[axis] 

1801 if n < 5: 

1802 raise ValueError( 

1803 "kurtosistest requires at least 5 observations; %i observations" 

1804 " were given." % int(n)) 

1805 if n < 20: 

1806 warnings.warn("kurtosistest only valid for n>=20 ... continuing " 

1807 "anyway, n=%i" % int(n)) 

1808 b2 = kurtosis(a, axis, fisher=False) 

1809 

1810 E = 3.0*(n-1) / (n+1) 

1811 varb2 = 24.0*n*(n-2)*(n-3) / ((n+1)*(n+1.)*(n+3)*(n+5)) # [1]_ Eq. 1 

1812 x = (b2-E) / np.sqrt(varb2) # [1]_ Eq. 4 

1813 # [1]_ Eq. 2: 

1814 sqrtbeta1 = 6.0*(n*n-5*n+2)/((n+7)*(n+9)) * np.sqrt((6.0*(n+3)*(n+5)) / 

1815 (n*(n-2)*(n-3))) 

1816 # [1]_ Eq. 3: 

1817 A = 6.0 + 8.0/sqrtbeta1 * (2.0/sqrtbeta1 + np.sqrt(1+4.0/(sqrtbeta1**2))) 

1818 term1 = 1 - 2/(9.0*A) 

1819 denom = 1 + x*np.sqrt(2/(A-4.0)) 

1820 term2 = np.sign(denom) * np.where(denom == 0.0, np.nan, 

1821 np.power((1-2.0/A)/np.abs(denom), 1/3.0)) 

1822 if np.any(denom == 0): 

1823 msg = "Test statistic not defined in some cases due to division by " \ 

1824 "zero. Return nan in that case..." 

1825 warnings.warn(msg, RuntimeWarning) 

1826 

1827 Z = (term1 - term2) / np.sqrt(2/(9.0*A)) # [1]_ Eq. 5 

1828 

1829 # zprob uses upper tail, so Z needs to be positive 

1830 return KurtosistestResult(*_normtest_finish(Z, alternative)) 

1831 

1832 

1833NormaltestResult = namedtuple('NormaltestResult', ('statistic', 'pvalue')) 

1834 

1835 

1836def normaltest(a, axis=0, nan_policy='propagate'): 

1837 r"""Test whether a sample differs from a normal distribution. 

1838 

1839 This function tests the null hypothesis that a sample comes 

1840 from a normal distribution. It is based on D'Agostino and 

1841 Pearson's [1]_, [2]_ test that combines skew and kurtosis to 

1842 produce an omnibus test of normality. 

1843 

1844 Parameters 

1845 ---------- 

1846 a : array_like 

1847 The array containing the sample to be tested. 

1848 axis : int or None, optional 

1849 Axis along which to compute test. Default is 0. If None, 

1850 compute over the whole array `a`. 

1851 nan_policy : {'propagate', 'raise', 'omit'}, optional 

1852 Defines how to handle when input contains nan. 

1853 The following options are available (default is 'propagate'): 

1854 

1855 * 'propagate': returns nan 

1856 * 'raise': throws an error 

1857 * 'omit': performs the calculations ignoring nan values 

1858 

1859 Returns 

1860 ------- 

1861 statistic : float or array 

1862 ``s^2 + k^2``, where ``s`` is the z-score returned by `skewtest` and 

1863 ``k`` is the z-score returned by `kurtosistest`. 

1864 pvalue : float or array 

1865 A 2-sided chi squared probability for the hypothesis test. 

1866 

1867 References 

1868 ---------- 

1869 .. [1] D'Agostino, R. B. (1971), "An omnibus test of normality for 

1870 moderate and large sample size", Biometrika, 58, 341-348 

1871 .. [2] D'Agostino, R. and Pearson, E. S. (1973), "Tests for departure from 

1872 normality", Biometrika, 60, 613-622 

1873 .. [3] Shapiro, S. S., & Wilk, M. B. (1965). An analysis of variance test 

1874 for normality (complete samples). Biometrika, 52(3/4), 591-611. 

1875 .. [4] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be 

1876 Zero: Calculating Exact P-values When Permutations Are Randomly 

1877 Drawn." Statistical Applications in Genetics and Molecular Biology 

1878 9.1 (2010). 

1879 .. [5] Panagiotakos, D. B. (2008). The value of p-value in biomedical 

1880 research. The open cardiovascular medicine journal, 2, 97. 

1881 

1882 Examples 

1883 -------- 

1884 Suppose we wish to infer from measurements whether the weights of adult 

1885 human males in a medical study are not normally distributed [3]_. 

1886 The weights (lbs) are recorded in the array ``x`` below. 

1887 

1888 >>> import numpy as np 

1889 >>> x = np.array([148, 154, 158, 160, 161, 162, 166, 170, 182, 195, 236]) 

1890 

1891 The normality test of [1]_ and [2]_ begins by computing a statistic based 

1892 on the sample skewness and kurtosis. 

1893 

1894 >>> from scipy import stats 

1895 >>> res = stats.normaltest(x) 

1896 >>> res.statistic 

1897 13.034263121192582 

1898 

1899 (The test warns that our sample has too few observations to perform the 

1900 test. We'll return to this at the end of the example.) 

1901 Because the normal distribution has zero skewness and zero 

1902 ("excess" or "Fisher") kurtosis, the value of this statistic tends to be 

1903 low for samples drawn from a normal distribution. 

1904 

1905 The test is performed by comparing the observed value of the statistic 

1906 against the null distribution: the distribution of statistic values derived 

1907 under the null hypothesis that the weights were drawn from a normal 

1908 distribution. 

1909 For this normality test, the null distribution for very large samples is 

1910 the chi-squared distribution with two degrees of freedom. 

1911 

1912 >>> import matplotlib.pyplot as plt 

1913 >>> dist = stats.chi2(df=2) 

1914 >>> stat_vals = np.linspace(0, 16, 100) 

1915 >>> pdf = dist.pdf(stat_vals) 

1916 >>> fig, ax = plt.subplots(figsize=(8, 5)) 

1917 >>> def plot(ax): # we'll re-use this 

1918 ... ax.plot(stat_vals, pdf) 

1919 ... ax.set_title("Normality Test Null Distribution") 

1920 ... ax.set_xlabel("statistic") 

1921 ... ax.set_ylabel("probability density") 

1922 >>> plot(ax) 

1923 >>> plt.show() 

1924 

1925 The comparison is quantified by the p-value: the proportion of values in 

1926 the null distribution greater than or equal to the observed value of the 

1927 statistic. 

1928 

1929 >>> fig, ax = plt.subplots(figsize=(8, 5)) 

1930 >>> plot(ax) 

1931 >>> pvalue = dist.sf(res.statistic) 

1932 >>> annotation = (f'p-value={pvalue:.6f}\n(shaded area)') 

1933 >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8) 

1934 >>> _ = ax.annotate(annotation, (13.5, 5e-4), (14, 5e-3), arrowprops=props) 

1935 >>> i = stat_vals >= res.statistic # index more extreme statistic values 

1936 >>> ax.fill_between(stat_vals[i], y1=0, y2=pdf[i]) 

1937 >>> ax.set_xlim(8, 16) 

1938 >>> ax.set_ylim(0, 0.01) 

1939 >>> plt.show() 

1940 >>> res.pvalue 

1941 0.0014779023013100172 

1942 

1943 If the p-value is "small" - that is, if there is a low probability of 

1944 sampling data from a normally distributed population that produces such an 

1945 extreme value of the statistic - this may be taken as evidence against 

1946 the null hypothesis in favor of the alternative: the weights were not 

1947 drawn from a normal distribution. Note that: 

1948 

1949 - The inverse is not true; that is, the test is not used to provide 

1950 evidence for the null hypothesis. 

1951 - The threshold for values that will be considered "small" is a choice that 

1952 should be made before the data is analyzed [4]_ with consideration of the 

1953 risks of both false positives (incorrectly rejecting the null hypothesis) 

1954 and false negatives (failure to reject a false null hypothesis). 

1955 

1956 Note that the chi-squared distribution provides an asymptotic 

1957 approximation of the null distribution; it is only accurate for samples 

1958 with many observations. This is the reason we received a warning at the 

1959 beginning of the example; our sample is quite small. In this case, 

1960 `scipy.stats.monte_carlo_test` may provide a more accurate, albeit 

1961 stochastic, approximation of the exact p-value. 

1962 

1963 >>> def statistic(x, axis): 

1964 ... # Get only the `normaltest` statistic; ignore approximate p-value 

1965 ... return stats.normaltest(x, axis=axis).statistic 

1966 >>> res = stats.monte_carlo_test(x, stats.norm.rvs, statistic, 

1967 ... alternative='greater') 

1968 >>> fig, ax = plt.subplots(figsize=(8, 5)) 

1969 >>> plot(ax) 

1970 >>> ax.hist(res.null_distribution, np.linspace(0, 25, 50), 

1971 ... density=True) 

1972 >>> ax.legend(['aymptotic approximation (many observations)', 

1973 ... 'Monte Carlo approximation (11 observations)']) 

1974 >>> ax.set_xlim(0, 14) 

1975 >>> plt.show() 

1976 >>> res.pvalue 

1977 0.0082 # may vary 

1978 

1979 Furthermore, despite their stochastic nature, p-values computed in this way 

1980 can be used to exactly control the rate of false rejections of the null 

1981 hypothesis [5]_. 

1982 

1983 """ 

1984 a, axis = _chk_asarray(a, axis) 

1985 

1986 contains_nan, nan_policy = _contains_nan(a, nan_policy) 

1987 

1988 if contains_nan and nan_policy == 'omit': 

1989 a = ma.masked_invalid(a) 

1990 return mstats_basic.normaltest(a, axis) 

1991 

1992 s, _ = skewtest(a, axis) 

1993 k, _ = kurtosistest(a, axis) 

1994 k2 = s*s + k*k 

1995 

1996 return NormaltestResult(k2, distributions.chi2.sf(k2, 2)) 

1997 

1998 

1999@_axis_nan_policy_factory(SignificanceResult, default_axis=None) 

2000def jarque_bera(x, *, axis=None): 

2001 r"""Perform the Jarque-Bera goodness of fit test on sample data. 

2002 

2003 The Jarque-Bera test tests whether the sample data has the skewness and 

2004 kurtosis matching a normal distribution. 

2005 

2006 Note that this test only works for a large enough number of data samples 

2007 (>2000) as the test statistic asymptotically has a Chi-squared distribution 

2008 with 2 degrees of freedom. 

2009 

2010 Parameters 

2011 ---------- 

2012 x : array_like 

2013 Observations of a random variable. 

2014 axis : int or None, default: 0 

2015 If an int, the axis of the input along which to compute the statistic. 

2016 The statistic of each axis-slice (e.g. row) of the input will appear in 

2017 a corresponding element of the output. 

2018 If ``None``, the input will be raveled before computing the statistic. 

2019 

2020 Returns 

2021 ------- 

2022 result : SignificanceResult 

2023 An object with the following attributes: 

2024 

2025 statistic : float 

2026 The test statistic. 

2027 pvalue : float 

2028 The p-value for the hypothesis test. 

2029 

2030 References 

2031 ---------- 

2032 .. [1] Jarque, C. and Bera, A. (1980) "Efficient tests for normality, 

2033 homoscedasticity and serial independence of regression residuals", 

2034 6 Econometric Letters 255-259. 

2035 .. [2] Shapiro, S. S., & Wilk, M. B. (1965). An analysis of variance test 

2036 for normality (complete samples). Biometrika, 52(3/4), 591-611. 

2037 .. [3] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be 

2038 Zero: Calculating Exact P-values When Permutations Are Randomly 

2039 Drawn." Statistical Applications in Genetics and Molecular Biology 

2040 9.1 (2010). 

2041 .. [4] Panagiotakos, D. B. (2008). The value of p-value in biomedical 

2042 research. The open cardiovascular medicine journal, 2, 97. 

2043 

2044 Examples 

2045 -------- 

2046 Suppose we wish to infer from measurements whether the weights of adult 

2047 human males in a medical study are not normally distributed [2]_. 

2048 The weights (lbs) are recorded in the array ``x`` below. 

2049 

2050 >>> import numpy as np 

2051 >>> x = np.array([148, 154, 158, 160, 161, 162, 166, 170, 182, 195, 236]) 

2052 

2053 The Jarque-Bera test begins by computing a statistic based on the sample 

2054 skewness and kurtosis. 

2055 

2056 >>> from scipy import stats 

2057 >>> res = stats.jarque_bera(x) 

2058 >>> res.statistic 

2059 6.982848237344646 

2060 

2061 Because the normal distribution has zero skewness and zero 

2062 ("excess" or "Fisher") kurtosis, the value of this statistic tends to be 

2063 low for samples drawn from a normal distribution. 

2064 

2065 The test is performed by comparing the observed value of the statistic 

2066 against the null distribution: the distribution of statistic values derived 

2067 under the null hypothesis that the weights were drawn from a normal 

2068 distribution. 

2069 For the Jarque-Bera test, the null distribution for very large samples is 

2070 the chi-squared distribution with two degrees of freedom. 

2071 

2072 >>> import matplotlib.pyplot as plt 

2073 >>> dist = stats.chi2(df=2) 

2074 >>> jb_val = np.linspace(0, 11, 100) 

2075 >>> pdf = dist.pdf(jb_val) 

2076 >>> fig, ax = plt.subplots(figsize=(8, 5)) 

2077 >>> def jb_plot(ax): # we'll re-use this 

2078 ... ax.plot(jb_val, pdf) 

2079 ... ax.set_title("Jarque-Bera Null Distribution") 

2080 ... ax.set_xlabel("statistic") 

2081 ... ax.set_ylabel("probability density") 

2082 >>> jb_plot(ax) 

2083 >>> plt.show() 

2084 

2085 The comparison is quantified by the p-value: the proportion of values in 

2086 the null distribution greater than or equal to the observed value of the 

2087 statistic. 

2088 

2089 >>> fig, ax = plt.subplots(figsize=(8, 5)) 

2090 >>> jb_plot(ax) 

2091 >>> pvalue = dist.sf(res.statistic) 

2092 >>> annotation = (f'p-value={pvalue:.6f}\n(shaded area)') 

2093 >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8) 

2094 >>> _ = ax.annotate(annotation, (7.5, 0.01), (8, 0.05), arrowprops=props) 

2095 >>> i = jb_val >= res.statistic # indices of more extreme statistic values 

2096 >>> ax.fill_between(jb_val[i], y1=0, y2=pdf[i]) 

2097 >>> ax.set_xlim(0, 11) 

2098 >>> ax.set_ylim(0, 0.3) 

2099 >>> plt.show() 

2100 >>> res.pvalue 

2101 0.03045746622458189 

2102 

2103 If the p-value is "small" - that is, if there is a low probability of 

2104 sampling data from a normally distributed population that produces such an 

2105 extreme value of the statistic - this may be taken as evidence against 

2106 the null hypothesis in favor of the alternative: the weights were not 

2107 drawn from a normal distribution. Note that: 

2108 

2109 - The inverse is not true; that is, the test is not used to provide 

2110 evidence for the null hypothesis. 

2111 - The threshold for values that will be considered "small" is a choice that 

2112 should be made before the data is analyzed [3]_ with consideration of the 

2113 risks of both false positives (incorrectly rejecting the null hypothesis) 

2114 and false negatives (failure to reject a false null hypothesis). 

2115 

2116 Note that the chi-squared distribution provides an asymptotic approximation 

2117 of the null distribution; it is only accurate for samples with many 

2118 observations. For small samples like ours, `scipy.stats.monte_carlo_test` 

2119 may provide a more accurate, albeit stochastic, approximation of the 

2120 exact p-value. 

2121 

2122 >>> def statistic(x, axis): 

2123 ... # underlying calculation of the Jarque Bera statistic 

2124 ... s = stats.skew(x, axis=axis) 

2125 ... k = stats.kurtosis(x, axis=axis) 

2126 ... return x.shape[axis]/6 * (s**2 + k**2/4) 

2127 >>> res = stats.monte_carlo_test(x, stats.norm.rvs, statistic, 

2128 ... alternative='greater') 

2129 >>> fig, ax = plt.subplots(figsize=(8, 5)) 

2130 >>> jb_plot(ax) 

2131 >>> ax.hist(res.null_distribution, np.linspace(0, 10, 50), 

2132 ... density=True) 

2133 >>> ax.legend(['aymptotic approximation (many observations)', 

2134 ... 'Monte Carlo approximation (11 observations)']) 

2135 >>> plt.show() 

2136 >>> res.pvalue 

2137 0.0097 # may vary 

2138 

2139 Furthermore, despite their stochastic nature, p-values computed in this way 

2140 can be used to exactly control the rate of false rejections of the null 

2141 hypothesis [4]_. 

2142 

2143 """ 

2144 x = np.asarray(x) 

2145 if axis is None: 

2146 x = x.ravel() 

2147 axis = 0 

2148 

2149 n = x.shape[axis] 

2150 if n == 0: 

2151 raise ValueError('At least one observation is required.') 

2152 

2153 mu = x.mean(axis=axis, keepdims=True) 

2154 diffx = x - mu 

2155 s = skew(diffx, axis=axis, _no_deco=True) 

2156 k = kurtosis(diffx, axis=axis, _no_deco=True) 

2157 statistic = n / 6 * (s**2 + k**2 / 4) 

2158 pvalue = distributions.chi2.sf(statistic, df=2) 

2159 

2160 return SignificanceResult(statistic, pvalue) 

2161 

2162 

2163##################################### 

2164# FREQUENCY FUNCTIONS # 

2165##################################### 

2166 

2167 

2168def scoreatpercentile(a, per, limit=(), interpolation_method='fraction', 

2169 axis=None): 

2170 """Calculate the score at a given percentile of the input sequence. 

2171 

2172 For example, the score at `per=50` is the median. If the desired quantile 

2173 lies between two data points, we interpolate between them, according to 

2174 the value of `interpolation`. If the parameter `limit` is provided, it 

2175 should be a tuple (lower, upper) of two values. 

2176 

2177 Parameters 

2178 ---------- 

2179 a : array_like 

2180 A 1-D array of values from which to extract score. 

2181 per : array_like 

2182 Percentile(s) at which to extract score. Values should be in range 

2183 [0,100]. 

2184 limit : tuple, optional 

2185 Tuple of two scalars, the lower and upper limits within which to 

2186 compute the percentile. Values of `a` outside 

2187 this (closed) interval will be ignored. 

2188 interpolation_method : {'fraction', 'lower', 'higher'}, optional 

2189 Specifies the interpolation method to use, 

2190 when the desired quantile lies between two data points `i` and `j` 

2191 The following options are available (default is 'fraction'): 

2192 

2193 * 'fraction': ``i + (j - i) * fraction`` where ``fraction`` is the 

2194 fractional part of the index surrounded by ``i`` and ``j`` 

2195 * 'lower': ``i`` 

2196 * 'higher': ``j`` 

2197 

2198 axis : int, optional 

2199 Axis along which the percentiles are computed. Default is None. If 

2200 None, compute over the whole array `a`. 

2201 

2202 Returns 

2203 ------- 

2204 score : float or ndarray 

2205 Score at percentile(s). 

2206 

2207 See Also 

2208 -------- 

2209 percentileofscore, numpy.percentile 

2210 

2211 Notes 

2212 ----- 

2213 This function will become obsolete in the future. 

2214 For NumPy 1.9 and higher, `numpy.percentile` provides all the functionality 

2215 that `scoreatpercentile` provides. And it's significantly faster. 

2216 Therefore it's recommended to use `numpy.percentile` for users that have 

2217 numpy >= 1.9. 

2218 

2219 Examples 

2220 -------- 

2221 >>> import numpy as np 

2222 >>> from scipy import stats 

2223 >>> a = np.arange(100) 

2224 >>> stats.scoreatpercentile(a, 50) 

2225 49.5 

2226 

2227 """ 

2228 # adapted from NumPy's percentile function. When we require numpy >= 1.8, 

2229 # the implementation of this function can be replaced by np.percentile. 

2230 a = np.asarray(a) 

2231 if a.size == 0: 

2232 # empty array, return nan(s) with shape matching `per` 

2233 if np.isscalar(per): 

2234 return np.nan 

2235 else: 

2236 return np.full(np.asarray(per).shape, np.nan, dtype=np.float64) 

2237 

2238 if limit: 

2239 a = a[(limit[0] <= a) & (a <= limit[1])] 

2240 

2241 sorted_ = np.sort(a, axis=axis) 

2242 if axis is None: 

2243 axis = 0 

2244 

2245 return _compute_qth_percentile(sorted_, per, interpolation_method, axis) 

2246 

2247 

2248# handle sequence of per's without calling sort multiple times 

2249def _compute_qth_percentile(sorted_, per, interpolation_method, axis): 

2250 if not np.isscalar(per): 

2251 score = [_compute_qth_percentile(sorted_, i, 

2252 interpolation_method, axis) 

2253 for i in per] 

2254 return np.array(score) 

2255 

2256 if not (0 <= per <= 100): 

2257 raise ValueError("percentile must be in the range [0, 100]") 

2258 

2259 indexer = [slice(None)] * sorted_.ndim 

2260 idx = per / 100. * (sorted_.shape[axis] - 1) 

2261 

2262 if int(idx) != idx: 

2263 # round fractional indices according to interpolation method 

2264 if interpolation_method == 'lower': 

2265 idx = int(np.floor(idx)) 

2266 elif interpolation_method == 'higher': 

2267 idx = int(np.ceil(idx)) 

2268 elif interpolation_method == 'fraction': 

2269 pass # keep idx as fraction and interpolate 

2270 else: 

2271 raise ValueError("interpolation_method can only be 'fraction', " 

2272 "'lower' or 'higher'") 

2273 

2274 i = int(idx) 

2275 if i == idx: 

2276 indexer[axis] = slice(i, i + 1) 

2277 weights = array(1) 

2278 sumval = 1.0 

2279 else: 

2280 indexer[axis] = slice(i, i + 2) 

2281 j = i + 1 

2282 weights = array([(j - idx), (idx - i)], float) 

2283 wshape = [1] * sorted_.ndim 

2284 wshape[axis] = 2 

2285 weights.shape = wshape 

2286 sumval = weights.sum() 

2287 

2288 # Use np.add.reduce (== np.sum but a little faster) to coerce data type 

2289 return np.add.reduce(sorted_[tuple(indexer)] * weights, axis=axis) / sumval 

2290 

2291 

2292def percentileofscore(a, score, kind='rank', nan_policy='propagate'): 

2293 """Compute the percentile rank of a score relative to a list of scores. 

2294 

2295 A `percentileofscore` of, for example, 80% means that 80% of the 

2296 scores in `a` are below the given score. In the case of gaps or 

2297 ties, the exact definition depends on the optional keyword, `kind`. 

2298 

2299 Parameters 

2300 ---------- 

2301 a : array_like 

2302 Array to which `score` is compared. 

2303 score : array_like 

2304 Scores to compute percentiles for. 

2305 kind : {'rank', 'weak', 'strict', 'mean'}, optional 

2306 Specifies the interpretation of the resulting score. 

2307 The following options are available (default is 'rank'): 

2308 

2309 * 'rank': Average percentage ranking of score. In case of multiple 

2310 matches, average the percentage rankings of all matching scores. 

2311 * 'weak': This kind corresponds to the definition of a cumulative 

2312 distribution function. A percentileofscore of 80% means that 80% 

2313 of values are less than or equal to the provided score. 

2314 * 'strict': Similar to "weak", except that only values that are 

2315 strictly less than the given score are counted. 

2316 * 'mean': The average of the "weak" and "strict" scores, often used 

2317 in testing. See https://en.wikipedia.org/wiki/Percentile_rank 

2318 nan_policy : {'propagate', 'raise', 'omit'}, optional 

2319 Specifies how to treat `nan` values in `a`. 

2320 The following options are available (default is 'propagate'): 

2321 

2322 * 'propagate': returns nan (for each value in `score`). 

2323 * 'raise': throws an error 

2324 * 'omit': performs the calculations ignoring nan values 

2325 

2326 Returns 

2327 ------- 

2328 pcos : float 

2329 Percentile-position of score (0-100) relative to `a`. 

2330 

2331 See Also 

2332 -------- 

2333 numpy.percentile 

2334 scipy.stats.scoreatpercentile, scipy.stats.rankdata 

2335 

2336 Examples 

2337 -------- 

2338 Three-quarters of the given values lie below a given score: 

2339 

2340 >>> import numpy as np 

2341 >>> from scipy import stats 

2342 >>> stats.percentileofscore([1, 2, 3, 4], 3) 

2343 75.0 

2344 

2345 With multiple matches, note how the scores of the two matches, 0.6 

2346 and 0.8 respectively, are averaged: 

2347 

2348 >>> stats.percentileofscore([1, 2, 3, 3, 4], 3) 

2349 70.0 

2350 

2351 Only 2/5 values are strictly less than 3: 

2352 

2353 >>> stats.percentileofscore([1, 2, 3, 3, 4], 3, kind='strict') 

2354 40.0 

2355 

2356 But 4/5 values are less than or equal to 3: 

2357 

2358 >>> stats.percentileofscore([1, 2, 3, 3, 4], 3, kind='weak') 

2359 80.0 

2360 

2361 The average between the weak and the strict scores is: 

2362 

2363 >>> stats.percentileofscore([1, 2, 3, 3, 4], 3, kind='mean') 

2364 60.0 

2365 

2366 Score arrays (of any dimensionality) are supported: 

2367 

2368 >>> stats.percentileofscore([1, 2, 3, 3, 4], [2, 3]) 

2369 array([40., 70.]) 

2370 

2371 The inputs can be infinite: 

2372 

2373 >>> stats.percentileofscore([-np.inf, 0, 1, np.inf], [1, 2, np.inf]) 

2374 array([75., 75., 100.]) 

2375 

2376 If `a` is empty, then the resulting percentiles are all `nan`: 

2377 

2378 >>> stats.percentileofscore([], [1, 2]) 

2379 array([nan, nan]) 

2380 """ 

2381 

2382 a = np.asarray(a) 

2383 n = len(a) 

2384 score = np.asarray(score) 

2385 

2386 # Nan treatment 

2387 cna, npa = _contains_nan(a, nan_policy, use_summation=False) 

2388 cns, nps = _contains_nan(score, nan_policy, use_summation=False) 

2389 

2390 if (cna or cns) and nan_policy == 'raise': 

2391 raise ValueError("The input contains nan values") 

2392 

2393 if cns: 

2394 # If a score is nan, then the output should be nan 

2395 # (also if nan_policy is "omit", because it only applies to `a`) 

2396 score = ma.masked_where(np.isnan(score), score) 

2397 

2398 if cna: 

2399 if nan_policy == "omit": 

2400 # Don't count nans 

2401 a = ma.masked_where(np.isnan(a), a) 

2402 n = a.count() 

2403 

2404 if nan_policy == "propagate": 

2405 # All outputs should be nans 

2406 n = 0 

2407 

2408 # Cannot compare to empty list ==> nan 

2409 if n == 0: 

2410 perct = np.full_like(score, np.nan, dtype=np.float64) 

2411 

2412 else: 

2413 # Prepare broadcasting 

2414 score = score[..., None] 

2415 

2416 def count(x): 

2417 return np.count_nonzero(x, -1) 

2418 

2419 # Despite using masked_array to omit nan values from processing, 

2420 # the CI tests on "Azure pipelines" (but not on the other CI servers) 

2421 # emits warnings when there are nan values, contrarily to the purpose 

2422 # of masked_arrays. As a fix, we simply suppress the warnings. 

2423 with suppress_warnings() as sup: 

2424 sup.filter(RuntimeWarning, 

2425 "invalid value encountered in less") 

2426 sup.filter(RuntimeWarning, 

2427 "invalid value encountered in greater") 

2428 

2429 # Main computations/logic 

2430 if kind == 'rank': 

2431 left = count(a < score) 

2432 right = count(a <= score) 

2433 plus1 = left < right 

2434 perct = (left + right + plus1) * (50.0 / n) 

2435 elif kind == 'strict': 

2436 perct = count(a < score) * (100.0 / n) 

2437 elif kind == 'weak': 

2438 perct = count(a <= score) * (100.0 / n) 

2439 elif kind == 'mean': 

2440 left = count(a < score) 

2441 right = count(a <= score) 

2442 perct = (left + right) * (50.0 / n) 

2443 else: 

2444 raise ValueError( 

2445 "kind can only be 'rank', 'strict', 'weak' or 'mean'") 

2446 

2447 # Re-insert nan values 

2448 perct = ma.filled(perct, np.nan) 

2449 

2450 if perct.ndim == 0: 

2451 return perct[()] 

2452 return perct 

2453 

2454 

2455HistogramResult = namedtuple('HistogramResult', 

2456 ('count', 'lowerlimit', 'binsize', 'extrapoints')) 

2457 

2458 

2459def _histogram(a, numbins=10, defaultlimits=None, weights=None, 

2460 printextras=False): 

2461 """Create a histogram. 

2462 

2463 Separate the range into several bins and return the number of instances 

2464 in each bin. 

2465 

2466 Parameters 

2467 ---------- 

2468 a : array_like 

2469 Array of scores which will be put into bins. 

2470 numbins : int, optional 

2471 The number of bins to use for the histogram. Default is 10. 

2472 defaultlimits : tuple (lower, upper), optional 

2473 The lower and upper values for the range of the histogram. 

2474 If no value is given, a range slightly larger than the range of the 

2475 values in a is used. Specifically ``(a.min() - s, a.max() + s)``, 

2476 where ``s = (1/2)(a.max() - a.min()) / (numbins - 1)``. 

2477 weights : array_like, optional 

2478 The weights for each value in `a`. Default is None, which gives each 

2479 value a weight of 1.0 

2480 printextras : bool, optional 

2481 If True, if there are extra points (i.e. the points that fall outside 

2482 the bin limits) a warning is raised saying how many of those points 

2483 there are. Default is False. 

2484 

2485 Returns 

2486 ------- 

2487 count : ndarray 

2488 Number of points (or sum of weights) in each bin. 

2489 lowerlimit : float 

2490 Lowest value of histogram, the lower limit of the first bin. 

2491 binsize : float 

2492 The size of the bins (all bins have the same size). 

2493 extrapoints : int 

2494 The number of points outside the range of the histogram. 

2495 

2496 See Also 

2497 -------- 

2498 numpy.histogram 

2499 

2500 Notes 

2501 ----- 

2502 This histogram is based on numpy's histogram but has a larger range by 

2503 default if default limits is not set. 

2504 

2505 """ 

2506 a = np.ravel(a) 

2507 if defaultlimits is None: 

2508 if a.size == 0: 

2509 # handle empty arrays. Undetermined range, so use 0-1. 

2510 defaultlimits = (0, 1) 

2511 else: 

2512 # no range given, so use values in `a` 

2513 data_min = a.min() 

2514 data_max = a.max() 

2515 # Have bins extend past min and max values slightly 

2516 s = (data_max - data_min) / (2. * (numbins - 1.)) 

2517 defaultlimits = (data_min - s, data_max + s) 

2518 

2519 # use numpy's histogram method to compute bins 

2520 hist, bin_edges = np.histogram(a, bins=numbins, range=defaultlimits, 

2521 weights=weights) 

2522 # hist are not always floats, convert to keep with old output 

2523 hist = np.array(hist, dtype=float) 

2524 # fixed width for bins is assumed, as numpy's histogram gives 

2525 # fixed width bins for int values for 'bins' 

2526 binsize = bin_edges[1] - bin_edges[0] 

2527 # calculate number of extra points 

2528 extrapoints = len([v for v in a 

2529 if defaultlimits[0] > v or v > defaultlimits[1]]) 

2530 if extrapoints > 0 and printextras: 

2531 warnings.warn("Points outside given histogram range = %s" 

2532 % extrapoints) 

2533 

2534 return HistogramResult(hist, defaultlimits[0], binsize, extrapoints) 

2535 

2536 

2537CumfreqResult = namedtuple('CumfreqResult', 

2538 ('cumcount', 'lowerlimit', 'binsize', 

2539 'extrapoints')) 

2540 

2541 

2542def cumfreq(a, numbins=10, defaultreallimits=None, weights=None): 

2543 """Return a cumulative frequency histogram, using the histogram function. 

2544 

2545 A cumulative histogram is a mapping that counts the cumulative number of 

2546 observations in all of the bins up to the specified bin. 

2547 

2548 Parameters 

2549 ---------- 

2550 a : array_like 

2551 Input array. 

2552 numbins : int, optional 

2553 The number of bins to use for the histogram. Default is 10. 

2554 defaultreallimits : tuple (lower, upper), optional 

2555 The lower and upper values for the range of the histogram. 

2556 If no value is given, a range slightly larger than the range of the 

2557 values in `a` is used. Specifically ``(a.min() - s, a.max() + s)``, 

2558 where ``s = (1/2)(a.max() - a.min()) / (numbins - 1)``. 

2559 weights : array_like, optional 

2560 The weights for each value in `a`. Default is None, which gives each 

2561 value a weight of 1.0 

2562 

2563 Returns 

2564 ------- 

2565 cumcount : ndarray 

2566 Binned values of cumulative frequency. 

2567 lowerlimit : float 

2568 Lower real limit 

2569 binsize : float 

2570 Width of each bin. 

2571 extrapoints : int 

2572 Extra points. 

2573 

2574 Examples 

2575 -------- 

2576 >>> import numpy as np 

2577 >>> import matplotlib.pyplot as plt 

2578 >>> from scipy import stats 

2579 >>> rng = np.random.default_rng() 

2580 >>> x = [1, 4, 2, 1, 3, 1] 

2581 >>> res = stats.cumfreq(x, numbins=4, defaultreallimits=(1.5, 5)) 

2582 >>> res.cumcount 

2583 array([ 1., 2., 3., 3.]) 

2584 >>> res.extrapoints 

2585 3 

2586 

2587 Create a normal distribution with 1000 random values 

2588 

2589 >>> samples = stats.norm.rvs(size=1000, random_state=rng) 

2590 

2591 Calculate cumulative frequencies 

2592 

2593 >>> res = stats.cumfreq(samples, numbins=25) 

2594 

2595 Calculate space of values for x 

2596 

2597 >>> x = res.lowerlimit + np.linspace(0, res.binsize*res.cumcount.size, 

2598 ... res.cumcount.size) 

2599 

2600 Plot histogram and cumulative histogram 

2601 

2602 >>> fig = plt.figure(figsize=(10, 4)) 

2603 >>> ax1 = fig.add_subplot(1, 2, 1) 

2604 >>> ax2 = fig.add_subplot(1, 2, 2) 

2605 >>> ax1.hist(samples, bins=25) 

2606 >>> ax1.set_title('Histogram') 

2607 >>> ax2.bar(x, res.cumcount, width=res.binsize) 

2608 >>> ax2.set_title('Cumulative histogram') 

2609 >>> ax2.set_xlim([x.min(), x.max()]) 

2610 

2611 >>> plt.show() 

2612 

2613 """ 

2614 h, l, b, e = _histogram(a, numbins, defaultreallimits, weights=weights) 

2615 cumhist = np.cumsum(h * 1, axis=0) 

2616 return CumfreqResult(cumhist, l, b, e) 

2617 

2618 

2619RelfreqResult = namedtuple('RelfreqResult', 

2620 ('frequency', 'lowerlimit', 'binsize', 

2621 'extrapoints')) 

2622 

2623 

2624def relfreq(a, numbins=10, defaultreallimits=None, weights=None): 

2625 """Return a relative frequency histogram, using the histogram function. 

2626 

2627 A relative frequency histogram is a mapping of the number of 

2628 observations in each of the bins relative to the total of observations. 

2629 

2630 Parameters 

2631 ---------- 

2632 a : array_like 

2633 Input array. 

2634 numbins : int, optional 

2635 The number of bins to use for the histogram. Default is 10. 

2636 defaultreallimits : tuple (lower, upper), optional 

2637 The lower and upper values for the range of the histogram. 

2638 If no value is given, a range slightly larger than the range of the 

2639 values in a is used. Specifically ``(a.min() - s, a.max() + s)``, 

2640 where ``s = (1/2)(a.max() - a.min()) / (numbins - 1)``. 

2641 weights : array_like, optional 

2642 The weights for each value in `a`. Default is None, which gives each 

2643 value a weight of 1.0 

2644 

2645 Returns 

2646 ------- 

2647 frequency : ndarray 

2648 Binned values of relative frequency. 

2649 lowerlimit : float 

2650 Lower real limit. 

2651 binsize : float 

2652 Width of each bin. 

2653 extrapoints : int 

2654 Extra points. 

2655 

2656 Examples 

2657 -------- 

2658 >>> import numpy as np 

2659 >>> import matplotlib.pyplot as plt 

2660 >>> from scipy import stats 

2661 >>> rng = np.random.default_rng() 

2662 >>> a = np.array([2, 4, 1, 2, 3, 2]) 

2663 >>> res = stats.relfreq(a, numbins=4) 

2664 >>> res.frequency 

2665 array([ 0.16666667, 0.5 , 0.16666667, 0.16666667]) 

2666 >>> np.sum(res.frequency) # relative frequencies should add up to 1 

2667 1.0 

2668 

2669 Create a normal distribution with 1000 random values 

2670 

2671 >>> samples = stats.norm.rvs(size=1000, random_state=rng) 

2672 

2673 Calculate relative frequencies 

2674 

2675 >>> res = stats.relfreq(samples, numbins=25) 

2676 

2677 Calculate space of values for x 

2678 

2679 >>> x = res.lowerlimit + np.linspace(0, res.binsize*res.frequency.size, 

2680 ... res.frequency.size) 

2681 

2682 Plot relative frequency histogram 

2683 

2684 >>> fig = plt.figure(figsize=(5, 4)) 

2685 >>> ax = fig.add_subplot(1, 1, 1) 

2686 >>> ax.bar(x, res.frequency, width=res.binsize) 

2687 >>> ax.set_title('Relative frequency histogram') 

2688 >>> ax.set_xlim([x.min(), x.max()]) 

2689 

2690 >>> plt.show() 

2691 

2692 """ 

2693 a = np.asanyarray(a) 

2694 h, l, b, e = _histogram(a, numbins, defaultreallimits, weights=weights) 

2695 h = h / a.shape[0] 

2696 

2697 return RelfreqResult(h, l, b, e) 

2698 

2699 

2700##################################### 

2701# VARIABILITY FUNCTIONS # 

2702##################################### 

2703 

2704def obrientransform(*samples): 

2705 """Compute the O'Brien transform on input data (any number of arrays). 

2706 

2707 Used to test for homogeneity of variance prior to running one-way stats. 

2708 Each array in ``*samples`` is one level of a factor. 

2709 If `f_oneway` is run on the transformed data and found significant, 

2710 the variances are unequal. From Maxwell and Delaney [1]_, p.112. 

2711 

2712 Parameters 

2713 ---------- 

2714 sample1, sample2, ... : array_like 

2715 Any number of arrays. 

2716 

2717 Returns 

2718 ------- 

2719 obrientransform : ndarray 

2720 Transformed data for use in an ANOVA. The first dimension 

2721 of the result corresponds to the sequence of transformed 

2722 arrays. If the arrays given are all 1-D of the same length, 

2723 the return value is a 2-D array; otherwise it is a 1-D array 

2724 of type object, with each element being an ndarray. 

2725 

2726 References 

2727 ---------- 

2728 .. [1] S. E. Maxwell and H. D. Delaney, "Designing Experiments and 

2729 Analyzing Data: A Model Comparison Perspective", Wadsworth, 1990. 

2730 

2731 Examples 

2732 -------- 

2733 We'll test the following data sets for differences in their variance. 

2734 

2735 >>> x = [10, 11, 13, 9, 7, 12, 12, 9, 10] 

2736 >>> y = [13, 21, 5, 10, 8, 14, 10, 12, 7, 15] 

2737 

2738 Apply the O'Brien transform to the data. 

2739 

2740 >>> from scipy.stats import obrientransform 

2741 >>> tx, ty = obrientransform(x, y) 

2742 

2743 Use `scipy.stats.f_oneway` to apply a one-way ANOVA test to the 

2744 transformed data. 

2745 

2746 >>> from scipy.stats import f_oneway 

2747 >>> F, p = f_oneway(tx, ty) 

2748 >>> p 

2749 0.1314139477040335 

2750 

2751 If we require that ``p < 0.05`` for significance, we cannot conclude 

2752 that the variances are different. 

2753 

2754 """ 

2755 TINY = np.sqrt(np.finfo(float).eps) 

2756 

2757 # `arrays` will hold the transformed arguments. 

2758 arrays = [] 

2759 sLast = None 

2760 

2761 for sample in samples: 

2762 a = np.asarray(sample) 

2763 n = len(a) 

2764 mu = np.mean(a) 

2765 sq = (a - mu)**2 

2766 sumsq = sq.sum() 

2767 

2768 # The O'Brien transform. 

2769 t = ((n - 1.5) * n * sq - 0.5 * sumsq) / ((n - 1) * (n - 2)) 

2770 

2771 # Check that the mean of the transformed data is equal to the 

2772 # original variance. 

2773 var = sumsq / (n - 1) 

2774 if abs(var - np.mean(t)) > TINY: 

2775 raise ValueError('Lack of convergence in obrientransform.') 

2776 

2777 arrays.append(t) 

2778 sLast = a.shape 

2779 

2780 if sLast: 

2781 for arr in arrays[:-1]: 

2782 if sLast != arr.shape: 

2783 return np.array(arrays, dtype=object) 

2784 return np.array(arrays) 

2785 

2786 

2787@_axis_nan_policy_factory( 

2788 lambda x: x, result_to_tuple=lambda x: (x,), n_outputs=1, too_small=1 

2789) 

2790def sem(a, axis=0, ddof=1, nan_policy='propagate'): 

2791 """Compute standard error of the mean. 

2792 

2793 Calculate the standard error of the mean (or standard error of 

2794 measurement) of the values in the input array. 

2795 

2796 Parameters 

2797 ---------- 

2798 a : array_like 

2799 An array containing the values for which the standard error is 

2800 returned. 

2801 axis : int or None, optional 

2802 Axis along which to operate. Default is 0. If None, compute over 

2803 the whole array `a`. 

2804 ddof : int, optional 

2805 Delta degrees-of-freedom. How many degrees of freedom to adjust 

2806 for bias in limited samples relative to the population estimate 

2807 of variance. Defaults to 1. 

2808 nan_policy : {'propagate', 'raise', 'omit'}, optional 

2809 Defines how to handle when input contains nan. 

2810 The following options are available (default is 'propagate'): 

2811 

2812 * 'propagate': returns nan 

2813 * 'raise': throws an error 

2814 * 'omit': performs the calculations ignoring nan values 

2815 

2816 Returns 

2817 ------- 

2818 s : ndarray or float 

2819 The standard error of the mean in the sample(s), along the input axis. 

2820 

2821 Notes 

2822 ----- 

2823 The default value for `ddof` is different to the default (0) used by other 

2824 ddof containing routines, such as np.std and np.nanstd. 

2825 

2826 Examples 

2827 -------- 

2828 Find standard error along the first axis: 

2829 

2830 >>> import numpy as np 

2831 >>> from scipy import stats 

2832 >>> a = np.arange(20).reshape(5,4) 

2833 >>> stats.sem(a) 

2834 array([ 2.8284, 2.8284, 2.8284, 2.8284]) 

2835 

2836 Find standard error across the whole array, using n degrees of freedom: 

2837 

2838 >>> stats.sem(a, axis=None, ddof=0) 

2839 1.2893796958227628 

2840 

2841 """ 

2842 n = a.shape[axis] 

2843 s = np.std(a, axis=axis, ddof=ddof) / np.sqrt(n) 

2844 return s 

2845 

2846 

2847def _isconst(x): 

2848 """ 

2849 Check if all values in x are the same. nans are ignored. 

2850 

2851 x must be a 1d array. 

2852 

2853 The return value is a 1d array with length 1, so it can be used 

2854 in np.apply_along_axis. 

2855 """ 

2856 y = x[~np.isnan(x)] 

2857 if y.size == 0: 

2858 return np.array([True]) 

2859 else: 

2860 return (y[0] == y).all(keepdims=True) 

2861 

2862 

2863def _quiet_nanmean(x): 

2864 """ 

2865 Compute nanmean for the 1d array x, but quietly return nan if x is all nan. 

2866 

2867 The return value is a 1d array with length 1, so it can be used 

2868 in np.apply_along_axis. 

2869 """ 

2870 y = x[~np.isnan(x)] 

2871 if y.size == 0: 

2872 return np.array([np.nan]) 

2873 else: 

2874 return np.mean(y, keepdims=True) 

2875 

2876 

2877def _quiet_nanstd(x, ddof=0): 

2878 """ 

2879 Compute nanstd for the 1d array x, but quietly return nan if x is all nan. 

2880 

2881 The return value is a 1d array with length 1, so it can be used 

2882 in np.apply_along_axis. 

2883 """ 

2884 y = x[~np.isnan(x)] 

2885 if y.size == 0: 

2886 return np.array([np.nan]) 

2887 else: 

2888 return np.std(y, keepdims=True, ddof=ddof) 

2889 

2890 

2891def zscore(a, axis=0, ddof=0, nan_policy='propagate'): 

2892 """ 

2893 Compute the z score. 

2894 

2895 Compute the z score of each value in the sample, relative to the 

2896 sample mean and standard deviation. 

2897 

2898 Parameters 

2899 ---------- 

2900 a : array_like 

2901 An array like object containing the sample data. 

2902 axis : int or None, optional 

2903 Axis along which to operate. Default is 0. If None, compute over 

2904 the whole array `a`. 

2905 ddof : int, optional 

2906 Degrees of freedom correction in the calculation of the 

2907 standard deviation. Default is 0. 

2908 nan_policy : {'propagate', 'raise', 'omit'}, optional 

2909 Defines how to handle when input contains nan. 'propagate' returns nan, 

2910 'raise' throws an error, 'omit' performs the calculations ignoring nan 

2911 values. Default is 'propagate'. Note that when the value is 'omit', 

2912 nans in the input also propagate to the output, but they do not affect 

2913 the z-scores computed for the non-nan values. 

2914 

2915 Returns 

2916 ------- 

2917 zscore : array_like 

2918 The z-scores, standardized by mean and standard deviation of 

2919 input array `a`. 

2920 

2921 See Also 

2922 -------- 

2923 numpy.mean : Arithmetic average 

2924 numpy.std : Arithmetic standard deviation 

2925 scipy.stats.gzscore : Geometric standard score 

2926 

2927 Notes 

2928 ----- 

2929 This function preserves ndarray subclasses, and works also with 

2930 matrices and masked arrays (it uses `asanyarray` instead of 

2931 `asarray` for parameters). 

2932 

2933 References 

2934 ---------- 

2935 .. [1] "Standard score", *Wikipedia*, 

2936 https://en.wikipedia.org/wiki/Standard_score. 

2937 .. [2] Huck, S. W., Cross, T. L., Clark, S. B, "Overcoming misconceptions 

2938 about Z-scores", Teaching Statistics, vol. 8, pp. 38-40, 1986 

2939 

2940 Examples 

2941 -------- 

2942 >>> import numpy as np 

2943 >>> a = np.array([ 0.7972, 0.0767, 0.4383, 0.7866, 0.8091, 

2944 ... 0.1954, 0.6307, 0.6599, 0.1065, 0.0508]) 

2945 >>> from scipy import stats 

2946 >>> stats.zscore(a) 

2947 array([ 1.1273, -1.247 , -0.0552, 1.0923, 1.1664, -0.8559, 0.5786, 

2948 0.6748, -1.1488, -1.3324]) 

2949 

2950 Computing along a specified axis, using n-1 degrees of freedom 

2951 (``ddof=1``) to calculate the standard deviation: 

2952 

2953 >>> b = np.array([[ 0.3148, 0.0478, 0.6243, 0.4608], 

2954 ... [ 0.7149, 0.0775, 0.6072, 0.9656], 

2955 ... [ 0.6341, 0.1403, 0.9759, 0.4064], 

2956 ... [ 0.5918, 0.6948, 0.904 , 0.3721], 

2957 ... [ 0.0921, 0.2481, 0.1188, 0.1366]]) 

2958 >>> stats.zscore(b, axis=1, ddof=1) 

2959 array([[-0.19264823, -1.28415119, 1.07259584, 0.40420358], 

2960 [ 0.33048416, -1.37380874, 0.04251374, 1.00081084], 

2961 [ 0.26796377, -1.12598418, 1.23283094, -0.37481053], 

2962 [-0.22095197, 0.24468594, 1.19042819, -1.21416216], 

2963 [-0.82780366, 1.4457416 , -0.43867764, -0.1792603 ]]) 

2964 

2965 An example with `nan_policy='omit'`: 

2966 

2967 >>> x = np.array([[25.11, 30.10, np.nan, 32.02, 43.15], 

2968 ... [14.95, 16.06, 121.25, 94.35, 29.81]]) 

2969 >>> stats.zscore(x, axis=1, nan_policy='omit') 

2970 array([[-1.13490897, -0.37830299, nan, -0.08718406, 1.60039602], 

2971 [-0.91611681, -0.89090508, 1.4983032 , 0.88731639, -0.5785977 ]]) 

2972 """ 

2973 return zmap(a, a, axis=axis, ddof=ddof, nan_policy=nan_policy) 

2974 

2975 

2976def gzscore(a, *, axis=0, ddof=0, nan_policy='propagate'): 

2977 """ 

2978 Compute the geometric standard score. 

2979 

2980 Compute the geometric z score of each strictly positive value in the 

2981 sample, relative to the geometric mean and standard deviation. 

2982 Mathematically the geometric z score can be evaluated as:: 

2983 

2984 gzscore = log(a/gmu) / log(gsigma) 

2985 

2986 where ``gmu`` (resp. ``gsigma``) is the geometric mean (resp. standard 

2987 deviation). 

2988 

2989 Parameters 

2990 ---------- 

2991 a : array_like 

2992 Sample data. 

2993 axis : int or None, optional 

2994 Axis along which to operate. Default is 0. If None, compute over 

2995 the whole array `a`. 

2996 ddof : int, optional 

2997 Degrees of freedom correction in the calculation of the 

2998 standard deviation. Default is 0. 

2999 nan_policy : {'propagate', 'raise', 'omit'}, optional 

3000 Defines how to handle when input contains nan. 'propagate' returns nan, 

3001 'raise' throws an error, 'omit' performs the calculations ignoring nan 

3002 values. Default is 'propagate'. Note that when the value is 'omit', 

3003 nans in the input also propagate to the output, but they do not affect 

3004 the geometric z scores computed for the non-nan values. 

3005 

3006 Returns 

3007 ------- 

3008 gzscore : array_like 

3009 The geometric z scores, standardized by geometric mean and geometric 

3010 standard deviation of input array `a`. 

3011 

3012 See Also 

3013 -------- 

3014 gmean : Geometric mean 

3015 gstd : Geometric standard deviation 

3016 zscore : Standard score 

3017 

3018 Notes 

3019 ----- 

3020 This function preserves ndarray subclasses, and works also with 

3021 matrices and masked arrays (it uses ``asanyarray`` instead of 

3022 ``asarray`` for parameters). 

3023 

3024 .. versionadded:: 1.8 

3025 

3026 References 

3027 ---------- 

3028 .. [1] "Geometric standard score", *Wikipedia*, 

3029 https://en.wikipedia.org/wiki/Geometric_standard_deviation#Geometric_standard_score. 

3030 

3031 Examples 

3032 -------- 

3033 Draw samples from a log-normal distribution: 

3034 

3035 >>> import numpy as np 

3036 >>> from scipy.stats import zscore, gzscore 

3037 >>> import matplotlib.pyplot as plt 

3038 

3039 >>> rng = np.random.default_rng() 

3040 >>> mu, sigma = 3., 1. # mean and standard deviation 

3041 >>> x = rng.lognormal(mu, sigma, size=500) 

3042 

3043 Display the histogram of the samples: 

3044 

3045 >>> fig, ax = plt.subplots() 

3046 >>> ax.hist(x, 50) 

3047 >>> plt.show() 

3048 

3049 Display the histogram of the samples standardized by the classical zscore. 

3050 Distribution is rescaled but its shape is unchanged. 

3051 

3052 >>> fig, ax = plt.subplots() 

3053 >>> ax.hist(zscore(x), 50) 

3054 >>> plt.show() 

3055 

3056 Demonstrate that the distribution of geometric zscores is rescaled and 

3057 quasinormal: 

3058 

3059 >>> fig, ax = plt.subplots() 

3060 >>> ax.hist(gzscore(x), 50) 

3061 >>> plt.show() 

3062 

3063 """ 

3064 a = np.asanyarray(a) 

3065 log = ma.log if isinstance(a, ma.MaskedArray) else np.log 

3066 

3067 return zscore(log(a), axis=axis, ddof=ddof, nan_policy=nan_policy) 

3068 

3069 

3070def zmap(scores, compare, axis=0, ddof=0, nan_policy='propagate'): 

3071 """ 

3072 Calculate the relative z-scores. 

3073 

3074 Return an array of z-scores, i.e., scores that are standardized to 

3075 zero mean and unit variance, where mean and variance are calculated 

3076 from the comparison array. 

3077 

3078 Parameters 

3079 ---------- 

3080 scores : array_like 

3081 The input for which z-scores are calculated. 

3082 compare : array_like 

3083 The input from which the mean and standard deviation of the 

3084 normalization are taken; assumed to have the same dimension as 

3085 `scores`. 

3086 axis : int or None, optional 

3087 Axis over which mean and variance of `compare` are calculated. 

3088 Default is 0. If None, compute over the whole array `scores`. 

3089 ddof : int, optional 

3090 Degrees of freedom correction in the calculation of the 

3091 standard deviation. Default is 0. 

3092 nan_policy : {'propagate', 'raise', 'omit'}, optional 

3093 Defines how to handle the occurrence of nans in `compare`. 

3094 'propagate' returns nan, 'raise' raises an exception, 'omit' 

3095 performs the calculations ignoring nan values. Default is 

3096 'propagate'. Note that when the value is 'omit', nans in `scores` 

3097 also propagate to the output, but they do not affect the z-scores 

3098 computed for the non-nan values. 

3099 

3100 Returns 

3101 ------- 

3102 zscore : array_like 

3103 Z-scores, in the same shape as `scores`. 

3104 

3105 Notes 

3106 ----- 

3107 This function preserves ndarray subclasses, and works also with 

3108 matrices and masked arrays (it uses `asanyarray` instead of 

3109 `asarray` for parameters). 

3110 

3111 Examples 

3112 -------- 

3113 >>> from scipy.stats import zmap 

3114 >>> a = [0.5, 2.0, 2.5, 3] 

3115 >>> b = [0, 1, 2, 3, 4] 

3116 >>> zmap(a, b) 

3117 array([-1.06066017, 0. , 0.35355339, 0.70710678]) 

3118 

3119 """ 

3120 a = np.asanyarray(compare) 

3121 

3122 if a.size == 0: 

3123 return np.empty(a.shape) 

3124 

3125 contains_nan, nan_policy = _contains_nan(a, nan_policy) 

3126 

3127 if contains_nan and nan_policy == 'omit': 

3128 if axis is None: 

3129 mn = _quiet_nanmean(a.ravel()) 

3130 std = _quiet_nanstd(a.ravel(), ddof=ddof) 

3131 isconst = _isconst(a.ravel()) 

3132 else: 

3133 mn = np.apply_along_axis(_quiet_nanmean, axis, a) 

3134 std = np.apply_along_axis(_quiet_nanstd, axis, a, ddof=ddof) 

3135 isconst = np.apply_along_axis(_isconst, axis, a) 

3136 else: 

3137 mn = a.mean(axis=axis, keepdims=True) 

3138 std = a.std(axis=axis, ddof=ddof, keepdims=True) 

3139 if axis is None: 

3140 isconst = (a.item(0) == a).all() 

3141 else: 

3142 isconst = (_first(a, axis) == a).all(axis=axis, keepdims=True) 

3143 

3144 # Set std deviations that are 0 to 1 to avoid division by 0. 

3145 std[isconst] = 1.0 

3146 z = (scores - mn) / std 

3147 # Set the outputs associated with a constant input to nan. 

3148 z[np.broadcast_to(isconst, z.shape)] = np.nan 

3149 return z 

3150 

3151 

3152def gstd(a, axis=0, ddof=1): 

3153 """ 

3154 Calculate the geometric standard deviation of an array. 

3155 

3156 The geometric standard deviation describes the spread of a set of numbers 

3157 where the geometric mean is preferred. It is a multiplicative factor, and 

3158 so a dimensionless quantity. 

3159 

3160 It is defined as the exponent of the standard deviation of ``log(a)``. 

3161 Mathematically the population geometric standard deviation can be 

3162 evaluated as:: 

3163 

3164 gstd = exp(std(log(a))) 

3165 

3166 .. versionadded:: 1.3.0 

3167 

3168 Parameters 

3169 ---------- 

3170 a : array_like 

3171 An array like object containing the sample data. 

3172 axis : int, tuple or None, optional 

3173 Axis along which to operate. Default is 0. If None, compute over 

3174 the whole array `a`. 

3175 ddof : int, optional 

3176 Degree of freedom correction in the calculation of the 

3177 geometric standard deviation. Default is 1. 

3178 

3179 Returns 

3180 ------- 

3181 gstd : ndarray or float 

3182 An array of the geometric standard deviation. If `axis` is None or `a` 

3183 is a 1d array a float is returned. 

3184 

3185 See Also 

3186 -------- 

3187 gmean : Geometric mean 

3188 numpy.std : Standard deviation 

3189 gzscore : Geometric standard score 

3190 

3191 Notes 

3192 ----- 

3193 As the calculation requires the use of logarithms the geometric standard 

3194 deviation only supports strictly positive values. Any non-positive or 

3195 infinite values will raise a `ValueError`. 

3196 The geometric standard deviation is sometimes confused with the exponent of 

3197 the standard deviation, ``exp(std(a))``. Instead the geometric standard 

3198 deviation is ``exp(std(log(a)))``. 

3199 The default value for `ddof` is different to the default value (0) used 

3200 by other ddof containing functions, such as ``np.std`` and ``np.nanstd``. 

3201 

3202 References 

3203 ---------- 

3204 .. [1] "Geometric standard deviation", *Wikipedia*, 

3205 https://en.wikipedia.org/wiki/Geometric_standard_deviation. 

3206 .. [2] Kirkwood, T. B., "Geometric means and measures of dispersion", 

3207 Biometrics, vol. 35, pp. 908-909, 1979 

3208 

3209 Examples 

3210 -------- 

3211 Find the geometric standard deviation of a log-normally distributed sample. 

3212 Note that the standard deviation of the distribution is one, on a 

3213 log scale this evaluates to approximately ``exp(1)``. 

3214 

3215 >>> import numpy as np 

3216 >>> from scipy.stats import gstd 

3217 >>> rng = np.random.default_rng() 

3218 >>> sample = rng.lognormal(mean=0, sigma=1, size=1000) 

3219 >>> gstd(sample) 

3220 2.810010162475324 

3221 

3222 Compute the geometric standard deviation of a multidimensional array and 

3223 of a given axis. 

3224 

3225 >>> a = np.arange(1, 25).reshape(2, 3, 4) 

3226 >>> gstd(a, axis=None) 

3227 2.2944076136018947 

3228 >>> gstd(a, axis=2) 

3229 array([[1.82424757, 1.22436866, 1.13183117], 

3230 [1.09348306, 1.07244798, 1.05914985]]) 

3231 >>> gstd(a, axis=(1,2)) 

3232 array([2.12939215, 1.22120169]) 

3233 

3234 The geometric standard deviation further handles masked arrays. 

3235 

3236 >>> a = np.arange(1, 25).reshape(2, 3, 4) 

3237 >>> ma = np.ma.masked_where(a > 16, a) 

3238 >>> ma 

3239 masked_array( 

3240 data=[[[1, 2, 3, 4], 

3241 [5, 6, 7, 8], 

3242 [9, 10, 11, 12]], 

3243 [[13, 14, 15, 16], 

3244 [--, --, --, --], 

3245 [--, --, --, --]]], 

3246 mask=[[[False, False, False, False], 

3247 [False, False, False, False], 

3248 [False, False, False, False]], 

3249 [[False, False, False, False], 

3250 [ True, True, True, True], 

3251 [ True, True, True, True]]], 

3252 fill_value=999999) 

3253 >>> gstd(ma, axis=2) 

3254 masked_array( 

3255 data=[[1.8242475707663655, 1.2243686572447428, 1.1318311657788478], 

3256 [1.0934830582350938, --, --]], 

3257 mask=[[False, False, False], 

3258 [False, True, True]], 

3259 fill_value=999999) 

3260 

3261 """ 

3262 a = np.asanyarray(a) 

3263 log = ma.log if isinstance(a, ma.MaskedArray) else np.log 

3264 

3265 try: 

3266 with warnings.catch_warnings(): 

3267 warnings.simplefilter("error", RuntimeWarning) 

3268 return np.exp(np.std(log(a), axis=axis, ddof=ddof)) 

3269 except RuntimeWarning as w: 

3270 if np.isinf(a).any(): 

3271 raise ValueError( 

3272 'Infinite value encountered. The geometric standard deviation ' 

3273 'is defined for strictly positive values only.' 

3274 ) from w 

3275 a_nan = np.isnan(a) 

3276 a_nan_any = a_nan.any() 

3277 # exclude NaN's from negativity check, but 

3278 # avoid expensive masking for arrays with no NaN 

3279 if ((a_nan_any and np.less_equal(np.nanmin(a), 0)) or 

3280 (not a_nan_any and np.less_equal(a, 0).any())): 

3281 raise ValueError( 

3282 'Non positive value encountered. The geometric standard ' 

3283 'deviation is defined for strictly positive values only.' 

3284 ) from w 

3285 elif 'Degrees of freedom <= 0 for slice' == str(w): 

3286 raise ValueError(w) from w 

3287 else: 

3288 # Remaining warnings don't need to be exceptions. 

3289 return np.exp(np.std(log(a, where=~a_nan), axis=axis, ddof=ddof)) 

3290 except TypeError as e: 

3291 raise ValueError( 

3292 'Invalid array input. The inputs could not be ' 

3293 'safely coerced to any supported types') from e 

3294 

3295 

3296# Private dictionary initialized only once at module level 

3297# See https://en.wikipedia.org/wiki/Robust_measures_of_scale 

3298_scale_conversions = {'raw': 1.0, 

3299 'normal': special.erfinv(0.5) * 2.0 * math.sqrt(2.0)} 

3300 

3301 

3302@_axis_nan_policy_factory( 

3303 lambda x: x, result_to_tuple=lambda x: (x,), n_outputs=1, 

3304 default_axis=None, override={'nan_propagation': False} 

3305) 

3306def iqr(x, axis=None, rng=(25, 75), scale=1.0, nan_policy='propagate', 

3307 interpolation='linear', keepdims=False): 

3308 r""" 

3309 Compute the interquartile range of the data along the specified axis. 

3310 

3311 The interquartile range (IQR) is the difference between the 75th and 

3312 25th percentile of the data. It is a measure of the dispersion 

3313 similar to standard deviation or variance, but is much more robust 

3314 against outliers [2]_. 

3315 

3316 The ``rng`` parameter allows this function to compute other 

3317 percentile ranges than the actual IQR. For example, setting 

3318 ``rng=(0, 100)`` is equivalent to `numpy.ptp`. 

3319 

3320 The IQR of an empty array is `np.nan`. 

3321 

3322 .. versionadded:: 0.18.0 

3323 

3324 Parameters 

3325 ---------- 

3326 x : array_like 

3327 Input array or object that can be converted to an array. 

3328 axis : int or sequence of int, optional 

3329 Axis along which the range is computed. The default is to 

3330 compute the IQR for the entire array. 

3331 rng : Two-element sequence containing floats in range of [0,100] optional 

3332 Percentiles over which to compute the range. Each must be 

3333 between 0 and 100, inclusive. The default is the true IQR: 

3334 ``(25, 75)``. The order of the elements is not important. 

3335 scale : scalar or str, optional 

3336 The numerical value of scale will be divided out of the final 

3337 result. The following string values are recognized: 

3338 

3339 * 'raw' : No scaling, just return the raw IQR. 

3340 **Deprecated!** Use ``scale=1`` instead. 

3341 * 'normal' : Scale by 

3342 :math:`2 \sqrt{2} erf^{-1}(\frac{1}{2}) \approx 1.349`. 

3343 

3344 The default is 1.0. The use of ``scale='raw'`` is deprecated infavor 

3345 of ``scale=1`` and will raise an error in SciPy 1.12.0. 

3346 Array-like `scale` is also allowed, as long 

3347 as it broadcasts correctly to the output such that 

3348 ``out / scale`` is a valid operation. The output dimensions 

3349 depend on the input array, `x`, the `axis` argument, and the 

3350 `keepdims` flag. 

3351 nan_policy : {'propagate', 'raise', 'omit'}, optional 

3352 Defines how to handle when input contains nan. 

3353 The following options are available (default is 'propagate'): 

3354 

3355 * 'propagate': returns nan 

3356 * 'raise': throws an error 

3357 * 'omit': performs the calculations ignoring nan values 

3358 interpolation : str, optional 

3359 

3360 Specifies the interpolation method to use when the percentile 

3361 boundaries lie between two data points ``i`` and ``j``. 

3362 The following options are available (default is 'linear'): 

3363 

3364 * 'linear': ``i + (j - i)*fraction``, where ``fraction`` is the 

3365 fractional part of the index surrounded by ``i`` and ``j``. 

3366 * 'lower': ``i``. 

3367 * 'higher': ``j``. 

3368 * 'nearest': ``i`` or ``j`` whichever is nearest. 

3369 * 'midpoint': ``(i + j)/2``. 

3370 

3371 For NumPy >= 1.22.0, the additional options provided by the ``method`` 

3372 keyword of `numpy.percentile` are also valid. 

3373 

3374 keepdims : bool, optional 

3375 If this is set to True, the reduced axes are left in the 

3376 result as dimensions with size one. With this option, the result 

3377 will broadcast correctly against the original array `x`. 

3378 

3379 Returns 

3380 ------- 

3381 iqr : scalar or ndarray 

3382 If ``axis=None``, a scalar is returned. If the input contains 

3383 integers or floats of smaller precision than ``np.float64``, then the 

3384 output data-type is ``np.float64``. Otherwise, the output data-type is 

3385 the same as that of the input. 

3386 

3387 See Also 

3388 -------- 

3389 numpy.std, numpy.var 

3390 

3391 References 

3392 ---------- 

3393 .. [1] "Interquartile range" https://en.wikipedia.org/wiki/Interquartile_range 

3394 .. [2] "Robust measures of scale" https://en.wikipedia.org/wiki/Robust_measures_of_scale 

3395 .. [3] "Quantile" https://en.wikipedia.org/wiki/Quantile 

3396 

3397 Examples 

3398 -------- 

3399 >>> import numpy as np 

3400 >>> from scipy.stats import iqr 

3401 >>> x = np.array([[10, 7, 4], [3, 2, 1]]) 

3402 >>> x 

3403 array([[10, 7, 4], 

3404 [ 3, 2, 1]]) 

3405 >>> iqr(x) 

3406 4.0 

3407 >>> iqr(x, axis=0) 

3408 array([ 3.5, 2.5, 1.5]) 

3409 >>> iqr(x, axis=1) 

3410 array([ 3., 1.]) 

3411 >>> iqr(x, axis=1, keepdims=True) 

3412 array([[ 3.], 

3413 [ 1.]]) 

3414 

3415 """ 

3416 x = asarray(x) 

3417 

3418 # This check prevents percentile from raising an error later. Also, it is 

3419 # consistent with `np.var` and `np.std`. 

3420 if not x.size: 

3421 return _get_nan(x) 

3422 

3423 # An error may be raised here, so fail-fast, before doing lengthy 

3424 # computations, even though `scale` is not used until later 

3425 if isinstance(scale, str): 

3426 scale_key = scale.lower() 

3427 if scale_key not in _scale_conversions: 

3428 raise ValueError(f"{scale} not a valid scale for `iqr`") 

3429 if scale_key == 'raw': 

3430 msg = ("The use of 'scale=\"raw\"' is deprecated infavor of " 

3431 "'scale=1' and will raise an error in SciPy 1.12.0.") 

3432 warnings.warn(msg, DeprecationWarning, stacklevel=2) 

3433 scale = _scale_conversions[scale_key] 

3434 

3435 # Select the percentile function to use based on nans and policy 

3436 contains_nan, nan_policy = _contains_nan(x, nan_policy) 

3437 

3438 if contains_nan and nan_policy == 'omit': 

3439 percentile_func = np.nanpercentile 

3440 else: 

3441 percentile_func = np.percentile 

3442 

3443 if len(rng) != 2: 

3444 raise TypeError("quantile range must be two element sequence") 

3445 

3446 if np.isnan(rng).any(): 

3447 raise ValueError("range must not contain NaNs") 

3448 

3449 rng = sorted(rng) 

3450 if NumpyVersion(np.__version__) >= '1.22.0': 

3451 pct = percentile_func(x, rng, axis=axis, method=interpolation, 

3452 keepdims=keepdims) 

3453 else: 

3454 pct = percentile_func(x, rng, axis=axis, interpolation=interpolation, 

3455 keepdims=keepdims) 

3456 out = np.subtract(pct[1], pct[0]) 

3457 

3458 if scale != 1.0: 

3459 out /= scale 

3460 

3461 return out 

3462 

3463 

3464def _mad_1d(x, center, nan_policy): 

3465 # Median absolute deviation for 1-d array x. 

3466 # This is a helper function for `median_abs_deviation`; it assumes its 

3467 # arguments have been validated already. In particular, x must be a 

3468 # 1-d numpy array, center must be callable, and if nan_policy is not 

3469 # 'propagate', it is assumed to be 'omit', because 'raise' is handled 

3470 # in `median_abs_deviation`. 

3471 # No warning is generated if x is empty or all nan. 

3472 isnan = np.isnan(x) 

3473 if isnan.any(): 

3474 if nan_policy == 'propagate': 

3475 return np.nan 

3476 x = x[~isnan] 

3477 if x.size == 0: 

3478 # MAD of an empty array is nan. 

3479 return np.nan 

3480 # Edge cases have been handled, so do the basic MAD calculation. 

3481 med = center(x) 

3482 mad = np.median(np.abs(x - med)) 

3483 return mad 

3484 

3485 

3486def median_abs_deviation(x, axis=0, center=np.median, scale=1.0, 

3487 nan_policy='propagate'): 

3488 r""" 

3489 Compute the median absolute deviation of the data along the given axis. 

3490 

3491 The median absolute deviation (MAD, [1]_) computes the median over the 

3492 absolute deviations from the median. It is a measure of dispersion 

3493 similar to the standard deviation but more robust to outliers [2]_. 

3494 

3495 The MAD of an empty array is ``np.nan``. 

3496 

3497 .. versionadded:: 1.5.0 

3498 

3499 Parameters 

3500 ---------- 

3501 x : array_like 

3502 Input array or object that can be converted to an array. 

3503 axis : int or None, optional 

3504 Axis along which the range is computed. Default is 0. If None, compute 

3505 the MAD over the entire array. 

3506 center : callable, optional 

3507 A function that will return the central value. The default is to use 

3508 np.median. Any user defined function used will need to have the 

3509 function signature ``func(arr, axis)``. 

3510 scale : scalar or str, optional 

3511 The numerical value of scale will be divided out of the final 

3512 result. The default is 1.0. The string "normal" is also accepted, 

3513 and results in `scale` being the inverse of the standard normal 

3514 quantile function at 0.75, which is approximately 0.67449. 

3515 Array-like scale is also allowed, as long as it broadcasts correctly 

3516 to the output such that ``out / scale`` is a valid operation. The 

3517 output dimensions depend on the input array, `x`, and the `axis` 

3518 argument. 

3519 nan_policy : {'propagate', 'raise', 'omit'}, optional 

3520 Defines how to handle when input contains nan. 

3521 The following options are available (default is 'propagate'): 

3522 

3523 * 'propagate': returns nan 

3524 * 'raise': throws an error 

3525 * 'omit': performs the calculations ignoring nan values 

3526 

3527 Returns 

3528 ------- 

3529 mad : scalar or ndarray 

3530 If ``axis=None``, a scalar is returned. If the input contains 

3531 integers or floats of smaller precision than ``np.float64``, then the 

3532 output data-type is ``np.float64``. Otherwise, the output data-type is 

3533 the same as that of the input. 

3534 

3535 See Also 

3536 -------- 

3537 numpy.std, numpy.var, numpy.median, scipy.stats.iqr, scipy.stats.tmean, 

3538 scipy.stats.tstd, scipy.stats.tvar 

3539 

3540 Notes 

3541 ----- 

3542 The `center` argument only affects the calculation of the central value 

3543 around which the MAD is calculated. That is, passing in ``center=np.mean`` 

3544 will calculate the MAD around the mean - it will not calculate the *mean* 

3545 absolute deviation. 

3546 

3547 The input array may contain `inf`, but if `center` returns `inf`, the 

3548 corresponding MAD for that data will be `nan`. 

3549 

3550 References 

3551 ---------- 

3552 .. [1] "Median absolute deviation", 

3553 https://en.wikipedia.org/wiki/Median_absolute_deviation 

3554 .. [2] "Robust measures of scale", 

3555 https://en.wikipedia.org/wiki/Robust_measures_of_scale 

3556 

3557 Examples 

3558 -------- 

3559 When comparing the behavior of `median_abs_deviation` with ``np.std``, 

3560 the latter is affected when we change a single value of an array to have an 

3561 outlier value while the MAD hardly changes: 

3562 

3563 >>> import numpy as np 

3564 >>> from scipy import stats 

3565 >>> x = stats.norm.rvs(size=100, scale=1, random_state=123456) 

3566 >>> x.std() 

3567 0.9973906394005013 

3568 >>> stats.median_abs_deviation(x) 

3569 0.82832610097857 

3570 >>> x[0] = 345.6 

3571 >>> x.std() 

3572 34.42304872314415 

3573 >>> stats.median_abs_deviation(x) 

3574 0.8323442311590675 

3575 

3576 Axis handling example: 

3577 

3578 >>> x = np.array([[10, 7, 4], [3, 2, 1]]) 

3579 >>> x 

3580 array([[10, 7, 4], 

3581 [ 3, 2, 1]]) 

3582 >>> stats.median_abs_deviation(x) 

3583 array([3.5, 2.5, 1.5]) 

3584 >>> stats.median_abs_deviation(x, axis=None) 

3585 2.0 

3586 

3587 Scale normal example: 

3588 

3589 >>> x = stats.norm.rvs(size=1000000, scale=2, random_state=123456) 

3590 >>> stats.median_abs_deviation(x) 

3591 1.3487398527041636 

3592 >>> stats.median_abs_deviation(x, scale='normal') 

3593 1.9996446978061115 

3594 

3595 """ 

3596 if not callable(center): 

3597 raise TypeError("The argument 'center' must be callable. The given " 

3598 f"value {repr(center)} is not callable.") 

3599 

3600 # An error may be raised here, so fail-fast, before doing lengthy 

3601 # computations, even though `scale` is not used until later 

3602 if isinstance(scale, str): 

3603 if scale.lower() == 'normal': 

3604 scale = 0.6744897501960817 # special.ndtri(0.75) 

3605 else: 

3606 raise ValueError(f"{scale} is not a valid scale value.") 

3607 

3608 x = asarray(x) 

3609 

3610 # Consistent with `np.var` and `np.std`. 

3611 if not x.size: 

3612 if axis is None: 

3613 return np.nan 

3614 nan_shape = tuple(item for i, item in enumerate(x.shape) if i != axis) 

3615 if nan_shape == (): 

3616 # Return nan, not array(nan) 

3617 return np.nan 

3618 return np.full(nan_shape, np.nan) 

3619 

3620 contains_nan, nan_policy = _contains_nan(x, nan_policy) 

3621 

3622 if contains_nan: 

3623 if axis is None: 

3624 mad = _mad_1d(x.ravel(), center, nan_policy) 

3625 else: 

3626 mad = np.apply_along_axis(_mad_1d, axis, x, center, nan_policy) 

3627 else: 

3628 if axis is None: 

3629 med = center(x, axis=None) 

3630 mad = np.median(np.abs(x - med)) 

3631 else: 

3632 # Wrap the call to center() in expand_dims() so it acts like 

3633 # keepdims=True was used. 

3634 med = np.expand_dims(center(x, axis=axis), axis) 

3635 mad = np.median(np.abs(x - med), axis=axis) 

3636 

3637 return mad / scale 

3638 

3639 

3640##################################### 

3641# TRIMMING FUNCTIONS # 

3642##################################### 

3643 

3644 

3645SigmaclipResult = namedtuple('SigmaclipResult', ('clipped', 'lower', 'upper')) 

3646 

3647 

3648def sigmaclip(a, low=4., high=4.): 

3649 """Perform iterative sigma-clipping of array elements. 

3650 

3651 Starting from the full sample, all elements outside the critical range are 

3652 removed, i.e. all elements of the input array `c` that satisfy either of 

3653 the following conditions:: 

3654 

3655 c < mean(c) - std(c)*low 

3656 c > mean(c) + std(c)*high 

3657 

3658 The iteration continues with the updated sample until no 

3659 elements are outside the (updated) range. 

3660 

3661 Parameters 

3662 ---------- 

3663 a : array_like 

3664 Data array, will be raveled if not 1-D. 

3665 low : float, optional 

3666 Lower bound factor of sigma clipping. Default is 4. 

3667 high : float, optional 

3668 Upper bound factor of sigma clipping. Default is 4. 

3669 

3670 Returns 

3671 ------- 

3672 clipped : ndarray 

3673 Input array with clipped elements removed. 

3674 lower : float 

3675 Lower threshold value use for clipping. 

3676 upper : float 

3677 Upper threshold value use for clipping. 

3678 

3679 Examples 

3680 -------- 

3681 >>> import numpy as np 

3682 >>> from scipy.stats import sigmaclip 

3683 >>> a = np.concatenate((np.linspace(9.5, 10.5, 31), 

3684 ... np.linspace(0, 20, 5))) 

3685 >>> fact = 1.5 

3686 >>> c, low, upp = sigmaclip(a, fact, fact) 

3687 >>> c 

3688 array([ 9.96666667, 10. , 10.03333333, 10. ]) 

3689 >>> c.var(), c.std() 

3690 (0.00055555555555555165, 0.023570226039551501) 

3691 >>> low, c.mean() - fact*c.std(), c.min() 

3692 (9.9646446609406727, 9.9646446609406727, 9.9666666666666668) 

3693 >>> upp, c.mean() + fact*c.std(), c.max() 

3694 (10.035355339059327, 10.035355339059327, 10.033333333333333) 

3695 

3696 >>> a = np.concatenate((np.linspace(9.5, 10.5, 11), 

3697 ... np.linspace(-100, -50, 3))) 

3698 >>> c, low, upp = sigmaclip(a, 1.8, 1.8) 

3699 >>> (c == np.linspace(9.5, 10.5, 11)).all() 

3700 True 

3701 

3702 """ 

3703 c = np.asarray(a).ravel() 

3704 delta = 1 

3705 while delta: 

3706 c_std = c.std() 

3707 c_mean = c.mean() 

3708 size = c.size 

3709 critlower = c_mean - c_std * low 

3710 critupper = c_mean + c_std * high 

3711 c = c[(c >= critlower) & (c <= critupper)] 

3712 delta = size - c.size 

3713 

3714 return SigmaclipResult(c, critlower, critupper) 

3715 

3716 

3717def trimboth(a, proportiontocut, axis=0): 

3718 """Slice off a proportion of items from both ends of an array. 

3719 

3720 Slice off the passed proportion of items from both ends of the passed 

3721 array (i.e., with `proportiontocut` = 0.1, slices leftmost 10% **and** 

3722 rightmost 10% of scores). The trimmed values are the lowest and 

3723 highest ones. 

3724 Slice off less if proportion results in a non-integer slice index (i.e. 

3725 conservatively slices off `proportiontocut`). 

3726 

3727 Parameters 

3728 ---------- 

3729 a : array_like 

3730 Data to trim. 

3731 proportiontocut : float 

3732 Proportion (in range 0-1) of total data set to trim of each end. 

3733 axis : int or None, optional 

3734 Axis along which to trim data. Default is 0. If None, compute over 

3735 the whole array `a`. 

3736 

3737 Returns 

3738 ------- 

3739 out : ndarray 

3740 Trimmed version of array `a`. The order of the trimmed content 

3741 is undefined. 

3742 

3743 See Also 

3744 -------- 

3745 trim_mean 

3746 

3747 Examples 

3748 -------- 

3749 Create an array of 10 values and trim 10% of those values from each end: 

3750 

3751 >>> import numpy as np 

3752 >>> from scipy import stats 

3753 >>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

3754 >>> stats.trimboth(a, 0.1) 

3755 array([1, 3, 2, 4, 5, 6, 7, 8]) 

3756 

3757 Note that the elements of the input array are trimmed by value, but the 

3758 output array is not necessarily sorted. 

3759 

3760 The proportion to trim is rounded down to the nearest integer. For 

3761 instance, trimming 25% of the values from each end of an array of 10 

3762 values will return an array of 6 values: 

3763 

3764 >>> b = np.arange(10) 

3765 >>> stats.trimboth(b, 1/4).shape 

3766 (6,) 

3767 

3768 Multidimensional arrays can be trimmed along any axis or across the entire 

3769 array: 

3770 

3771 >>> c = [2, 4, 6, 8, 0, 1, 3, 5, 7, 9] 

3772 >>> d = np.array([a, b, c]) 

3773 >>> stats.trimboth(d, 0.4, axis=0).shape 

3774 (1, 10) 

3775 >>> stats.trimboth(d, 0.4, axis=1).shape 

3776 (3, 2) 

3777 >>> stats.trimboth(d, 0.4, axis=None).shape 

3778 (6,) 

3779 

3780 """ 

3781 a = np.asarray(a) 

3782 

3783 if a.size == 0: 

3784 return a 

3785 

3786 if axis is None: 

3787 a = a.ravel() 

3788 axis = 0 

3789 

3790 nobs = a.shape[axis] 

3791 lowercut = int(proportiontocut * nobs) 

3792 uppercut = nobs - lowercut 

3793 if (lowercut >= uppercut): 

3794 raise ValueError("Proportion too big.") 

3795 

3796 atmp = np.partition(a, (lowercut, uppercut - 1), axis) 

3797 

3798 sl = [slice(None)] * atmp.ndim 

3799 sl[axis] = slice(lowercut, uppercut) 

3800 return atmp[tuple(sl)] 

3801 

3802 

3803def trim1(a, proportiontocut, tail='right', axis=0): 

3804 """Slice off a proportion from ONE end of the passed array distribution. 

3805 

3806 If `proportiontocut` = 0.1, slices off 'leftmost' or 'rightmost' 

3807 10% of scores. The lowest or highest values are trimmed (depending on 

3808 the tail). 

3809 Slice off less if proportion results in a non-integer slice index 

3810 (i.e. conservatively slices off `proportiontocut` ). 

3811 

3812 Parameters 

3813 ---------- 

3814 a : array_like 

3815 Input array. 

3816 proportiontocut : float 

3817 Fraction to cut off of 'left' or 'right' of distribution. 

3818 tail : {'left', 'right'}, optional 

3819 Defaults to 'right'. 

3820 axis : int or None, optional 

3821 Axis along which to trim data. Default is 0. If None, compute over 

3822 the whole array `a`. 

3823 

3824 Returns 

3825 ------- 

3826 trim1 : ndarray 

3827 Trimmed version of array `a`. The order of the trimmed content is 

3828 undefined. 

3829 

3830 Examples 

3831 -------- 

3832 Create an array of 10 values and trim 20% of its lowest values: 

3833 

3834 >>> import numpy as np 

3835 >>> from scipy import stats 

3836 >>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

3837 >>> stats.trim1(a, 0.2, 'left') 

3838 array([2, 4, 3, 5, 6, 7, 8, 9]) 

3839 

3840 Note that the elements of the input array are trimmed by value, but the 

3841 output array is not necessarily sorted. 

3842 

3843 The proportion to trim is rounded down to the nearest integer. For 

3844 instance, trimming 25% of the values from an array of 10 values will 

3845 return an array of 8 values: 

3846 

3847 >>> b = np.arange(10) 

3848 >>> stats.trim1(b, 1/4).shape 

3849 (8,) 

3850 

3851 Multidimensional arrays can be trimmed along any axis or across the entire 

3852 array: 

3853 

3854 >>> c = [2, 4, 6, 8, 0, 1, 3, 5, 7, 9] 

3855 >>> d = np.array([a, b, c]) 

3856 >>> stats.trim1(d, 0.8, axis=0).shape 

3857 (1, 10) 

3858 >>> stats.trim1(d, 0.8, axis=1).shape 

3859 (3, 2) 

3860 >>> stats.trim1(d, 0.8, axis=None).shape 

3861 (6,) 

3862 

3863 """ 

3864 a = np.asarray(a) 

3865 if axis is None: 

3866 a = a.ravel() 

3867 axis = 0 

3868 

3869 nobs = a.shape[axis] 

3870 

3871 # avoid possible corner case 

3872 if proportiontocut >= 1: 

3873 return [] 

3874 

3875 if tail.lower() == 'right': 

3876 lowercut = 0 

3877 uppercut = nobs - int(proportiontocut * nobs) 

3878 

3879 elif tail.lower() == 'left': 

3880 lowercut = int(proportiontocut * nobs) 

3881 uppercut = nobs 

3882 

3883 atmp = np.partition(a, (lowercut, uppercut - 1), axis) 

3884 

3885 sl = [slice(None)] * atmp.ndim 

3886 sl[axis] = slice(lowercut, uppercut) 

3887 return atmp[tuple(sl)] 

3888 

3889 

3890def trim_mean(a, proportiontocut, axis=0): 

3891 """Return mean of array after trimming distribution from both tails. 

3892 

3893 If `proportiontocut` = 0.1, slices off 'leftmost' and 'rightmost' 10% of 

3894 scores. The input is sorted before slicing. Slices off less if proportion 

3895 results in a non-integer slice index (i.e., conservatively slices off 

3896 `proportiontocut` ). 

3897 

3898 Parameters 

3899 ---------- 

3900 a : array_like 

3901 Input array. 

3902 proportiontocut : float 

3903 Fraction to cut off of both tails of the distribution. 

3904 axis : int or None, optional 

3905 Axis along which the trimmed means are computed. Default is 0. 

3906 If None, compute over the whole array `a`. 

3907 

3908 Returns 

3909 ------- 

3910 trim_mean : ndarray 

3911 Mean of trimmed array. 

3912 

3913 See Also 

3914 -------- 

3915 trimboth 

3916 tmean : Compute the trimmed mean ignoring values outside given `limits`. 

3917 

3918 Examples 

3919 -------- 

3920 >>> import numpy as np 

3921 >>> from scipy import stats 

3922 >>> x = np.arange(20) 

3923 >>> stats.trim_mean(x, 0.1) 

3924 9.5 

3925 >>> x2 = x.reshape(5, 4) 

3926 >>> x2 

3927 array([[ 0, 1, 2, 3], 

3928 [ 4, 5, 6, 7], 

3929 [ 8, 9, 10, 11], 

3930 [12, 13, 14, 15], 

3931 [16, 17, 18, 19]]) 

3932 >>> stats.trim_mean(x2, 0.25) 

3933 array([ 8., 9., 10., 11.]) 

3934 >>> stats.trim_mean(x2, 0.25, axis=1) 

3935 array([ 1.5, 5.5, 9.5, 13.5, 17.5]) 

3936 

3937 """ 

3938 a = np.asarray(a) 

3939 

3940 if a.size == 0: 

3941 return np.nan 

3942 

3943 if axis is None: 

3944 a = a.ravel() 

3945 axis = 0 

3946 

3947 nobs = a.shape[axis] 

3948 lowercut = int(proportiontocut * nobs) 

3949 uppercut = nobs - lowercut 

3950 if (lowercut > uppercut): 

3951 raise ValueError("Proportion too big.") 

3952 

3953 atmp = np.partition(a, (lowercut, uppercut - 1), axis) 

3954 

3955 sl = [slice(None)] * atmp.ndim 

3956 sl[axis] = slice(lowercut, uppercut) 

3957 return np.mean(atmp[tuple(sl)], axis=axis) 

3958 

3959 

3960F_onewayResult = namedtuple('F_onewayResult', ('statistic', 'pvalue')) 

3961 

3962 

3963def _create_f_oneway_nan_result(shape, axis): 

3964 """ 

3965 This is a helper function for f_oneway for creating the return values 

3966 in certain degenerate conditions. It creates return values that are 

3967 all nan with the appropriate shape for the given `shape` and `axis`. 

3968 """ 

3969 axis = np.core.multiarray.normalize_axis_index(axis, len(shape)) 

3970 shp = shape[:axis] + shape[axis+1:] 

3971 if shp == (): 

3972 f = np.nan 

3973 prob = np.nan 

3974 else: 

3975 f = np.full(shp, fill_value=np.nan) 

3976 prob = f.copy() 

3977 return F_onewayResult(f, prob) 

3978 

3979 

3980def _first(arr, axis): 

3981 """Return arr[..., 0:1, ...] where 0:1 is in the `axis` position.""" 

3982 return np.take_along_axis(arr, np.array(0, ndmin=arr.ndim), axis) 

3983 

3984 

3985def f_oneway(*samples, axis=0): 

3986 """Perform one-way ANOVA. 

3987 

3988 The one-way ANOVA tests the null hypothesis that two or more groups have 

3989 the same population mean. The test is applied to samples from two or 

3990 more groups, possibly with differing sizes. 

3991 

3992 Parameters 

3993 ---------- 

3994 sample1, sample2, ... : array_like 

3995 The sample measurements for each group. There must be at least 

3996 two arguments. If the arrays are multidimensional, then all the 

3997 dimensions of the array must be the same except for `axis`. 

3998 axis : int, optional 

3999 Axis of the input arrays along which the test is applied. 

4000 Default is 0. 

4001 

4002 Returns 

4003 ------- 

4004 statistic : float 

4005 The computed F statistic of the test. 

4006 pvalue : float 

4007 The associated p-value from the F distribution. 

4008 

4009 Warns 

4010 ----- 

4011 `~scipy.stats.ConstantInputWarning` 

4012 Raised if all values within each of the input arrays are identical. 

4013 In this case the F statistic is either infinite or isn't defined, 

4014 so ``np.inf`` or ``np.nan`` is returned. 

4015 

4016 `~scipy.stats.DegenerateDataWarning` 

4017 Raised if the length of any input array is 0, or if all the input 

4018 arrays have length 1. ``np.nan`` is returned for the F statistic 

4019 and the p-value in these cases. 

4020 

4021 Notes 

4022 ----- 

4023 The ANOVA test has important assumptions that must be satisfied in order 

4024 for the associated p-value to be valid. 

4025 

4026 1. The samples are independent. 

4027 2. Each sample is from a normally distributed population. 

4028 3. The population standard deviations of the groups are all equal. This 

4029 property is known as homoscedasticity. 

4030 

4031 If these assumptions are not true for a given set of data, it may still 

4032 be possible to use the Kruskal-Wallis H-test (`scipy.stats.kruskal`) or 

4033 the Alexander-Govern test (`scipy.stats.alexandergovern`) although with 

4034 some loss of power. 

4035 

4036 The length of each group must be at least one, and there must be at 

4037 least one group with length greater than one. If these conditions 

4038 are not satisfied, a warning is generated and (``np.nan``, ``np.nan``) 

4039 is returned. 

4040 

4041 If all values in each group are identical, and there exist at least two 

4042 groups with different values, the function generates a warning and 

4043 returns (``np.inf``, 0). 

4044 

4045 If all values in all groups are the same, function generates a warning 

4046 and returns (``np.nan``, ``np.nan``). 

4047 

4048 The algorithm is from Heiman [2]_, pp.394-7. 

4049 

4050 References 

4051 ---------- 

4052 .. [1] R. Lowry, "Concepts and Applications of Inferential Statistics", 

4053 Chapter 14, 2014, http://vassarstats.net/textbook/ 

4054 

4055 .. [2] G.W. Heiman, "Understanding research methods and statistics: An 

4056 integrated introduction for psychology", Houghton, Mifflin and 

4057 Company, 2001. 

4058 

4059 .. [3] G.H. McDonald, "Handbook of Biological Statistics", One-way ANOVA. 

4060 http://www.biostathandbook.com/onewayanova.html 

4061 

4062 Examples 

4063 -------- 

4064 >>> import numpy as np 

4065 >>> from scipy.stats import f_oneway 

4066 

4067 Here are some data [3]_ on a shell measurement (the length of the anterior 

4068 adductor muscle scar, standardized by dividing by length) in the mussel 

4069 Mytilus trossulus from five locations: Tillamook, Oregon; Newport, Oregon; 

4070 Petersburg, Alaska; Magadan, Russia; and Tvarminne, Finland, taken from a 

4071 much larger data set used in McDonald et al. (1991). 

4072 

4073 >>> tillamook = [0.0571, 0.0813, 0.0831, 0.0976, 0.0817, 0.0859, 0.0735, 

4074 ... 0.0659, 0.0923, 0.0836] 

4075 >>> newport = [0.0873, 0.0662, 0.0672, 0.0819, 0.0749, 0.0649, 0.0835, 

4076 ... 0.0725] 

4077 >>> petersburg = [0.0974, 0.1352, 0.0817, 0.1016, 0.0968, 0.1064, 0.105] 

4078 >>> magadan = [0.1033, 0.0915, 0.0781, 0.0685, 0.0677, 0.0697, 0.0764, 

4079 ... 0.0689] 

4080 >>> tvarminne = [0.0703, 0.1026, 0.0956, 0.0973, 0.1039, 0.1045] 

4081 >>> f_oneway(tillamook, newport, petersburg, magadan, tvarminne) 

4082 F_onewayResult(statistic=7.121019471642447, pvalue=0.0002812242314534544) 

4083 

4084 `f_oneway` accepts multidimensional input arrays. When the inputs 

4085 are multidimensional and `axis` is not given, the test is performed 

4086 along the first axis of the input arrays. For the following data, the 

4087 test is performed three times, once for each column. 

4088 

4089 >>> a = np.array([[9.87, 9.03, 6.81], 

4090 ... [7.18, 8.35, 7.00], 

4091 ... [8.39, 7.58, 7.68], 

4092 ... [7.45, 6.33, 9.35], 

4093 ... [6.41, 7.10, 9.33], 

4094 ... [8.00, 8.24, 8.44]]) 

4095 >>> b = np.array([[6.35, 7.30, 7.16], 

4096 ... [6.65, 6.68, 7.63], 

4097 ... [5.72, 7.73, 6.72], 

4098 ... [7.01, 9.19, 7.41], 

4099 ... [7.75, 7.87, 8.30], 

4100 ... [6.90, 7.97, 6.97]]) 

4101 >>> c = np.array([[3.31, 8.77, 1.01], 

4102 ... [8.25, 3.24, 3.62], 

4103 ... [6.32, 8.81, 5.19], 

4104 ... [7.48, 8.83, 8.91], 

4105 ... [8.59, 6.01, 6.07], 

4106 ... [3.07, 9.72, 7.48]]) 

4107 >>> F, p = f_oneway(a, b, c) 

4108 >>> F 

4109 array([1.75676344, 0.03701228, 3.76439349]) 

4110 >>> p 

4111 array([0.20630784, 0.96375203, 0.04733157]) 

4112 

4113 """ 

4114 if len(samples) < 2: 

4115 raise TypeError('at least two inputs are required;' 

4116 f' got {len(samples)}.') 

4117 

4118 samples = [np.asarray(sample, dtype=float) for sample in samples] 

4119 

4120 # ANOVA on N groups, each in its own array 

4121 num_groups = len(samples) 

4122 

4123 # We haven't explicitly validated axis, but if it is bad, this call of 

4124 # np.concatenate will raise np.AxisError. The call will raise ValueError 

4125 # if the dimensions of all the arrays, except the axis dimension, are not 

4126 # the same. 

4127 alldata = np.concatenate(samples, axis=axis) 

4128 bign = alldata.shape[axis] 

4129 

4130 # Check this after forming alldata, so shape errors are detected 

4131 # and reported before checking for 0 length inputs. 

4132 if any(sample.shape[axis] == 0 for sample in samples): 

4133 warnings.warn(stats.DegenerateDataWarning('at least one input ' 

4134 'has length 0')) 

4135 return _create_f_oneway_nan_result(alldata.shape, axis) 

4136 

4137 # Must have at least one group with length greater than 1. 

4138 if all(sample.shape[axis] == 1 for sample in samples): 

4139 msg = ('all input arrays have length 1. f_oneway requires that at ' 

4140 'least one input has length greater than 1.') 

4141 warnings.warn(stats.DegenerateDataWarning(msg)) 

4142 return _create_f_oneway_nan_result(alldata.shape, axis) 

4143 

4144 # Check if all values within each group are identical, and if the common 

4145 # value in at least one group is different from that in another group. 

4146 # Based on https://github.com/scipy/scipy/issues/11669 

4147 

4148 # If axis=0, say, and the groups have shape (n0, ...), (n1, ...), ..., 

4149 # then is_const is a boolean array with shape (num_groups, ...). 

4150 # It is True if the values within the groups along the axis slice are 

4151 # identical. In the typical case where each input array is 1-d, is_const is 

4152 # a 1-d array with length num_groups. 

4153 is_const = np.concatenate( 

4154 [(_first(sample, axis) == sample).all(axis=axis, 

4155 keepdims=True) 

4156 for sample in samples], 

4157 axis=axis 

4158 ) 

4159 

4160 # all_const is a boolean array with shape (...) (see previous comment). 

4161 # It is True if the values within each group along the axis slice are 

4162 # the same (e.g. [[3, 3, 3], [5, 5, 5, 5], [4, 4, 4]]). 

4163 all_const = is_const.all(axis=axis) 

4164 if all_const.any(): 

4165 msg = ("Each of the input arrays is constant;" 

4166 "the F statistic is not defined or infinite") 

4167 warnings.warn(stats.ConstantInputWarning(msg)) 

4168 

4169 # all_same_const is True if all the values in the groups along the axis=0 

4170 # slice are the same (e.g. [[3, 3, 3], [3, 3, 3, 3], [3, 3, 3]]). 

4171 all_same_const = (_first(alldata, axis) == alldata).all(axis=axis) 

4172 

4173 # Determine the mean of the data, and subtract that from all inputs to a 

4174 # variance (via sum_of_sq / sq_of_sum) calculation. Variance is invariant 

4175 # to a shift in location, and centering all data around zero vastly 

4176 # improves numerical stability. 

4177 offset = alldata.mean(axis=axis, keepdims=True) 

4178 alldata -= offset 

4179 

4180 normalized_ss = _square_of_sums(alldata, axis=axis) / bign 

4181 

4182 sstot = _sum_of_squares(alldata, axis=axis) - normalized_ss 

4183 

4184 ssbn = 0 

4185 for sample in samples: 

4186 ssbn += _square_of_sums(sample - offset, 

4187 axis=axis) / sample.shape[axis] 

4188 

4189 # Naming: variables ending in bn/b are for "between treatments", wn/w are 

4190 # for "within treatments" 

4191 ssbn -= normalized_ss 

4192 sswn = sstot - ssbn 

4193 dfbn = num_groups - 1 

4194 dfwn = bign - num_groups 

4195 msb = ssbn / dfbn 

4196 msw = sswn / dfwn 

4197 with np.errstate(divide='ignore', invalid='ignore'): 

4198 f = msb / msw 

4199 

4200 prob = special.fdtrc(dfbn, dfwn, f) # equivalent to stats.f.sf 

4201 

4202 # Fix any f values that should be inf or nan because the corresponding 

4203 # inputs were constant. 

4204 if np.isscalar(f): 

4205 if all_same_const: 

4206 f = np.nan 

4207 prob = np.nan 

4208 elif all_const: 

4209 f = np.inf 

4210 prob = 0.0 

4211 else: 

4212 f[all_const] = np.inf 

4213 prob[all_const] = 0.0 

4214 f[all_same_const] = np.nan 

4215 prob[all_same_const] = np.nan 

4216 

4217 return F_onewayResult(f, prob) 

4218 

4219 

4220def alexandergovern(*samples, nan_policy='propagate'): 

4221 """Performs the Alexander Govern test. 

4222 

4223 The Alexander-Govern approximation tests the equality of k independent 

4224 means in the face of heterogeneity of variance. The test is applied to 

4225 samples from two or more groups, possibly with differing sizes. 

4226 

4227 Parameters 

4228 ---------- 

4229 sample1, sample2, ... : array_like 

4230 The sample measurements for each group. There must be at least 

4231 two samples. 

4232 nan_policy : {'propagate', 'raise', 'omit'}, optional 

4233 Defines how to handle when input contains nan. 

4234 The following options are available (default is 'propagate'): 

4235 

4236 * 'propagate': returns nan 

4237 * 'raise': throws an error 

4238 * 'omit': performs the calculations ignoring nan values 

4239 

4240 Returns 

4241 ------- 

4242 res : AlexanderGovernResult 

4243 An object with attributes: 

4244 

4245 statistic : float 

4246 The computed A statistic of the test. 

4247 pvalue : float 

4248 The associated p-value from the chi-squared distribution. 

4249 

4250 Warns 

4251 ----- 

4252 `~scipy.stats.ConstantInputWarning` 

4253 Raised if an input is a constant array. The statistic is not defined 

4254 in this case, so ``np.nan`` is returned. 

4255 

4256 See Also 

4257 -------- 

4258 f_oneway : one-way ANOVA 

4259 

4260 Notes 

4261 ----- 

4262 The use of this test relies on several assumptions. 

4263 

4264 1. The samples are independent. 

4265 2. Each sample is from a normally distributed population. 

4266 3. Unlike `f_oneway`, this test does not assume on homoscedasticity, 

4267 instead relaxing the assumption of equal variances. 

4268 

4269 Input samples must be finite, one dimensional, and with size greater than 

4270 one. 

4271 

4272 References 

4273 ---------- 

4274 .. [1] Alexander, Ralph A., and Diane M. Govern. "A New and Simpler 

4275 Approximation for ANOVA under Variance Heterogeneity." Journal 

4276 of Educational Statistics, vol. 19, no. 2, 1994, pp. 91-101. 

4277 JSTOR, www.jstor.org/stable/1165140. Accessed 12 Sept. 2020. 

4278 

4279 Examples 

4280 -------- 

4281 >>> from scipy.stats import alexandergovern 

4282 

4283 Here are some data on annual percentage rate of interest charged on 

4284 new car loans at nine of the largest banks in four American cities 

4285 taken from the National Institute of Standards and Technology's 

4286 ANOVA dataset. 

4287 

4288 We use `alexandergovern` to test the null hypothesis that all cities 

4289 have the same mean APR against the alternative that the cities do not 

4290 all have the same mean APR. We decide that a significance level of 5% 

4291 is required to reject the null hypothesis in favor of the alternative. 

4292 

4293 >>> atlanta = [13.75, 13.75, 13.5, 13.5, 13.0, 13.0, 13.0, 12.75, 12.5] 

4294 >>> chicago = [14.25, 13.0, 12.75, 12.5, 12.5, 12.4, 12.3, 11.9, 11.9] 

4295 >>> houston = [14.0, 14.0, 13.51, 13.5, 13.5, 13.25, 13.0, 12.5, 12.5] 

4296 >>> memphis = [15.0, 14.0, 13.75, 13.59, 13.25, 12.97, 12.5, 12.25, 

4297 ... 11.89] 

4298 >>> alexandergovern(atlanta, chicago, houston, memphis) 

4299 AlexanderGovernResult(statistic=4.65087071883494, 

4300 pvalue=0.19922132490385214) 

4301 

4302 The p-value is 0.1992, indicating a nearly 20% chance of observing 

4303 such an extreme value of the test statistic under the null hypothesis. 

4304 This exceeds 5%, so we do not reject the null hypothesis in favor of 

4305 the alternative. 

4306 

4307 """ 

4308 samples = _alexandergovern_input_validation(samples, nan_policy) 

4309 

4310 if np.any([(sample == sample[0]).all() for sample in samples]): 

4311 msg = "An input array is constant; the statistic is not defined." 

4312 warnings.warn(stats.ConstantInputWarning(msg)) 

4313 return AlexanderGovernResult(np.nan, np.nan) 

4314 

4315 # The following formula numbers reference the equation described on 

4316 # page 92 by Alexander, Govern. Formulas 5, 6, and 7 describe other 

4317 # tests that serve as the basis for equation (8) but are not needed 

4318 # to perform the test. 

4319 

4320 # precalculate mean and length of each sample 

4321 lengths = np.array([ma.count(sample) if nan_policy == 'omit' 

4322 else len(sample) for sample in samples]) 

4323 means = np.array([np.mean(sample) for sample in samples]) 

4324 

4325 # (1) determine standard error of the mean for each sample 

4326 standard_errors = [np.std(sample, ddof=1) / np.sqrt(length) 

4327 for sample, length in zip(samples, lengths)] 

4328 

4329 # (2) define a weight for each sample 

4330 inv_sq_se = 1 / np.square(standard_errors) 

4331 weights = inv_sq_se / np.sum(inv_sq_se) 

4332 

4333 # (3) determine variance-weighted estimate of the common mean 

4334 var_w = np.sum(weights * means) 

4335 

4336 # (4) determine one-sample t statistic for each group 

4337 t_stats = (means - var_w)/standard_errors 

4338 

4339 # calculate parameters to be used in transformation 

4340 v = lengths - 1 

4341 a = v - .5 

4342 b = 48 * a**2 

4343 c = (a * np.log(1 + (t_stats ** 2)/v))**.5 

4344 

4345 # (8) perform a normalizing transformation on t statistic 

4346 z = (c + ((c**3 + 3*c)/b) - 

4347 ((4*c**7 + 33*c**5 + 240*c**3 + 855*c) / 

4348 (b**2*10 + 8*b*c**4 + 1000*b))) 

4349 

4350 # (9) calculate statistic 

4351 A = np.sum(np.square(z)) 

4352 

4353 # "[the p value is determined from] central chi-square random deviates 

4354 # with k - 1 degrees of freedom". Alexander, Govern (94) 

4355 p = distributions.chi2.sf(A, len(samples) - 1) 

4356 return AlexanderGovernResult(A, p) 

4357 

4358 

4359def _alexandergovern_input_validation(samples, nan_policy): 

4360 if len(samples) < 2: 

4361 raise TypeError(f"2 or more inputs required, got {len(samples)}") 

4362 

4363 # input arrays are flattened 

4364 samples = [np.asarray(sample, dtype=float) for sample in samples] 

4365 

4366 for i, sample in enumerate(samples): 

4367 if np.size(sample) <= 1: 

4368 raise ValueError("Input sample size must be greater than one.") 

4369 if sample.ndim != 1: 

4370 raise ValueError("Input samples must be one-dimensional") 

4371 if np.isinf(sample).any(): 

4372 raise ValueError("Input samples must be finite.") 

4373 

4374 contains_nan, nan_policy = _contains_nan(sample, 

4375 nan_policy=nan_policy) 

4376 if contains_nan and nan_policy == 'omit': 

4377 samples[i] = ma.masked_invalid(sample) 

4378 return samples 

4379 

4380 

4381@dataclass 

4382class AlexanderGovernResult: 

4383 statistic: float 

4384 pvalue: float 

4385 

4386 

4387def _pearsonr_fisher_ci(r, n, confidence_level, alternative): 

4388 """ 

4389 Compute the confidence interval for Pearson's R. 

4390 

4391 Fisher's transformation is used to compute the confidence interval 

4392 (https://en.wikipedia.org/wiki/Fisher_transformation). 

4393 """ 

4394 if r == 1: 

4395 zr = np.inf 

4396 elif r == -1: 

4397 zr = -np.inf 

4398 else: 

4399 zr = np.arctanh(r) 

4400 

4401 if n > 3: 

4402 se = np.sqrt(1 / (n - 3)) 

4403 if alternative == "two-sided": 

4404 h = special.ndtri(0.5 + confidence_level/2) 

4405 zlo = zr - h*se 

4406 zhi = zr + h*se 

4407 rlo = np.tanh(zlo) 

4408 rhi = np.tanh(zhi) 

4409 elif alternative == "less": 

4410 h = special.ndtri(confidence_level) 

4411 zhi = zr + h*se 

4412 rhi = np.tanh(zhi) 

4413 rlo = -1.0 

4414 else: 

4415 # alternative == "greater": 

4416 h = special.ndtri(confidence_level) 

4417 zlo = zr - h*se 

4418 rlo = np.tanh(zlo) 

4419 rhi = 1.0 

4420 else: 

4421 rlo, rhi = -1.0, 1.0 

4422 

4423 return ConfidenceInterval(low=rlo, high=rhi) 

4424 

4425 

4426def _pearsonr_bootstrap_ci(confidence_level, method, x, y, alternative): 

4427 """ 

4428 Compute the confidence interval for Pearson's R using the bootstrap. 

4429 """ 

4430 def statistic(x, y): 

4431 statistic, _ = pearsonr(x, y) 

4432 return statistic 

4433 

4434 res = bootstrap((x, y), statistic, confidence_level=confidence_level, 

4435 paired=True, alternative=alternative, **method._asdict()) 

4436 # for one-sided confidence intervals, bootstrap gives +/- inf on one side 

4437 res.confidence_interval = np.clip(res.confidence_interval, -1, 1) 

4438 

4439 return ConfidenceInterval(*res.confidence_interval) 

4440 

4441 

4442ConfidenceInterval = namedtuple('ConfidenceInterval', ['low', 'high']) 

4443 

4444PearsonRResultBase = _make_tuple_bunch('PearsonRResultBase', 

4445 ['statistic', 'pvalue'], []) 

4446 

4447 

4448class PearsonRResult(PearsonRResultBase): 

4449 """ 

4450 Result of `scipy.stats.pearsonr` 

4451 

4452 Attributes 

4453 ---------- 

4454 statistic : float 

4455 Pearson product-moment correlation coefficient. 

4456 pvalue : float 

4457 The p-value associated with the chosen alternative. 

4458 

4459 Methods 

4460 ------- 

4461 confidence_interval 

4462 Computes the confidence interval of the correlation 

4463 coefficient `statistic` for the given confidence level. 

4464 

4465 """ 

4466 def __init__(self, statistic, pvalue, alternative, n, x, y): 

4467 super().__init__(statistic, pvalue) 

4468 self._alternative = alternative 

4469 self._n = n 

4470 self._x = x 

4471 self._y = y 

4472 

4473 # add alias for consistency with other correlation functions 

4474 self.correlation = statistic 

4475 

4476 def confidence_interval(self, confidence_level=0.95, method=None): 

4477 """ 

4478 The confidence interval for the correlation coefficient. 

4479 

4480 Compute the confidence interval for the correlation coefficient 

4481 ``statistic`` with the given confidence level. 

4482 

4483 If `method` is not provided, 

4484 The confidence interval is computed using the Fisher transformation 

4485 F(r) = arctanh(r) [1]_. When the sample pairs are drawn from a 

4486 bivariate normal distribution, F(r) approximately follows a normal 

4487 distribution with standard error ``1/sqrt(n - 3)``, where ``n`` is the 

4488 length of the original samples along the calculation axis. When 

4489 ``n <= 3``, this approximation does not yield a finite, real standard 

4490 error, so we define the confidence interval to be -1 to 1. 

4491 

4492 If `method` is an instance of `BootstrapMethod`, the confidence 

4493 interval is computed using `scipy.stats.bootstrap` with the provided 

4494 configuration options and other appropriate settings. In some cases, 

4495 confidence limits may be NaN due to a degenerate resample, and this is 

4496 typical for very small samples (~6 observations). 

4497 

4498 Parameters 

4499 ---------- 

4500 confidence_level : float 

4501 The confidence level for the calculation of the correlation 

4502 coefficient confidence interval. Default is 0.95. 

4503 

4504 method : BootstrapMethod, optional 

4505 Defines the method used to compute the confidence interval. See 

4506 method description for details. 

4507 

4508 .. versionadded:: 1.11.0 

4509 

4510 Returns 

4511 ------- 

4512 ci : namedtuple 

4513 The confidence interval is returned in a ``namedtuple`` with 

4514 fields `low` and `high`. 

4515 

4516 References 

4517 ---------- 

4518 .. [1] "Pearson correlation coefficient", Wikipedia, 

4519 https://en.wikipedia.org/wiki/Pearson_correlation_coefficient 

4520 """ 

4521 if isinstance(method, BootstrapMethod): 

4522 ci = _pearsonr_bootstrap_ci(confidence_level, method, 

4523 self._x, self._y, self._alternative) 

4524 elif method is None: 

4525 ci = _pearsonr_fisher_ci(self.statistic, self._n, confidence_level, 

4526 self._alternative) 

4527 else: 

4528 message = ('`method` must be an instance of `BootstrapMethod` ' 

4529 'or None.') 

4530 raise ValueError(message) 

4531 return ci 

4532 

4533def pearsonr(x, y, *, alternative='two-sided', method=None): 

4534 r""" 

4535 Pearson correlation coefficient and p-value for testing non-correlation. 

4536 

4537 The Pearson correlation coefficient [1]_ measures the linear relationship 

4538 between two datasets. Like other correlation 

4539 coefficients, this one varies between -1 and +1 with 0 implying no 

4540 correlation. Correlations of -1 or +1 imply an exact linear relationship. 

4541 Positive correlations imply that as x increases, so does y. Negative 

4542 correlations imply that as x increases, y decreases. 

4543 

4544 This function also performs a test of the null hypothesis that the 

4545 distributions underlying the samples are uncorrelated and normally 

4546 distributed. (See Kowalski [3]_ 

4547 for a discussion of the effects of non-normality of the input on the 

4548 distribution of the correlation coefficient.) 

4549 The p-value roughly indicates the probability of an uncorrelated system 

4550 producing datasets that have a Pearson correlation at least as extreme 

4551 as the one computed from these datasets. 

4552 

4553 Parameters 

4554 ---------- 

4555 x : (N,) array_like 

4556 Input array. 

4557 y : (N,) array_like 

4558 Input array. 

4559 alternative : {'two-sided', 'greater', 'less'}, optional 

4560 Defines the alternative hypothesis. Default is 'two-sided'. 

4561 The following options are available: 

4562 

4563 * 'two-sided': the correlation is nonzero 

4564 * 'less': the correlation is negative (less than zero) 

4565 * 'greater': the correlation is positive (greater than zero) 

4566 

4567 .. versionadded:: 1.9.0 

4568 method : ResamplingMethod, optional 

4569 Defines the method used to compute the p-value. If `method` is an 

4570 instance of `PermutationMethod`/`MonteCarloMethod`, the p-value is 

4571 computed using 

4572 `scipy.stats.permutation_test`/`scipy.stats.monte_carlo_test` with the 

4573 provided configuration options and other appropriate settings. 

4574 Otherwise, the p-value is computed as documented in the notes. 

4575 

4576 .. versionadded:: 1.11.0 

4577 

4578 Returns 

4579 ------- 

4580 result : `~scipy.stats._result_classes.PearsonRResult` 

4581 An object with the following attributes: 

4582 

4583 statistic : float 

4584 Pearson product-moment correlation coefficient. 

4585 pvalue : float 

4586 The p-value associated with the chosen alternative. 

4587 

4588 The object has the following method: 

4589 

4590 confidence_interval(confidence_level, method) 

4591 This computes the confidence interval of the correlation 

4592 coefficient `statistic` for the given confidence level. 

4593 The confidence interval is returned in a ``namedtuple`` with 

4594 fields `low` and `high`. If `method` is not provided, the 

4595 confidence interval is computed using the Fisher transformation 

4596 [1]_. If `method` is an instance of `BootstrapMethod`, the 

4597 confidence interval is computed using `scipy.stats.bootstrap` with 

4598 the provided configuration options and other appropriate settings. 

4599 In some cases, confidence limits may be NaN due to a degenerate 

4600 resample, and this is typical for very small samples (~6 

4601 observations). 

4602 

4603 Warns 

4604 ----- 

4605 `~scipy.stats.ConstantInputWarning` 

4606 Raised if an input is a constant array. The correlation coefficient 

4607 is not defined in this case, so ``np.nan`` is returned. 

4608 

4609 `~scipy.stats.NearConstantInputWarning` 

4610 Raised if an input is "nearly" constant. The array ``x`` is considered 

4611 nearly constant if ``norm(x - mean(x)) < 1e-13 * abs(mean(x))``. 

4612 Numerical errors in the calculation ``x - mean(x)`` in this case might 

4613 result in an inaccurate calculation of r. 

4614 

4615 See Also 

4616 -------- 

4617 spearmanr : Spearman rank-order correlation coefficient. 

4618 kendalltau : Kendall's tau, a correlation measure for ordinal data. 

4619 

4620 Notes 

4621 ----- 

4622 The correlation coefficient is calculated as follows: 

4623 

4624 .. math:: 

4625 

4626 r = \frac{\sum (x - m_x) (y - m_y)} 

4627 {\sqrt{\sum (x - m_x)^2 \sum (y - m_y)^2}} 

4628 

4629 where :math:`m_x` is the mean of the vector x and :math:`m_y` is 

4630 the mean of the vector y. 

4631 

4632 Under the assumption that x and y are drawn from 

4633 independent normal distributions (so the population correlation coefficient 

4634 is 0), the probability density function of the sample correlation 

4635 coefficient r is ([1]_, [2]_): 

4636 

4637 .. math:: 

4638 f(r) = \frac{{(1-r^2)}^{n/2-2}}{\mathrm{B}(\frac{1}{2},\frac{n}{2}-1)} 

4639 

4640 where n is the number of samples, and B is the beta function. This 

4641 is sometimes referred to as the exact distribution of r. This is 

4642 the distribution that is used in `pearsonr` to compute the p-value when 

4643 the `method` parameter is left at its default value (None). 

4644 The distribution is a beta distribution on the interval [-1, 1], 

4645 with equal shape parameters a = b = n/2 - 1. In terms of SciPy's 

4646 implementation of the beta distribution, the distribution of r is:: 

4647 

4648 dist = scipy.stats.beta(n/2 - 1, n/2 - 1, loc=-1, scale=2) 

4649 

4650 The default p-value returned by `pearsonr` is a two-sided p-value. For a 

4651 given sample with correlation coefficient r, the p-value is 

4652 the probability that abs(r') of a random sample x' and y' drawn from 

4653 the population with zero correlation would be greater than or equal 

4654 to abs(r). In terms of the object ``dist`` shown above, the p-value 

4655 for a given r and length n can be computed as:: 

4656 

4657 p = 2*dist.cdf(-abs(r)) 

4658 

4659 When n is 2, the above continuous distribution is not well-defined. 

4660 One can interpret the limit of the beta distribution as the shape 

4661 parameters a and b approach a = b = 0 as a discrete distribution with 

4662 equal probability masses at r = 1 and r = -1. More directly, one 

4663 can observe that, given the data x = [x1, x2] and y = [y1, y2], and 

4664 assuming x1 != x2 and y1 != y2, the only possible values for r are 1 

4665 and -1. Because abs(r') for any sample x' and y' with length 2 will 

4666 be 1, the two-sided p-value for a sample of length 2 is always 1. 

4667 

4668 For backwards compatibility, the object that is returned also behaves 

4669 like a tuple of length two that holds the statistic and the p-value. 

4670 

4671 References 

4672 ---------- 

4673 .. [1] "Pearson correlation coefficient", Wikipedia, 

4674 https://en.wikipedia.org/wiki/Pearson_correlation_coefficient 

4675 .. [2] Student, "Probable error of a correlation coefficient", 

4676 Biometrika, Volume 6, Issue 2-3, 1 September 1908, pp. 302-310. 

4677 .. [3] C. J. Kowalski, "On the Effects of Non-Normality on the Distribution 

4678 of the Sample Product-Moment Correlation Coefficient" 

4679 Journal of the Royal Statistical Society. Series C (Applied 

4680 Statistics), Vol. 21, No. 1 (1972), pp. 1-12. 

4681 

4682 Examples 

4683 -------- 

4684 >>> import numpy as np 

4685 >>> from scipy import stats 

4686 >>> x, y = [1, 2, 3, 4, 5, 6, 7], [10, 9, 2.5, 6, 4, 3, 2] 

4687 >>> res = stats.pearsonr(x, y) 

4688 >>> res 

4689 PearsonRResult(statistic=-0.828503883588428, pvalue=0.021280260007523286) 

4690 

4691 To perform an exact permutation version of the test: 

4692 

4693 >>> rng = np.random.default_rng(7796654889291491997) 

4694 >>> method = stats.PermutationMethod(n_resamples=np.inf, random_state=rng) 

4695 >>> stats.pearsonr(x, y, method=method) 

4696 PearsonRResult(statistic=-0.828503883588428, pvalue=0.028174603174603175) 

4697 

4698 To perform the test under the null hypothesis that the data were drawn from 

4699 *uniform* distributions: 

4700 

4701 >>> method = stats.MonteCarloMethod(rvs=(rng.uniform, rng.uniform)) 

4702 >>> stats.pearsonr(x, y, method=method) 

4703 PearsonRResult(statistic=-0.828503883588428, pvalue=0.0188) 

4704 

4705 To produce an asymptotic 90% confidence interval: 

4706 

4707 >>> res.confidence_interval(confidence_level=0.9) 

4708 ConfidenceInterval(low=-0.9644331982722841, high=-0.3460237473272273) 

4709 

4710 And for a bootstrap confidence interval: 

4711 

4712 >>> method = stats.BootstrapMethod(method='BCa', random_state=rng) 

4713 >>> res.confidence_interval(confidence_level=0.9, method=method) 

4714 ConfidenceInterval(low=-0.9983163756488651, high=-0.22771001702132443) # may vary 

4715 

4716 There is a linear dependence between x and y if y = a + b*x + e, where 

4717 a,b are constants and e is a random error term, assumed to be independent 

4718 of x. For simplicity, assume that x is standard normal, a=0, b=1 and let 

4719 e follow a normal distribution with mean zero and standard deviation s>0. 

4720 

4721 >>> rng = np.random.default_rng() 

4722 >>> s = 0.5 

4723 >>> x = stats.norm.rvs(size=500, random_state=rng) 

4724 >>> e = stats.norm.rvs(scale=s, size=500, random_state=rng) 

4725 >>> y = x + e 

4726 >>> stats.pearsonr(x, y).statistic 

4727 0.9001942438244763 

4728 

4729 This should be close to the exact value given by 

4730 

4731 >>> 1/np.sqrt(1 + s**2) 

4732 0.8944271909999159 

4733 

4734 For s=0.5, we observe a high level of correlation. In general, a large 

4735 variance of the noise reduces the correlation, while the correlation 

4736 approaches one as the variance of the error goes to zero. 

4737 

4738 It is important to keep in mind that no correlation does not imply 

4739 independence unless (x, y) is jointly normal. Correlation can even be zero 

4740 when there is a very simple dependence structure: if X follows a 

4741 standard normal distribution, let y = abs(x). Note that the correlation 

4742 between x and y is zero. Indeed, since the expectation of x is zero, 

4743 cov(x, y) = E[x*y]. By definition, this equals E[x*abs(x)] which is zero 

4744 by symmetry. The following lines of code illustrate this observation: 

4745 

4746 >>> y = np.abs(x) 

4747 >>> stats.pearsonr(x, y) 

4748 PearsonRResult(statistic=-0.05444919272687482, pvalue=0.22422294836207743) 

4749 

4750 A non-zero correlation coefficient can be misleading. For example, if X has 

4751 a standard normal distribution, define y = x if x < 0 and y = 0 otherwise. 

4752 A simple calculation shows that corr(x, y) = sqrt(2/Pi) = 0.797..., 

4753 implying a high level of correlation: 

4754 

4755 >>> y = np.where(x < 0, x, 0) 

4756 >>> stats.pearsonr(x, y) 

4757 PearsonRResult(statistic=0.861985781588, pvalue=4.813432002751103e-149) 

4758 

4759 This is unintuitive since there is no dependence of x and y if x is larger 

4760 than zero which happens in about half of the cases if we sample x and y. 

4761 

4762 """ 

4763 n = len(x) 

4764 if n != len(y): 

4765 raise ValueError('x and y must have the same length.') 

4766 

4767 if n < 2: 

4768 raise ValueError('x and y must have length at least 2.') 

4769 

4770 x = np.asarray(x) 

4771 y = np.asarray(y) 

4772 

4773 if (np.issubdtype(x.dtype, np.complexfloating) 

4774 or np.issubdtype(y.dtype, np.complexfloating)): 

4775 raise ValueError('This function does not support complex data') 

4776 

4777 # If an input is constant, the correlation coefficient is not defined. 

4778 if (x == x[0]).all() or (y == y[0]).all(): 

4779 msg = ("An input array is constant; the correlation coefficient " 

4780 "is not defined.") 

4781 warnings.warn(stats.ConstantInputWarning(msg)) 

4782 result = PearsonRResult(statistic=np.nan, pvalue=np.nan, n=n, 

4783 alternative=alternative, x=x, y=y) 

4784 return result 

4785 

4786 if isinstance(method, PermutationMethod): 

4787 def statistic(y): 

4788 statistic, _ = pearsonr(x, y, alternative=alternative) 

4789 return statistic 

4790 

4791 res = permutation_test((y,), statistic, permutation_type='pairings', 

4792 alternative=alternative, **method._asdict()) 

4793 

4794 return PearsonRResult(statistic=res.statistic, pvalue=res.pvalue, n=n, 

4795 alternative=alternative, x=x, y=y) 

4796 elif isinstance(method, MonteCarloMethod): 

4797 def statistic(x, y): 

4798 statistic, _ = pearsonr(x, y, alternative=alternative) 

4799 return statistic 

4800 

4801 if method.rvs is None: 

4802 rng = np.random.default_rng() 

4803 method.rvs = rng.normal, rng.normal 

4804 

4805 res = monte_carlo_test((x, y,), statistic=statistic, 

4806 alternative=alternative, **method._asdict()) 

4807 

4808 return PearsonRResult(statistic=res.statistic, pvalue=res.pvalue, n=n, 

4809 alternative=alternative, x=x, y=y) 

4810 elif method is not None: 

4811 message = ('`method` must be an instance of `PermutationMethod`,' 

4812 '`MonteCarloMethod`, or None.') 

4813 raise ValueError(message) 

4814 

4815 # dtype is the data type for the calculations. This expression ensures 

4816 # that the data type is at least 64 bit floating point. It might have 

4817 # more precision if the input is, for example, np.longdouble. 

4818 dtype = type(1.0 + x[0] + y[0]) 

4819 

4820 if n == 2: 

4821 r = dtype(np.sign(x[1] - x[0])*np.sign(y[1] - y[0])) 

4822 result = PearsonRResult(statistic=r, pvalue=1.0, n=n, 

4823 alternative=alternative, x=x, y=y) 

4824 return result 

4825 

4826 xmean = x.mean(dtype=dtype) 

4827 ymean = y.mean(dtype=dtype) 

4828 

4829 # By using `astype(dtype)`, we ensure that the intermediate calculations 

4830 # use at least 64 bit floating point. 

4831 xm = x.astype(dtype) - xmean 

4832 ym = y.astype(dtype) - ymean 

4833 

4834 # Unlike np.linalg.norm or the expression sqrt((xm*xm).sum()), 

4835 # scipy.linalg.norm(xm) does not overflow if xm is, for example, 

4836 # [-5e210, 5e210, 3e200, -3e200] 

4837 normxm = linalg.norm(xm) 

4838 normym = linalg.norm(ym) 

4839 

4840 threshold = 1e-13 

4841 if normxm < threshold*abs(xmean) or normym < threshold*abs(ymean): 

4842 # If all the values in x (likewise y) are very close to the mean, 

4843 # the loss of precision that occurs in the subtraction xm = x - xmean 

4844 # might result in large errors in r. 

4845 msg = ("An input array is nearly constant; the computed " 

4846 "correlation coefficient may be inaccurate.") 

4847 warnings.warn(stats.NearConstantInputWarning(msg)) 

4848 

4849 r = np.dot(xm/normxm, ym/normym) 

4850 

4851 # Presumably, if abs(r) > 1, then it is only some small artifact of 

4852 # floating point arithmetic. 

4853 r = max(min(r, 1.0), -1.0) 

4854 

4855 # As explained in the docstring, the distribution of `r` under the null 

4856 # hypothesis is the beta distribution on (-1, 1) with a = b = n/2 - 1. 

4857 ab = n/2 - 1 

4858 dist = stats.beta(ab, ab, loc=-1, scale=2) 

4859 if alternative == 'two-sided': 

4860 prob = 2*dist.sf(abs(r)) 

4861 elif alternative == 'less': 

4862 prob = dist.cdf(r) 

4863 elif alternative == 'greater': 

4864 prob = dist.sf(r) 

4865 else: 

4866 raise ValueError('alternative must be one of ' 

4867 '["two-sided", "less", "greater"]') 

4868 

4869 return PearsonRResult(statistic=r, pvalue=prob, n=n, 

4870 alternative=alternative, x=x, y=y) 

4871 

4872 

4873def fisher_exact(table, alternative='two-sided'): 

4874 """Perform a Fisher exact test on a 2x2 contingency table. 

4875 

4876 The null hypothesis is that the true odds ratio of the populations 

4877 underlying the observations is one, and the observations were sampled 

4878 from these populations under a condition: the marginals of the 

4879 resulting table must equal those of the observed table. The statistic 

4880 returned is the unconditional maximum likelihood estimate of the odds 

4881 ratio, and the p-value is the probability under the null hypothesis of 

4882 obtaining a table at least as extreme as the one that was actually 

4883 observed. There are other possible choices of statistic and two-sided 

4884 p-value definition associated with Fisher's exact test; please see the 

4885 Notes for more information. 

4886 

4887 Parameters 

4888 ---------- 

4889 table : array_like of ints 

4890 A 2x2 contingency table. Elements must be non-negative integers. 

4891 alternative : {'two-sided', 'less', 'greater'}, optional 

4892 Defines the alternative hypothesis. 

4893 The following options are available (default is 'two-sided'): 

4894 

4895 * 'two-sided': the odds ratio of the underlying population is not one 

4896 * 'less': the odds ratio of the underlying population is less than one 

4897 * 'greater': the odds ratio of the underlying population is greater 

4898 than one 

4899 

4900 See the Notes for more details. 

4901 

4902 Returns 

4903 ------- 

4904 res : SignificanceResult 

4905 An object containing attributes: 

4906 

4907 statistic : float 

4908 This is the prior odds ratio, not a posterior estimate. 

4909 pvalue : float 

4910 The probability under the null hypothesis of obtaining a 

4911 table at least as extreme as the one that was actually observed. 

4912 

4913 See Also 

4914 -------- 

4915 chi2_contingency : Chi-square test of independence of variables in a 

4916 contingency table. This can be used as an alternative to 

4917 `fisher_exact` when the numbers in the table are large. 

4918 contingency.odds_ratio : Compute the odds ratio (sample or conditional 

4919 MLE) for a 2x2 contingency table. 

4920 barnard_exact : Barnard's exact test, which is a more powerful alternative 

4921 than Fisher's exact test for 2x2 contingency tables. 

4922 boschloo_exact : Boschloo's exact test, which is a more powerful 

4923 alternative than Fisher's exact test for 2x2 contingency tables. 

4924 

4925 Notes 

4926 ----- 

4927 *Null hypothesis and p-values* 

4928 

4929 The null hypothesis is that the true odds ratio of the populations 

4930 underlying the observations is one, and the observations were sampled at 

4931 random from these populations under a condition: the marginals of the 

4932 resulting table must equal those of the observed table. Equivalently, 

4933 the null hypothesis is that the input table is from the hypergeometric 

4934 distribution with parameters (as used in `hypergeom`) 

4935 ``M = a + b + c + d``, ``n = a + b`` and ``N = a + c``, where the 

4936 input table is ``[[a, b], [c, d]]``. This distribution has support 

4937 ``max(0, N + n - M) <= x <= min(N, n)``, or, in terms of the values 

4938 in the input table, ``min(0, a - d) <= x <= a + min(b, c)``. ``x`` 

4939 can be interpreted as the upper-left element of a 2x2 table, so the 

4940 tables in the distribution have form:: 

4941 

4942 [ x n - x ] 

4943 [N - x M - (n + N) + x] 

4944 

4945 For example, if:: 

4946 

4947 table = [6 2] 

4948 [1 4] 

4949 

4950 then the support is ``2 <= x <= 7``, and the tables in the distribution 

4951 are:: 

4952 

4953 [2 6] [3 5] [4 4] [5 3] [6 2] [7 1] 

4954 [5 0] [4 1] [3 2] [2 3] [1 4] [0 5] 

4955 

4956 The probability of each table is given by the hypergeometric distribution 

4957 ``hypergeom.pmf(x, M, n, N)``. For this example, these are (rounded to 

4958 three significant digits):: 

4959 

4960 x 2 3 4 5 6 7 

4961 p 0.0163 0.163 0.408 0.326 0.0816 0.00466 

4962 

4963 These can be computed with:: 

4964 

4965 >>> import numpy as np 

4966 >>> from scipy.stats import hypergeom 

4967 >>> table = np.array([[6, 2], [1, 4]]) 

4968 >>> M = table.sum() 

4969 >>> n = table[0].sum() 

4970 >>> N = table[:, 0].sum() 

4971 >>> start, end = hypergeom.support(M, n, N) 

4972 >>> hypergeom.pmf(np.arange(start, end+1), M, n, N) 

4973 array([0.01631702, 0.16317016, 0.40792541, 0.32634033, 0.08158508, 

4974 0.004662 ]) 

4975 

4976 The two-sided p-value is the probability that, under the null hypothesis, 

4977 a random table would have a probability equal to or less than the 

4978 probability of the input table. For our example, the probability of 

4979 the input table (where ``x = 6``) is 0.0816. The x values where the 

4980 probability does not exceed this are 2, 6 and 7, so the two-sided p-value 

4981 is ``0.0163 + 0.0816 + 0.00466 ~= 0.10256``:: 

4982 

4983 >>> from scipy.stats import fisher_exact 

4984 >>> res = fisher_exact(table, alternative='two-sided') 

4985 >>> res.pvalue 

4986 0.10256410256410257 

4987 

4988 The one-sided p-value for ``alternative='greater'`` is the probability 

4989 that a random table has ``x >= a``, which in our example is ``x >= 6``, 

4990 or ``0.0816 + 0.00466 ~= 0.08626``:: 

4991 

4992 >>> res = fisher_exact(table, alternative='greater') 

4993 >>> res.pvalue 

4994 0.08624708624708627 

4995 

4996 This is equivalent to computing the survival function of the 

4997 distribution at ``x = 5`` (one less than ``x`` from the input table, 

4998 because we want to include the probability of ``x = 6`` in the sum):: 

4999 

5000 >>> hypergeom.sf(5, M, n, N) 

5001 0.08624708624708627 

5002 

5003 For ``alternative='less'``, the one-sided p-value is the probability 

5004 that a random table has ``x <= a``, (i.e. ``x <= 6`` in our example), 

5005 or ``0.0163 + 0.163 + 0.408 + 0.326 + 0.0816 ~= 0.9949``:: 

5006 

5007 >>> res = fisher_exact(table, alternative='less') 

5008 >>> res.pvalue 

5009 0.9953379953379957 

5010 

5011 This is equivalent to computing the cumulative distribution function 

5012 of the distribution at ``x = 6``: 

5013 

5014 >>> hypergeom.cdf(6, M, n, N) 

5015 0.9953379953379957 

5016 

5017 *Odds ratio* 

5018 

5019 The calculated odds ratio is different from the value computed by the 

5020 R function ``fisher.test``. This implementation returns the "sample" 

5021 or "unconditional" maximum likelihood estimate, while ``fisher.test`` 

5022 in R uses the conditional maximum likelihood estimate. To compute the 

5023 conditional maximum likelihood estimate of the odds ratio, use 

5024 `scipy.stats.contingency.odds_ratio`. 

5025 

5026 References 

5027 ---------- 

5028 .. [1] Fisher, Sir Ronald A, "The Design of Experiments: 

5029 Mathematics of a Lady Tasting Tea." ISBN 978-0-486-41151-4, 1935. 

5030 .. [2] "Fisher's exact test", 

5031 https://en.wikipedia.org/wiki/Fisher's_exact_test 

5032 .. [3] Emma V. Low et al. "Identifying the lowest effective dose of 

5033 acetazolamide for the prophylaxis of acute mountain sickness: 

5034 systematic review and meta-analysis." 

5035 BMJ, 345, :doi:`10.1136/bmj.e6779`, 2012. 

5036 

5037 Examples 

5038 -------- 

5039 In [3]_, the effective dose of acetazolamide for the prophylaxis of acute 

5040 mountain sickness was investigated. The study notably concluded: 

5041 

5042 Acetazolamide 250 mg, 500 mg, and 750 mg daily were all efficacious for 

5043 preventing acute mountain sickness. Acetazolamide 250 mg was the lowest 

5044 effective dose with available evidence for this indication. 

5045 

5046 The following table summarizes the results of the experiment in which 

5047 some participants took a daily dose of acetazolamide 250 mg while others 

5048 took a placebo. 

5049 Cases of acute mountain sickness were recorded:: 

5050 

5051 Acetazolamide Control/Placebo 

5052 Acute mountain sickness 7 17 

5053 No 15 5 

5054 

5055 

5056 Is there evidence that the acetazolamide 250 mg reduces the risk of 

5057 acute mountain sickness? 

5058 We begin by formulating a null hypothesis :math:`H_0`: 

5059 

5060 The odds of experiencing acute mountain sickness are the same with 

5061 the acetazolamide treatment as they are with placebo. 

5062 

5063 Let's assess the plausibility of this hypothesis with 

5064 Fisher's test. 

5065 

5066 >>> from scipy.stats import fisher_exact 

5067 >>> res = fisher_exact([[7, 17], [15, 5]], alternative='less') 

5068 >>> res.statistic 

5069 0.13725490196078433 

5070 >>> res.pvalue 

5071 0.0028841933752349743 

5072 

5073 Using a significance level of 5%, we would reject the null hypothesis in 

5074 favor of the alternative hypothesis: "The odds of experiencing acute 

5075 mountain sickness with acetazolamide treatment are less than the odds of 

5076 experiencing acute mountain sickness with placebo." 

5077 

5078 .. note:: 

5079 

5080 Because the null distribution of Fisher's exact test is formed under 

5081 the assumption that both row and column sums are fixed, the result of 

5082 the test are conservative when applied to an experiment in which the 

5083 row sums are not fixed. 

5084 

5085 In this case, the column sums are fixed; there are 22 subjects in each 

5086 group. But the number of cases of acute mountain sickness is not 

5087 (and cannot be) fixed before conducting the experiment. It is a 

5088 consequence. 

5089 

5090 Boschloo's test does not depend on the assumption that the row sums 

5091 are fixed, and consequently, it provides a more powerful test in this 

5092 situation. 

5093 

5094 >>> from scipy.stats import boschloo_exact 

5095 >>> res = boschloo_exact([[7, 17], [15, 5]], alternative='less') 

5096 >>> res.statistic 

5097 0.0028841933752349743 

5098 >>> res.pvalue 

5099 0.0015141406667567101 

5100 

5101 We verify that the p-value is less than with `fisher_exact`. 

5102 

5103 """ 

5104 hypergeom = distributions.hypergeom 

5105 # int32 is not enough for the algorithm 

5106 c = np.asarray(table, dtype=np.int64) 

5107 if not c.shape == (2, 2): 

5108 raise ValueError("The input `table` must be of shape (2, 2).") 

5109 

5110 if np.any(c < 0): 

5111 raise ValueError("All values in `table` must be nonnegative.") 

5112 

5113 if 0 in c.sum(axis=0) or 0 in c.sum(axis=1): 

5114 # If both values in a row or column are zero, the p-value is 1 and 

5115 # the odds ratio is NaN. 

5116 return SignificanceResult(np.nan, 1.0) 

5117 

5118 if c[1, 0] > 0 and c[0, 1] > 0: 

5119 oddsratio = c[0, 0] * c[1, 1] / (c[1, 0] * c[0, 1]) 

5120 else: 

5121 oddsratio = np.inf 

5122 

5123 n1 = c[0, 0] + c[0, 1] 

5124 n2 = c[1, 0] + c[1, 1] 

5125 n = c[0, 0] + c[1, 0] 

5126 

5127 def pmf(x): 

5128 return hypergeom.pmf(x, n1 + n2, n1, n) 

5129 

5130 if alternative == 'less': 

5131 pvalue = hypergeom.cdf(c[0, 0], n1 + n2, n1, n) 

5132 elif alternative == 'greater': 

5133 # Same formula as the 'less' case, but with the second column. 

5134 pvalue = hypergeom.cdf(c[0, 1], n1 + n2, n1, c[0, 1] + c[1, 1]) 

5135 elif alternative == 'two-sided': 

5136 mode = int((n + 1) * (n1 + 1) / (n1 + n2 + 2)) 

5137 pexact = hypergeom.pmf(c[0, 0], n1 + n2, n1, n) 

5138 pmode = hypergeom.pmf(mode, n1 + n2, n1, n) 

5139 

5140 epsilon = 1e-14 

5141 gamma = 1 + epsilon 

5142 

5143 if np.abs(pexact - pmode) / np.maximum(pexact, pmode) <= epsilon: 

5144 return SignificanceResult(oddsratio, 1.) 

5145 

5146 elif c[0, 0] < mode: 

5147 plower = hypergeom.cdf(c[0, 0], n1 + n2, n1, n) 

5148 if hypergeom.pmf(n, n1 + n2, n1, n) > pexact * gamma: 

5149 return SignificanceResult(oddsratio, plower) 

5150 

5151 guess = _binary_search(lambda x: -pmf(x), -pexact * gamma, mode, n) 

5152 pvalue = plower + hypergeom.sf(guess, n1 + n2, n1, n) 

5153 else: 

5154 pupper = hypergeom.sf(c[0, 0] - 1, n1 + n2, n1, n) 

5155 if hypergeom.pmf(0, n1 + n2, n1, n) > pexact * gamma: 

5156 return SignificanceResult(oddsratio, pupper) 

5157 

5158 guess = _binary_search(pmf, pexact * gamma, 0, mode) 

5159 pvalue = pupper + hypergeom.cdf(guess, n1 + n2, n1, n) 

5160 else: 

5161 msg = "`alternative` should be one of {'two-sided', 'less', 'greater'}" 

5162 raise ValueError(msg) 

5163 

5164 pvalue = min(pvalue, 1.0) 

5165 

5166 return SignificanceResult(oddsratio, pvalue) 

5167 

5168 

5169def spearmanr(a, b=None, axis=0, nan_policy='propagate', 

5170 alternative='two-sided'): 

5171 r"""Calculate a Spearman correlation coefficient with associated p-value. 

5172 

5173 The Spearman rank-order correlation coefficient is a nonparametric measure 

5174 of the monotonicity of the relationship between two datasets. 

5175 Like other correlation coefficients, 

5176 this one varies between -1 and +1 with 0 implying no correlation. 

5177 Correlations of -1 or +1 imply an exact monotonic relationship. Positive 

5178 correlations imply that as x increases, so does y. Negative correlations 

5179 imply that as x increases, y decreases. 

5180 

5181 The p-value roughly indicates the probability of an uncorrelated system 

5182 producing datasets that have a Spearman correlation at least as extreme 

5183 as the one computed from these datasets. Although calculation of the 

5184 p-value does not make strong assumptions about the distributions underlying 

5185 the samples, it is only accurate for very large samples (>500 

5186 observations). For smaller sample sizes, consider a permutation test (see 

5187 Examples section below). 

5188 

5189 Parameters 

5190 ---------- 

5191 a, b : 1D or 2D array_like, b is optional 

5192 One or two 1-D or 2-D arrays containing multiple variables and 

5193 observations. When these are 1-D, each represents a vector of 

5194 observations of a single variable. For the behavior in the 2-D case, 

5195 see under ``axis``, below. 

5196 Both arrays need to have the same length in the ``axis`` dimension. 

5197 axis : int or None, optional 

5198 If axis=0 (default), then each column represents a variable, with 

5199 observations in the rows. If axis=1, the relationship is transposed: 

5200 each row represents a variable, while the columns contain observations. 

5201 If axis=None, then both arrays will be raveled. 

5202 nan_policy : {'propagate', 'raise', 'omit'}, optional 

5203 Defines how to handle when input contains nan. 

5204 The following options are available (default is 'propagate'): 

5205 

5206 * 'propagate': returns nan 

5207 * 'raise': throws an error 

5208 * 'omit': performs the calculations ignoring nan values 

5209 

5210 alternative : {'two-sided', 'less', 'greater'}, optional 

5211 Defines the alternative hypothesis. Default is 'two-sided'. 

5212 The following options are available: 

5213 

5214 * 'two-sided': the correlation is nonzero 

5215 * 'less': the correlation is negative (less than zero) 

5216 * 'greater': the correlation is positive (greater than zero) 

5217 

5218 .. versionadded:: 1.7.0 

5219 

5220 Returns 

5221 ------- 

5222 res : SignificanceResult 

5223 An object containing attributes: 

5224 

5225 statistic : float or ndarray (2-D square) 

5226 Spearman correlation matrix or correlation coefficient (if only 2 

5227 variables are given as parameters). Correlation matrix is square 

5228 with length equal to total number of variables (columns or rows) in 

5229 ``a`` and ``b`` combined. 

5230 pvalue : float 

5231 The p-value for a hypothesis test whose null hypothesis 

5232 is that two samples have no ordinal correlation. See 

5233 `alternative` above for alternative hypotheses. `pvalue` has the 

5234 same shape as `statistic`. 

5235 

5236 Warns 

5237 ----- 

5238 `~scipy.stats.ConstantInputWarning` 

5239 Raised if an input is a constant array. The correlation coefficient 

5240 is not defined in this case, so ``np.nan`` is returned. 

5241 

5242 References 

5243 ---------- 

5244 .. [1] Zwillinger, D. and Kokoska, S. (2000). CRC Standard 

5245 Probability and Statistics Tables and Formulae. Chapman & Hall: New 

5246 York. 2000. 

5247 Section 14.7 

5248 .. [2] Kendall, M. G. and Stuart, A. (1973). 

5249 The Advanced Theory of Statistics, Volume 2: Inference and Relationship. 

5250 Griffin. 1973. 

5251 Section 31.18 

5252 .. [3] Kershenobich, D., Fierro, F. J., & Rojkind, M. (1970). The 

5253 relationship between the free pool of proline and collagen content in 

5254 human liver cirrhosis. The Journal of Clinical Investigation, 49(12), 

5255 2246-2249. 

5256 .. [4] Hollander, M., Wolfe, D. A., & Chicken, E. (2013). Nonparametric 

5257 statistical methods. John Wiley & Sons. 

5258 .. [5] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be 

5259 Zero: Calculating Exact P-values When Permutations Are Randomly Drawn." 

5260 Statistical Applications in Genetics and Molecular Biology 9.1 (2010). 

5261 .. [6] Ludbrook, J., & Dudley, H. (1998). Why permutation tests are 

5262 superior to t and F tests in biomedical research. The American 

5263 Statistician, 52(2), 127-132. 

5264 

5265 Examples 

5266 -------- 

5267 Consider the following data from [3]_, which studied the relationship 

5268 between free proline (an amino acid) and total collagen (a protein often 

5269 found in connective tissue) in unhealthy human livers. 

5270 

5271 The ``x`` and ``y`` arrays below record measurements of the two compounds. 

5272 The observations are paired: each free proline measurement was taken from 

5273 the same liver as the total collagen measurement at the same index. 

5274 

5275 >>> import numpy as np 

5276 >>> # total collagen (mg/g dry weight of liver) 

5277 >>> x = np.array([7.1, 7.1, 7.2, 8.3, 9.4, 10.5, 11.4]) 

5278 >>> # free proline (μ mole/g dry weight of liver) 

5279 >>> y = np.array([2.8, 2.9, 2.8, 2.6, 3.5, 4.6, 5.0]) 

5280 

5281 These data were analyzed in [4]_ using Spearman's correlation coefficient, 

5282 a statistic sensitive to monotonic correlation between the samples. 

5283 

5284 >>> from scipy import stats 

5285 >>> res = stats.spearmanr(x, y) 

5286 >>> res.statistic 

5287 0.7000000000000001 

5288 

5289 The value of this statistic tends to be high (close to 1) for samples with 

5290 a strongly positive ordinal correlation, low (close to -1) for samples with 

5291 a strongly negative ordinal correlation, and small in magnitude (close to 

5292 zero) for samples with weak ordinal correlation. 

5293 

5294 The test is performed by comparing the observed value of the 

5295 statistic against the null distribution: the distribution of statistic 

5296 values derived under the null hypothesis that total collagen and free 

5297 proline measurements are independent. 

5298 

5299 For this test, the statistic can be transformed such that the null 

5300 distribution for large samples is Student's t distribution with 

5301 ``len(x) - 2`` degrees of freedom. 

5302 

5303 >>> import matplotlib.pyplot as plt 

5304 >>> dof = len(x)-2 # len(x) == len(y) 

5305 >>> dist = stats.t(df=dof) 

5306 >>> t_vals = np.linspace(-5, 5, 100) 

5307 >>> pdf = dist.pdf(t_vals) 

5308 >>> fig, ax = plt.subplots(figsize=(8, 5)) 

5309 >>> def plot(ax): # we'll re-use this 

5310 ... ax.plot(t_vals, pdf) 

5311 ... ax.set_title("Spearman's Rho Test Null Distribution") 

5312 ... ax.set_xlabel("statistic") 

5313 ... ax.set_ylabel("probability density") 

5314 >>> plot(ax) 

5315 >>> plt.show() 

5316 

5317 The comparison is quantified by the p-value: the proportion of values in 

5318 the null distribution as extreme or more extreme than the observed 

5319 value of the statistic. In a two-sided test in which the statistic is 

5320 positive, elements of the null distribution greater than the transformed 

5321 statistic and elements of the null distribution less than the negative of 

5322 the observed statistic are both considered "more extreme". 

5323 

5324 >>> fig, ax = plt.subplots(figsize=(8, 5)) 

5325 >>> plot(ax) 

5326 >>> rs = res.statistic # original statistic 

5327 >>> transformed = rs * np.sqrt(dof / ((rs+1.0)*(1.0-rs))) 

5328 >>> pvalue = dist.cdf(-transformed) + dist.sf(transformed) 

5329 >>> annotation = (f'p-value={pvalue:.4f}\n(shaded area)') 

5330 >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8) 

5331 >>> _ = ax.annotate(annotation, (2.7, 0.025), (3, 0.03), arrowprops=props) 

5332 >>> i = t_vals >= transformed 

5333 >>> ax.fill_between(t_vals[i], y1=0, y2=pdf[i], color='C0') 

5334 >>> i = t_vals <= -transformed 

5335 >>> ax.fill_between(t_vals[i], y1=0, y2=pdf[i], color='C0') 

5336 >>> ax.set_xlim(-5, 5) 

5337 >>> ax.set_ylim(0, 0.1) 

5338 >>> plt.show() 

5339 >>> res.pvalue 

5340 0.07991669030889909 # two-sided p-value 

5341 

5342 If the p-value is "small" - that is, if there is a low probability of 

5343 sampling data from independent distributions that produces such an extreme 

5344 value of the statistic - this may be taken as evidence against the null 

5345 hypothesis in favor of the alternative: the distribution of total collagen 

5346 and free proline are *not* independent. Note that: 

5347 

5348 - The inverse is not true; that is, the test is not used to provide 

5349 evidence for the null hypothesis. 

5350 - The threshold for values that will be considered "small" is a choice that 

5351 should be made before the data is analyzed [5]_ with consideration of the 

5352 risks of both false positives (incorrectly rejecting the null hypothesis) 

5353 and false negatives (failure to reject a false null hypothesis). 

5354 - Small p-values are not evidence for a *large* effect; rather, they can 

5355 only provide evidence for a "significant" effect, meaning that they are 

5356 unlikely to have occurred under the null hypothesis. 

5357 

5358 Suppose that before performing the experiment, the authors had reason 

5359 to predict a positive correlation between the total collagen and free 

5360 proline measurements, and that they had chosen to assess the plausibility 

5361 of the null hypothesis against a one-sided alternative: free proline has a 

5362 positive ordinal correlation with total collagen. In this case, only those 

5363 values in the null distribution that are as great or greater than the 

5364 observed statistic are considered to be more extreme. 

5365 

5366 >>> res = stats.spearmanr(x, y, alternative='greater') 

5367 >>> res.statistic 

5368 0.7000000000000001 # same statistic 

5369 >>> fig, ax = plt.subplots(figsize=(8, 5)) 

5370 >>> plot(ax) 

5371 >>> pvalue = dist.sf(transformed) 

5372 >>> annotation = (f'p-value={pvalue:.6f}\n(shaded area)') 

5373 >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8) 

5374 >>> _ = ax.annotate(annotation, (3, 0.018), (3.5, 0.03), arrowprops=props) 

5375 >>> i = t_vals >= transformed 

5376 >>> ax.fill_between(t_vals[i], y1=0, y2=pdf[i], color='C0') 

5377 >>> ax.set_xlim(1, 5) 

5378 >>> ax.set_ylim(0, 0.1) 

5379 >>> plt.show() 

5380 >>> res.pvalue 

5381 0.03995834515444954 # one-sided p-value; half of the two-sided p-value 

5382 

5383 Note that the t-distribution provides an asymptotic approximation of the 

5384 null distribution; it is only accurate for samples with many observations. 

5385 For small samples, it may be more appropriate to perform a permutation 

5386 test: Under the null hypothesis that total collagen and free proline are 

5387 independent, each of the free proline measurements were equally likely to 

5388 have been observed with any of the total collagen measurements. Therefore, 

5389 we can form an *exact* null distribution by calculating the statistic under 

5390 each possible pairing of elements between ``x`` and ``y``. 

5391 

5392 >>> def statistic(x): # explore all possible pairings by permuting `x` 

5393 ... rs = stats.spearmanr(x, y).statistic # ignore pvalue 

5394 ... transformed = rs * np.sqrt(dof / ((rs+1.0)*(1.0-rs))) 

5395 ... return transformed 

5396 >>> ref = stats.permutation_test((x,), statistic, alternative='greater', 

5397 ... permutation_type='pairings') 

5398 >>> fig, ax = plt.subplots(figsize=(8, 5)) 

5399 >>> plot(ax) 

5400 >>> ax.hist(ref.null_distribution, np.linspace(-5, 5, 26), 

5401 ... density=True) 

5402 >>> ax.legend(['aymptotic approximation\n(many observations)', 

5403 ... f'exact \n({len(ref.null_distribution)} permutations)']) 

5404 >>> plt.show() 

5405 >>> ref.pvalue 

5406 0.04563492063492063 # exact one-sided p-value 

5407 

5408 """ 

5409 if axis is not None and axis > 1: 

5410 raise ValueError("spearmanr only handles 1-D or 2-D arrays, " 

5411 "supplied axis argument {}, please use only " 

5412 "values 0, 1 or None for axis".format(axis)) 

5413 

5414 a, axisout = _chk_asarray(a, axis) 

5415 if a.ndim > 2: 

5416 raise ValueError("spearmanr only handles 1-D or 2-D arrays") 

5417 

5418 if b is None: 

5419 if a.ndim < 2: 

5420 raise ValueError("`spearmanr` needs at least 2 " 

5421 "variables to compare") 

5422 else: 

5423 # Concatenate a and b, so that we now only have to handle the case 

5424 # of a 2-D `a`. 

5425 b, _ = _chk_asarray(b, axis) 

5426 if axisout == 0: 

5427 a = np.column_stack((a, b)) 

5428 else: 

5429 a = np.row_stack((a, b)) 

5430 

5431 n_vars = a.shape[1 - axisout] 

5432 n_obs = a.shape[axisout] 

5433 if n_obs <= 1: 

5434 # Handle empty arrays or single observations. 

5435 res = SignificanceResult(np.nan, np.nan) 

5436 res.correlation = np.nan 

5437 return res 

5438 

5439 warn_msg = ("An input array is constant; the correlation coefficient " 

5440 "is not defined.") 

5441 if axisout == 0: 

5442 if (a[:, 0][0] == a[:, 0]).all() or (a[:, 1][0] == a[:, 1]).all(): 

5443 # If an input is constant, the correlation coefficient 

5444 # is not defined. 

5445 warnings.warn(stats.ConstantInputWarning(warn_msg)) 

5446 res = SignificanceResult(np.nan, np.nan) 

5447 res.correlation = np.nan 

5448 return res 

5449 else: # case when axisout == 1 b/c a is 2 dim only 

5450 if (a[0, :][0] == a[0, :]).all() or (a[1, :][0] == a[1, :]).all(): 

5451 # If an input is constant, the correlation coefficient 

5452 # is not defined. 

5453 warnings.warn(stats.ConstantInputWarning(warn_msg)) 

5454 res = SignificanceResult(np.nan, np.nan) 

5455 res.correlation = np.nan 

5456 return res 

5457 

5458 a_contains_nan, nan_policy = _contains_nan(a, nan_policy) 

5459 variable_has_nan = np.zeros(n_vars, dtype=bool) 

5460 if a_contains_nan: 

5461 if nan_policy == 'omit': 

5462 return mstats_basic.spearmanr(a, axis=axis, nan_policy=nan_policy, 

5463 alternative=alternative) 

5464 elif nan_policy == 'propagate': 

5465 if a.ndim == 1 or n_vars <= 2: 

5466 res = SignificanceResult(np.nan, np.nan) 

5467 res.correlation = np.nan 

5468 return res 

5469 else: 

5470 # Keep track of variables with NaNs, set the outputs to NaN 

5471 # only for those variables 

5472 variable_has_nan = np.isnan(a).any(axis=axisout) 

5473 

5474 a_ranked = np.apply_along_axis(rankdata, axisout, a) 

5475 rs = np.corrcoef(a_ranked, rowvar=axisout) 

5476 dof = n_obs - 2 # degrees of freedom 

5477 

5478 # rs can have elements equal to 1, so avoid zero division warnings 

5479 with np.errstate(divide='ignore'): 

5480 # clip the small negative values possibly caused by rounding 

5481 # errors before taking the square root 

5482 t = rs * np.sqrt((dof/((rs+1.0)*(1.0-rs))).clip(0)) 

5483 

5484 t, prob = _ttest_finish(dof, t, alternative) 

5485 

5486 # For backwards compatibility, return scalars when comparing 2 columns 

5487 if rs.shape == (2, 2): 

5488 res = SignificanceResult(rs[1, 0], prob[1, 0]) 

5489 res.correlation = rs[1, 0] 

5490 return res 

5491 else: 

5492 rs[variable_has_nan, :] = np.nan 

5493 rs[:, variable_has_nan] = np.nan 

5494 res = SignificanceResult(rs, prob) 

5495 res.correlation = rs 

5496 return res 

5497 

5498 

5499def pointbiserialr(x, y): 

5500 r"""Calculate a point biserial correlation coefficient and its p-value. 

5501 

5502 The point biserial correlation is used to measure the relationship 

5503 between a binary variable, x, and a continuous variable, y. Like other 

5504 correlation coefficients, this one varies between -1 and +1 with 0 

5505 implying no correlation. Correlations of -1 or +1 imply a determinative 

5506 relationship. 

5507 

5508 This function may be computed using a shortcut formula but produces the 

5509 same result as `pearsonr`. 

5510 

5511 Parameters 

5512 ---------- 

5513 x : array_like of bools 

5514 Input array. 

5515 y : array_like 

5516 Input array. 

5517 

5518 Returns 

5519 ------- 

5520 res: SignificanceResult 

5521 An object containing attributes: 

5522 

5523 statistic : float 

5524 The R value. 

5525 pvalue : float 

5526 The two-sided p-value. 

5527 

5528 Notes 

5529 ----- 

5530 `pointbiserialr` uses a t-test with ``n-1`` degrees of freedom. 

5531 It is equivalent to `pearsonr`. 

5532 

5533 The value of the point-biserial correlation can be calculated from: 

5534 

5535 .. math:: 

5536 

5537 r_{pb} = \frac{\overline{Y_1} - \overline{Y_0}} 

5538 {s_y} 

5539 \sqrt{\frac{N_0 N_1} 

5540 {N (N - 1)}} 

5541 

5542 Where :math:`\overline{Y_{0}}` and :math:`\overline{Y_{1}}` are means 

5543 of the metric observations coded 0 and 1 respectively; :math:`N_{0}` and 

5544 :math:`N_{1}` are number of observations coded 0 and 1 respectively; 

5545 :math:`N` is the total number of observations and :math:`s_{y}` is the 

5546 standard deviation of all the metric observations. 

5547 

5548 A value of :math:`r_{pb}` that is significantly different from zero is 

5549 completely equivalent to a significant difference in means between the two 

5550 groups. Thus, an independent groups t Test with :math:`N-2` degrees of 

5551 freedom may be used to test whether :math:`r_{pb}` is nonzero. The 

5552 relation between the t-statistic for comparing two independent groups and 

5553 :math:`r_{pb}` is given by: 

5554 

5555 .. math:: 

5556 

5557 t = \sqrt{N - 2}\frac{r_{pb}}{\sqrt{1 - r^{2}_{pb}}} 

5558 

5559 References 

5560 ---------- 

5561 .. [1] J. Lev, "The Point Biserial Coefficient of Correlation", Ann. Math. 

5562 Statist., Vol. 20, no.1, pp. 125-126, 1949. 

5563 

5564 .. [2] R.F. Tate, "Correlation Between a Discrete and a Continuous 

5565 Variable. Point-Biserial Correlation.", Ann. Math. Statist., Vol. 25, 

5566 np. 3, pp. 603-607, 1954. 

5567 

5568 .. [3] D. Kornbrot "Point Biserial Correlation", In Wiley StatsRef: 

5569 Statistics Reference Online (eds N. Balakrishnan, et al.), 2014. 

5570 :doi:`10.1002/9781118445112.stat06227` 

5571 

5572 Examples 

5573 -------- 

5574 >>> import numpy as np 

5575 >>> from scipy import stats 

5576 >>> a = np.array([0, 0, 0, 1, 1, 1, 1]) 

5577 >>> b = np.arange(7) 

5578 >>> stats.pointbiserialr(a, b) 

5579 (0.8660254037844386, 0.011724811003954652) 

5580 >>> stats.pearsonr(a, b) 

5581 (0.86602540378443871, 0.011724811003954626) 

5582 >>> np.corrcoef(a, b) 

5583 array([[ 1. , 0.8660254], 

5584 [ 0.8660254, 1. ]]) 

5585 

5586 """ 

5587 rpb, prob = pearsonr(x, y) 

5588 # create result object with alias for backward compatibility 

5589 res = SignificanceResult(rpb, prob) 

5590 res.correlation = rpb 

5591 return res 

5592 

5593 

5594def kendalltau(x, y, initial_lexsort=None, nan_policy='propagate', 

5595 method='auto', variant='b', alternative='two-sided'): 

5596 r"""Calculate Kendall's tau, a correlation measure for ordinal data. 

5597 

5598 Kendall's tau is a measure of the correspondence between two rankings. 

5599 Values close to 1 indicate strong agreement, and values close to -1 

5600 indicate strong disagreement. This implements two variants of Kendall's 

5601 tau: tau-b (the default) and tau-c (also known as Stuart's tau-c). These 

5602 differ only in how they are normalized to lie within the range -1 to 1; 

5603 the hypothesis tests (their p-values) are identical. Kendall's original 

5604 tau-a is not implemented separately because both tau-b and tau-c reduce 

5605 to tau-a in the absence of ties. 

5606 

5607 Parameters 

5608 ---------- 

5609 x, y : array_like 

5610 Arrays of rankings, of the same shape. If arrays are not 1-D, they 

5611 will be flattened to 1-D. 

5612 initial_lexsort : bool, optional, deprecated 

5613 This argument is unused. 

5614 

5615 .. deprecated:: 1.10.0 

5616 `kendalltau` keyword argument `initial_lexsort` is deprecated as it 

5617 is unused and will be removed in SciPy 1.12.0. 

5618 nan_policy : {'propagate', 'raise', 'omit'}, optional 

5619 Defines how to handle when input contains nan. 

5620 The following options are available (default is 'propagate'): 

5621 

5622 * 'propagate': returns nan 

5623 * 'raise': throws an error 

5624 * 'omit': performs the calculations ignoring nan values 

5625 

5626 method : {'auto', 'asymptotic', 'exact'}, optional 

5627 Defines which method is used to calculate the p-value [5]_. 

5628 The following options are available (default is 'auto'): 

5629 

5630 * 'auto': selects the appropriate method based on a trade-off 

5631 between speed and accuracy 

5632 * 'asymptotic': uses a normal approximation valid for large samples 

5633 * 'exact': computes the exact p-value, but can only be used if no ties 

5634 are present. As the sample size increases, the 'exact' computation 

5635 time may grow and the result may lose some precision. 

5636 variant : {'b', 'c'}, optional 

5637 Defines which variant of Kendall's tau is returned. Default is 'b'. 

5638 alternative : {'two-sided', 'less', 'greater'}, optional 

5639 Defines the alternative hypothesis. Default is 'two-sided'. 

5640 The following options are available: 

5641 

5642 * 'two-sided': the rank correlation is nonzero 

5643 * 'less': the rank correlation is negative (less than zero) 

5644 * 'greater': the rank correlation is positive (greater than zero) 

5645 

5646 Returns 

5647 ------- 

5648 res : SignificanceResult 

5649 An object containing attributes: 

5650 

5651 statistic : float 

5652 The tau statistic. 

5653 pvalue : float 

5654 The p-value for a hypothesis test whose null hypothesis is 

5655 an absence of association, tau = 0. 

5656 

5657 See Also 

5658 -------- 

5659 spearmanr : Calculates a Spearman rank-order correlation coefficient. 

5660 theilslopes : Computes the Theil-Sen estimator for a set of points (x, y). 

5661 weightedtau : Computes a weighted version of Kendall's tau. 

5662 

5663 Notes 

5664 ----- 

5665 The definition of Kendall's tau that is used is [2]_:: 

5666 

5667 tau_b = (P - Q) / sqrt((P + Q + T) * (P + Q + U)) 

5668 

5669 tau_c = 2 (P - Q) / (n**2 * (m - 1) / m) 

5670 

5671 where P is the number of concordant pairs, Q the number of discordant 

5672 pairs, T the number of ties only in `x`, and U the number of ties only in 

5673 `y`. If a tie occurs for the same pair in both `x` and `y`, it is not 

5674 added to either T or U. n is the total number of samples, and m is the 

5675 number of unique values in either `x` or `y`, whichever is smaller. 

5676 

5677 References 

5678 ---------- 

5679 .. [1] Maurice G. Kendall, "A New Measure of Rank Correlation", Biometrika 

5680 Vol. 30, No. 1/2, pp. 81-93, 1938. 

5681 .. [2] Maurice G. Kendall, "The treatment of ties in ranking problems", 

5682 Biometrika Vol. 33, No. 3, pp. 239-251. 1945. 

5683 .. [3] Gottfried E. Noether, "Elements of Nonparametric Statistics", John 

5684 Wiley & Sons, 1967. 

5685 .. [4] Peter M. Fenwick, "A new data structure for cumulative frequency 

5686 tables", Software: Practice and Experience, Vol. 24, No. 3, 

5687 pp. 327-336, 1994. 

5688 .. [5] Maurice G. Kendall, "Rank Correlation Methods" (4th Edition), 

5689 Charles Griffin & Co., 1970. 

5690 .. [6] Kershenobich, D., Fierro, F. J., & Rojkind, M. (1970). The 

5691 relationship between the free pool of proline and collagen content 

5692 in human liver cirrhosis. The Journal of Clinical Investigation, 

5693 49(12), 2246-2249. 

5694 .. [7] Hollander, M., Wolfe, D. A., & Chicken, E. (2013). Nonparametric 

5695 statistical methods. John Wiley & Sons. 

5696 .. [8] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be 

5697 Zero: Calculating Exact P-values When Permutations Are Randomly 

5698 Drawn." Statistical Applications in Genetics and Molecular Biology 

5699 9.1 (2010). 

5700 

5701 Examples 

5702 -------- 

5703 Consider the following data from [6]_, which studied the relationship 

5704 between free proline (an amino acid) and total collagen (a protein often 

5705 found in connective tissue) in unhealthy human livers. 

5706 

5707 The ``x`` and ``y`` arrays below record measurements of the two compounds. 

5708 The observations are paired: each free proline measurement was taken from 

5709 the same liver as the total collagen measurement at the same index. 

5710 

5711 >>> import numpy as np 

5712 >>> # total collagen (mg/g dry weight of liver) 

5713 >>> x = np.array([7.1, 7.1, 7.2, 8.3, 9.4, 10.5, 11.4]) 

5714 >>> # free proline (μ mole/g dry weight of liver) 

5715 >>> y = np.array([2.8, 2.9, 2.8, 2.6, 3.5, 4.6, 5.0]) 

5716 

5717 These data were analyzed in [7]_ using Spearman's correlation coefficient, 

5718 a statistic similar to to Kendall's tau in that it is also sensitive to 

5719 ordinal correlation between the samples. Let's perform an analagous study 

5720 using Kendall's tau. 

5721 

5722 >>> from scipy import stats 

5723 >>> res = stats.kendalltau(x, y) 

5724 >>> res.statistic 

5725 0.5499999999999999 

5726 

5727 The value of this statistic tends to be high (close to 1) for samples with 

5728 a strongly positive ordinal correlation, low (close to -1) for samples with 

5729 a strongly negative ordinal correlation, and small in magnitude (close to 

5730 zero) for samples with weak ordinal correlation. 

5731 

5732 The test is performed by comparing the observed value of the 

5733 statistic against the null distribution: the distribution of statistic 

5734 values derived under the null hypothesis that total collagen and free 

5735 proline measurements are independent. 

5736 

5737 For this test, the null distribution for large samples without ties is 

5738 approximated as the normal distribution with variance 

5739 ``(2*(2*n + 5))/(9*n*(n - 1))``, where ``n = len(x)``. 

5740 

5741 >>> import matplotlib.pyplot as plt 

5742 >>> n = len(x) # len(x) == len(y) 

5743 >>> var = (2*(2*n + 5))/(9*n*(n - 1)) 

5744 >>> dist = stats.norm(scale=np.sqrt(var)) 

5745 >>> z_vals = np.linspace(-1.25, 1.25, 100) 

5746 >>> pdf = dist.pdf(z_vals) 

5747 >>> fig, ax = plt.subplots(figsize=(8, 5)) 

5748 >>> def plot(ax): # we'll re-use this 

5749 ... ax.plot(z_vals, pdf) 

5750 ... ax.set_title("Kendall Tau Test Null Distribution") 

5751 ... ax.set_xlabel("statistic") 

5752 ... ax.set_ylabel("probability density") 

5753 >>> plot(ax) 

5754 >>> plt.show() 

5755 

5756 The comparison is quantified by the p-value: the proportion of values in 

5757 the null distribution as extreme or more extreme than the observed 

5758 value of the statistic. In a two-sided test in which the statistic is 

5759 positive, elements of the null distribution greater than the transformed 

5760 statistic and elements of the null distribution less than the negative of 

5761 the observed statistic are both considered "more extreme". 

5762 

5763 >>> fig, ax = plt.subplots(figsize=(8, 5)) 

5764 >>> plot(ax) 

5765 >>> pvalue = dist.cdf(-res.statistic) + dist.sf(res.statistic) 

5766 >>> annotation = (f'p-value={pvalue:.4f}\n(shaded area)') 

5767 >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8) 

5768 >>> _ = ax.annotate(annotation, (0.65, 0.15), (0.8, 0.3), arrowprops=props) 

5769 >>> i = z_vals >= res.statistic 

5770 >>> ax.fill_between(z_vals[i], y1=0, y2=pdf[i], color='C0') 

5771 >>> i = z_vals <= -res.statistic 

5772 >>> ax.fill_between(z_vals[i], y1=0, y2=pdf[i], color='C0') 

5773 >>> ax.set_xlim(-1.25, 1.25) 

5774 >>> ax.set_ylim(0, 0.5) 

5775 >>> plt.show() 

5776 >>> res.pvalue 

5777 0.09108705741631495 # approximate p-value 

5778 

5779 Note that there is slight disagreement between the shaded area of the curve 

5780 and the p-value returned by `kendalltau`. This is because our data has 

5781 ties, and we have neglected a tie correction to the null distribution 

5782 variance that `kendalltau` performs. For samples without ties, the shaded 

5783 areas of our plot and p-value returned by `kendalltau` would match exactly. 

5784 

5785 If the p-value is "small" - that is, if there is a low probability of 

5786 sampling data from independent distributions that produces such an extreme 

5787 value of the statistic - this may be taken as evidence against the null 

5788 hypothesis in favor of the alternative: the distribution of total collagen 

5789 and free proline are *not* independent. Note that: 

5790 

5791 - The inverse is not true; that is, the test is not used to provide 

5792 evidence for the null hypothesis. 

5793 - The threshold for values that will be considered "small" is a choice that 

5794 should be made before the data is analyzed [8]_ with consideration of the 

5795 risks of both false positives (incorrectly rejecting the null hypothesis) 

5796 and false negatives (failure to reject a false null hypothesis). 

5797 - Small p-values are not evidence for a *large* effect; rather, they can 

5798 only provide evidence for a "significant" effect, meaning that they are 

5799 unlikely to have occurred under the null hypothesis. 

5800 

5801 For samples without ties of moderate size, `kendalltau` can compute the 

5802 p-value exactly. However, in the presence of ties, `kendalltau` resorts 

5803 to an asymptotic approximation. Nonetheles, we can use a permutation test 

5804 to compute the null distribution exactly: Under the null hypothesis that 

5805 total collagen and free proline are independent, each of the free proline 

5806 measurements were equally likely to have been observed with any of the 

5807 total collagen measurements. Therefore, we can form an *exact* null 

5808 distribution by calculating the statistic under each possible pairing of 

5809 elements between ``x`` and ``y``. 

5810 

5811 >>> def statistic(x): # explore all possible pairings by permuting `x` 

5812 ... return stats.kendalltau(x, y).statistic # ignore pvalue 

5813 >>> ref = stats.permutation_test((x,), statistic, 

5814 ... permutation_type='pairings') 

5815 >>> fig, ax = plt.subplots(figsize=(8, 5)) 

5816 >>> plot(ax) 

5817 >>> bins = np.linspace(-1.25, 1.25, 25) 

5818 >>> ax.hist(ref.null_distribution, bins=bins, density=True) 

5819 >>> ax.legend(['aymptotic approximation\n(many observations)', 

5820 ... 'exact null distribution']) 

5821 >>> plot(ax) 

5822 >>> plt.show() 

5823 >>> ref.pvalue 

5824 0.12222222222222222 # exact p-value 

5825 

5826 Note that there is significant disagreement between the exact p-value 

5827 calculated here and the approximation returned by `kendalltau` above. For 

5828 small samples with ties, consider performing a permutation test for more 

5829 accurate results. 

5830 

5831 """ 

5832 if initial_lexsort is not None: 

5833 msg = ("'kendalltau' keyword argument 'initial_lexsort' is deprecated" 

5834 " as it is unused and will be removed in SciPy 1.12.0.") 

5835 warnings.warn(msg, DeprecationWarning, stacklevel=2) 

5836 

5837 x = np.asarray(x).ravel() 

5838 y = np.asarray(y).ravel() 

5839 

5840 if x.size != y.size: 

5841 raise ValueError("All inputs to `kendalltau` must be of the same " 

5842 f"size, found x-size {x.size} and y-size {y.size}") 

5843 elif not x.size or not y.size: 

5844 # Return NaN if arrays are empty 

5845 res = SignificanceResult(np.nan, np.nan) 

5846 res.correlation = np.nan 

5847 return res 

5848 

5849 # check both x and y 

5850 cnx, npx = _contains_nan(x, nan_policy) 

5851 cny, npy = _contains_nan(y, nan_policy) 

5852 contains_nan = cnx or cny 

5853 if npx == 'omit' or npy == 'omit': 

5854 nan_policy = 'omit' 

5855 

5856 if contains_nan and nan_policy == 'propagate': 

5857 res = SignificanceResult(np.nan, np.nan) 

5858 res.correlation = np.nan 

5859 return res 

5860 

5861 elif contains_nan and nan_policy == 'omit': 

5862 x = ma.masked_invalid(x) 

5863 y = ma.masked_invalid(y) 

5864 if variant == 'b': 

5865 return mstats_basic.kendalltau(x, y, method=method, use_ties=True, 

5866 alternative=alternative) 

5867 else: 

5868 message = ("nan_policy='omit' is currently compatible only with " 

5869 "variant='b'.") 

5870 raise ValueError(message) 

5871 

5872 def count_rank_tie(ranks): 

5873 cnt = np.bincount(ranks).astype('int64', copy=False) 

5874 cnt = cnt[cnt > 1] 

5875 # Python ints to avoid overflow down the line 

5876 return (int((cnt * (cnt - 1) // 2).sum()), 

5877 int((cnt * (cnt - 1.) * (cnt - 2)).sum()), 

5878 int((cnt * (cnt - 1.) * (2*cnt + 5)).sum())) 

5879 

5880 size = x.size 

5881 perm = np.argsort(y) # sort on y and convert y to dense ranks 

5882 x, y = x[perm], y[perm] 

5883 y = np.r_[True, y[1:] != y[:-1]].cumsum(dtype=np.intp) 

5884 

5885 # stable sort on x and convert x to dense ranks 

5886 perm = np.argsort(x, kind='mergesort') 

5887 x, y = x[perm], y[perm] 

5888 x = np.r_[True, x[1:] != x[:-1]].cumsum(dtype=np.intp) 

5889 

5890 dis = _kendall_dis(x, y) # discordant pairs 

5891 

5892 obs = np.r_[True, (x[1:] != x[:-1]) | (y[1:] != y[:-1]), True] 

5893 cnt = np.diff(np.nonzero(obs)[0]).astype('int64', copy=False) 

5894 

5895 ntie = int((cnt * (cnt - 1) // 2).sum()) # joint ties 

5896 xtie, x0, x1 = count_rank_tie(x) # ties in x, stats 

5897 ytie, y0, y1 = count_rank_tie(y) # ties in y, stats 

5898 

5899 tot = (size * (size - 1)) // 2 

5900 

5901 if xtie == tot or ytie == tot: 

5902 res = SignificanceResult(np.nan, np.nan) 

5903 res.correlation = np.nan 

5904 return res 

5905 

5906 # Note that tot = con + dis + (xtie - ntie) + (ytie - ntie) + ntie 

5907 # = con + dis + xtie + ytie - ntie 

5908 con_minus_dis = tot - xtie - ytie + ntie - 2 * dis 

5909 if variant == 'b': 

5910 tau = con_minus_dis / np.sqrt(tot - xtie) / np.sqrt(tot - ytie) 

5911 elif variant == 'c': 

5912 minclasses = min(len(set(x)), len(set(y))) 

5913 tau = 2*con_minus_dis / (size**2 * (minclasses-1)/minclasses) 

5914 else: 

5915 raise ValueError(f"Unknown variant of the method chosen: {variant}. " 

5916 "variant must be 'b' or 'c'.") 

5917 

5918 # Limit range to fix computational errors 

5919 tau = min(1., max(-1., tau)) 

5920 

5921 # The p-value calculation is the same for all variants since the p-value 

5922 # depends only on con_minus_dis. 

5923 if method == 'exact' and (xtie != 0 or ytie != 0): 

5924 raise ValueError("Ties found, exact method cannot be used.") 

5925 

5926 if method == 'auto': 

5927 if (xtie == 0 and ytie == 0) and (size <= 33 or 

5928 min(dis, tot-dis) <= 1): 

5929 method = 'exact' 

5930 else: 

5931 method = 'asymptotic' 

5932 

5933 if xtie == 0 and ytie == 0 and method == 'exact': 

5934 pvalue = mstats_basic._kendall_p_exact(size, tot-dis, alternative) 

5935 elif method == 'asymptotic': 

5936 # con_minus_dis is approx normally distributed with this variance [3]_ 

5937 m = size * (size - 1.) 

5938 var = ((m * (2*size + 5) - x1 - y1) / 18 + 

5939 (2 * xtie * ytie) / m + x0 * y0 / (9 * m * (size - 2))) 

5940 z = con_minus_dis / np.sqrt(var) 

5941 _, pvalue = _normtest_finish(z, alternative) 

5942 else: 

5943 raise ValueError(f"Unknown method {method} specified. Use 'auto', " 

5944 "'exact' or 'asymptotic'.") 

5945 

5946 # create result object with alias for backward compatibility 

5947 res = SignificanceResult(tau, pvalue) 

5948 res.correlation = tau 

5949 return res 

5950 

5951 

5952def weightedtau(x, y, rank=True, weigher=None, additive=True): 

5953 r"""Compute a weighted version of Kendall's :math:`\tau`. 

5954 

5955 The weighted :math:`\tau` is a weighted version of Kendall's 

5956 :math:`\tau` in which exchanges of high weight are more influential than 

5957 exchanges of low weight. The default parameters compute the additive 

5958 hyperbolic version of the index, :math:`\tau_\mathrm h`, which has 

5959 been shown to provide the best balance between important and 

5960 unimportant elements [1]_. 

5961 

5962 The weighting is defined by means of a rank array, which assigns a 

5963 nonnegative rank to each element (higher importance ranks being 

5964 associated with smaller values, e.g., 0 is the highest possible rank), 

5965 and a weigher function, which assigns a weight based on the rank to 

5966 each element. The weight of an exchange is then the sum or the product 

5967 of the weights of the ranks of the exchanged elements. The default 

5968 parameters compute :math:`\tau_\mathrm h`: an exchange between 

5969 elements with rank :math:`r` and :math:`s` (starting from zero) has 

5970 weight :math:`1/(r+1) + 1/(s+1)`. 

5971 

5972 Specifying a rank array is meaningful only if you have in mind an 

5973 external criterion of importance. If, as it usually happens, you do 

5974 not have in mind a specific rank, the weighted :math:`\tau` is 

5975 defined by averaging the values obtained using the decreasing 

5976 lexicographical rank by (`x`, `y`) and by (`y`, `x`). This is the 

5977 behavior with default parameters. Note that the convention used 

5978 here for ranking (lower values imply higher importance) is opposite 

5979 to that used by other SciPy statistical functions. 

5980 

5981 Parameters 

5982 ---------- 

5983 x, y : array_like 

5984 Arrays of scores, of the same shape. If arrays are not 1-D, they will 

5985 be flattened to 1-D. 

5986 rank : array_like of ints or bool, optional 

5987 A nonnegative rank assigned to each element. If it is None, the 

5988 decreasing lexicographical rank by (`x`, `y`) will be used: elements of 

5989 higher rank will be those with larger `x`-values, using `y`-values to 

5990 break ties (in particular, swapping `x` and `y` will give a different 

5991 result). If it is False, the element indices will be used 

5992 directly as ranks. The default is True, in which case this 

5993 function returns the average of the values obtained using the 

5994 decreasing lexicographical rank by (`x`, `y`) and by (`y`, `x`). 

5995 weigher : callable, optional 

5996 The weigher function. Must map nonnegative integers (zero 

5997 representing the most important element) to a nonnegative weight. 

5998 The default, None, provides hyperbolic weighing, that is, 

5999 rank :math:`r` is mapped to weight :math:`1/(r+1)`. 

6000 additive : bool, optional 

6001 If True, the weight of an exchange is computed by adding the 

6002 weights of the ranks of the exchanged elements; otherwise, the weights 

6003 are multiplied. The default is True. 

6004 

6005 Returns 

6006 ------- 

6007 res: SignificanceResult 

6008 An object containing attributes: 

6009 

6010 statistic : float 

6011 The weighted :math:`\tau` correlation index. 

6012 pvalue : float 

6013 Presently ``np.nan``, as the null distribution of the statistic is 

6014 unknown (even in the additive hyperbolic case). 

6015 

6016 See Also 

6017 -------- 

6018 kendalltau : Calculates Kendall's tau. 

6019 spearmanr : Calculates a Spearman rank-order correlation coefficient. 

6020 theilslopes : Computes the Theil-Sen estimator for a set of points (x, y). 

6021 

6022 Notes 

6023 ----- 

6024 This function uses an :math:`O(n \log n)`, mergesort-based algorithm 

6025 [1]_ that is a weighted extension of Knight's algorithm for Kendall's 

6026 :math:`\tau` [2]_. It can compute Shieh's weighted :math:`\tau` [3]_ 

6027 between rankings without ties (i.e., permutations) by setting 

6028 `additive` and `rank` to False, as the definition given in [1]_ is a 

6029 generalization of Shieh's. 

6030 

6031 NaNs are considered the smallest possible score. 

6032 

6033 .. versionadded:: 0.19.0 

6034 

6035 References 

6036 ---------- 

6037 .. [1] Sebastiano Vigna, "A weighted correlation index for rankings with 

6038 ties", Proceedings of the 24th international conference on World 

6039 Wide Web, pp. 1166-1176, ACM, 2015. 

6040 .. [2] W.R. Knight, "A Computer Method for Calculating Kendall's Tau with 

6041 Ungrouped Data", Journal of the American Statistical Association, 

6042 Vol. 61, No. 314, Part 1, pp. 436-439, 1966. 

6043 .. [3] Grace S. Shieh. "A weighted Kendall's tau statistic", Statistics & 

6044 Probability Letters, Vol. 39, No. 1, pp. 17-24, 1998. 

6045 

6046 Examples 

6047 -------- 

6048 >>> import numpy as np 

6049 >>> from scipy import stats 

6050 >>> x = [12, 2, 1, 12, 2] 

6051 >>> y = [1, 4, 7, 1, 0] 

6052 >>> res = stats.weightedtau(x, y) 

6053 >>> res.statistic 

6054 -0.56694968153682723 

6055 >>> res.pvalue 

6056 nan 

6057 >>> res = stats.weightedtau(x, y, additive=False) 

6058 >>> res.statistic 

6059 -0.62205716951801038 

6060 

6061 NaNs are considered the smallest possible score: 

6062 

6063 >>> x = [12, 2, 1, 12, 2] 

6064 >>> y = [1, 4, 7, 1, np.nan] 

6065 >>> res = stats.weightedtau(x, y) 

6066 >>> res.statistic 

6067 -0.56694968153682723 

6068 

6069 This is exactly Kendall's tau: 

6070 

6071 >>> x = [12, 2, 1, 12, 2] 

6072 >>> y = [1, 4, 7, 1, 0] 

6073 >>> res = stats.weightedtau(x, y, weigher=lambda x: 1) 

6074 >>> res.statistic 

6075 -0.47140452079103173 

6076 

6077 >>> x = [12, 2, 1, 12, 2] 

6078 >>> y = [1, 4, 7, 1, 0] 

6079 >>> stats.weightedtau(x, y, rank=None) 

6080 SignificanceResult(statistic=-0.4157652301037516, pvalue=nan) 

6081 >>> stats.weightedtau(y, x, rank=None) 

6082 SignificanceResult(statistic=-0.7181341329699028, pvalue=nan) 

6083 

6084 """ 

6085 x = np.asarray(x).ravel() 

6086 y = np.asarray(y).ravel() 

6087 

6088 if x.size != y.size: 

6089 raise ValueError("All inputs to `weightedtau` must be " 

6090 "of the same size, " 

6091 "found x-size {} and y-size {}".format(x.size, y.size)) 

6092 if not x.size: 

6093 # Return NaN if arrays are empty 

6094 res = SignificanceResult(np.nan, np.nan) 

6095 res.correlation = np.nan 

6096 return res 

6097 

6098 # If there are NaNs we apply _toint64() 

6099 if np.isnan(np.sum(x)): 

6100 x = _toint64(x) 

6101 if np.isnan(np.sum(y)): 

6102 y = _toint64(y) 

6103 

6104 # Reduce to ranks unsupported types 

6105 if x.dtype != y.dtype: 

6106 if x.dtype != np.int64: 

6107 x = _toint64(x) 

6108 if y.dtype != np.int64: 

6109 y = _toint64(y) 

6110 else: 

6111 if x.dtype not in (np.int32, np.int64, np.float32, np.float64): 

6112 x = _toint64(x) 

6113 y = _toint64(y) 

6114 

6115 if rank is True: 

6116 tau = ( 

6117 _weightedrankedtau(x, y, None, weigher, additive) + 

6118 _weightedrankedtau(y, x, None, weigher, additive) 

6119 ) / 2 

6120 res = SignificanceResult(tau, np.nan) 

6121 res.correlation = tau 

6122 return res 

6123 

6124 if rank is False: 

6125 rank = np.arange(x.size, dtype=np.intp) 

6126 elif rank is not None: 

6127 rank = np.asarray(rank).ravel() 

6128 if rank.size != x.size: 

6129 raise ValueError( 

6130 "All inputs to `weightedtau` must be of the same size, " 

6131 "found x-size {} and rank-size {}".format(x.size, rank.size) 

6132 ) 

6133 

6134 tau = _weightedrankedtau(x, y, rank, weigher, additive) 

6135 res = SignificanceResult(tau, np.nan) 

6136 res.correlation = tau 

6137 return res 

6138 

6139 

6140# FROM MGCPY: https://github.com/neurodata/mgcpy 

6141 

6142 

6143class _ParallelP: 

6144 """Helper function to calculate parallel p-value.""" 

6145 

6146 def __init__(self, x, y, random_states): 

6147 self.x = x 

6148 self.y = y 

6149 self.random_states = random_states 

6150 

6151 def __call__(self, index): 

6152 order = self.random_states[index].permutation(self.y.shape[0]) 

6153 permy = self.y[order][:, order] 

6154 

6155 # calculate permuted stats, store in null distribution 

6156 perm_stat = _mgc_stat(self.x, permy)[0] 

6157 

6158 return perm_stat 

6159 

6160 

6161def _perm_test(x, y, stat, reps=1000, workers=-1, random_state=None): 

6162 r"""Helper function that calculates the p-value. See below for uses. 

6163 

6164 Parameters 

6165 ---------- 

6166 x, y : ndarray 

6167 `x` and `y` have shapes `(n, p)` and `(n, q)`. 

6168 stat : float 

6169 The sample test statistic. 

6170 reps : int, optional 

6171 The number of replications used to estimate the null when using the 

6172 permutation test. The default is 1000 replications. 

6173 workers : int or map-like callable, optional 

6174 If `workers` is an int the population is subdivided into `workers` 

6175 sections and evaluated in parallel (uses 

6176 `multiprocessing.Pool <multiprocessing>`). Supply `-1` to use all cores 

6177 available to the Process. Alternatively supply a map-like callable, 

6178 such as `multiprocessing.Pool.map` for evaluating the population in 

6179 parallel. This evaluation is carried out as `workers(func, iterable)`. 

6180 Requires that `func` be pickleable. 

6181 random_state : {None, int, `numpy.random.Generator`, 

6182 `numpy.random.RandomState`}, optional 

6183 

6184 If `seed` is None (or `np.random`), the `numpy.random.RandomState` 

6185 singleton is used. 

6186 If `seed` is an int, a new ``RandomState`` instance is used, 

6187 seeded with `seed`. 

6188 If `seed` is already a ``Generator`` or ``RandomState`` instance then 

6189 that instance is used. 

6190 

6191 Returns 

6192 ------- 

6193 pvalue : float 

6194 The sample test p-value. 

6195 null_dist : list 

6196 The approximated null distribution. 

6197 

6198 """ 

6199 # generate seeds for each rep (change to new parallel random number 

6200 # capabilities in numpy >= 1.17+) 

6201 random_state = check_random_state(random_state) 

6202 random_states = [np.random.RandomState(rng_integers(random_state, 1 << 32, 

6203 size=4, dtype=np.uint32)) for _ in range(reps)] 

6204 

6205 # parallelizes with specified workers over number of reps and set seeds 

6206 parallelp = _ParallelP(x=x, y=y, random_states=random_states) 

6207 with MapWrapper(workers) as mapwrapper: 

6208 null_dist = np.array(list(mapwrapper(parallelp, range(reps)))) 

6209 

6210 # calculate p-value and significant permutation map through list 

6211 pvalue = (1 + (null_dist >= stat).sum()) / (1 + reps) 

6212 

6213 return pvalue, null_dist 

6214 

6215 

6216def _euclidean_dist(x): 

6217 return cdist(x, x) 

6218 

6219 

6220MGCResult = _make_tuple_bunch('MGCResult', 

6221 ['statistic', 'pvalue', 'mgc_dict'], []) 

6222 

6223 

6224def multiscale_graphcorr(x, y, compute_distance=_euclidean_dist, reps=1000, 

6225 workers=1, is_twosamp=False, random_state=None): 

6226 r"""Computes the Multiscale Graph Correlation (MGC) test statistic. 

6227 

6228 Specifically, for each point, MGC finds the :math:`k`-nearest neighbors for 

6229 one property (e.g. cloud density), and the :math:`l`-nearest neighbors for 

6230 the other property (e.g. grass wetness) [1]_. This pair :math:`(k, l)` is 

6231 called the "scale". A priori, however, it is not know which scales will be 

6232 most informative. So, MGC computes all distance pairs, and then efficiently 

6233 computes the distance correlations for all scales. The local correlations 

6234 illustrate which scales are relatively informative about the relationship. 

6235 The key, therefore, to successfully discover and decipher relationships 

6236 between disparate data modalities is to adaptively determine which scales 

6237 are the most informative, and the geometric implication for the most 

6238 informative scales. Doing so not only provides an estimate of whether the 

6239 modalities are related, but also provides insight into how the 

6240 determination was made. This is especially important in high-dimensional 

6241 data, where simple visualizations do not reveal relationships to the 

6242 unaided human eye. Characterizations of this implementation in particular 

6243 have been derived from and benchmarked within in [2]_. 

6244 

6245 Parameters 

6246 ---------- 

6247 x, y : ndarray 

6248 If ``x`` and ``y`` have shapes ``(n, p)`` and ``(n, q)`` where `n` is 

6249 the number of samples and `p` and `q` are the number of dimensions, 

6250 then the MGC independence test will be run. Alternatively, ``x`` and 

6251 ``y`` can have shapes ``(n, n)`` if they are distance or similarity 

6252 matrices, and ``compute_distance`` must be sent to ``None``. If ``x`` 

6253 and ``y`` have shapes ``(n, p)`` and ``(m, p)``, an unpaired 

6254 two-sample MGC test will be run. 

6255 compute_distance : callable, optional 

6256 A function that computes the distance or similarity among the samples 

6257 within each data matrix. Set to ``None`` if ``x`` and ``y`` are 

6258 already distance matrices. The default uses the euclidean norm metric. 

6259 If you are calling a custom function, either create the distance 

6260 matrix before-hand or create a function of the form 

6261 ``compute_distance(x)`` where `x` is the data matrix for which 

6262 pairwise distances are calculated. 

6263 reps : int, optional 

6264 The number of replications used to estimate the null when using the 

6265 permutation test. The default is ``1000``. 

6266 workers : int or map-like callable, optional 

6267 If ``workers`` is an int the population is subdivided into ``workers`` 

6268 sections and evaluated in parallel (uses ``multiprocessing.Pool 

6269 <multiprocessing>``). Supply ``-1`` to use all cores available to the 

6270 Process. Alternatively supply a map-like callable, such as 

6271 ``multiprocessing.Pool.map`` for evaluating the p-value in parallel. 

6272 This evaluation is carried out as ``workers(func, iterable)``. 

6273 Requires that `func` be pickleable. The default is ``1``. 

6274 is_twosamp : bool, optional 

6275 If `True`, a two sample test will be run. If ``x`` and ``y`` have 

6276 shapes ``(n, p)`` and ``(m, p)``, this optional will be overridden and 

6277 set to ``True``. Set to ``True`` if ``x`` and ``y`` both have shapes 

6278 ``(n, p)`` and a two sample test is desired. The default is ``False``. 

6279 Note that this will not run if inputs are distance matrices. 

6280 random_state : {None, int, `numpy.random.Generator`, 

6281 `numpy.random.RandomState`}, optional 

6282 

6283 If `seed` is None (or `np.random`), the `numpy.random.RandomState` 

6284 singleton is used. 

6285 If `seed` is an int, a new ``RandomState`` instance is used, 

6286 seeded with `seed`. 

6287 If `seed` is already a ``Generator`` or ``RandomState`` instance then 

6288 that instance is used. 

6289 

6290 Returns 

6291 ------- 

6292 res : MGCResult 

6293 An object containing attributes: 

6294 

6295 statistic : float 

6296 The sample MGC test statistic within `[-1, 1]`. 

6297 pvalue : float 

6298 The p-value obtained via permutation. 

6299 mgc_dict : dict 

6300 Contains additional useful results: 

6301 

6302 - mgc_map : ndarray 

6303 A 2D representation of the latent geometry of the 

6304 relationship. 

6305 - opt_scale : (int, int) 

6306 The estimated optimal scale as a `(x, y)` pair. 

6307 - null_dist : list 

6308 The null distribution derived from the permuted matrices. 

6309 

6310 See Also 

6311 -------- 

6312 pearsonr : Pearson correlation coefficient and p-value for testing 

6313 non-correlation. 

6314 kendalltau : Calculates Kendall's tau. 

6315 spearmanr : Calculates a Spearman rank-order correlation coefficient. 

6316 

6317 Notes 

6318 ----- 

6319 A description of the process of MGC and applications on neuroscience data 

6320 can be found in [1]_. It is performed using the following steps: 

6321 

6322 #. Two distance matrices :math:`D^X` and :math:`D^Y` are computed and 

6323 modified to be mean zero columnwise. This results in two 

6324 :math:`n \times n` distance matrices :math:`A` and :math:`B` (the 

6325 centering and unbiased modification) [3]_. 

6326 

6327 #. For all values :math:`k` and :math:`l` from :math:`1, ..., n`, 

6328 

6329 * The :math:`k`-nearest neighbor and :math:`l`-nearest neighbor graphs 

6330 are calculated for each property. Here, :math:`G_k (i, j)` indicates 

6331 the :math:`k`-smallest values of the :math:`i`-th row of :math:`A` 

6332 and :math:`H_l (i, j)` indicates the :math:`l` smallested values of 

6333 the :math:`i`-th row of :math:`B` 

6334 

6335 * Let :math:`\circ` denotes the entry-wise matrix product, then local 

6336 correlations are summed and normalized using the following statistic: 

6337 

6338 .. math:: 

6339 

6340 c^{kl} = \frac{\sum_{ij} A G_k B H_l} 

6341 {\sqrt{\sum_{ij} A^2 G_k \times \sum_{ij} B^2 H_l}} 

6342 

6343 #. The MGC test statistic is the smoothed optimal local correlation of 

6344 :math:`\{ c^{kl} \}`. Denote the smoothing operation as :math:`R(\cdot)` 

6345 (which essentially set all isolated large correlations) as 0 and 

6346 connected large correlations the same as before, see [3]_.) MGC is, 

6347 

6348 .. math:: 

6349 

6350 MGC_n (x, y) = \max_{(k, l)} R \left(c^{kl} \left( x_n, y_n \right) 

6351 \right) 

6352 

6353 The test statistic returns a value between :math:`(-1, 1)` since it is 

6354 normalized. 

6355 

6356 The p-value returned is calculated using a permutation test. This process 

6357 is completed by first randomly permuting :math:`y` to estimate the null 

6358 distribution and then calculating the probability of observing a test 

6359 statistic, under the null, at least as extreme as the observed test 

6360 statistic. 

6361 

6362 MGC requires at least 5 samples to run with reliable results. It can also 

6363 handle high-dimensional data sets. 

6364 In addition, by manipulating the input data matrices, the two-sample 

6365 testing problem can be reduced to the independence testing problem [4]_. 

6366 Given sample data :math:`U` and :math:`V` of sizes :math:`p \times n` 

6367 :math:`p \times m`, data matrix :math:`X` and :math:`Y` can be created as 

6368 follows: 

6369 

6370 .. math:: 

6371 

6372 X = [U | V] \in \mathcal{R}^{p \times (n + m)} 

6373 Y = [0_{1 \times n} | 1_{1 \times m}] \in \mathcal{R}^{(n + m)} 

6374 

6375 Then, the MGC statistic can be calculated as normal. This methodology can 

6376 be extended to similar tests such as distance correlation [4]_. 

6377 

6378 .. versionadded:: 1.4.0 

6379 

6380 References 

6381 ---------- 

6382 .. [1] Vogelstein, J. T., Bridgeford, E. W., Wang, Q., Priebe, C. E., 

6383 Maggioni, M., & Shen, C. (2019). Discovering and deciphering 

6384 relationships across disparate data modalities. ELife. 

6385 .. [2] Panda, S., Palaniappan, S., Xiong, J., Swaminathan, A., 

6386 Ramachandran, S., Bridgeford, E. W., ... Vogelstein, J. T. (2019). 

6387 mgcpy: A Comprehensive High Dimensional Independence Testing Python 

6388 Package. :arXiv:`1907.02088` 

6389 .. [3] Shen, C., Priebe, C.E., & Vogelstein, J. T. (2019). From distance 

6390 correlation to multiscale graph correlation. Journal of the American 

6391 Statistical Association. 

6392 .. [4] Shen, C. & Vogelstein, J. T. (2018). The Exact Equivalence of 

6393 Distance and Kernel Methods for Hypothesis Testing. 

6394 :arXiv:`1806.05514` 

6395 

6396 Examples 

6397 -------- 

6398 >>> import numpy as np 

6399 >>> from scipy.stats import multiscale_graphcorr 

6400 >>> x = np.arange(100) 

6401 >>> y = x 

6402 >>> res = multiscale_graphcorr(x, y) 

6403 >>> res.statistic, res.pvalue 

6404 (1.0, 0.001) 

6405 

6406 To run an unpaired two-sample test, 

6407 

6408 >>> x = np.arange(100) 

6409 >>> y = np.arange(79) 

6410 >>> res = multiscale_graphcorr(x, y) 

6411 >>> res.statistic, res.pvalue # doctest: +SKIP 

6412 (0.033258146255703246, 0.023) 

6413 

6414 or, if shape of the inputs are the same, 

6415 

6416 >>> x = np.arange(100) 

6417 >>> y = x 

6418 >>> res = multiscale_graphcorr(x, y, is_twosamp=True) 

6419 >>> res.statistic, res.pvalue # doctest: +SKIP 

6420 (-0.008021809890200488, 1.0) 

6421 

6422 """ 

6423 if not isinstance(x, np.ndarray) or not isinstance(y, np.ndarray): 

6424 raise ValueError("x and y must be ndarrays") 

6425 

6426 # convert arrays of type (n,) to (n, 1) 

6427 if x.ndim == 1: 

6428 x = x[:, np.newaxis] 

6429 elif x.ndim != 2: 

6430 raise ValueError("Expected a 2-D array `x`, found shape " 

6431 "{}".format(x.shape)) 

6432 if y.ndim == 1: 

6433 y = y[:, np.newaxis] 

6434 elif y.ndim != 2: 

6435 raise ValueError("Expected a 2-D array `y`, found shape " 

6436 "{}".format(y.shape)) 

6437 

6438 nx, px = x.shape 

6439 ny, py = y.shape 

6440 

6441 # check for NaNs 

6442 _contains_nan(x, nan_policy='raise') 

6443 _contains_nan(y, nan_policy='raise') 

6444 

6445 # check for positive or negative infinity and raise error 

6446 if np.sum(np.isinf(x)) > 0 or np.sum(np.isinf(y)) > 0: 

6447 raise ValueError("Inputs contain infinities") 

6448 

6449 if nx != ny: 

6450 if px == py: 

6451 # reshape x and y for two sample testing 

6452 is_twosamp = True 

6453 else: 

6454 raise ValueError("Shape mismatch, x and y must have shape [n, p] " 

6455 "and [n, q] or have shape [n, p] and [m, p].") 

6456 

6457 if nx < 5 or ny < 5: 

6458 raise ValueError("MGC requires at least 5 samples to give reasonable " 

6459 "results.") 

6460 

6461 # convert x and y to float 

6462 x = x.astype(np.float64) 

6463 y = y.astype(np.float64) 

6464 

6465 # check if compute_distance_matrix if a callable() 

6466 if not callable(compute_distance) and compute_distance is not None: 

6467 raise ValueError("Compute_distance must be a function.") 

6468 

6469 # check if number of reps exists, integer, or > 0 (if under 1000 raises 

6470 # warning) 

6471 if not isinstance(reps, int) or reps < 0: 

6472 raise ValueError("Number of reps must be an integer greater than 0.") 

6473 elif reps < 1000: 

6474 msg = ("The number of replications is low (under 1000), and p-value " 

6475 "calculations may be unreliable. Use the p-value result, with " 

6476 "caution!") 

6477 warnings.warn(msg, RuntimeWarning) 

6478 

6479 if is_twosamp: 

6480 if compute_distance is None: 

6481 raise ValueError("Cannot run if inputs are distance matrices") 

6482 x, y = _two_sample_transform(x, y) 

6483 

6484 if compute_distance is not None: 

6485 # compute distance matrices for x and y 

6486 x = compute_distance(x) 

6487 y = compute_distance(y) 

6488 

6489 # calculate MGC stat 

6490 stat, stat_dict = _mgc_stat(x, y) 

6491 stat_mgc_map = stat_dict["stat_mgc_map"] 

6492 opt_scale = stat_dict["opt_scale"] 

6493 

6494 # calculate permutation MGC p-value 

6495 pvalue, null_dist = _perm_test(x, y, stat, reps=reps, workers=workers, 

6496 random_state=random_state) 

6497 

6498 # save all stats (other than stat/p-value) in dictionary 

6499 mgc_dict = {"mgc_map": stat_mgc_map, 

6500 "opt_scale": opt_scale, 

6501 "null_dist": null_dist} 

6502 

6503 # create result object with alias for backward compatibility 

6504 res = MGCResult(stat, pvalue, mgc_dict) 

6505 res.stat = stat 

6506 return res 

6507 

6508 

6509def _mgc_stat(distx, disty): 

6510 r"""Helper function that calculates the MGC stat. See above for use. 

6511 

6512 Parameters 

6513 ---------- 

6514 distx, disty : ndarray 

6515 `distx` and `disty` have shapes `(n, p)` and `(n, q)` or 

6516 `(n, n)` and `(n, n)` 

6517 if distance matrices. 

6518 

6519 Returns 

6520 ------- 

6521 stat : float 

6522 The sample MGC test statistic within `[-1, 1]`. 

6523 stat_dict : dict 

6524 Contains additional useful additional returns containing the following 

6525 keys: 

6526 

6527 - stat_mgc_map : ndarray 

6528 MGC-map of the statistics. 

6529 - opt_scale : (float, float) 

6530 The estimated optimal scale as a `(x, y)` pair. 

6531 

6532 """ 

6533 # calculate MGC map and optimal scale 

6534 stat_mgc_map = _local_correlations(distx, disty, global_corr='mgc') 

6535 

6536 n, m = stat_mgc_map.shape 

6537 if m == 1 or n == 1: 

6538 # the global scale at is the statistic calculated at maximial nearest 

6539 # neighbors. There is not enough local scale to search over, so 

6540 # default to global scale 

6541 stat = stat_mgc_map[m - 1][n - 1] 

6542 opt_scale = m * n 

6543 else: 

6544 samp_size = len(distx) - 1 

6545 

6546 # threshold to find connected region of significant local correlations 

6547 sig_connect = _threshold_mgc_map(stat_mgc_map, samp_size) 

6548 

6549 # maximum within the significant region 

6550 stat, opt_scale = _smooth_mgc_map(sig_connect, stat_mgc_map) 

6551 

6552 stat_dict = {"stat_mgc_map": stat_mgc_map, 

6553 "opt_scale": opt_scale} 

6554 

6555 return stat, stat_dict 

6556 

6557 

6558def _threshold_mgc_map(stat_mgc_map, samp_size): 

6559 r""" 

6560 Finds a connected region of significance in the MGC-map by thresholding. 

6561 

6562 Parameters 

6563 ---------- 

6564 stat_mgc_map : ndarray 

6565 All local correlations within `[-1,1]`. 

6566 samp_size : int 

6567 The sample size of original data. 

6568 

6569 Returns 

6570 ------- 

6571 sig_connect : ndarray 

6572 A binary matrix with 1's indicating the significant region. 

6573 

6574 """ 

6575 m, n = stat_mgc_map.shape 

6576 

6577 # 0.02 is simply an empirical threshold, this can be set to 0.01 or 0.05 

6578 # with varying levels of performance. Threshold is based on a beta 

6579 # approximation. 

6580 per_sig = 1 - (0.02 / samp_size) # Percentile to consider as significant 

6581 threshold = samp_size * (samp_size - 3)/4 - 1/2 # Beta approximation 

6582 threshold = distributions.beta.ppf(per_sig, threshold, threshold) * 2 - 1 

6583 

6584 # the global scale at is the statistic calculated at maximial nearest 

6585 # neighbors. Threshold is the maximum on the global and local scales 

6586 threshold = max(threshold, stat_mgc_map[m - 1][n - 1]) 

6587 

6588 # find the largest connected component of significant correlations 

6589 sig_connect = stat_mgc_map > threshold 

6590 if np.sum(sig_connect) > 0: 

6591 sig_connect, _ = _measurements.label(sig_connect) 

6592 _, label_counts = np.unique(sig_connect, return_counts=True) 

6593 

6594 # skip the first element in label_counts, as it is count(zeros) 

6595 max_label = np.argmax(label_counts[1:]) + 1 

6596 sig_connect = sig_connect == max_label 

6597 else: 

6598 sig_connect = np.array([[False]]) 

6599 

6600 return sig_connect 

6601 

6602 

6603def _smooth_mgc_map(sig_connect, stat_mgc_map): 

6604 """Finds the smoothed maximal within the significant region R. 

6605 

6606 If area of R is too small it returns the last local correlation. Otherwise, 

6607 returns the maximum within significant_connected_region. 

6608 

6609 Parameters 

6610 ---------- 

6611 sig_connect : ndarray 

6612 A binary matrix with 1's indicating the significant region. 

6613 stat_mgc_map : ndarray 

6614 All local correlations within `[-1, 1]`. 

6615 

6616 Returns 

6617 ------- 

6618 stat : float 

6619 The sample MGC statistic within `[-1, 1]`. 

6620 opt_scale: (float, float) 

6621 The estimated optimal scale as an `(x, y)` pair. 

6622 

6623 """ 

6624 m, n = stat_mgc_map.shape 

6625 

6626 # the global scale at is the statistic calculated at maximial nearest 

6627 # neighbors. By default, statistic and optimal scale are global. 

6628 stat = stat_mgc_map[m - 1][n - 1] 

6629 opt_scale = [m, n] 

6630 

6631 if np.linalg.norm(sig_connect) != 0: 

6632 # proceed only when the connected region's area is sufficiently large 

6633 # 0.02 is simply an empirical threshold, this can be set to 0.01 or 0.05 

6634 # with varying levels of performance 

6635 if np.sum(sig_connect) >= np.ceil(0.02 * max(m, n)) * min(m, n): 

6636 max_corr = max(stat_mgc_map[sig_connect]) 

6637 

6638 # find all scales within significant_connected_region that maximize 

6639 # the local correlation 

6640 max_corr_index = np.where((stat_mgc_map >= max_corr) & sig_connect) 

6641 

6642 if max_corr >= stat: 

6643 stat = max_corr 

6644 

6645 k, l = max_corr_index 

6646 one_d_indices = k * n + l # 2D to 1D indexing 

6647 k = np.max(one_d_indices) // n 

6648 l = np.max(one_d_indices) % n 

6649 opt_scale = [k+1, l+1] # adding 1s to match R indexing 

6650 

6651 return stat, opt_scale 

6652 

6653 

6654def _two_sample_transform(u, v): 

6655 """Helper function that concatenates x and y for two sample MGC stat. 

6656 

6657 See above for use. 

6658 

6659 Parameters 

6660 ---------- 

6661 u, v : ndarray 

6662 `u` and `v` have shapes `(n, p)` and `(m, p)`. 

6663 

6664 Returns 

6665 ------- 

6666 x : ndarray 

6667 Concatenate `u` and `v` along the `axis = 0`. `x` thus has shape 

6668 `(2n, p)`. 

6669 y : ndarray 

6670 Label matrix for `x` where 0 refers to samples that comes from `u` and 

6671 1 refers to samples that come from `v`. `y` thus has shape `(2n, 1)`. 

6672 

6673 """ 

6674 nx = u.shape[0] 

6675 ny = v.shape[0] 

6676 x = np.concatenate([u, v], axis=0) 

6677 y = np.concatenate([np.zeros(nx), np.ones(ny)], axis=0).reshape(-1, 1) 

6678 return x, y 

6679 

6680 

6681##################################### 

6682# INFERENTIAL STATISTICS # 

6683##################################### 

6684 

6685TtestResultBase = _make_tuple_bunch('TtestResultBase', 

6686 ['statistic', 'pvalue'], ['df']) 

6687 

6688 

6689class TtestResult(TtestResultBase): 

6690 """ 

6691 Result of a t-test. 

6692 

6693 See the documentation of the particular t-test function for more 

6694 information about the definition of the statistic and meaning of 

6695 the confidence interval. 

6696 

6697 Attributes 

6698 ---------- 

6699 statistic : float or array 

6700 The t-statistic of the sample. 

6701 pvalue : float or array 

6702 The p-value associated with the given alternative. 

6703 df : float or array 

6704 The number of degrees of freedom used in calculation of the 

6705 t-statistic; this is one less than the size of the sample 

6706 (``a.shape[axis]-1`` if there are no masked elements or omitted NaNs). 

6707 

6708 Methods 

6709 ------- 

6710 confidence_interval 

6711 Computes a confidence interval around the population statistic 

6712 for the given confidence level. 

6713 The confidence interval is returned in a ``namedtuple`` with 

6714 fields `low` and `high`. 

6715 

6716 """ 

6717 

6718 def __init__(self, statistic, pvalue, df, # public 

6719 alternative, standard_error, estimate): # private 

6720 super().__init__(statistic, pvalue, df=df) 

6721 self._alternative = alternative 

6722 self._standard_error = standard_error # denominator of t-statistic 

6723 self._estimate = estimate # point estimate of sample mean 

6724 

6725 def confidence_interval(self, confidence_level=0.95): 

6726 """ 

6727 Parameters 

6728 ---------- 

6729 confidence_level : float 

6730 The confidence level for the calculation of the population mean 

6731 confidence interval. Default is 0.95. 

6732 

6733 Returns 

6734 ------- 

6735 ci : namedtuple 

6736 The confidence interval is returned in a ``namedtuple`` with 

6737 fields `low` and `high`. 

6738 

6739 """ 

6740 low, high = _t_confidence_interval(self.df, self.statistic, 

6741 confidence_level, self._alternative) 

6742 low = low * self._standard_error + self._estimate 

6743 high = high * self._standard_error + self._estimate 

6744 return ConfidenceInterval(low=low, high=high) 

6745 

6746 

6747def pack_TtestResult(statistic, pvalue, df, alternative, standard_error, 

6748 estimate): 

6749 # this could be any number of dimensions (including 0d), but there is 

6750 # at most one unique non-NaN value 

6751 alternative = np.atleast_1d(alternative) # can't index 0D object 

6752 alternative = alternative[np.isfinite(alternative)] 

6753 alternative = alternative[0] if alternative.size else np.nan 

6754 return TtestResult(statistic, pvalue, df=df, alternative=alternative, 

6755 standard_error=standard_error, estimate=estimate) 

6756 

6757 

6758def unpack_TtestResult(res): 

6759 return (res.statistic, res.pvalue, res.df, res._alternative, 

6760 res._standard_error, res._estimate) 

6761 

6762 

6763@_axis_nan_policy_factory(pack_TtestResult, default_axis=0, n_samples=2, 

6764 result_to_tuple=unpack_TtestResult, n_outputs=6) 

6765def ttest_1samp(a, popmean, axis=0, nan_policy='propagate', 

6766 alternative="two-sided"): 

6767 """Calculate the T-test for the mean of ONE group of scores. 

6768 

6769 This is a test for the null hypothesis that the expected value 

6770 (mean) of a sample of independent observations `a` is equal to the given 

6771 population mean, `popmean`. 

6772 

6773 Parameters 

6774 ---------- 

6775 a : array_like 

6776 Sample observation. 

6777 popmean : float or array_like 

6778 Expected value in null hypothesis. If array_like, then its length along 

6779 `axis` must equal 1, and it must otherwise be broadcastable with `a`. 

6780 axis : int or None, optional 

6781 Axis along which to compute test; default is 0. If None, compute over 

6782 the whole array `a`. 

6783 nan_policy : {'propagate', 'raise', 'omit'}, optional 

6784 Defines how to handle when input contains nan. 

6785 The following options are available (default is 'propagate'): 

6786 

6787 * 'propagate': returns nan 

6788 * 'raise': throws an error 

6789 * 'omit': performs the calculations ignoring nan values 

6790 

6791 alternative : {'two-sided', 'less', 'greater'}, optional 

6792 Defines the alternative hypothesis. 

6793 The following options are available (default is 'two-sided'): 

6794 

6795 * 'two-sided': the mean of the underlying distribution of the sample 

6796 is different than the given population mean (`popmean`) 

6797 * 'less': the mean of the underlying distribution of the sample is 

6798 less than the given population mean (`popmean`) 

6799 * 'greater': the mean of the underlying distribution of the sample is 

6800 greater than the given population mean (`popmean`) 

6801 

6802 Returns 

6803 ------- 

6804 result : `~scipy.stats._result_classes.TtestResult` 

6805 An object with the following attributes: 

6806 

6807 statistic : float or array 

6808 The t-statistic. 

6809 pvalue : float or array 

6810 The p-value associated with the given alternative. 

6811 df : float or array 

6812 The number of degrees of freedom used in calculation of the 

6813 t-statistic; this is one less than the size of the sample 

6814 (``a.shape[axis]``). 

6815 

6816 .. versionadded:: 1.10.0 

6817 

6818 The object also has the following method: 

6819 

6820 confidence_interval(confidence_level=0.95) 

6821 Computes a confidence interval around the population 

6822 mean for the given confidence level. 

6823 The confidence interval is returned in a ``namedtuple`` with 

6824 fields `low` and `high`. 

6825 

6826 .. versionadded:: 1.10.0 

6827 

6828 Notes 

6829 ----- 

6830 The statistic is calculated as ``(np.mean(a) - popmean)/se``, where 

6831 ``se`` is the standard error. Therefore, the statistic will be positive 

6832 when the sample mean is greater than the population mean and negative when 

6833 the sample mean is less than the population mean. 

6834 

6835 Examples 

6836 -------- 

6837 Suppose we wish to test the null hypothesis that the mean of a population 

6838 is equal to 0.5. We choose a confidence level of 99%; that is, we will 

6839 reject the null hypothesis in favor of the alternative if the p-value is 

6840 less than 0.01. 

6841 

6842 When testing random variates from the standard uniform distribution, which 

6843 has a mean of 0.5, we expect the data to be consistent with the null 

6844 hypothesis most of the time. 

6845 

6846 >>> import numpy as np 

6847 >>> from scipy import stats 

6848 >>> rng = np.random.default_rng() 

6849 >>> rvs = stats.uniform.rvs(size=50, random_state=rng) 

6850 >>> stats.ttest_1samp(rvs, popmean=0.5) 

6851 TtestResult(statistic=2.456308468440, pvalue=0.017628209047638, df=49) 

6852 

6853 As expected, the p-value of 0.017 is not below our threshold of 0.01, so 

6854 we cannot reject the null hypothesis. 

6855 

6856 When testing data from the standard *normal* distribution, which has a mean 

6857 of 0, we would expect the null hypothesis to be rejected. 

6858 

6859 >>> rvs = stats.norm.rvs(size=50, random_state=rng) 

6860 >>> stats.ttest_1samp(rvs, popmean=0.5) 

6861 TtestResult(statistic=-7.433605518875, pvalue=1.416760157221e-09, df=49) 

6862 

6863 Indeed, the p-value is lower than our threshold of 0.01, so we reject the 

6864 null hypothesis in favor of the default "two-sided" alternative: the mean 

6865 of the population is *not* equal to 0.5. 

6866 

6867 However, suppose we were to test the null hypothesis against the 

6868 one-sided alternative that the mean of the population is *greater* than 

6869 0.5. Since the mean of the standard normal is less than 0.5, we would not 

6870 expect the null hypothesis to be rejected. 

6871 

6872 >>> stats.ttest_1samp(rvs, popmean=0.5, alternative='greater') 

6873 TtestResult(statistic=-7.433605518875, pvalue=0.99999999929, df=49) 

6874 

6875 Unsurprisingly, with a p-value greater than our threshold, we would not 

6876 reject the null hypothesis. 

6877 

6878 Note that when working with a confidence level of 99%, a true null 

6879 hypothesis will be rejected approximately 1% of the time. 

6880 

6881 >>> rvs = stats.uniform.rvs(size=(100, 50), random_state=rng) 

6882 >>> res = stats.ttest_1samp(rvs, popmean=0.5, axis=1) 

6883 >>> np.sum(res.pvalue < 0.01) 

6884 1 

6885 

6886 Indeed, even though all 100 samples above were drawn from the standard 

6887 uniform distribution, which *does* have a population mean of 0.5, we would 

6888 mistakenly reject the null hypothesis for one of them. 

6889 

6890 `ttest_1samp` can also compute a confidence interval around the population 

6891 mean. 

6892 

6893 >>> rvs = stats.norm.rvs(size=50, random_state=rng) 

6894 >>> res = stats.ttest_1samp(rvs, popmean=0) 

6895 >>> ci = res.confidence_interval(confidence_level=0.95) 

6896 >>> ci 

6897 ConfidenceInterval(low=-0.3193887540880017, high=0.2898583388980972) 

6898 

6899 The bounds of the 95% confidence interval are the 

6900 minimum and maximum values of the parameter `popmean` for which the 

6901 p-value of the test would be 0.05. 

6902 

6903 >>> res = stats.ttest_1samp(rvs, popmean=ci.low) 

6904 >>> np.testing.assert_allclose(res.pvalue, 0.05) 

6905 >>> res = stats.ttest_1samp(rvs, popmean=ci.high) 

6906 >>> np.testing.assert_allclose(res.pvalue, 0.05) 

6907 

6908 Under certain assumptions about the population from which a sample 

6909 is drawn, the confidence interval with confidence level 95% is expected 

6910 to contain the true population mean in 95% of sample replications. 

6911 

6912 >>> rvs = stats.norm.rvs(size=(50, 1000), loc=1, random_state=rng) 

6913 >>> res = stats.ttest_1samp(rvs, popmean=0) 

6914 >>> ci = res.confidence_interval() 

6915 >>> contains_pop_mean = (ci.low < 1) & (ci.high > 1) 

6916 >>> contains_pop_mean.sum() 

6917 953 

6918 

6919 """ 

6920 a, axis = _chk_asarray(a, axis) 

6921 

6922 n = a.shape[axis] 

6923 df = n - 1 

6924 

6925 mean = np.mean(a, axis) 

6926 try: 

6927 popmean = np.squeeze(popmean, axis=axis) 

6928 except ValueError as e: 

6929 raise ValueError("`popmean.shape[axis]` must equal 1.") from e 

6930 d = mean - popmean 

6931 v = _var(a, axis, ddof=1) 

6932 denom = np.sqrt(v / n) 

6933 

6934 with np.errstate(divide='ignore', invalid='ignore'): 

6935 t = np.divide(d, denom) 

6936 t, prob = _ttest_finish(df, t, alternative) 

6937 

6938 # when nan_policy='omit', `df` can be different for different axis-slices 

6939 df = np.broadcast_to(df, t.shape)[()] 

6940 # _axis_nan_policy decorator doesn't play well with strings 

6941 alternative_num = {"less": -1, "two-sided": 0, "greater": 1}[alternative] 

6942 return TtestResult(t, prob, df=df, alternative=alternative_num, 

6943 standard_error=denom, estimate=mean) 

6944 

6945 

6946def _t_confidence_interval(df, t, confidence_level, alternative): 

6947 # Input validation on `alternative` is already done 

6948 # We just need IV on confidence_level 

6949 if confidence_level < 0 or confidence_level > 1: 

6950 message = "`confidence_level` must be a number between 0 and 1." 

6951 raise ValueError(message) 

6952 

6953 if alternative < 0: # 'less' 

6954 p = confidence_level 

6955 low, high = np.broadcast_arrays(-np.inf, special.stdtrit(df, p)) 

6956 elif alternative > 0: # 'greater' 

6957 p = 1 - confidence_level 

6958 low, high = np.broadcast_arrays(special.stdtrit(df, p), np.inf) 

6959 elif alternative == 0: # 'two-sided' 

6960 tail_probability = (1 - confidence_level)/2 

6961 p = tail_probability, 1-tail_probability 

6962 # axis of p must be the zeroth and orthogonal to all the rest 

6963 p = np.reshape(p, [2] + [1]*np.asarray(df).ndim) 

6964 low, high = special.stdtrit(df, p) 

6965 else: # alternative is NaN when input is empty (see _axis_nan_policy) 

6966 p, nans = np.broadcast_arrays(t, np.nan) 

6967 low, high = nans, nans 

6968 

6969 return low[()], high[()] 

6970 

6971 

6972def _ttest_finish(df, t, alternative): 

6973 """Common code between all 3 t-test functions.""" 

6974 # We use ``stdtr`` directly here as it handles the case when ``nan`` 

6975 # values are present in the data and masked arrays are passed 

6976 # while ``t.cdf`` emits runtime warnings. This way ``_ttest_finish`` 

6977 # can be shared between the ``stats`` and ``mstats`` versions. 

6978 

6979 if alternative == 'less': 

6980 pval = special.stdtr(df, t) 

6981 elif alternative == 'greater': 

6982 pval = special.stdtr(df, -t) 

6983 elif alternative == 'two-sided': 

6984 pval = special.stdtr(df, -np.abs(t))*2 

6985 else: 

6986 raise ValueError("alternative must be " 

6987 "'less', 'greater' or 'two-sided'") 

6988 

6989 if t.ndim == 0: 

6990 t = t[()] 

6991 if pval.ndim == 0: 

6992 pval = pval[()] 

6993 

6994 return t, pval 

6995 

6996 

6997def _ttest_ind_from_stats(mean1, mean2, denom, df, alternative): 

6998 

6999 d = mean1 - mean2 

7000 with np.errstate(divide='ignore', invalid='ignore'): 

7001 t = np.divide(d, denom) 

7002 t, prob = _ttest_finish(df, t, alternative) 

7003 

7004 return (t, prob) 

7005 

7006 

7007def _unequal_var_ttest_denom(v1, n1, v2, n2): 

7008 vn1 = v1 / n1 

7009 vn2 = v2 / n2 

7010 with np.errstate(divide='ignore', invalid='ignore'): 

7011 df = (vn1 + vn2)**2 / (vn1**2 / (n1 - 1) + vn2**2 / (n2 - 1)) 

7012 

7013 # If df is undefined, variances are zero (assumes n1 > 0 & n2 > 0). 

7014 # Hence it doesn't matter what df is as long as it's not NaN. 

7015 df = np.where(np.isnan(df), 1, df) 

7016 denom = np.sqrt(vn1 + vn2) 

7017 return df, denom 

7018 

7019 

7020def _equal_var_ttest_denom(v1, n1, v2, n2): 

7021 # If there is a single observation in one sample, this formula for pooled 

7022 # variance breaks down because the variance of that sample is undefined. 

7023 # The pooled variance is still defined, though, because the (n-1) in the 

7024 # numerator should cancel with the (n-1) in the denominator, leaving only 

7025 # the sum of squared differences from the mean: zero. 

7026 v1 = np.where(n1 == 1, 0, v1)[()] 

7027 v2 = np.where(n2 == 1, 0, v2)[()] 

7028 

7029 df = n1 + n2 - 2.0 

7030 svar = ((n1 - 1) * v1 + (n2 - 1) * v2) / df 

7031 denom = np.sqrt(svar * (1.0 / n1 + 1.0 / n2)) 

7032 return df, denom 

7033 

7034 

7035Ttest_indResult = namedtuple('Ttest_indResult', ('statistic', 'pvalue')) 

7036 

7037 

7038def ttest_ind_from_stats(mean1, std1, nobs1, mean2, std2, nobs2, 

7039 equal_var=True, alternative="two-sided"): 

7040 r""" 

7041 T-test for means of two independent samples from descriptive statistics. 

7042 

7043 This is a test for the null hypothesis that two independent 

7044 samples have identical average (expected) values. 

7045 

7046 Parameters 

7047 ---------- 

7048 mean1 : array_like 

7049 The mean(s) of sample 1. 

7050 std1 : array_like 

7051 The corrected sample standard deviation of sample 1 (i.e. ``ddof=1``). 

7052 nobs1 : array_like 

7053 The number(s) of observations of sample 1. 

7054 mean2 : array_like 

7055 The mean(s) of sample 2. 

7056 std2 : array_like 

7057 The corrected sample standard deviation of sample 2 (i.e. ``ddof=1``). 

7058 nobs2 : array_like 

7059 The number(s) of observations of sample 2. 

7060 equal_var : bool, optional 

7061 If True (default), perform a standard independent 2 sample test 

7062 that assumes equal population variances [1]_. 

7063 If False, perform Welch's t-test, which does not assume equal 

7064 population variance [2]_. 

7065 alternative : {'two-sided', 'less', 'greater'}, optional 

7066 Defines the alternative hypothesis. 

7067 The following options are available (default is 'two-sided'): 

7068 

7069 * 'two-sided': the means of the distributions are unequal. 

7070 * 'less': the mean of the first distribution is less than the 

7071 mean of the second distribution. 

7072 * 'greater': the mean of the first distribution is greater than the 

7073 mean of the second distribution. 

7074 

7075 .. versionadded:: 1.6.0 

7076 

7077 Returns 

7078 ------- 

7079 statistic : float or array 

7080 The calculated t-statistics. 

7081 pvalue : float or array 

7082 The two-tailed p-value. 

7083 

7084 See Also 

7085 -------- 

7086 scipy.stats.ttest_ind 

7087 

7088 Notes 

7089 ----- 

7090 The statistic is calculated as ``(mean1 - mean2)/se``, where ``se`` is the 

7091 standard error. Therefore, the statistic will be positive when `mean1` is 

7092 greater than `mean2` and negative when `mean1` is less than `mean2`. 

7093 

7094 This method does not check whether any of the elements of `std1` or `std2` 

7095 are negative. If any elements of the `std1` or `std2` parameters are 

7096 negative in a call to this method, this method will return the same result 

7097 as if it were passed ``numpy.abs(std1)`` and ``numpy.abs(std2)``, 

7098 respectively, instead; no exceptions or warnings will be emitted. 

7099 

7100 References 

7101 ---------- 

7102 .. [1] https://en.wikipedia.org/wiki/T-test#Independent_two-sample_t-test 

7103 

7104 .. [2] https://en.wikipedia.org/wiki/Welch%27s_t-test 

7105 

7106 Examples 

7107 -------- 

7108 Suppose we have the summary data for two samples, as follows (with the 

7109 Sample Variance being the corrected sample variance):: 

7110 

7111 Sample Sample 

7112 Size Mean Variance 

7113 Sample 1 13 15.0 87.5 

7114 Sample 2 11 12.0 39.0 

7115 

7116 Apply the t-test to this data (with the assumption that the population 

7117 variances are equal): 

7118 

7119 >>> import numpy as np 

7120 >>> from scipy.stats import ttest_ind_from_stats 

7121 >>> ttest_ind_from_stats(mean1=15.0, std1=np.sqrt(87.5), nobs1=13, 

7122 ... mean2=12.0, std2=np.sqrt(39.0), nobs2=11) 

7123 Ttest_indResult(statistic=0.9051358093310269, pvalue=0.3751996797581487) 

7124 

7125 For comparison, here is the data from which those summary statistics 

7126 were taken. With this data, we can compute the same result using 

7127 `scipy.stats.ttest_ind`: 

7128 

7129 >>> a = np.array([1, 3, 4, 6, 11, 13, 15, 19, 22, 24, 25, 26, 26]) 

7130 >>> b = np.array([2, 4, 6, 9, 11, 13, 14, 15, 18, 19, 21]) 

7131 >>> from scipy.stats import ttest_ind 

7132 >>> ttest_ind(a, b) 

7133 Ttest_indResult(statistic=0.905135809331027, pvalue=0.3751996797581486) 

7134 

7135 Suppose we instead have binary data and would like to apply a t-test to 

7136 compare the proportion of 1s in two independent groups:: 

7137 

7138 Number of Sample Sample 

7139 Size ones Mean Variance 

7140 Sample 1 150 30 0.2 0.161073 

7141 Sample 2 200 45 0.225 0.175251 

7142 

7143 The sample mean :math:`\hat{p}` is the proportion of ones in the sample 

7144 and the variance for a binary observation is estimated by 

7145 :math:`\hat{p}(1-\hat{p})`. 

7146 

7147 >>> ttest_ind_from_stats(mean1=0.2, std1=np.sqrt(0.161073), nobs1=150, 

7148 ... mean2=0.225, std2=np.sqrt(0.175251), nobs2=200) 

7149 Ttest_indResult(statistic=-0.5627187905196761, pvalue=0.5739887114209541) 

7150 

7151 For comparison, we could compute the t statistic and p-value using 

7152 arrays of 0s and 1s and `scipy.stat.ttest_ind`, as above. 

7153 

7154 >>> group1 = np.array([1]*30 + [0]*(150-30)) 

7155 >>> group2 = np.array([1]*45 + [0]*(200-45)) 

7156 >>> ttest_ind(group1, group2) 

7157 Ttest_indResult(statistic=-0.5627179589855622, pvalue=0.573989277115258) 

7158 

7159 """ 

7160 mean1 = np.asarray(mean1) 

7161 std1 = np.asarray(std1) 

7162 mean2 = np.asarray(mean2) 

7163 std2 = np.asarray(std2) 

7164 if equal_var: 

7165 df, denom = _equal_var_ttest_denom(std1**2, nobs1, std2**2, nobs2) 

7166 else: 

7167 df, denom = _unequal_var_ttest_denom(std1**2, nobs1, 

7168 std2**2, nobs2) 

7169 

7170 res = _ttest_ind_from_stats(mean1, mean2, denom, df, alternative) 

7171 return Ttest_indResult(*res) 

7172 

7173 

7174@_axis_nan_policy_factory(pack_TtestResult, default_axis=0, n_samples=2, 

7175 result_to_tuple=unpack_TtestResult, n_outputs=6) 

7176def ttest_ind(a, b, axis=0, equal_var=True, nan_policy='propagate', 

7177 permutations=None, random_state=None, alternative="two-sided", 

7178 trim=0): 

7179 """ 

7180 Calculate the T-test for the means of *two independent* samples of scores. 

7181 

7182 This is a test for the null hypothesis that 2 independent samples 

7183 have identical average (expected) values. This test assumes that the 

7184 populations have identical variances by default. 

7185 

7186 Parameters 

7187 ---------- 

7188 a, b : array_like 

7189 The arrays must have the same shape, except in the dimension 

7190 corresponding to `axis` (the first, by default). 

7191 axis : int or None, optional 

7192 Axis along which to compute test. If None, compute over the whole 

7193 arrays, `a`, and `b`. 

7194 equal_var : bool, optional 

7195 If True (default), perform a standard independent 2 sample test 

7196 that assumes equal population variances [1]_. 

7197 If False, perform Welch's t-test, which does not assume equal 

7198 population variance [2]_. 

7199 

7200 .. versionadded:: 0.11.0 

7201 

7202 nan_policy : {'propagate', 'raise', 'omit'}, optional 

7203 Defines how to handle when input contains nan. 

7204 The following options are available (default is 'propagate'): 

7205 

7206 * 'propagate': returns nan 

7207 * 'raise': throws an error 

7208 * 'omit': performs the calculations ignoring nan values 

7209 

7210 The 'omit' option is not currently available for permutation tests or 

7211 one-sided asympyotic tests. 

7212 

7213 permutations : non-negative int, np.inf, or None (default), optional 

7214 If 0 or None (default), use the t-distribution to calculate p-values. 

7215 Otherwise, `permutations` is the number of random permutations that 

7216 will be used to estimate p-values using a permutation test. If 

7217 `permutations` equals or exceeds the number of distinct partitions of 

7218 the pooled data, an exact test is performed instead (i.e. each 

7219 distinct partition is used exactly once). See Notes for details. 

7220 

7221 .. versionadded:: 1.7.0 

7222 

7223 random_state : {None, int, `numpy.random.Generator`, 

7224 `numpy.random.RandomState`}, optional 

7225 

7226 If `seed` is None (or `np.random`), the `numpy.random.RandomState` 

7227 singleton is used. 

7228 If `seed` is an int, a new ``RandomState`` instance is used, 

7229 seeded with `seed`. 

7230 If `seed` is already a ``Generator`` or ``RandomState`` instance then 

7231 that instance is used. 

7232 

7233 Pseudorandom number generator state used to generate permutations 

7234 (used only when `permutations` is not None). 

7235 

7236 .. versionadded:: 1.7.0 

7237 

7238 alternative : {'two-sided', 'less', 'greater'}, optional 

7239 Defines the alternative hypothesis. 

7240 The following options are available (default is 'two-sided'): 

7241 

7242 * 'two-sided': the means of the distributions underlying the samples 

7243 are unequal. 

7244 * 'less': the mean of the distribution underlying the first sample 

7245 is less than the mean of the distribution underlying the second 

7246 sample. 

7247 * 'greater': the mean of the distribution underlying the first 

7248 sample is greater than the mean of the distribution underlying 

7249 the second sample. 

7250 

7251 .. versionadded:: 1.6.0 

7252 

7253 trim : float, optional 

7254 If nonzero, performs a trimmed (Yuen's) t-test. 

7255 Defines the fraction of elements to be trimmed from each end of the 

7256 input samples. If 0 (default), no elements will be trimmed from either 

7257 side. The number of trimmed elements from each tail is the floor of the 

7258 trim times the number of elements. Valid range is [0, .5). 

7259 

7260 .. versionadded:: 1.7 

7261 

7262 Returns 

7263 ------- 

7264 result : `~scipy.stats._result_classes.TtestResult` 

7265 An object with the following attributes: 

7266 

7267 statistic : float or ndarray 

7268 The t-statistic. 

7269 pvalue : float or ndarray 

7270 The p-value associated with the given alternative. 

7271 df : float or ndarray 

7272 The number of degrees of freedom used in calculation of the 

7273 t-statistic. This is always NaN for a permutation t-test. 

7274 

7275 .. versionadded:: 1.11.0 

7276 

7277 The object also has the following method: 

7278 

7279 confidence_interval(confidence_level=0.95) 

7280 Computes a confidence interval around the difference in 

7281 population means for the given confidence level. 

7282 The confidence interval is returned in a ``namedtuple`` with 

7283 fields ``low`` and ``high``. 

7284 When a permutation t-test is performed, the confidence interval 

7285 is not computed, and fields ``low`` and ``high`` contain NaN. 

7286 

7287 .. versionadded:: 1.11.0 

7288 

7289 Notes 

7290 ----- 

7291 Suppose we observe two independent samples, e.g. flower petal lengths, and 

7292 we are considering whether the two samples were drawn from the same 

7293 population (e.g. the same species of flower or two species with similar 

7294 petal characteristics) or two different populations. 

7295 

7296 The t-test quantifies the difference between the arithmetic means 

7297 of the two samples. The p-value quantifies the probability of observing 

7298 as or more extreme values assuming the null hypothesis, that the 

7299 samples are drawn from populations with the same population means, is true. 

7300 A p-value larger than a chosen threshold (e.g. 5% or 1%) indicates that 

7301 our observation is not so unlikely to have occurred by chance. Therefore, 

7302 we do not reject the null hypothesis of equal population means. 

7303 If the p-value is smaller than our threshold, then we have evidence 

7304 against the null hypothesis of equal population means. 

7305 

7306 By default, the p-value is determined by comparing the t-statistic of the 

7307 observed data against a theoretical t-distribution. 

7308 When ``1 < permutations < binom(n, k)``, where 

7309 

7310 * ``k`` is the number of observations in `a`, 

7311 * ``n`` is the total number of observations in `a` and `b`, and 

7312 * ``binom(n, k)`` is the binomial coefficient (``n`` choose ``k``), 

7313 

7314 the data are pooled (concatenated), randomly assigned to either group `a` 

7315 or `b`, and the t-statistic is calculated. This process is performed 

7316 repeatedly (`permutation` times), generating a distribution of the 

7317 t-statistic under the null hypothesis, and the t-statistic of the observed 

7318 data is compared to this distribution to determine the p-value. 

7319 Specifically, the p-value reported is the "achieved significance level" 

7320 (ASL) as defined in 4.4 of [3]_. Note that there are other ways of 

7321 estimating p-values using randomized permutation tests; for other 

7322 options, see the more general `permutation_test`. 

7323 

7324 When ``permutations >= binom(n, k)``, an exact test is performed: the data 

7325 are partitioned between the groups in each distinct way exactly once. 

7326 

7327 The permutation test can be computationally expensive and not necessarily 

7328 more accurate than the analytical test, but it does not make strong 

7329 assumptions about the shape of the underlying distribution. 

7330 

7331 Use of trimming is commonly referred to as the trimmed t-test. At times 

7332 called Yuen's t-test, this is an extension of Welch's t-test, with the 

7333 difference being the use of winsorized means in calculation of the variance 

7334 and the trimmed sample size in calculation of the statistic. Trimming is 

7335 recommended if the underlying distribution is long-tailed or contaminated 

7336 with outliers [4]_. 

7337 

7338 The statistic is calculated as ``(np.mean(a) - np.mean(b))/se``, where 

7339 ``se`` is the standard error. Therefore, the statistic will be positive 

7340 when the sample mean of `a` is greater than the sample mean of `b` and 

7341 negative when the sample mean of `a` is less than the sample mean of 

7342 `b`. 

7343 

7344 References 

7345 ---------- 

7346 .. [1] https://en.wikipedia.org/wiki/T-test#Independent_two-sample_t-test 

7347 

7348 .. [2] https://en.wikipedia.org/wiki/Welch%27s_t-test 

7349 

7350 .. [3] B. Efron and T. Hastie. Computer Age Statistical Inference. (2016). 

7351 

7352 .. [4] Yuen, Karen K. "The Two-Sample Trimmed t for Unequal Population 

7353 Variances." Biometrika, vol. 61, no. 1, 1974, pp. 165-170. JSTOR, 

7354 www.jstor.org/stable/2334299. Accessed 30 Mar. 2021. 

7355 

7356 .. [5] Yuen, Karen K., and W. J. Dixon. "The Approximate Behaviour and 

7357 Performance of the Two-Sample Trimmed t." Biometrika, vol. 60, 

7358 no. 2, 1973, pp. 369-374. JSTOR, www.jstor.org/stable/2334550. 

7359 Accessed 30 Mar. 2021. 

7360 

7361 Examples 

7362 -------- 

7363 >>> import numpy as np 

7364 >>> from scipy import stats 

7365 >>> rng = np.random.default_rng() 

7366 

7367 Test with sample with identical means: 

7368 

7369 >>> rvs1 = stats.norm.rvs(loc=5, scale=10, size=500, random_state=rng) 

7370 >>> rvs2 = stats.norm.rvs(loc=5, scale=10, size=500, random_state=rng) 

7371 >>> stats.ttest_ind(rvs1, rvs2) 

7372 Ttest_indResult(statistic=-0.4390847099199348, pvalue=0.6606952038870015) 

7373 >>> stats.ttest_ind(rvs1, rvs2, equal_var=False) 

7374 Ttest_indResult(statistic=-0.4390847099199348, pvalue=0.6606952553131064) 

7375 

7376 `ttest_ind` underestimates p for unequal variances: 

7377 

7378 >>> rvs3 = stats.norm.rvs(loc=5, scale=20, size=500, random_state=rng) 

7379 >>> stats.ttest_ind(rvs1, rvs3) 

7380 Ttest_indResult(statistic=-1.6370984482905417, pvalue=0.1019251574705033) 

7381 >>> stats.ttest_ind(rvs1, rvs3, equal_var=False) 

7382 Ttest_indResult(statistic=-1.637098448290542, pvalue=0.10202110497954867) 

7383 

7384 When ``n1 != n2``, the equal variance t-statistic is no longer equal to the 

7385 unequal variance t-statistic: 

7386 

7387 >>> rvs4 = stats.norm.rvs(loc=5, scale=20, size=100, random_state=rng) 

7388 >>> stats.ttest_ind(rvs1, rvs4) 

7389 Ttest_indResult(statistic=-1.9481646859513422, pvalue=0.05186270935842703) 

7390 >>> stats.ttest_ind(rvs1, rvs4, equal_var=False) 

7391 Ttest_indResult(statistic=-1.3146566100751664, pvalue=0.1913495266513811) 

7392 

7393 T-test with different means, variance, and n: 

7394 

7395 >>> rvs5 = stats.norm.rvs(loc=8, scale=20, size=100, random_state=rng) 

7396 >>> stats.ttest_ind(rvs1, rvs5) 

7397 Ttest_indResult(statistic=-2.8415950600298774, pvalue=0.0046418707568707885) 

7398 >>> stats.ttest_ind(rvs1, rvs5, equal_var=False) 

7399 Ttest_indResult(statistic=-1.8686598649188084, pvalue=0.06434714193919686) 

7400 

7401 When performing a permutation test, more permutations typically yields 

7402 more accurate results. Use a ``np.random.Generator`` to ensure 

7403 reproducibility: 

7404 

7405 >>> stats.ttest_ind(rvs1, rvs5, permutations=10000, 

7406 ... random_state=rng) 

7407 Ttest_indResult(statistic=-2.8415950600298774, pvalue=0.0052994700529947) 

7408 

7409 Take these two samples, one of which has an extreme tail. 

7410 

7411 >>> a = (56, 128.6, 12, 123.8, 64.34, 78, 763.3) 

7412 >>> b = (1.1, 2.9, 4.2) 

7413 

7414 Use the `trim` keyword to perform a trimmed (Yuen) t-test. For example, 

7415 using 20% trimming, ``trim=.2``, the test will reduce the impact of one 

7416 (``np.floor(trim*len(a))``) element from each tail of sample `a`. It will 

7417 have no effect on sample `b` because ``np.floor(trim*len(b))`` is 0. 

7418 

7419 >>> stats.ttest_ind(a, b, trim=.2) 

7420 Ttest_indResult(statistic=3.4463884028073513, 

7421 pvalue=0.01369338726499547) 

7422 """ 

7423 if not (0 <= trim < .5): 

7424 raise ValueError("Trimming percentage should be 0 <= `trim` < .5.") 

7425 

7426 NaN = _get_nan(a, b) 

7427 

7428 if a.size == 0 or b.size == 0: 

7429 # _axis_nan_policy decorator ensures this only happens with 1d input 

7430 return TtestResult(NaN, NaN, df=NaN, alternative=NaN, 

7431 standard_error=NaN, estimate=NaN) 

7432 

7433 if permutations is not None and permutations != 0: 

7434 if trim != 0: 

7435 raise ValueError("Permutations are currently not supported " 

7436 "with trimming.") 

7437 if permutations < 0 or (np.isfinite(permutations) and 

7438 int(permutations) != permutations): 

7439 raise ValueError("Permutations must be a non-negative integer.") 

7440 

7441 t, prob = _permutation_ttest(a, b, permutations=permutations, 

7442 axis=axis, equal_var=equal_var, 

7443 nan_policy=nan_policy, 

7444 random_state=random_state, 

7445 alternative=alternative) 

7446 df, denom, estimate = NaN, NaN, NaN 

7447 

7448 else: 

7449 n1 = a.shape[axis] 

7450 n2 = b.shape[axis] 

7451 

7452 if trim == 0: 

7453 if equal_var: 

7454 old_errstate = np.geterr() 

7455 np.seterr(divide='ignore', invalid='ignore') 

7456 v1 = _var(a, axis, ddof=1) 

7457 v2 = _var(b, axis, ddof=1) 

7458 if equal_var: 

7459 np.seterr(**old_errstate) 

7460 m1 = np.mean(a, axis) 

7461 m2 = np.mean(b, axis) 

7462 else: 

7463 v1, m1, n1 = _ttest_trim_var_mean_len(a, trim, axis) 

7464 v2, m2, n2 = _ttest_trim_var_mean_len(b, trim, axis) 

7465 

7466 if equal_var: 

7467 df, denom = _equal_var_ttest_denom(v1, n1, v2, n2) 

7468 else: 

7469 df, denom = _unequal_var_ttest_denom(v1, n1, v2, n2) 

7470 t, prob = _ttest_ind_from_stats(m1, m2, denom, df, alternative) 

7471 

7472 # when nan_policy='omit', `df` can be different for different axis-slices 

7473 df = np.broadcast_to(df, t.shape)[()] 

7474 estimate = m1-m2 

7475 

7476 # _axis_nan_policy decorator doesn't play well with strings 

7477 alternative_num = {"less": -1, "two-sided": 0, "greater": 1}[alternative] 

7478 return TtestResult(t, prob, df=df, alternative=alternative_num, 

7479 standard_error=denom, estimate=estimate) 

7480 

7481 

7482def _ttest_trim_var_mean_len(a, trim, axis): 

7483 """Variance, mean, and length of winsorized input along specified axis""" 

7484 # for use with `ttest_ind` when trimming. 

7485 # further calculations in this test assume that the inputs are sorted. 

7486 # From [4] Section 1 "Let x_1, ..., x_n be n ordered observations..." 

7487 a = np.sort(a, axis=axis) 

7488 

7489 # `g` is the number of elements to be replaced on each tail, converted 

7490 # from a percentage amount of trimming 

7491 n = a.shape[axis] 

7492 g = int(n * trim) 

7493 

7494 # Calculate the Winsorized variance of the input samples according to 

7495 # specified `g` 

7496 v = _calculate_winsorized_variance(a, g, axis) 

7497 

7498 # the total number of elements in the trimmed samples 

7499 n -= 2 * g 

7500 

7501 # calculate the g-times trimmed mean, as defined in [4] (1-1) 

7502 m = trim_mean(a, trim, axis=axis) 

7503 return v, m, n 

7504 

7505 

7506def _calculate_winsorized_variance(a, g, axis): 

7507 """Calculates g-times winsorized variance along specified axis""" 

7508 # it is expected that the input `a` is sorted along the correct axis 

7509 if g == 0: 

7510 return _var(a, ddof=1, axis=axis) 

7511 # move the intended axis to the end that way it is easier to manipulate 

7512 a_win = np.moveaxis(a, axis, -1) 

7513 

7514 # save where NaNs are for later use. 

7515 nans_indices = np.any(np.isnan(a_win), axis=-1) 

7516 

7517 # Winsorization and variance calculation are done in one step in [4] 

7518 # (1-3), but here winsorization is done first; replace the left and 

7519 # right sides with the repeating value. This can be see in effect in ( 

7520 # 1-3) in [4], where the leftmost and rightmost tails are replaced with 

7521 # `(g + 1) * x_{g + 1}` on the left and `(g + 1) * x_{n - g}` on the 

7522 # right. Zero-indexing turns `g + 1` to `g`, and `n - g` to `- g - 1` in 

7523 # array indexing. 

7524 a_win[..., :g] = a_win[..., [g]] 

7525 a_win[..., -g:] = a_win[..., [-g - 1]] 

7526 

7527 # Determine the variance. In [4], the degrees of freedom is expressed as 

7528 # `h - 1`, where `h = n - 2g` (unnumbered equations in Section 1, end of 

7529 # page 369, beginning of page 370). This is converted to NumPy's format, 

7530 # `n - ddof` for use with `np.var`. The result is converted to an 

7531 # array to accommodate indexing later. 

7532 var_win = np.asarray(_var(a_win, ddof=(2 * g + 1), axis=-1)) 

7533 

7534 # with `nan_policy='propagate'`, NaNs may be completely trimmed out 

7535 # because they were sorted into the tail of the array. In these cases, 

7536 # replace computed variances with `np.nan`. 

7537 var_win[nans_indices] = np.nan 

7538 return var_win 

7539 

7540 

7541def _permutation_distribution_t(data, permutations, size_a, equal_var, 

7542 random_state=None): 

7543 """Generation permutation distribution of t statistic""" 

7544 

7545 random_state = check_random_state(random_state) 

7546 

7547 # prepare permutation indices 

7548 size = data.shape[-1] 

7549 # number of distinct combinations 

7550 n_max = special.comb(size, size_a) 

7551 

7552 if permutations < n_max: 

7553 perm_generator = (random_state.permutation(size) 

7554 for i in range(permutations)) 

7555 else: 

7556 permutations = n_max 

7557 perm_generator = (np.concatenate(z) 

7558 for z in _all_partitions(size_a, size-size_a)) 

7559 

7560 t_stat = [] 

7561 for indices in _batch_generator(perm_generator, batch=50): 

7562 # get one batch from perm_generator at a time as a list 

7563 indices = np.array(indices) 

7564 # generate permutations 

7565 data_perm = data[..., indices] 

7566 # move axis indexing permutations to position 0 to broadcast 

7567 # nicely with t_stat_observed, which doesn't have this dimension 

7568 data_perm = np.moveaxis(data_perm, -2, 0) 

7569 

7570 a = data_perm[..., :size_a] 

7571 b = data_perm[..., size_a:] 

7572 t_stat.append(_calc_t_stat(a, b, equal_var)) 

7573 

7574 t_stat = np.concatenate(t_stat, axis=0) 

7575 

7576 return t_stat, permutations, n_max 

7577 

7578 

7579def _calc_t_stat(a, b, equal_var, axis=-1): 

7580 """Calculate the t statistic along the given dimension.""" 

7581 na = a.shape[axis] 

7582 nb = b.shape[axis] 

7583 avg_a = np.mean(a, axis=axis) 

7584 avg_b = np.mean(b, axis=axis) 

7585 var_a = _var(a, axis=axis, ddof=1) 

7586 var_b = _var(b, axis=axis, ddof=1) 

7587 

7588 if not equal_var: 

7589 denom = _unequal_var_ttest_denom(var_a, na, var_b, nb)[1] 

7590 else: 

7591 denom = _equal_var_ttest_denom(var_a, na, var_b, nb)[1] 

7592 

7593 return (avg_a-avg_b)/denom 

7594 

7595 

7596def _permutation_ttest(a, b, permutations, axis=0, equal_var=True, 

7597 nan_policy='propagate', random_state=None, 

7598 alternative="two-sided"): 

7599 """ 

7600 Calculates the T-test for the means of TWO INDEPENDENT samples of scores 

7601 using permutation methods. 

7602 

7603 This test is similar to `stats.ttest_ind`, except it doesn't rely on an 

7604 approximate normality assumption since it uses a permutation test. 

7605 This function is only called from ttest_ind when permutations is not None. 

7606 

7607 Parameters 

7608 ---------- 

7609 a, b : array_like 

7610 The arrays must be broadcastable, except along the dimension 

7611 corresponding to `axis` (the zeroth, by default). 

7612 axis : int, optional 

7613 The axis over which to operate on a and b. 

7614 permutations : int, optional 

7615 Number of permutations used to calculate p-value. If greater than or 

7616 equal to the number of distinct permutations, perform an exact test. 

7617 equal_var : bool, optional 

7618 If False, an equal variance (Welch's) t-test is conducted. Otherwise, 

7619 an ordinary t-test is conducted. 

7620 random_state : {None, int, `numpy.random.Generator`}, optional 

7621 If `seed` is None the `numpy.random.Generator` singleton is used. 

7622 If `seed` is an int, a new ``Generator`` instance is used, 

7623 seeded with `seed`. 

7624 If `seed` is already a ``Generator`` instance then that instance is 

7625 used. 

7626 Pseudorandom number generator state used for generating random 

7627 permutations. 

7628 

7629 Returns 

7630 ------- 

7631 statistic : float or array 

7632 The calculated t-statistic. 

7633 pvalue : float or array 

7634 The p-value. 

7635 

7636 """ 

7637 random_state = check_random_state(random_state) 

7638 

7639 t_stat_observed = _calc_t_stat(a, b, equal_var, axis=axis) 

7640 

7641 na = a.shape[axis] 

7642 mat = _broadcast_concatenate((a, b), axis=axis) 

7643 mat = np.moveaxis(mat, axis, -1) 

7644 

7645 t_stat, permutations, n_max = _permutation_distribution_t( 

7646 mat, permutations, size_a=na, equal_var=equal_var, 

7647 random_state=random_state) 

7648 

7649 compare = {"less": np.less_equal, 

7650 "greater": np.greater_equal, 

7651 "two-sided": lambda x, y: (x <= -np.abs(y)) | (x >= np.abs(y))} 

7652 

7653 # Calculate the p-values 

7654 cmps = compare[alternative](t_stat, t_stat_observed) 

7655 # Randomized test p-value calculation should use biased estimate; see e.g. 

7656 # https://www.degruyter.com/document/doi/10.2202/1544-6115.1585/ 

7657 adjustment = 1 if n_max > permutations else 0 

7658 pvalues = (cmps.sum(axis=0) + adjustment) / (permutations + adjustment) 

7659 

7660 # nans propagate naturally in statistic calculation, but need to be 

7661 # propagated manually into pvalues 

7662 if nan_policy == 'propagate' and np.isnan(t_stat_observed).any(): 

7663 if np.ndim(pvalues) == 0: 

7664 pvalues = np.float64(np.nan) 

7665 else: 

7666 pvalues[np.isnan(t_stat_observed)] = np.nan 

7667 

7668 return (t_stat_observed, pvalues) 

7669 

7670 

7671def _get_len(a, axis, msg): 

7672 try: 

7673 n = a.shape[axis] 

7674 except IndexError: 

7675 raise np.AxisError(axis, a.ndim, msg) from None 

7676 return n 

7677 

7678 

7679@_axis_nan_policy_factory(pack_TtestResult, default_axis=0, n_samples=2, 

7680 result_to_tuple=unpack_TtestResult, n_outputs=6, 

7681 paired=True) 

7682def ttest_rel(a, b, axis=0, nan_policy='propagate', alternative="two-sided"): 

7683 """Calculate the t-test on TWO RELATED samples of scores, a and b. 

7684 

7685 This is a test for the null hypothesis that two related or 

7686 repeated samples have identical average (expected) values. 

7687 

7688 Parameters 

7689 ---------- 

7690 a, b : array_like 

7691 The arrays must have the same shape. 

7692 axis : int or None, optional 

7693 Axis along which to compute test. If None, compute over the whole 

7694 arrays, `a`, and `b`. 

7695 nan_policy : {'propagate', 'raise', 'omit'}, optional 

7696 Defines how to handle when input contains nan. 

7697 The following options are available (default is 'propagate'): 

7698 

7699 * 'propagate': returns nan 

7700 * 'raise': throws an error 

7701 * 'omit': performs the calculations ignoring nan values 

7702 alternative : {'two-sided', 'less', 'greater'}, optional 

7703 Defines the alternative hypothesis. 

7704 The following options are available (default is 'two-sided'): 

7705 

7706 * 'two-sided': the means of the distributions underlying the samples 

7707 are unequal. 

7708 * 'less': the mean of the distribution underlying the first sample 

7709 is less than the mean of the distribution underlying the second 

7710 sample. 

7711 * 'greater': the mean of the distribution underlying the first 

7712 sample is greater than the mean of the distribution underlying 

7713 the second sample. 

7714 

7715 .. versionadded:: 1.6.0 

7716 

7717 Returns 

7718 ------- 

7719 result : `~scipy.stats._result_classes.TtestResult` 

7720 An object with the following attributes: 

7721 

7722 statistic : float or array 

7723 The t-statistic. 

7724 pvalue : float or array 

7725 The p-value associated with the given alternative. 

7726 df : float or array 

7727 The number of degrees of freedom used in calculation of the 

7728 t-statistic; this is one less than the size of the sample 

7729 (``a.shape[axis]``). 

7730 

7731 .. versionadded:: 1.10.0 

7732 

7733 The object also has the following method: 

7734 

7735 confidence_interval(confidence_level=0.95) 

7736 Computes a confidence interval around the difference in 

7737 population means for the given confidence level. 

7738 The confidence interval is returned in a ``namedtuple`` with 

7739 fields `low` and `high`. 

7740 

7741 .. versionadded:: 1.10.0 

7742 

7743 Notes 

7744 ----- 

7745 Examples for use are scores of the same set of student in 

7746 different exams, or repeated sampling from the same units. The 

7747 test measures whether the average score differs significantly 

7748 across samples (e.g. exams). If we observe a large p-value, for 

7749 example greater than 0.05 or 0.1 then we cannot reject the null 

7750 hypothesis of identical average scores. If the p-value is smaller 

7751 than the threshold, e.g. 1%, 5% or 10%, then we reject the null 

7752 hypothesis of equal averages. Small p-values are associated with 

7753 large t-statistics. 

7754 

7755 The t-statistic is calculated as ``np.mean(a - b)/se``, where ``se`` is the 

7756 standard error. Therefore, the t-statistic will be positive when the sample 

7757 mean of ``a - b`` is greater than zero and negative when the sample mean of 

7758 ``a - b`` is less than zero. 

7759 

7760 References 

7761 ---------- 

7762 https://en.wikipedia.org/wiki/T-test#Dependent_t-test_for_paired_samples 

7763 

7764 Examples 

7765 -------- 

7766 >>> import numpy as np 

7767 >>> from scipy import stats 

7768 >>> rng = np.random.default_rng() 

7769 

7770 >>> rvs1 = stats.norm.rvs(loc=5, scale=10, size=500, random_state=rng) 

7771 >>> rvs2 = (stats.norm.rvs(loc=5, scale=10, size=500, random_state=rng) 

7772 ... + stats.norm.rvs(scale=0.2, size=500, random_state=rng)) 

7773 >>> stats.ttest_rel(rvs1, rvs2) 

7774 TtestResult(statistic=-0.4549717054410304, pvalue=0.6493274702088672, df=499) # noqa 

7775 >>> rvs3 = (stats.norm.rvs(loc=8, scale=10, size=500, random_state=rng) 

7776 ... + stats.norm.rvs(scale=0.2, size=500, random_state=rng)) 

7777 >>> stats.ttest_rel(rvs1, rvs3) 

7778 TtestResult(statistic=-5.879467544540889, pvalue=7.540777129099917e-09, df=499) # noqa 

7779 

7780 """ 

7781 a, b, axis = _chk2_asarray(a, b, axis) 

7782 

7783 na = _get_len(a, axis, "first argument") 

7784 nb = _get_len(b, axis, "second argument") 

7785 if na != nb: 

7786 raise ValueError('unequal length arrays') 

7787 

7788 if na == 0 or nb == 0: 

7789 # _axis_nan_policy decorator ensures this only happens with 1d input 

7790 NaN = _get_nan(a, b) 

7791 return TtestResult(NaN, NaN, df=NaN, alternative=NaN, 

7792 standard_error=NaN, estimate=NaN) 

7793 

7794 n = a.shape[axis] 

7795 df = n - 1 

7796 

7797 d = (a - b).astype(np.float64) 

7798 v = _var(d, axis, ddof=1) 

7799 dm = np.mean(d, axis) 

7800 denom = np.sqrt(v / n) 

7801 

7802 with np.errstate(divide='ignore', invalid='ignore'): 

7803 t = np.divide(dm, denom) 

7804 t, prob = _ttest_finish(df, t, alternative) 

7805 

7806 # when nan_policy='omit', `df` can be different for different axis-slices 

7807 df = np.broadcast_to(df, t.shape)[()] 

7808 

7809 # _axis_nan_policy decorator doesn't play well with strings 

7810 alternative_num = {"less": -1, "two-sided": 0, "greater": 1}[alternative] 

7811 return TtestResult(t, prob, df=df, alternative=alternative_num, 

7812 standard_error=denom, estimate=dm) 

7813 

7814 

7815# Map from names to lambda_ values used in power_divergence(). 

7816_power_div_lambda_names = { 

7817 "pearson": 1, 

7818 "log-likelihood": 0, 

7819 "freeman-tukey": -0.5, 

7820 "mod-log-likelihood": -1, 

7821 "neyman": -2, 

7822 "cressie-read": 2/3, 

7823} 

7824 

7825 

7826def _count(a, axis=None): 

7827 """Count the number of non-masked elements of an array. 

7828 

7829 This function behaves like `np.ma.count`, but is much faster 

7830 for ndarrays. 

7831 """ 

7832 if hasattr(a, 'count'): 

7833 num = a.count(axis=axis) 

7834 if isinstance(num, np.ndarray) and num.ndim == 0: 

7835 # In some cases, the `count` method returns a scalar array (e.g. 

7836 # np.array(3)), but we want a plain integer. 

7837 num = int(num) 

7838 else: 

7839 if axis is None: 

7840 num = a.size 

7841 else: 

7842 num = a.shape[axis] 

7843 return num 

7844 

7845 

7846def _m_broadcast_to(a, shape): 

7847 if np.ma.isMaskedArray(a): 

7848 return np.ma.masked_array(np.broadcast_to(a, shape), 

7849 mask=np.broadcast_to(a.mask, shape)) 

7850 return np.broadcast_to(a, shape, subok=True) 

7851 

7852 

7853Power_divergenceResult = namedtuple('Power_divergenceResult', 

7854 ('statistic', 'pvalue')) 

7855 

7856 

7857def power_divergence(f_obs, f_exp=None, ddof=0, axis=0, lambda_=None): 

7858 """Cressie-Read power divergence statistic and goodness of fit test. 

7859 

7860 This function tests the null hypothesis that the categorical data 

7861 has the given frequencies, using the Cressie-Read power divergence 

7862 statistic. 

7863 

7864 Parameters 

7865 ---------- 

7866 f_obs : array_like 

7867 Observed frequencies in each category. 

7868 f_exp : array_like, optional 

7869 Expected frequencies in each category. By default the categories are 

7870 assumed to be equally likely. 

7871 ddof : int, optional 

7872 "Delta degrees of freedom": adjustment to the degrees of freedom 

7873 for the p-value. The p-value is computed using a chi-squared 

7874 distribution with ``k - 1 - ddof`` degrees of freedom, where `k` 

7875 is the number of observed frequencies. The default value of `ddof` 

7876 is 0. 

7877 axis : int or None, optional 

7878 The axis of the broadcast result of `f_obs` and `f_exp` along which to 

7879 apply the test. If axis is None, all values in `f_obs` are treated 

7880 as a single data set. Default is 0. 

7881 lambda_ : float or str, optional 

7882 The power in the Cressie-Read power divergence statistic. The default 

7883 is 1. For convenience, `lambda_` may be assigned one of the following 

7884 strings, in which case the corresponding numerical value is used: 

7885 

7886 * ``"pearson"`` (value 1) 

7887 Pearson's chi-squared statistic. In this case, the function is 

7888 equivalent to `chisquare`. 

7889 * ``"log-likelihood"`` (value 0) 

7890 Log-likelihood ratio. Also known as the G-test [3]_. 

7891 * ``"freeman-tukey"`` (value -1/2) 

7892 Freeman-Tukey statistic. 

7893 * ``"mod-log-likelihood"`` (value -1) 

7894 Modified log-likelihood ratio. 

7895 * ``"neyman"`` (value -2) 

7896 Neyman's statistic. 

7897 * ``"cressie-read"`` (value 2/3) 

7898 The power recommended in [5]_. 

7899 

7900 Returns 

7901 ------- 

7902 res: Power_divergenceResult 

7903 An object containing attributes: 

7904 

7905 statistic : float or ndarray 

7906 The Cressie-Read power divergence test statistic. The value is 

7907 a float if `axis` is None or if` `f_obs` and `f_exp` are 1-D. 

7908 pvalue : float or ndarray 

7909 The p-value of the test. The value is a float if `ddof` and the 

7910 return value `stat` are scalars. 

7911 

7912 See Also 

7913 -------- 

7914 chisquare 

7915 

7916 Notes 

7917 ----- 

7918 This test is invalid when the observed or expected frequencies in each 

7919 category are too small. A typical rule is that all of the observed 

7920 and expected frequencies should be at least 5. 

7921 

7922 Also, the sum of the observed and expected frequencies must be the same 

7923 for the test to be valid; `power_divergence` raises an error if the sums 

7924 do not agree within a relative tolerance of ``1e-8``. 

7925 

7926 When `lambda_` is less than zero, the formula for the statistic involves 

7927 dividing by `f_obs`, so a warning or error may be generated if any value 

7928 in `f_obs` is 0. 

7929 

7930 Similarly, a warning or error may be generated if any value in `f_exp` is 

7931 zero when `lambda_` >= 0. 

7932 

7933 The default degrees of freedom, k-1, are for the case when no parameters 

7934 of the distribution are estimated. If p parameters are estimated by 

7935 efficient maximum likelihood then the correct degrees of freedom are 

7936 k-1-p. If the parameters are estimated in a different way, then the 

7937 dof can be between k-1-p and k-1. However, it is also possible that 

7938 the asymptotic distribution is not a chisquare, in which case this 

7939 test is not appropriate. 

7940 

7941 This function handles masked arrays. If an element of `f_obs` or `f_exp` 

7942 is masked, then data at that position is ignored, and does not count 

7943 towards the size of the data set. 

7944 

7945 .. versionadded:: 0.13.0 

7946 

7947 References 

7948 ---------- 

7949 .. [1] Lowry, Richard. "Concepts and Applications of Inferential 

7950 Statistics". Chapter 8. 

7951 https://web.archive.org/web/20171015035606/http://faculty.vassar.edu/lowry/ch8pt1.html 

7952 .. [2] "Chi-squared test", https://en.wikipedia.org/wiki/Chi-squared_test 

7953 .. [3] "G-test", https://en.wikipedia.org/wiki/G-test 

7954 .. [4] Sokal, R. R. and Rohlf, F. J. "Biometry: the principles and 

7955 practice of statistics in biological research", New York: Freeman 

7956 (1981) 

7957 .. [5] Cressie, N. and Read, T. R. C., "Multinomial Goodness-of-Fit 

7958 Tests", J. Royal Stat. Soc. Series B, Vol. 46, No. 3 (1984), 

7959 pp. 440-464. 

7960 

7961 Examples 

7962 -------- 

7963 (See `chisquare` for more examples.) 

7964 

7965 When just `f_obs` is given, it is assumed that the expected frequencies 

7966 are uniform and given by the mean of the observed frequencies. Here we 

7967 perform a G-test (i.e. use the log-likelihood ratio statistic): 

7968 

7969 >>> import numpy as np 

7970 >>> from scipy.stats import power_divergence 

7971 >>> power_divergence([16, 18, 16, 14, 12, 12], lambda_='log-likelihood') 

7972 (2.006573162632538, 0.84823476779463769) 

7973 

7974 The expected frequencies can be given with the `f_exp` argument: 

7975 

7976 >>> power_divergence([16, 18, 16, 14, 12, 12], 

7977 ... f_exp=[16, 16, 16, 16, 16, 8], 

7978 ... lambda_='log-likelihood') 

7979 (3.3281031458963746, 0.6495419288047497) 

7980 

7981 When `f_obs` is 2-D, by default the test is applied to each column. 

7982 

7983 >>> obs = np.array([[16, 18, 16, 14, 12, 12], [32, 24, 16, 28, 20, 24]]).T 

7984 >>> obs.shape 

7985 (6, 2) 

7986 >>> power_divergence(obs, lambda_="log-likelihood") 

7987 (array([ 2.00657316, 6.77634498]), array([ 0.84823477, 0.23781225])) 

7988 

7989 By setting ``axis=None``, the test is applied to all data in the array, 

7990 which is equivalent to applying the test to the flattened array. 

7991 

7992 >>> power_divergence(obs, axis=None) 

7993 (23.31034482758621, 0.015975692534127565) 

7994 >>> power_divergence(obs.ravel()) 

7995 (23.31034482758621, 0.015975692534127565) 

7996 

7997 `ddof` is the change to make to the default degrees of freedom. 

7998 

7999 >>> power_divergence([16, 18, 16, 14, 12, 12], ddof=1) 

8000 (2.0, 0.73575888234288467) 

8001 

8002 The calculation of the p-values is done by broadcasting the 

8003 test statistic with `ddof`. 

8004 

8005 >>> power_divergence([16, 18, 16, 14, 12, 12], ddof=[0,1,2]) 

8006 (2.0, array([ 0.84914504, 0.73575888, 0.5724067 ])) 

8007 

8008 `f_obs` and `f_exp` are also broadcast. In the following, `f_obs` has 

8009 shape (6,) and `f_exp` has shape (2, 6), so the result of broadcasting 

8010 `f_obs` and `f_exp` has shape (2, 6). To compute the desired chi-squared 

8011 statistics, we must use ``axis=1``: 

8012 

8013 >>> power_divergence([16, 18, 16, 14, 12, 12], 

8014 ... f_exp=[[16, 16, 16, 16, 16, 8], 

8015 ... [8, 20, 20, 16, 12, 12]], 

8016 ... axis=1) 

8017 (array([ 3.5 , 9.25]), array([ 0.62338763, 0.09949846])) 

8018 

8019 """ 

8020 # Convert the input argument `lambda_` to a numerical value. 

8021 if isinstance(lambda_, str): 

8022 if lambda_ not in _power_div_lambda_names: 

8023 names = repr(list(_power_div_lambda_names.keys()))[1:-1] 

8024 raise ValueError("invalid string for lambda_: {!r}. " 

8025 "Valid strings are {}".format(lambda_, names)) 

8026 lambda_ = _power_div_lambda_names[lambda_] 

8027 elif lambda_ is None: 

8028 lambda_ = 1 

8029 

8030 f_obs = np.asanyarray(f_obs) 

8031 f_obs_float = f_obs.astype(np.float64) 

8032 

8033 if f_exp is not None: 

8034 f_exp = np.asanyarray(f_exp) 

8035 bshape = np.broadcast_shapes(f_obs_float.shape, f_exp.shape) 

8036 f_obs_float = _m_broadcast_to(f_obs_float, bshape) 

8037 f_exp = _m_broadcast_to(f_exp, bshape) 

8038 rtol = 1e-8 # to pass existing tests 

8039 with np.errstate(invalid='ignore'): 

8040 f_obs_sum = f_obs_float.sum(axis=axis) 

8041 f_exp_sum = f_exp.sum(axis=axis) 

8042 relative_diff = (np.abs(f_obs_sum - f_exp_sum) / 

8043 np.minimum(f_obs_sum, f_exp_sum)) 

8044 diff_gt_tol = (relative_diff > rtol).any() 

8045 if diff_gt_tol: 

8046 msg = (f"For each axis slice, the sum of the observed " 

8047 f"frequencies must agree with the sum of the " 

8048 f"expected frequencies to a relative tolerance " 

8049 f"of {rtol}, but the percent differences are:\n" 

8050 f"{relative_diff}") 

8051 raise ValueError(msg) 

8052 

8053 else: 

8054 # Ignore 'invalid' errors so the edge case of a data set with length 0 

8055 # is handled without spurious warnings. 

8056 with np.errstate(invalid='ignore'): 

8057 f_exp = f_obs.mean(axis=axis, keepdims=True) 

8058 

8059 # `terms` is the array of terms that are summed along `axis` to create 

8060 # the test statistic. We use some specialized code for a few special 

8061 # cases of lambda_. 

8062 if lambda_ == 1: 

8063 # Pearson's chi-squared statistic 

8064 terms = (f_obs_float - f_exp)**2 / f_exp 

8065 elif lambda_ == 0: 

8066 # Log-likelihood ratio (i.e. G-test) 

8067 terms = 2.0 * special.xlogy(f_obs, f_obs / f_exp) 

8068 elif lambda_ == -1: 

8069 # Modified log-likelihood ratio 

8070 terms = 2.0 * special.xlogy(f_exp, f_exp / f_obs) 

8071 else: 

8072 # General Cressie-Read power divergence. 

8073 terms = f_obs * ((f_obs / f_exp)**lambda_ - 1) 

8074 terms /= 0.5 * lambda_ * (lambda_ + 1) 

8075 

8076 stat = terms.sum(axis=axis) 

8077 

8078 num_obs = _count(terms, axis=axis) 

8079 ddof = asarray(ddof) 

8080 p = distributions.chi2.sf(stat, num_obs - 1 - ddof) 

8081 

8082 return Power_divergenceResult(stat, p) 

8083 

8084 

8085def chisquare(f_obs, f_exp=None, ddof=0, axis=0): 

8086 """Calculate a one-way chi-square test. 

8087 

8088 The chi-square test tests the null hypothesis that the categorical data 

8089 has the given frequencies. 

8090 

8091 Parameters 

8092 ---------- 

8093 f_obs : array_like 

8094 Observed frequencies in each category. 

8095 f_exp : array_like, optional 

8096 Expected frequencies in each category. By default the categories are 

8097 assumed to be equally likely. 

8098 ddof : int, optional 

8099 "Delta degrees of freedom": adjustment to the degrees of freedom 

8100 for the p-value. The p-value is computed using a chi-squared 

8101 distribution with ``k - 1 - ddof`` degrees of freedom, where `k` 

8102 is the number of observed frequencies. The default value of `ddof` 

8103 is 0. 

8104 axis : int or None, optional 

8105 The axis of the broadcast result of `f_obs` and `f_exp` along which to 

8106 apply the test. If axis is None, all values in `f_obs` are treated 

8107 as a single data set. Default is 0. 

8108 

8109 Returns 

8110 ------- 

8111 res: Power_divergenceResult 

8112 An object containing attributes: 

8113 

8114 chisq : float or ndarray 

8115 The chi-squared test statistic. The value is a float if `axis` is 

8116 None or `f_obs` and `f_exp` are 1-D. 

8117 pvalue : float or ndarray 

8118 The p-value of the test. The value is a float if `ddof` and the 

8119 return value `chisq` are scalars. 

8120 

8121 See Also 

8122 -------- 

8123 scipy.stats.power_divergence 

8124 scipy.stats.fisher_exact : Fisher exact test on a 2x2 contingency table. 

8125 scipy.stats.barnard_exact : An unconditional exact test. An alternative 

8126 to chi-squared test for small sample sizes. 

8127 

8128 Notes 

8129 ----- 

8130 This test is invalid when the observed or expected frequencies in each 

8131 category are too small. A typical rule is that all of the observed 

8132 and expected frequencies should be at least 5. According to [3]_, the 

8133 total number of samples is recommended to be greater than 13, 

8134 otherwise exact tests (such as Barnard's Exact test) should be used 

8135 because they do not overreject. 

8136 

8137 Also, the sum of the observed and expected frequencies must be the same 

8138 for the test to be valid; `chisquare` raises an error if the sums do not 

8139 agree within a relative tolerance of ``1e-8``. 

8140 

8141 The default degrees of freedom, k-1, are for the case when no parameters 

8142 of the distribution are estimated. If p parameters are estimated by 

8143 efficient maximum likelihood then the correct degrees of freedom are 

8144 k-1-p. If the parameters are estimated in a different way, then the 

8145 dof can be between k-1-p and k-1. However, it is also possible that 

8146 the asymptotic distribution is not chi-square, in which case this test 

8147 is not appropriate. 

8148 

8149 References 

8150 ---------- 

8151 .. [1] Lowry, Richard. "Concepts and Applications of Inferential 

8152 Statistics". Chapter 8. 

8153 https://web.archive.org/web/20171022032306/http://vassarstats.net:80/textbook/ch8pt1.html 

8154 .. [2] "Chi-squared test", https://en.wikipedia.org/wiki/Chi-squared_test 

8155 .. [3] Pearson, Karl. "On the criterion that a given system of deviations from the probable 

8156 in the case of a correlated system of variables is such that it can be reasonably 

8157 supposed to have arisen from random sampling", Philosophical Magazine. Series 5. 50 

8158 (1900), pp. 157-175. 

8159 .. [4] Mannan, R. William and E. Charles. Meslow. "Bird populations and 

8160 vegetation characteristics in managed and old-growth forests, 

8161 northeastern Oregon." Journal of Wildlife Management 

8162 48, 1219-1238, :doi:`10.2307/3801783`, 1984. 

8163 

8164 Examples 

8165 -------- 

8166 In [4]_, bird foraging behavior was investigated in an old-growth forest 

8167 of Oregon. 

8168 In the forest, 44% of the canopy volume was Douglas fir, 

8169 24% was ponderosa pine, 29% was grand fir, and 3% was western larch. 

8170 The authors observed the behavior of several species of birds, one of 

8171 which was the red-breasted nuthatch. They made 189 observations of this 

8172 species foraging, recording 43 ("23%") of observations in Douglas fir, 

8173 52 ("28%") in ponderosa pine, 54 ("29%") in grand fir, and 40 ("21%") in 

8174 western larch. 

8175 

8176 Using a chi-square test, we can test the null hypothesis that the 

8177 proportions of foraging events are equal to the proportions of canopy 

8178 volume. The authors of the paper considered a p-value less than 1% to be 

8179 significant. 

8180 

8181 Using the above proportions of canopy volume and observed events, we can 

8182 infer expected frequencies. 

8183 

8184 >>> import numpy as np 

8185 >>> f_exp = np.array([44, 24, 29, 3]) / 100 * 189 

8186 

8187 The observed frequencies of foraging were: 

8188 

8189 >>> f_obs = np.array([43, 52, 54, 40]) 

8190 

8191 We can now compare the observed frequencies with the expected frequencies. 

8192 

8193 >>> from scipy.stats import chisquare 

8194 >>> chisquare(f_obs=f_obs, f_exp=f_exp) 

8195 Power_divergenceResult(statistic=228.23515947653874, pvalue=3.3295585338846486e-49) 

8196 

8197 The p-value is well below the chosen significance level. Hence, the 

8198 authors considered the difference to be significant and concluded 

8199 that the relative proportions of foraging events were not the same 

8200 as the relative proportions of tree canopy volume. 

8201 

8202 Following are other generic examples to demonstrate how the other 

8203 parameters can be used. 

8204 

8205 When just `f_obs` is given, it is assumed that the expected frequencies 

8206 are uniform and given by the mean of the observed frequencies. 

8207 

8208 >>> chisquare([16, 18, 16, 14, 12, 12]) 

8209 Power_divergenceResult(statistic=2.0, pvalue=0.84914503608460956) 

8210 

8211 With `f_exp` the expected frequencies can be given. 

8212 

8213 >>> chisquare([16, 18, 16, 14, 12, 12], f_exp=[16, 16, 16, 16, 16, 8]) 

8214 Power_divergenceResult(statistic=3.5, pvalue=0.62338762774958223) 

8215 

8216 When `f_obs` is 2-D, by default the test is applied to each column. 

8217 

8218 >>> obs = np.array([[16, 18, 16, 14, 12, 12], [32, 24, 16, 28, 20, 24]]).T 

8219 >>> obs.shape 

8220 (6, 2) 

8221 >>> chisquare(obs) 

8222 Power_divergenceResult(statistic=array([2. , 6.66666667]), pvalue=array([0.84914504, 0.24663415])) 

8223 

8224 By setting ``axis=None``, the test is applied to all data in the array, 

8225 which is equivalent to applying the test to the flattened array. 

8226 

8227 >>> chisquare(obs, axis=None) 

8228 Power_divergenceResult(statistic=23.31034482758621, pvalue=0.015975692534127565) 

8229 >>> chisquare(obs.ravel()) 

8230 Power_divergenceResult(statistic=23.310344827586206, pvalue=0.01597569253412758) 

8231 

8232 `ddof` is the change to make to the default degrees of freedom. 

8233 

8234 >>> chisquare([16, 18, 16, 14, 12, 12], ddof=1) 

8235 Power_divergenceResult(statistic=2.0, pvalue=0.7357588823428847) 

8236 

8237 The calculation of the p-values is done by broadcasting the 

8238 chi-squared statistic with `ddof`. 

8239 

8240 >>> chisquare([16, 18, 16, 14, 12, 12], ddof=[0,1,2]) 

8241 Power_divergenceResult(statistic=2.0, pvalue=array([0.84914504, 0.73575888, 0.5724067 ])) 

8242 

8243 `f_obs` and `f_exp` are also broadcast. In the following, `f_obs` has 

8244 shape (6,) and `f_exp` has shape (2, 6), so the result of broadcasting 

8245 `f_obs` and `f_exp` has shape (2, 6). To compute the desired chi-squared 

8246 statistics, we use ``axis=1``: 

8247 

8248 >>> chisquare([16, 18, 16, 14, 12, 12], 

8249 ... f_exp=[[16, 16, 16, 16, 16, 8], [8, 20, 20, 16, 12, 12]], 

8250 ... axis=1) 

8251 Power_divergenceResult(statistic=array([3.5 , 9.25]), pvalue=array([0.62338763, 0.09949846])) 

8252 

8253 """ # noqa 

8254 return power_divergence(f_obs, f_exp=f_exp, ddof=ddof, axis=axis, 

8255 lambda_="pearson") 

8256 

8257 

8258KstestResult = _make_tuple_bunch('KstestResult', ['statistic', 'pvalue'], 

8259 ['statistic_location', 'statistic_sign']) 

8260 

8261 

8262def _compute_dplus(cdfvals, x): 

8263 """Computes D+ as used in the Kolmogorov-Smirnov test. 

8264 

8265 Parameters 

8266 ---------- 

8267 cdfvals : array_like 

8268 Sorted array of CDF values between 0 and 1 

8269 x: array_like 

8270 Sorted array of the stochastic variable itself 

8271 

8272 Returns 

8273 ------- 

8274 res: Pair with the following elements: 

8275 - The maximum distance of the CDF values below Uniform(0, 1). 

8276 - The location at which the maximum is reached. 

8277 

8278 """ 

8279 n = len(cdfvals) 

8280 dplus = (np.arange(1.0, n + 1) / n - cdfvals) 

8281 amax = dplus.argmax() 

8282 loc_max = x[amax] 

8283 return (dplus[amax], loc_max) 

8284 

8285 

8286def _compute_dminus(cdfvals, x): 

8287 """Computes D- as used in the Kolmogorov-Smirnov test. 

8288 

8289 Parameters 

8290 ---------- 

8291 cdfvals : array_like 

8292 Sorted array of CDF values between 0 and 1 

8293 x: array_like 

8294 Sorted array of the stochastic variable itself 

8295 

8296 Returns 

8297 ------- 

8298 res: Pair with the following elements: 

8299 - Maximum distance of the CDF values above Uniform(0, 1) 

8300 - The location at which the maximum is reached. 

8301 """ 

8302 n = len(cdfvals) 

8303 dminus = (cdfvals - np.arange(0.0, n)/n) 

8304 amax = dminus.argmax() 

8305 loc_max = x[amax] 

8306 return (dminus[amax], loc_max) 

8307 

8308 

8309@_rename_parameter("mode", "method") 

8310def ks_1samp(x, cdf, args=(), alternative='two-sided', method='auto'): 

8311 """ 

8312 Performs the one-sample Kolmogorov-Smirnov test for goodness of fit. 

8313 

8314 This test compares the underlying distribution F(x) of a sample 

8315 against a given continuous distribution G(x). See Notes for a description 

8316 of the available null and alternative hypotheses. 

8317 

8318 Parameters 

8319 ---------- 

8320 x : array_like 

8321 a 1-D array of observations of iid random variables. 

8322 cdf : callable 

8323 callable used to calculate the cdf. 

8324 args : tuple, sequence, optional 

8325 Distribution parameters, used with `cdf`. 

8326 alternative : {'two-sided', 'less', 'greater'}, optional 

8327 Defines the null and alternative hypotheses. Default is 'two-sided'. 

8328 Please see explanations in the Notes below. 

8329 method : {'auto', 'exact', 'approx', 'asymp'}, optional 

8330 Defines the distribution used for calculating the p-value. 

8331 The following options are available (default is 'auto'): 

8332 

8333 * 'auto' : selects one of the other options. 

8334 * 'exact' : uses the exact distribution of test statistic. 

8335 * 'approx' : approximates the two-sided probability with twice 

8336 the one-sided probability 

8337 * 'asymp': uses asymptotic distribution of test statistic 

8338 

8339 Returns 

8340 ------- 

8341 res: KstestResult 

8342 An object containing attributes: 

8343 

8344 statistic : float 

8345 KS test statistic, either D+, D-, or D (the maximum of the two) 

8346 pvalue : float 

8347 One-tailed or two-tailed p-value. 

8348 statistic_location : float 

8349 Value of `x` corresponding with the KS statistic; i.e., the 

8350 distance between the empirical distribution function and the 

8351 hypothesized cumulative distribution function is measured at this 

8352 observation. 

8353 statistic_sign : int 

8354 +1 if the KS statistic is the maximum positive difference between 

8355 the empirical distribution function and the hypothesized cumulative 

8356 distribution function (D+); -1 if the KS statistic is the maximum 

8357 negative difference (D-). 

8358 

8359 

8360 See Also 

8361 -------- 

8362 ks_2samp, kstest 

8363 

8364 Notes 

8365 ----- 

8366 There are three options for the null and corresponding alternative 

8367 hypothesis that can be selected using the `alternative` parameter. 

8368 

8369 - `two-sided`: The null hypothesis is that the two distributions are 

8370 identical, F(x)=G(x) for all x; the alternative is that they are not 

8371 identical. 

8372 

8373 - `less`: The null hypothesis is that F(x) >= G(x) for all x; the 

8374 alternative is that F(x) < G(x) for at least one x. 

8375 

8376 - `greater`: The null hypothesis is that F(x) <= G(x) for all x; the 

8377 alternative is that F(x) > G(x) for at least one x. 

8378 

8379 Note that the alternative hypotheses describe the *CDFs* of the 

8380 underlying distributions, not the observed values. For example, 

8381 suppose x1 ~ F and x2 ~ G. If F(x) > G(x) for all x, the values in 

8382 x1 tend to be less than those in x2. 

8383 

8384 Examples 

8385 -------- 

8386 Suppose we wish to test the null hypothesis that a sample is distributed 

8387 according to the standard normal. 

8388 We choose a confidence level of 95%; that is, we will reject the null 

8389 hypothesis in favor of the alternative if the p-value is less than 0.05. 

8390 

8391 When testing uniformly distributed data, we would expect the 

8392 null hypothesis to be rejected. 

8393 

8394 >>> import numpy as np 

8395 >>> from scipy import stats 

8396 >>> rng = np.random.default_rng() 

8397 >>> stats.ks_1samp(stats.uniform.rvs(size=100, random_state=rng), 

8398 ... stats.norm.cdf) 

8399 KstestResult(statistic=0.5001899973268688, pvalue=1.1616392184763533e-23) 

8400 

8401 Indeed, the p-value is lower than our threshold of 0.05, so we reject the 

8402 null hypothesis in favor of the default "two-sided" alternative: the data 

8403 are *not* distributed according to the standard normal. 

8404 

8405 When testing random variates from the standard normal distribution, we 

8406 expect the data to be consistent with the null hypothesis most of the time. 

8407 

8408 >>> x = stats.norm.rvs(size=100, random_state=rng) 

8409 >>> stats.ks_1samp(x, stats.norm.cdf) 

8410 KstestResult(statistic=0.05345882212970396, pvalue=0.9227159037744717) 

8411 

8412 As expected, the p-value of 0.92 is not below our threshold of 0.05, so 

8413 we cannot reject the null hypothesis. 

8414 

8415 Suppose, however, that the random variates are distributed according to 

8416 a normal distribution that is shifted toward greater values. In this case, 

8417 the cumulative density function (CDF) of the underlying distribution tends 

8418 to be *less* than the CDF of the standard normal. Therefore, we would 

8419 expect the null hypothesis to be rejected with ``alternative='less'``: 

8420 

8421 >>> x = stats.norm.rvs(size=100, loc=0.5, random_state=rng) 

8422 >>> stats.ks_1samp(x, stats.norm.cdf, alternative='less') 

8423 KstestResult(statistic=0.17482387821055168, pvalue=0.001913921057766743) 

8424 

8425 and indeed, with p-value smaller than our threshold, we reject the null 

8426 hypothesis in favor of the alternative. 

8427 

8428 """ 

8429 mode = method 

8430 

8431 alternative = {'t': 'two-sided', 'g': 'greater', 'l': 'less'}.get( 

8432 alternative.lower()[0], alternative) 

8433 if alternative not in ['two-sided', 'greater', 'less']: 

8434 raise ValueError("Unexpected alternative %s" % alternative) 

8435 if np.ma.is_masked(x): 

8436 x = x.compressed() 

8437 

8438 N = len(x) 

8439 x = np.sort(x) 

8440 cdfvals = cdf(x, *args) 

8441 

8442 if alternative == 'greater': 

8443 Dplus, d_location = _compute_dplus(cdfvals, x) 

8444 return KstestResult(Dplus, distributions.ksone.sf(Dplus, N), 

8445 statistic_location=d_location, 

8446 statistic_sign=1) 

8447 

8448 if alternative == 'less': 

8449 Dminus, d_location = _compute_dminus(cdfvals, x) 

8450 return KstestResult(Dminus, distributions.ksone.sf(Dminus, N), 

8451 statistic_location=d_location, 

8452 statistic_sign=-1) 

8453 

8454 # alternative == 'two-sided': 

8455 Dplus, dplus_location = _compute_dplus(cdfvals, x) 

8456 Dminus, dminus_location = _compute_dminus(cdfvals, x) 

8457 if Dplus > Dminus: 

8458 D = Dplus 

8459 d_location = dplus_location 

8460 d_sign = 1 

8461 else: 

8462 D = Dminus 

8463 d_location = dminus_location 

8464 d_sign = -1 

8465 

8466 if mode == 'auto': # Always select exact 

8467 mode = 'exact' 

8468 if mode == 'exact': 

8469 prob = distributions.kstwo.sf(D, N) 

8470 elif mode == 'asymp': 

8471 prob = distributions.kstwobign.sf(D * np.sqrt(N)) 

8472 else: 

8473 # mode == 'approx' 

8474 prob = 2 * distributions.ksone.sf(D, N) 

8475 prob = np.clip(prob, 0, 1) 

8476 return KstestResult(D, prob, 

8477 statistic_location=d_location, 

8478 statistic_sign=d_sign) 

8479 

8480 

8481Ks_2sampResult = KstestResult 

8482 

8483 

8484def _compute_prob_outside_square(n, h): 

8485 """ 

8486 Compute the proportion of paths that pass outside the two diagonal lines. 

8487 

8488 Parameters 

8489 ---------- 

8490 n : integer 

8491 n > 0 

8492 h : integer 

8493 0 <= h <= n 

8494 

8495 Returns 

8496 ------- 

8497 p : float 

8498 The proportion of paths that pass outside the lines x-y = +/-h. 

8499 

8500 """ 

8501 # Compute Pr(D_{n,n} >= h/n) 

8502 # Prob = 2 * ( binom(2n, n-h) - binom(2n, n-2a) + binom(2n, n-3a) - ... ) 

8503 # / binom(2n, n) 

8504 # This formulation exhibits subtractive cancellation. 

8505 # Instead divide each term by binom(2n, n), then factor common terms 

8506 # and use a Horner-like algorithm 

8507 # P = 2 * A0 * (1 - A1*(1 - A2*(1 - A3*(1 - A4*(...))))) 

8508 

8509 P = 0.0 

8510 k = int(np.floor(n / h)) 

8511 while k >= 0: 

8512 p1 = 1.0 

8513 # Each of the Ai terms has numerator and denominator with 

8514 # h simple terms. 

8515 for j in range(h): 

8516 p1 = (n - k * h - j) * p1 / (n + k * h + j + 1) 

8517 P = p1 * (1.0 - P) 

8518 k -= 1 

8519 return 2 * P 

8520 

8521 

8522def _count_paths_outside_method(m, n, g, h): 

8523 """Count the number of paths that pass outside the specified diagonal. 

8524 

8525 Parameters 

8526 ---------- 

8527 m : integer 

8528 m > 0 

8529 n : integer 

8530 n > 0 

8531 g : integer 

8532 g is greatest common divisor of m and n 

8533 h : integer 

8534 0 <= h <= lcm(m,n) 

8535 

8536 Returns 

8537 ------- 

8538 p : float 

8539 The number of paths that go low. 

8540 The calculation may overflow - check for a finite answer. 

8541 

8542 Notes 

8543 ----- 

8544 Count the integer lattice paths from (0, 0) to (m, n), which at some 

8545 point (x, y) along the path, satisfy: 

8546 m*y <= n*x - h*g 

8547 The paths make steps of size +1 in either positive x or positive y 

8548 directions. 

8549 

8550 We generally follow Hodges' treatment of Drion/Gnedenko/Korolyuk. 

8551 Hodges, J.L. Jr., 

8552 "The Significance Probability of the Smirnov Two-Sample Test," 

8553 Arkiv fiur Matematik, 3, No. 43 (1958), 469-86. 

8554 

8555 """ 

8556 # Compute #paths which stay lower than x/m-y/n = h/lcm(m,n) 

8557 # B(x, y) = #{paths from (0,0) to (x,y) without 

8558 # previously crossing the boundary} 

8559 # = binom(x, y) - #{paths which already reached the boundary} 

8560 # Multiply by the number of path extensions going from (x, y) to (m, n) 

8561 # Sum. 

8562 

8563 # Probability is symmetrical in m, n. Computation below assumes m >= n. 

8564 if m < n: 

8565 m, n = n, m 

8566 mg = m // g 

8567 ng = n // g 

8568 

8569 # Not every x needs to be considered. 

8570 # xj holds the list of x values to be checked. 

8571 # Wherever n*x/m + ng*h crosses an integer 

8572 lxj = n + (mg-h)//mg 

8573 xj = [(h + mg * j + ng-1)//ng for j in range(lxj)] 

8574 # B is an array just holding a few values of B(x,y), the ones needed. 

8575 # B[j] == B(x_j, j) 

8576 if lxj == 0: 

8577 return special.binom(m + n, n) 

8578 B = np.zeros(lxj) 

8579 B[0] = 1 

8580 # Compute the B(x, y) terms 

8581 for j in range(1, lxj): 

8582 Bj = special.binom(xj[j] + j, j) 

8583 for i in range(j): 

8584 bin = special.binom(xj[j] - xj[i] + j - i, j-i) 

8585 Bj -= bin * B[i] 

8586 B[j] = Bj 

8587 # Compute the number of path extensions... 

8588 num_paths = 0 

8589 for j in range(lxj): 

8590 bin = special.binom((m-xj[j]) + (n - j), n-j) 

8591 term = B[j] * bin 

8592 num_paths += term 

8593 return num_paths 

8594 

8595 

8596def _attempt_exact_2kssamp(n1, n2, g, d, alternative): 

8597 """Attempts to compute the exact 2sample probability. 

8598 

8599 n1, n2 are the sample sizes 

8600 g is the gcd(n1, n2) 

8601 d is the computed max difference in ECDFs 

8602 

8603 Returns (success, d, probability) 

8604 """ 

8605 lcm = (n1 // g) * n2 

8606 h = int(np.round(d * lcm)) 

8607 d = h * 1.0 / lcm 

8608 if h == 0: 

8609 return True, d, 1.0 

8610 saw_fp_error, prob = False, np.nan 

8611 try: 

8612 with np.errstate(invalid="raise", over="raise"): 

8613 if alternative == 'two-sided': 

8614 if n1 == n2: 

8615 prob = _compute_prob_outside_square(n1, h) 

8616 else: 

8617 prob = _compute_outer_prob_inside_method(n1, n2, g, h) 

8618 else: 

8619 if n1 == n2: 

8620 # prob = binom(2n, n-h) / binom(2n, n) 

8621 # Evaluating in that form incurs roundoff errors 

8622 # from special.binom. Instead calculate directly 

8623 jrange = np.arange(h) 

8624 prob = np.prod((n1 - jrange) / (n1 + jrange + 1.0)) 

8625 else: 

8626 with np.errstate(over='raise'): 

8627 num_paths = _count_paths_outside_method(n1, n2, g, h) 

8628 bin = special.binom(n1 + n2, n1) 

8629 if num_paths > bin or np.isinf(bin): 

8630 saw_fp_error = True 

8631 else: 

8632 prob = num_paths / bin 

8633 

8634 except (FloatingPointError, OverflowError): 

8635 saw_fp_error = True 

8636 

8637 if saw_fp_error: 

8638 return False, d, np.nan 

8639 if not (0 <= prob <= 1): 

8640 return False, d, prob 

8641 return True, d, prob 

8642 

8643 

8644@_rename_parameter("mode", "method") 

8645def ks_2samp(data1, data2, alternative='two-sided', method='auto'): 

8646 """ 

8647 Performs the two-sample Kolmogorov-Smirnov test for goodness of fit. 

8648 

8649 This test compares the underlying continuous distributions F(x) and G(x) 

8650 of two independent samples. See Notes for a description of the available 

8651 null and alternative hypotheses. 

8652 

8653 Parameters 

8654 ---------- 

8655 data1, data2 : array_like, 1-Dimensional 

8656 Two arrays of sample observations assumed to be drawn from a continuous 

8657 distribution, sample sizes can be different. 

8658 alternative : {'two-sided', 'less', 'greater'}, optional 

8659 Defines the null and alternative hypotheses. Default is 'two-sided'. 

8660 Please see explanations in the Notes below. 

8661 method : {'auto', 'exact', 'asymp'}, optional 

8662 Defines the method used for calculating the p-value. 

8663 The following options are available (default is 'auto'): 

8664 

8665 * 'auto' : use 'exact' for small size arrays, 'asymp' for large 

8666 * 'exact' : use exact distribution of test statistic 

8667 * 'asymp' : use asymptotic distribution of test statistic 

8668 

8669 Returns 

8670 ------- 

8671 res: KstestResult 

8672 An object containing attributes: 

8673 

8674 statistic : float 

8675 KS test statistic. 

8676 pvalue : float 

8677 One-tailed or two-tailed p-value. 

8678 statistic_location : float 

8679 Value from `data1` or `data2` corresponding with the KS statistic; 

8680 i.e., the distance between the empirical distribution functions is 

8681 measured at this observation. 

8682 statistic_sign : int 

8683 +1 if the empirical distribution function of `data1` exceeds 

8684 the empirical distribution function of `data2` at 

8685 `statistic_location`, otherwise -1. 

8686 

8687 See Also 

8688 -------- 

8689 kstest, ks_1samp, epps_singleton_2samp, anderson_ksamp 

8690 

8691 Notes 

8692 ----- 

8693 There are three options for the null and corresponding alternative 

8694 hypothesis that can be selected using the `alternative` parameter. 

8695 

8696 - `less`: The null hypothesis is that F(x) >= G(x) for all x; the 

8697 alternative is that F(x) < G(x) for at least one x. The statistic 

8698 is the magnitude of the minimum (most negative) difference between the 

8699 empirical distribution functions of the samples. 

8700 

8701 - `greater`: The null hypothesis is that F(x) <= G(x) for all x; the 

8702 alternative is that F(x) > G(x) for at least one x. The statistic 

8703 is the maximum (most positive) difference between the empirical 

8704 distribution functions of the samples. 

8705 

8706 - `two-sided`: The null hypothesis is that the two distributions are 

8707 identical, F(x)=G(x) for all x; the alternative is that they are not 

8708 identical. The statistic is the maximum absolute difference between the 

8709 empirical distribution functions of the samples. 

8710 

8711 Note that the alternative hypotheses describe the *CDFs* of the 

8712 underlying distributions, not the observed values of the data. For example, 

8713 suppose x1 ~ F and x2 ~ G. If F(x) > G(x) for all x, the values in 

8714 x1 tend to be less than those in x2. 

8715 

8716 If the KS statistic is large, then the p-value will be small, and this may 

8717 be taken as evidence against the null hypothesis in favor of the 

8718 alternative. 

8719 

8720 If ``method='exact'``, `ks_2samp` attempts to compute an exact p-value, 

8721 that is, the probability under the null hypothesis of obtaining a test 

8722 statistic value as extreme as the value computed from the data. 

8723 If ``method='asymp'``, the asymptotic Kolmogorov-Smirnov distribution is 

8724 used to compute an approximate p-value. 

8725 If ``method='auto'``, an exact p-value computation is attempted if both 

8726 sample sizes are less than 10000; otherwise, the asymptotic method is used. 

8727 In any case, if an exact p-value calculation is attempted and fails, a 

8728 warning will be emitted, and the asymptotic p-value will be returned. 

8729 

8730 The 'two-sided' 'exact' computation computes the complementary probability 

8731 and then subtracts from 1. As such, the minimum probability it can return 

8732 is about 1e-16. While the algorithm itself is exact, numerical 

8733 errors may accumulate for large sample sizes. It is most suited to 

8734 situations in which one of the sample sizes is only a few thousand. 

8735 

8736 We generally follow Hodges' treatment of Drion/Gnedenko/Korolyuk [1]_. 

8737 

8738 References 

8739 ---------- 

8740 .. [1] Hodges, J.L. Jr., "The Significance Probability of the Smirnov 

8741 Two-Sample Test," Arkiv fiur Matematik, 3, No. 43 (1958), 469-86. 

8742 

8743 Examples 

8744 -------- 

8745 Suppose we wish to test the null hypothesis that two samples were drawn 

8746 from the same distribution. 

8747 We choose a confidence level of 95%; that is, we will reject the null 

8748 hypothesis in favor of the alternative if the p-value is less than 0.05. 

8749 

8750 If the first sample were drawn from a uniform distribution and the second 

8751 were drawn from the standard normal, we would expect the null hypothesis 

8752 to be rejected. 

8753 

8754 >>> import numpy as np 

8755 >>> from scipy import stats 

8756 >>> rng = np.random.default_rng() 

8757 >>> sample1 = stats.uniform.rvs(size=100, random_state=rng) 

8758 >>> sample2 = stats.norm.rvs(size=110, random_state=rng) 

8759 >>> stats.ks_2samp(sample1, sample2) 

8760 KstestResult(statistic=0.5454545454545454, pvalue=7.37417839555191e-15) 

8761 

8762 Indeed, the p-value is lower than our threshold of 0.05, so we reject the 

8763 null hypothesis in favor of the default "two-sided" alternative: the data 

8764 were *not* drawn from the same distribution. 

8765 

8766 When both samples are drawn from the same distribution, we expect the data 

8767 to be consistent with the null hypothesis most of the time. 

8768 

8769 >>> sample1 = stats.norm.rvs(size=105, random_state=rng) 

8770 >>> sample2 = stats.norm.rvs(size=95, random_state=rng) 

8771 >>> stats.ks_2samp(sample1, sample2) 

8772 KstestResult(statistic=0.10927318295739348, pvalue=0.5438289009927495) 

8773 

8774 As expected, the p-value of 0.54 is not below our threshold of 0.05, so 

8775 we cannot reject the null hypothesis. 

8776 

8777 Suppose, however, that the first sample were drawn from 

8778 a normal distribution shifted toward greater values. In this case, 

8779 the cumulative density function (CDF) of the underlying distribution tends 

8780 to be *less* than the CDF underlying the second sample. Therefore, we would 

8781 expect the null hypothesis to be rejected with ``alternative='less'``: 

8782 

8783 >>> sample1 = stats.norm.rvs(size=105, loc=0.5, random_state=rng) 

8784 >>> stats.ks_2samp(sample1, sample2, alternative='less') 

8785 KstestResult(statistic=0.4055137844611529, pvalue=3.5474563068855554e-08) 

8786 

8787 and indeed, with p-value smaller than our threshold, we reject the null 

8788 hypothesis in favor of the alternative. 

8789 

8790 """ 

8791 mode = method 

8792 

8793 if mode not in ['auto', 'exact', 'asymp']: 

8794 raise ValueError(f'Invalid value for mode: {mode}') 

8795 alternative = {'t': 'two-sided', 'g': 'greater', 'l': 'less'}.get( 

8796 alternative.lower()[0], alternative) 

8797 if alternative not in ['two-sided', 'less', 'greater']: 

8798 raise ValueError(f'Invalid value for alternative: {alternative}') 

8799 MAX_AUTO_N = 10000 # 'auto' will attempt to be exact if n1,n2 <= MAX_AUTO_N 

8800 if np.ma.is_masked(data1): 

8801 data1 = data1.compressed() 

8802 if np.ma.is_masked(data2): 

8803 data2 = data2.compressed() 

8804 data1 = np.sort(data1) 

8805 data2 = np.sort(data2) 

8806 n1 = data1.shape[0] 

8807 n2 = data2.shape[0] 

8808 if min(n1, n2) == 0: 

8809 raise ValueError('Data passed to ks_2samp must not be empty') 

8810 

8811 data_all = np.concatenate([data1, data2]) 

8812 # using searchsorted solves equal data problem 

8813 cdf1 = np.searchsorted(data1, data_all, side='right') / n1 

8814 cdf2 = np.searchsorted(data2, data_all, side='right') / n2 

8815 cddiffs = cdf1 - cdf2 

8816 

8817 # Identify the location of the statistic 

8818 argminS = np.argmin(cddiffs) 

8819 argmaxS = np.argmax(cddiffs) 

8820 loc_minS = data_all[argminS] 

8821 loc_maxS = data_all[argmaxS] 

8822 

8823 # Ensure sign of minS is not negative. 

8824 minS = np.clip(-cddiffs[argminS], 0, 1) 

8825 maxS = cddiffs[argmaxS] 

8826 

8827 if alternative == 'less' or (alternative == 'two-sided' and minS > maxS): 

8828 d = minS 

8829 d_location = loc_minS 

8830 d_sign = -1 

8831 else: 

8832 d = maxS 

8833 d_location = loc_maxS 

8834 d_sign = 1 

8835 g = gcd(n1, n2) 

8836 n1g = n1 // g 

8837 n2g = n2 // g 

8838 prob = -np.inf 

8839 if mode == 'auto': 

8840 mode = 'exact' if max(n1, n2) <= MAX_AUTO_N else 'asymp' 

8841 elif mode == 'exact': 

8842 # If lcm(n1, n2) is too big, switch from exact to asymp 

8843 if n1g >= np.iinfo(np.int32).max / n2g: 

8844 mode = 'asymp' 

8845 warnings.warn( 

8846 f"Exact ks_2samp calculation not possible with samples sizes " 

8847 f"{n1} and {n2}. Switching to 'asymp'.", RuntimeWarning, 

8848 stacklevel=3) 

8849 

8850 if mode == 'exact': 

8851 success, d, prob = _attempt_exact_2kssamp(n1, n2, g, d, alternative) 

8852 if not success: 

8853 mode = 'asymp' 

8854 warnings.warn(f"ks_2samp: Exact calculation unsuccessful. " 

8855 f"Switching to method={mode}.", RuntimeWarning, 

8856 stacklevel=3) 

8857 

8858 if mode == 'asymp': 

8859 # The product n1*n2 is large. Use Smirnov's asymptoptic formula. 

8860 # Ensure float to avoid overflow in multiplication 

8861 # sorted because the one-sided formula is not symmetric in n1, n2 

8862 m, n = sorted([float(n1), float(n2)], reverse=True) 

8863 en = m * n / (m + n) 

8864 if alternative == 'two-sided': 

8865 prob = distributions.kstwo.sf(d, np.round(en)) 

8866 else: 

8867 z = np.sqrt(en) * d 

8868 # Use Hodges' suggested approximation Eqn 5.3 

8869 # Requires m to be the larger of (n1, n2) 

8870 expt = -2 * z**2 - 2 * z * (m + 2*n)/np.sqrt(m*n*(m+n))/3.0 

8871 prob = np.exp(expt) 

8872 

8873 prob = np.clip(prob, 0, 1) 

8874 return KstestResult(d, prob, statistic_location=d_location, 

8875 statistic_sign=d_sign) 

8876 

8877 

8878def _parse_kstest_args(data1, data2, args, N): 

8879 # kstest allows many different variations of arguments. 

8880 # Pull out the parsing into a separate function 

8881 # (xvals, yvals, ) # 2sample 

8882 # (xvals, cdf function,..) 

8883 # (xvals, name of distribution, ...) 

8884 # (name of distribution, name of distribution, ...) 

8885 

8886 # Returns xvals, yvals, cdf 

8887 # where cdf is a cdf function, or None 

8888 # and yvals is either an array_like of values, or None 

8889 # and xvals is array_like. 

8890 rvsfunc, cdf = None, None 

8891 if isinstance(data1, str): 

8892 rvsfunc = getattr(distributions, data1).rvs 

8893 elif callable(data1): 

8894 rvsfunc = data1 

8895 

8896 if isinstance(data2, str): 

8897 cdf = getattr(distributions, data2).cdf 

8898 data2 = None 

8899 elif callable(data2): 

8900 cdf = data2 

8901 data2 = None 

8902 

8903 data1 = np.sort(rvsfunc(*args, size=N) if rvsfunc else data1) 

8904 return data1, data2, cdf 

8905 

8906 

8907@_rename_parameter("mode", "method") 

8908def kstest(rvs, cdf, args=(), N=20, alternative='two-sided', method='auto'): 

8909 """ 

8910 Performs the (one-sample or two-sample) Kolmogorov-Smirnov test for 

8911 goodness of fit. 

8912 

8913 The one-sample test compares the underlying distribution F(x) of a sample 

8914 against a given distribution G(x). The two-sample test compares the 

8915 underlying distributions of two independent samples. Both tests are valid 

8916 only for continuous distributions. 

8917 

8918 Parameters 

8919 ---------- 

8920 rvs : str, array_like, or callable 

8921 If an array, it should be a 1-D array of observations of random 

8922 variables. 

8923 If a callable, it should be a function to generate random variables; 

8924 it is required to have a keyword argument `size`. 

8925 If a string, it should be the name of a distribution in `scipy.stats`, 

8926 which will be used to generate random variables. 

8927 cdf : str, array_like or callable 

8928 If array_like, it should be a 1-D array of observations of random 

8929 variables, and the two-sample test is performed 

8930 (and rvs must be array_like). 

8931 If a callable, that callable is used to calculate the cdf. 

8932 If a string, it should be the name of a distribution in `scipy.stats`, 

8933 which will be used as the cdf function. 

8934 args : tuple, sequence, optional 

8935 Distribution parameters, used if `rvs` or `cdf` are strings or 

8936 callables. 

8937 N : int, optional 

8938 Sample size if `rvs` is string or callable. Default is 20. 

8939 alternative : {'two-sided', 'less', 'greater'}, optional 

8940 Defines the null and alternative hypotheses. Default is 'two-sided'. 

8941 Please see explanations in the Notes below. 

8942 method : {'auto', 'exact', 'approx', 'asymp'}, optional 

8943 Defines the distribution used for calculating the p-value. 

8944 The following options are available (default is 'auto'): 

8945 

8946 * 'auto' : selects one of the other options. 

8947 * 'exact' : uses the exact distribution of test statistic. 

8948 * 'approx' : approximates the two-sided probability with twice the 

8949 one-sided probability 

8950 * 'asymp': uses asymptotic distribution of test statistic 

8951 

8952 Returns 

8953 ------- 

8954 res: KstestResult 

8955 An object containing attributes: 

8956 

8957 statistic : float 

8958 KS test statistic, either D+, D-, or D (the maximum of the two) 

8959 pvalue : float 

8960 One-tailed or two-tailed p-value. 

8961 statistic_location : float 

8962 In a one-sample test, this is the value of `rvs` 

8963 corresponding with the KS statistic; i.e., the distance between 

8964 the empirical distribution function and the hypothesized cumulative 

8965 distribution function is measured at this observation. 

8966 

8967 In a two-sample test, this is the value from `rvs` or `cdf` 

8968 corresponding with the KS statistic; i.e., the distance between 

8969 the empirical distribution functions is measured at this 

8970 observation. 

8971 statistic_sign : int 

8972 In a one-sample test, this is +1 if the KS statistic is the 

8973 maximum positive difference between the empirical distribution 

8974 function and the hypothesized cumulative distribution function 

8975 (D+); it is -1 if the KS statistic is the maximum negative 

8976 difference (D-). 

8977 

8978 In a two-sample test, this is +1 if the empirical distribution 

8979 function of `rvs` exceeds the empirical distribution 

8980 function of `cdf` at `statistic_location`, otherwise -1. 

8981 

8982 See Also 

8983 -------- 

8984 ks_1samp, ks_2samp 

8985 

8986 Notes 

8987 ----- 

8988 There are three options for the null and corresponding alternative 

8989 hypothesis that can be selected using the `alternative` parameter. 

8990 

8991 - `two-sided`: The null hypothesis is that the two distributions are 

8992 identical, F(x)=G(x) for all x; the alternative is that they are not 

8993 identical. 

8994 

8995 - `less`: The null hypothesis is that F(x) >= G(x) for all x; the 

8996 alternative is that F(x) < G(x) for at least one x. 

8997 

8998 - `greater`: The null hypothesis is that F(x) <= G(x) for all x; the 

8999 alternative is that F(x) > G(x) for at least one x. 

9000 

9001 Note that the alternative hypotheses describe the *CDFs* of the 

9002 underlying distributions, not the observed values. For example, 

9003 suppose x1 ~ F and x2 ~ G. If F(x) > G(x) for all x, the values in 

9004 x1 tend to be less than those in x2. 

9005 

9006 

9007 Examples 

9008 -------- 

9009 Suppose we wish to test the null hypothesis that a sample is distributed 

9010 according to the standard normal. 

9011 We choose a confidence level of 95%; that is, we will reject the null 

9012 hypothesis in favor of the alternative if the p-value is less than 0.05. 

9013 

9014 When testing uniformly distributed data, we would expect the 

9015 null hypothesis to be rejected. 

9016 

9017 >>> import numpy as np 

9018 >>> from scipy import stats 

9019 >>> rng = np.random.default_rng() 

9020 >>> stats.kstest(stats.uniform.rvs(size=100, random_state=rng), 

9021 ... stats.norm.cdf) 

9022 KstestResult(statistic=0.5001899973268688, pvalue=1.1616392184763533e-23) 

9023 

9024 Indeed, the p-value is lower than our threshold of 0.05, so we reject the 

9025 null hypothesis in favor of the default "two-sided" alternative: the data 

9026 are *not* distributed according to the standard normal. 

9027 

9028 When testing random variates from the standard normal distribution, we 

9029 expect the data to be consistent with the null hypothesis most of the time. 

9030 

9031 >>> x = stats.norm.rvs(size=100, random_state=rng) 

9032 >>> stats.kstest(x, stats.norm.cdf) 

9033 KstestResult(statistic=0.05345882212970396, pvalue=0.9227159037744717) 

9034 

9035 As expected, the p-value of 0.92 is not below our threshold of 0.05, so 

9036 we cannot reject the null hypothesis. 

9037 

9038 Suppose, however, that the random variates are distributed according to 

9039 a normal distribution that is shifted toward greater values. In this case, 

9040 the cumulative density function (CDF) of the underlying distribution tends 

9041 to be *less* than the CDF of the standard normal. Therefore, we would 

9042 expect the null hypothesis to be rejected with ``alternative='less'``: 

9043 

9044 >>> x = stats.norm.rvs(size=100, loc=0.5, random_state=rng) 

9045 >>> stats.kstest(x, stats.norm.cdf, alternative='less') 

9046 KstestResult(statistic=0.17482387821055168, pvalue=0.001913921057766743) 

9047 

9048 and indeed, with p-value smaller than our threshold, we reject the null 

9049 hypothesis in favor of the alternative. 

9050 

9051 For convenience, the previous test can be performed using the name of the 

9052 distribution as the second argument. 

9053 

9054 >>> stats.kstest(x, "norm", alternative='less') 

9055 KstestResult(statistic=0.17482387821055168, pvalue=0.001913921057766743) 

9056 

9057 The examples above have all been one-sample tests identical to those 

9058 performed by `ks_1samp`. Note that `kstest` can also perform two-sample 

9059 tests identical to those performed by `ks_2samp`. For example, when two 

9060 samples are drawn from the same distribution, we expect the data to be 

9061 consistent with the null hypothesis most of the time. 

9062 

9063 >>> sample1 = stats.laplace.rvs(size=105, random_state=rng) 

9064 >>> sample2 = stats.laplace.rvs(size=95, random_state=rng) 

9065 >>> stats.kstest(sample1, sample2) 

9066 KstestResult(statistic=0.11779448621553884, pvalue=0.4494256912629795) 

9067 

9068 As expected, the p-value of 0.45 is not below our threshold of 0.05, so 

9069 we cannot reject the null hypothesis. 

9070 

9071 """ 

9072 # to not break compatibility with existing code 

9073 if alternative == 'two_sided': 

9074 alternative = 'two-sided' 

9075 if alternative not in ['two-sided', 'greater', 'less']: 

9076 raise ValueError("Unexpected alternative %s" % alternative) 

9077 xvals, yvals, cdf = _parse_kstest_args(rvs, cdf, args, N) 

9078 if cdf: 

9079 return ks_1samp(xvals, cdf, args=args, alternative=alternative, 

9080 method=method) 

9081 return ks_2samp(xvals, yvals, alternative=alternative, method=method) 

9082 

9083 

9084def tiecorrect(rankvals): 

9085 """Tie correction factor for Mann-Whitney U and Kruskal-Wallis H tests. 

9086 

9087 Parameters 

9088 ---------- 

9089 rankvals : array_like 

9090 A 1-D sequence of ranks. Typically this will be the array 

9091 returned by `~scipy.stats.rankdata`. 

9092 

9093 Returns 

9094 ------- 

9095 factor : float 

9096 Correction factor for U or H. 

9097 

9098 See Also 

9099 -------- 

9100 rankdata : Assign ranks to the data 

9101 mannwhitneyu : Mann-Whitney rank test 

9102 kruskal : Kruskal-Wallis H test 

9103 

9104 References 

9105 ---------- 

9106 .. [1] Siegel, S. (1956) Nonparametric Statistics for the Behavioral 

9107 Sciences. New York: McGraw-Hill. 

9108 

9109 Examples 

9110 -------- 

9111 >>> from scipy.stats import tiecorrect, rankdata 

9112 >>> tiecorrect([1, 2.5, 2.5, 4]) 

9113 0.9 

9114 >>> ranks = rankdata([1, 3, 2, 4, 5, 7, 2, 8, 4]) 

9115 >>> ranks 

9116 array([ 1. , 4. , 2.5, 5.5, 7. , 8. , 2.5, 9. , 5.5]) 

9117 >>> tiecorrect(ranks) 

9118 0.9833333333333333 

9119 

9120 """ 

9121 arr = np.sort(rankvals) 

9122 idx = np.nonzero(np.r_[True, arr[1:] != arr[:-1], True])[0] 

9123 cnt = np.diff(idx).astype(np.float64) 

9124 

9125 size = np.float64(arr.size) 

9126 return 1.0 if size < 2 else 1.0 - (cnt**3 - cnt).sum() / (size**3 - size) 

9127 

9128 

9129RanksumsResult = namedtuple('RanksumsResult', ('statistic', 'pvalue')) 

9130 

9131 

9132@_axis_nan_policy_factory(RanksumsResult, n_samples=2) 

9133def ranksums(x, y, alternative='two-sided'): 

9134 """Compute the Wilcoxon rank-sum statistic for two samples. 

9135 

9136 The Wilcoxon rank-sum test tests the null hypothesis that two sets 

9137 of measurements are drawn from the same distribution. The alternative 

9138 hypothesis is that values in one sample are more likely to be 

9139 larger than the values in the other sample. 

9140 

9141 This test should be used to compare two samples from continuous 

9142 distributions. It does not handle ties between measurements 

9143 in x and y. For tie-handling and an optional continuity correction 

9144 see `scipy.stats.mannwhitneyu`. 

9145 

9146 Parameters 

9147 ---------- 

9148 x,y : array_like 

9149 The data from the two samples. 

9150 alternative : {'two-sided', 'less', 'greater'}, optional 

9151 Defines the alternative hypothesis. Default is 'two-sided'. 

9152 The following options are available: 

9153 

9154 * 'two-sided': one of the distributions (underlying `x` or `y`) is 

9155 stochastically greater than the other. 

9156 * 'less': the distribution underlying `x` is stochastically less 

9157 than the distribution underlying `y`. 

9158 * 'greater': the distribution underlying `x` is stochastically greater 

9159 than the distribution underlying `y`. 

9160 

9161 .. versionadded:: 1.7.0 

9162 

9163 Returns 

9164 ------- 

9165 statistic : float 

9166 The test statistic under the large-sample approximation that the 

9167 rank sum statistic is normally distributed. 

9168 pvalue : float 

9169 The p-value of the test. 

9170 

9171 References 

9172 ---------- 

9173 .. [1] https://en.wikipedia.org/wiki/Wilcoxon_rank-sum_test 

9174 

9175 Examples 

9176 -------- 

9177 We can test the hypothesis that two independent unequal-sized samples are 

9178 drawn from the same distribution with computing the Wilcoxon rank-sum 

9179 statistic. 

9180 

9181 >>> import numpy as np 

9182 >>> from scipy.stats import ranksums 

9183 >>> rng = np.random.default_rng() 

9184 >>> sample1 = rng.uniform(-1, 1, 200) 

9185 >>> sample2 = rng.uniform(-0.5, 1.5, 300) # a shifted distribution 

9186 >>> ranksums(sample1, sample2) 

9187 RanksumsResult(statistic=-7.887059, pvalue=3.09390448e-15) # may vary 

9188 >>> ranksums(sample1, sample2, alternative='less') 

9189 RanksumsResult(statistic=-7.750585297581713, pvalue=4.573497606342543e-15) # may vary 

9190 >>> ranksums(sample1, sample2, alternative='greater') 

9191 RanksumsResult(statistic=-7.750585297581713, pvalue=0.9999999999999954) # may vary 

9192 

9193 The p-value of less than ``0.05`` indicates that this test rejects the 

9194 hypothesis at the 5% significance level. 

9195 

9196 """ 

9197 x, y = map(np.asarray, (x, y)) 

9198 n1 = len(x) 

9199 n2 = len(y) 

9200 alldata = np.concatenate((x, y)) 

9201 ranked = rankdata(alldata) 

9202 x = ranked[:n1] 

9203 s = np.sum(x, axis=0) 

9204 expected = n1 * (n1+n2+1) / 2.0 

9205 z = (s - expected) / np.sqrt(n1*n2*(n1+n2+1)/12.0) 

9206 z, prob = _normtest_finish(z, alternative) 

9207 

9208 return RanksumsResult(z, prob) 

9209 

9210 

9211KruskalResult = namedtuple('KruskalResult', ('statistic', 'pvalue')) 

9212 

9213 

9214@_axis_nan_policy_factory(KruskalResult, n_samples=None) 

9215def kruskal(*samples, nan_policy='propagate'): 

9216 """Compute the Kruskal-Wallis H-test for independent samples. 

9217 

9218 The Kruskal-Wallis H-test tests the null hypothesis that the population 

9219 median of all of the groups are equal. It is a non-parametric version of 

9220 ANOVA. The test works on 2 or more independent samples, which may have 

9221 different sizes. Note that rejecting the null hypothesis does not 

9222 indicate which of the groups differs. Post hoc comparisons between 

9223 groups are required to determine which groups are different. 

9224 

9225 Parameters 

9226 ---------- 

9227 sample1, sample2, ... : array_like 

9228 Two or more arrays with the sample measurements can be given as 

9229 arguments. Samples must be one-dimensional. 

9230 nan_policy : {'propagate', 'raise', 'omit'}, optional 

9231 Defines how to handle when input contains nan. 

9232 The following options are available (default is 'propagate'): 

9233 

9234 * 'propagate': returns nan 

9235 * 'raise': throws an error 

9236 * 'omit': performs the calculations ignoring nan values 

9237 

9238 Returns 

9239 ------- 

9240 statistic : float 

9241 The Kruskal-Wallis H statistic, corrected for ties. 

9242 pvalue : float 

9243 The p-value for the test using the assumption that H has a chi 

9244 square distribution. The p-value returned is the survival function of 

9245 the chi square distribution evaluated at H. 

9246 

9247 See Also 

9248 -------- 

9249 f_oneway : 1-way ANOVA. 

9250 mannwhitneyu : Mann-Whitney rank test on two samples. 

9251 friedmanchisquare : Friedman test for repeated measurements. 

9252 

9253 Notes 

9254 ----- 

9255 Due to the assumption that H has a chi square distribution, the number 

9256 of samples in each group must not be too small. A typical rule is 

9257 that each sample must have at least 5 measurements. 

9258 

9259 References 

9260 ---------- 

9261 .. [1] W. H. Kruskal & W. W. Wallis, "Use of Ranks in 

9262 One-Criterion Variance Analysis", Journal of the American Statistical 

9263 Association, Vol. 47, Issue 260, pp. 583-621, 1952. 

9264 .. [2] https://en.wikipedia.org/wiki/Kruskal-Wallis_one-way_analysis_of_variance 

9265 

9266 Examples 

9267 -------- 

9268 >>> from scipy import stats 

9269 >>> x = [1, 3, 5, 7, 9] 

9270 >>> y = [2, 4, 6, 8, 10] 

9271 >>> stats.kruskal(x, y) 

9272 KruskalResult(statistic=0.2727272727272734, pvalue=0.6015081344405895) 

9273 

9274 >>> x = [1, 1, 1] 

9275 >>> y = [2, 2, 2] 

9276 >>> z = [2, 2] 

9277 >>> stats.kruskal(x, y, z) 

9278 KruskalResult(statistic=7.0, pvalue=0.0301973834223185) 

9279 

9280 """ 

9281 samples = list(map(np.asarray, samples)) 

9282 

9283 num_groups = len(samples) 

9284 if num_groups < 2: 

9285 raise ValueError("Need at least two groups in stats.kruskal()") 

9286 

9287 for sample in samples: 

9288 if sample.size == 0: 

9289 NaN = _get_nan(*samples) 

9290 return KruskalResult(NaN, NaN) 

9291 elif sample.ndim != 1: 

9292 raise ValueError("Samples must be one-dimensional.") 

9293 

9294 n = np.asarray(list(map(len, samples))) 

9295 

9296 if nan_policy not in ('propagate', 'raise', 'omit'): 

9297 raise ValueError("nan_policy must be 'propagate', 'raise' or 'omit'") 

9298 

9299 contains_nan = False 

9300 for sample in samples: 

9301 cn = _contains_nan(sample, nan_policy) 

9302 if cn[0]: 

9303 contains_nan = True 

9304 break 

9305 

9306 if contains_nan and nan_policy == 'omit': 

9307 for sample in samples: 

9308 sample = ma.masked_invalid(sample) 

9309 return mstats_basic.kruskal(*samples) 

9310 

9311 if contains_nan and nan_policy == 'propagate': 

9312 return KruskalResult(np.nan, np.nan) 

9313 

9314 alldata = np.concatenate(samples) 

9315 ranked = rankdata(alldata) 

9316 ties = tiecorrect(ranked) 

9317 if ties == 0: 

9318 raise ValueError('All numbers are identical in kruskal') 

9319 

9320 # Compute sum^2/n for each group and sum 

9321 j = np.insert(np.cumsum(n), 0, 0) 

9322 ssbn = 0 

9323 for i in range(num_groups): 

9324 ssbn += _square_of_sums(ranked[j[i]:j[i+1]]) / n[i] 

9325 

9326 totaln = np.sum(n, dtype=float) 

9327 h = 12.0 / (totaln * (totaln + 1)) * ssbn - 3 * (totaln + 1) 

9328 df = num_groups - 1 

9329 h /= ties 

9330 

9331 return KruskalResult(h, distributions.chi2.sf(h, df)) 

9332 

9333 

9334FriedmanchisquareResult = namedtuple('FriedmanchisquareResult', 

9335 ('statistic', 'pvalue')) 

9336 

9337 

9338def friedmanchisquare(*samples): 

9339 """Compute the Friedman test for repeated samples. 

9340 

9341 The Friedman test tests the null hypothesis that repeated samples of 

9342 the same individuals have the same distribution. It is often used 

9343 to test for consistency among samples obtained in different ways. 

9344 For example, if two sampling techniques are used on the same set of 

9345 individuals, the Friedman test can be used to determine if the two 

9346 sampling techniques are consistent. 

9347 

9348 Parameters 

9349 ---------- 

9350 sample1, sample2, sample3... : array_like 

9351 Arrays of observations. All of the arrays must have the same number 

9352 of elements. At least three samples must be given. 

9353 

9354 Returns 

9355 ------- 

9356 statistic : float 

9357 The test statistic, correcting for ties. 

9358 pvalue : float 

9359 The associated p-value assuming that the test statistic has a chi 

9360 squared distribution. 

9361 

9362 Notes 

9363 ----- 

9364 Due to the assumption that the test statistic has a chi squared 

9365 distribution, the p-value is only reliable for n > 10 and more than 

9366 6 repeated samples. 

9367 

9368 References 

9369 ---------- 

9370 .. [1] https://en.wikipedia.org/wiki/Friedman_test 

9371 .. [2] P. Sprent and N.C. Smeeton, "Applied Nonparametric Statistical 

9372 Methods, Third Edition". Chapter 6, Section 6.3.2. 

9373 

9374 Examples 

9375 -------- 

9376 In [2]_, the pulse rate (per minute) of a group of seven students was 

9377 measured before exercise, immediately after exercise and 5 minutes 

9378 after exercise. Is there evidence to suggest that the pulse rates on 

9379 these three occasions are similar? 

9380 

9381 We begin by formulating a null hypothesis :math:`H_0`: 

9382 

9383 The pulse rates are identical on these three occasions. 

9384 

9385 Let's assess the plausibility of this hypothesis with a Friedman test. 

9386 

9387 >>> from scipy.stats import friedmanchisquare 

9388 >>> before = [72, 96, 88, 92, 74, 76, 82] 

9389 >>> immediately_after = [120, 120, 132, 120, 101, 96, 112] 

9390 >>> five_min_after = [76, 95, 104, 96, 84, 72, 76] 

9391 >>> res = friedmanchisquare(before, immediately_after, five_min_after) 

9392 >>> res.statistic 

9393 10.57142857142857 

9394 >>> res.pvalue 

9395 0.005063414171757498 

9396 

9397 Using a significance level of 5%, we would reject the null hypothesis in 

9398 favor of the alternative hypothesis: "the pulse rates are different on 

9399 these three occasions". 

9400 

9401 """ 

9402 k = len(samples) 

9403 if k < 3: 

9404 raise ValueError('At least 3 sets of samples must be given ' 

9405 'for Friedman test, got {}.'.format(k)) 

9406 

9407 n = len(samples[0]) 

9408 for i in range(1, k): 

9409 if len(samples[i]) != n: 

9410 raise ValueError('Unequal N in friedmanchisquare. Aborting.') 

9411 

9412 # Rank data 

9413 data = np.vstack(samples).T 

9414 data = data.astype(float) 

9415 for i in range(len(data)): 

9416 data[i] = rankdata(data[i]) 

9417 

9418 # Handle ties 

9419 ties = 0 

9420 for d in data: 

9421 replist, repnum = find_repeats(array(d)) 

9422 for t in repnum: 

9423 ties += t * (t*t - 1) 

9424 c = 1 - ties / (k*(k*k - 1)*n) 

9425 

9426 ssbn = np.sum(data.sum(axis=0)**2) 

9427 chisq = (12.0 / (k*n*(k+1)) * ssbn - 3*n*(k+1)) / c 

9428 

9429 return FriedmanchisquareResult(chisq, distributions.chi2.sf(chisq, k - 1)) 

9430 

9431 

9432BrunnerMunzelResult = namedtuple('BrunnerMunzelResult', 

9433 ('statistic', 'pvalue')) 

9434 

9435 

9436def brunnermunzel(x, y, alternative="two-sided", distribution="t", 

9437 nan_policy='propagate'): 

9438 """Compute the Brunner-Munzel test on samples x and y. 

9439 

9440 The Brunner-Munzel test is a nonparametric test of the null hypothesis that 

9441 when values are taken one by one from each group, the probabilities of 

9442 getting large values in both groups are equal. 

9443 Unlike the Wilcoxon-Mann-Whitney's U test, this does not require the 

9444 assumption of equivariance of two groups. Note that this does not assume 

9445 the distributions are same. This test works on two independent samples, 

9446 which may have different sizes. 

9447 

9448 Parameters 

9449 ---------- 

9450 x, y : array_like 

9451 Array of samples, should be one-dimensional. 

9452 alternative : {'two-sided', 'less', 'greater'}, optional 

9453 Defines the alternative hypothesis. 

9454 The following options are available (default is 'two-sided'): 

9455 

9456 * 'two-sided' 

9457 * 'less': one-sided 

9458 * 'greater': one-sided 

9459 distribution : {'t', 'normal'}, optional 

9460 Defines how to get the p-value. 

9461 The following options are available (default is 't'): 

9462 

9463 * 't': get the p-value by t-distribution 

9464 * 'normal': get the p-value by standard normal distribution. 

9465 nan_policy : {'propagate', 'raise', 'omit'}, optional 

9466 Defines how to handle when input contains nan. 

9467 The following options are available (default is 'propagate'): 

9468 

9469 * 'propagate': returns nan 

9470 * 'raise': throws an error 

9471 * 'omit': performs the calculations ignoring nan values 

9472 

9473 Returns 

9474 ------- 

9475 statistic : float 

9476 The Brunner-Munzer W statistic. 

9477 pvalue : float 

9478 p-value assuming an t distribution. One-sided or 

9479 two-sided, depending on the choice of `alternative` and `distribution`. 

9480 

9481 See Also 

9482 -------- 

9483 mannwhitneyu : Mann-Whitney rank test on two samples. 

9484 

9485 Notes 

9486 ----- 

9487 Brunner and Munzel recommended to estimate the p-value by t-distribution 

9488 when the size of data is 50 or less. If the size is lower than 10, it would 

9489 be better to use permuted Brunner Munzel test (see [2]_). 

9490 

9491 References 

9492 ---------- 

9493 .. [1] Brunner, E. and Munzel, U. "The nonparametric Benhrens-Fisher 

9494 problem: Asymptotic theory and a small-sample approximation". 

9495 Biometrical Journal. Vol. 42(2000): 17-25. 

9496 .. [2] Neubert, K. and Brunner, E. "A studentized permutation test for the 

9497 non-parametric Behrens-Fisher problem". Computational Statistics and 

9498 Data Analysis. Vol. 51(2007): 5192-5204. 

9499 

9500 Examples 

9501 -------- 

9502 >>> from scipy import stats 

9503 >>> x1 = [1,2,1,1,1,1,1,1,1,1,2,4,1,1] 

9504 >>> x2 = [3,3,4,3,1,2,3,1,1,5,4] 

9505 >>> w, p_value = stats.brunnermunzel(x1, x2) 

9506 >>> w 

9507 3.1374674823029505 

9508 >>> p_value 

9509 0.0057862086661515377 

9510 

9511 """ 

9512 x = np.asarray(x) 

9513 y = np.asarray(y) 

9514 

9515 # check both x and y 

9516 cnx, npx = _contains_nan(x, nan_policy) 

9517 cny, npy = _contains_nan(y, nan_policy) 

9518 contains_nan = cnx or cny 

9519 if npx == "omit" or npy == "omit": 

9520 nan_policy = "omit" 

9521 

9522 if contains_nan and nan_policy == "propagate": 

9523 return BrunnerMunzelResult(np.nan, np.nan) 

9524 elif contains_nan and nan_policy == "omit": 

9525 x = ma.masked_invalid(x) 

9526 y = ma.masked_invalid(y) 

9527 return mstats_basic.brunnermunzel(x, y, alternative, distribution) 

9528 

9529 nx = len(x) 

9530 ny = len(y) 

9531 if nx == 0 or ny == 0: 

9532 return BrunnerMunzelResult(np.nan, np.nan) 

9533 rankc = rankdata(np.concatenate((x, y))) 

9534 rankcx = rankc[0:nx] 

9535 rankcy = rankc[nx:nx+ny] 

9536 rankcx_mean = np.mean(rankcx) 

9537 rankcy_mean = np.mean(rankcy) 

9538 rankx = rankdata(x) 

9539 ranky = rankdata(y) 

9540 rankx_mean = np.mean(rankx) 

9541 ranky_mean = np.mean(ranky) 

9542 

9543 Sx = np.sum(np.power(rankcx - rankx - rankcx_mean + rankx_mean, 2.0)) 

9544 Sx /= nx - 1 

9545 Sy = np.sum(np.power(rankcy - ranky - rankcy_mean + ranky_mean, 2.0)) 

9546 Sy /= ny - 1 

9547 

9548 wbfn = nx * ny * (rankcy_mean - rankcx_mean) 

9549 wbfn /= (nx + ny) * np.sqrt(nx * Sx + ny * Sy) 

9550 

9551 if distribution == "t": 

9552 df_numer = np.power(nx * Sx + ny * Sy, 2.0) 

9553 df_denom = np.power(nx * Sx, 2.0) / (nx - 1) 

9554 df_denom += np.power(ny * Sy, 2.0) / (ny - 1) 

9555 df = df_numer / df_denom 

9556 

9557 if (df_numer == 0) and (df_denom == 0): 

9558 message = ("p-value cannot be estimated with `distribution='t' " 

9559 "because degrees of freedom parameter is undefined " 

9560 "(0/0). Try using `distribution='normal'") 

9561 warnings.warn(message, RuntimeWarning) 

9562 

9563 p = distributions.t.cdf(wbfn, df) 

9564 elif distribution == "normal": 

9565 p = distributions.norm.cdf(wbfn) 

9566 else: 

9567 raise ValueError( 

9568 "distribution should be 't' or 'normal'") 

9569 

9570 if alternative == "greater": 

9571 pass 

9572 elif alternative == "less": 

9573 p = 1 - p 

9574 elif alternative == "two-sided": 

9575 p = 2 * np.min([p, 1-p]) 

9576 else: 

9577 raise ValueError( 

9578 "alternative should be 'less', 'greater' or 'two-sided'") 

9579 

9580 return BrunnerMunzelResult(wbfn, p) 

9581 

9582 

9583def combine_pvalues(pvalues, method='fisher', weights=None): 

9584 """ 

9585 Combine p-values from independent tests that bear upon the same hypothesis. 

9586 

9587 These methods are intended only for combining p-values from hypothesis 

9588 tests based upon continuous distributions. 

9589 

9590 Each method assumes that under the null hypothesis, the p-values are 

9591 sampled independently and uniformly from the interval [0, 1]. A test 

9592 statistic (different for each method) is computed and a combined 

9593 p-value is calculated based upon the distribution of this test statistic 

9594 under the null hypothesis. 

9595 

9596 Parameters 

9597 ---------- 

9598 pvalues : array_like, 1-D 

9599 Array of p-values assumed to come from independent tests based on 

9600 continuous distributions. 

9601 method : {'fisher', 'pearson', 'tippett', 'stouffer', 'mudholkar_george'} 

9602 

9603 Name of method to use to combine p-values. 

9604 

9605 The available methods are (see Notes for details): 

9606 

9607 * 'fisher': Fisher's method (Fisher's combined probability test) 

9608 * 'pearson': Pearson's method 

9609 * 'mudholkar_george': Mudholkar's and George's method 

9610 * 'tippett': Tippett's method 

9611 * 'stouffer': Stouffer's Z-score method 

9612 weights : array_like, 1-D, optional 

9613 Optional array of weights used only for Stouffer's Z-score method. 

9614 

9615 Returns 

9616 ------- 

9617 res : SignificanceResult 

9618 An object containing attributes: 

9619 

9620 statistic : float 

9621 The statistic calculated by the specified method. 

9622 pvalue : float 

9623 The combined p-value. 

9624 

9625 Notes 

9626 ----- 

9627 If this function is applied to tests with a discrete statistics such as 

9628 any rank test or contingency-table test, it will yield systematically 

9629 wrong results, e.g. Fisher's method will systematically overestimate the 

9630 p-value [1]_. This problem becomes less severe for large sample sizes 

9631 when the discrete distributions become approximately continuous. 

9632 

9633 The differences between the methods can be best illustrated by their 

9634 statistics and what aspects of a combination of p-values they emphasise 

9635 when considering significance [2]_. For example, methods emphasising large 

9636 p-values are more sensitive to strong false and true negatives; conversely 

9637 methods focussing on small p-values are sensitive to positives. 

9638 

9639 * The statistics of Fisher's method (also known as Fisher's combined 

9640 probability test) [3]_ is :math:`-2\\sum_i \\log(p_i)`, which is 

9641 equivalent (as a test statistics) to the product of individual p-values: 

9642 :math:`\\prod_i p_i`. Under the null hypothesis, this statistics follows 

9643 a :math:`\\chi^2` distribution. This method emphasises small p-values. 

9644 * Pearson's method uses :math:`-2\\sum_i\\log(1-p_i)`, which is equivalent 

9645 to :math:`\\prod_i \\frac{1}{1-p_i}` [2]_. 

9646 It thus emphasises large p-values. 

9647 * Mudholkar and George compromise between Fisher's and Pearson's method by 

9648 averaging their statistics [4]_. Their method emphasises extreme 

9649 p-values, both close to 1 and 0. 

9650 * Stouffer's method [5]_ uses Z-scores and the statistic: 

9651 :math:`\\sum_i \\Phi^{-1} (p_i)`, where :math:`\\Phi` is the CDF of the 

9652 standard normal distribution. The advantage of this method is that it is 

9653 straightforward to introduce weights, which can make Stouffer's method 

9654 more powerful than Fisher's method when the p-values are from studies 

9655 of different size [6]_ [7]_. 

9656 * Tippett's method uses the smallest p-value as a statistic. 

9657 (Mind that this minimum is not the combined p-value.) 

9658 

9659 Fisher's method may be extended to combine p-values from dependent tests 

9660 [8]_. Extensions such as Brown's method and Kost's method are not currently 

9661 implemented. 

9662 

9663 .. versionadded:: 0.15.0 

9664 

9665 References 

9666 ---------- 

9667 .. [1] Kincaid, W. M., "The Combination of Tests Based on Discrete 

9668 Distributions." Journal of the American Statistical Association 57, 

9669 no. 297 (1962), 10-19. 

9670 .. [2] Heard, N. and Rubin-Delanchey, P. "Choosing between methods of 

9671 combining p-values." Biometrika 105.1 (2018): 239-246. 

9672 .. [3] https://en.wikipedia.org/wiki/Fisher%27s_method 

9673 .. [4] George, E. O., and G. S. Mudholkar. "On the convolution of logistic 

9674 random variables." Metrika 30.1 (1983): 1-13. 

9675 .. [5] https://en.wikipedia.org/wiki/Fisher%27s_method#Relation_to_Stouffer.27s_Z-score_method 

9676 .. [6] Whitlock, M. C. "Combining probability from independent tests: the 

9677 weighted Z-method is superior to Fisher's approach." Journal of 

9678 Evolutionary Biology 18, no. 5 (2005): 1368-1373. 

9679 .. [7] Zaykin, Dmitri V. "Optimally weighted Z-test is a powerful method 

9680 for combining probabilities in meta-analysis." Journal of 

9681 Evolutionary Biology 24, no. 8 (2011): 1836-1841. 

9682 .. [8] https://en.wikipedia.org/wiki/Extensions_of_Fisher%27s_method 

9683 

9684 """ 

9685 pvalues = np.asarray(pvalues) 

9686 if pvalues.ndim != 1: 

9687 raise ValueError("pvalues is not 1-D") 

9688 

9689 if method == 'fisher': 

9690 statistic = -2 * np.sum(np.log(pvalues)) 

9691 pval = distributions.chi2.sf(statistic, 2 * len(pvalues)) 

9692 elif method == 'pearson': 

9693 statistic = 2 * np.sum(np.log1p(-pvalues)) 

9694 pval = distributions.chi2.cdf(-statistic, 2 * len(pvalues)) 

9695 elif method == 'mudholkar_george': 

9696 normalizing_factor = np.sqrt(3/len(pvalues))/np.pi 

9697 statistic = -np.sum(np.log(pvalues)) + np.sum(np.log1p(-pvalues)) 

9698 nu = 5 * len(pvalues) + 4 

9699 approx_factor = np.sqrt(nu / (nu - 2)) 

9700 pval = distributions.t.sf(statistic * normalizing_factor 

9701 * approx_factor, nu) 

9702 elif method == 'tippett': 

9703 statistic = np.min(pvalues) 

9704 pval = distributions.beta.cdf(statistic, 1, len(pvalues)) 

9705 elif method == 'stouffer': 

9706 if weights is None: 

9707 weights = np.ones_like(pvalues) 

9708 elif len(weights) != len(pvalues): 

9709 raise ValueError("pvalues and weights must be of the same size.") 

9710 

9711 weights = np.asarray(weights) 

9712 if weights.ndim != 1: 

9713 raise ValueError("weights is not 1-D") 

9714 

9715 Zi = distributions.norm.isf(pvalues) 

9716 statistic = np.dot(weights, Zi) / np.linalg.norm(weights) 

9717 pval = distributions.norm.sf(statistic) 

9718 

9719 else: 

9720 raise ValueError( 

9721 f"Invalid method {method!r}. Valid methods are 'fisher', " 

9722 "'pearson', 'mudholkar_george', 'tippett', and 'stouffer'" 

9723 ) 

9724 

9725 return SignificanceResult(statistic, pval) 

9726 

9727 

9728##################################### 

9729# STATISTICAL DISTANCES # 

9730##################################### 

9731 

9732 

9733def wasserstein_distance(u_values, v_values, u_weights=None, v_weights=None): 

9734 r""" 

9735 Compute the first Wasserstein distance between two 1D distributions. 

9736 

9737 This distance is also known as the earth mover's distance, since it can be 

9738 seen as the minimum amount of "work" required to transform :math:`u` into 

9739 :math:`v`, where "work" is measured as the amount of distribution weight 

9740 that must be moved, multiplied by the distance it has to be moved. 

9741 

9742 .. versionadded:: 1.0.0 

9743 

9744 Parameters 

9745 ---------- 

9746 u_values, v_values : array_like 

9747 Values observed in the (empirical) distribution. 

9748 u_weights, v_weights : array_like, optional 

9749 Weight for each value. If unspecified, each value is assigned the same 

9750 weight. 

9751 `u_weights` (resp. `v_weights`) must have the same length as 

9752 `u_values` (resp. `v_values`). If the weight sum differs from 1, it 

9753 must still be positive and finite so that the weights can be normalized 

9754 to sum to 1. 

9755 

9756 Returns 

9757 ------- 

9758 distance : float 

9759 The computed distance between the distributions. 

9760 

9761 Notes 

9762 ----- 

9763 The first Wasserstein distance between the distributions :math:`u` and 

9764 :math:`v` is: 

9765 

9766 .. math:: 

9767 

9768 l_1 (u, v) = \inf_{\pi \in \Gamma (u, v)} \int_{\mathbb{R} \times 

9769 \mathbb{R}} |x-y| \mathrm{d} \pi (x, y) 

9770 

9771 where :math:`\Gamma (u, v)` is the set of (probability) distributions on 

9772 :math:`\mathbb{R} \times \mathbb{R}` whose marginals are :math:`u` and 

9773 :math:`v` on the first and second factors respectively. 

9774 

9775 If :math:`U` and :math:`V` are the respective CDFs of :math:`u` and 

9776 :math:`v`, this distance also equals to: 

9777 

9778 .. math:: 

9779 

9780 l_1(u, v) = \int_{-\infty}^{+\infty} |U-V| 

9781 

9782 See [2]_ for a proof of the equivalence of both definitions. 

9783 

9784 The input distributions can be empirical, therefore coming from samples 

9785 whose values are effectively inputs of the function, or they can be seen as 

9786 generalized functions, in which case they are weighted sums of Dirac delta 

9787 functions located at the specified values. 

9788 

9789 References 

9790 ---------- 

9791 .. [1] "Wasserstein metric", https://en.wikipedia.org/wiki/Wasserstein_metric 

9792 .. [2] Ramdas, Garcia, Cuturi "On Wasserstein Two Sample Testing and Related 

9793 Families of Nonparametric Tests" (2015). :arXiv:`1509.02237`. 

9794 

9795 Examples 

9796 -------- 

9797 >>> from scipy.stats import wasserstein_distance 

9798 >>> wasserstein_distance([0, 1, 3], [5, 6, 8]) 

9799 5.0 

9800 >>> wasserstein_distance([0, 1], [0, 1], [3, 1], [2, 2]) 

9801 0.25 

9802 >>> wasserstein_distance([3.4, 3.9, 7.5, 7.8], [4.5, 1.4], 

9803 ... [1.4, 0.9, 3.1, 7.2], [3.2, 3.5]) 

9804 4.0781331438047861 

9805 

9806 """ 

9807 return _cdf_distance(1, u_values, v_values, u_weights, v_weights) 

9808 

9809 

9810def energy_distance(u_values, v_values, u_weights=None, v_weights=None): 

9811 r"""Compute the energy distance between two 1D distributions. 

9812 

9813 .. versionadded:: 1.0.0 

9814 

9815 Parameters 

9816 ---------- 

9817 u_values, v_values : array_like 

9818 Values observed in the (empirical) distribution. 

9819 u_weights, v_weights : array_like, optional 

9820 Weight for each value. If unspecified, each value is assigned the same 

9821 weight. 

9822 `u_weights` (resp. `v_weights`) must have the same length as 

9823 `u_values` (resp. `v_values`). If the weight sum differs from 1, it 

9824 must still be positive and finite so that the weights can be normalized 

9825 to sum to 1. 

9826 

9827 Returns 

9828 ------- 

9829 distance : float 

9830 The computed distance between the distributions. 

9831 

9832 Notes 

9833 ----- 

9834 The energy distance between two distributions :math:`u` and :math:`v`, whose 

9835 respective CDFs are :math:`U` and :math:`V`, equals to: 

9836 

9837 .. math:: 

9838 

9839 D(u, v) = \left( 2\mathbb E|X - Y| - \mathbb E|X - X'| - 

9840 \mathbb E|Y - Y'| \right)^{1/2} 

9841 

9842 where :math:`X` and :math:`X'` (resp. :math:`Y` and :math:`Y'`) are 

9843 independent random variables whose probability distribution is :math:`u` 

9844 (resp. :math:`v`). 

9845 

9846 Sometimes the square of this quantity is referred to as the "energy 

9847 distance" (e.g. in [2]_, [4]_), but as noted in [1]_ and [3]_, only the 

9848 definition above satisfies the axioms of a distance function (metric). 

9849 

9850 As shown in [2]_, for one-dimensional real-valued variables, the energy 

9851 distance is linked to the non-distribution-free version of the Cramér-von 

9852 Mises distance: 

9853 

9854 .. math:: 

9855 

9856 D(u, v) = \sqrt{2} l_2(u, v) = \left( 2 \int_{-\infty}^{+\infty} (U-V)^2 

9857 \right)^{1/2} 

9858 

9859 Note that the common Cramér-von Mises criterion uses the distribution-free 

9860 version of the distance. See [2]_ (section 2), for more details about both 

9861 versions of the distance. 

9862 

9863 The input distributions can be empirical, therefore coming from samples 

9864 whose values are effectively inputs of the function, or they can be seen as 

9865 generalized functions, in which case they are weighted sums of Dirac delta 

9866 functions located at the specified values. 

9867 

9868 References 

9869 ---------- 

9870 .. [1] Rizzo, Szekely "Energy distance." Wiley Interdisciplinary Reviews: 

9871 Computational Statistics, 8(1):27-38 (2015). 

9872 .. [2] Szekely "E-statistics: The energy of statistical samples." Bowling 

9873 Green State University, Department of Mathematics and Statistics, 

9874 Technical Report 02-16 (2002). 

9875 .. [3] "Energy distance", https://en.wikipedia.org/wiki/Energy_distance 

9876 .. [4] Bellemare, Danihelka, Dabney, Mohamed, Lakshminarayanan, Hoyer, 

9877 Munos "The Cramer Distance as a Solution to Biased Wasserstein 

9878 Gradients" (2017). :arXiv:`1705.10743`. 

9879 

9880 Examples 

9881 -------- 

9882 >>> from scipy.stats import energy_distance 

9883 >>> energy_distance([0], [2]) 

9884 2.0000000000000004 

9885 >>> energy_distance([0, 8], [0, 8], [3, 1], [2, 2]) 

9886 1.0000000000000002 

9887 >>> energy_distance([0.7, 7.4, 2.4, 6.8], [1.4, 8. ], 

9888 ... [2.1, 4.2, 7.4, 8. ], [7.6, 8.8]) 

9889 0.88003340976158217 

9890 

9891 """ 

9892 return np.sqrt(2) * _cdf_distance(2, u_values, v_values, 

9893 u_weights, v_weights) 

9894 

9895 

9896def _cdf_distance(p, u_values, v_values, u_weights=None, v_weights=None): 

9897 r""" 

9898 Compute, between two one-dimensional distributions :math:`u` and 

9899 :math:`v`, whose respective CDFs are :math:`U` and :math:`V`, the 

9900 statistical distance that is defined as: 

9901 

9902 .. math:: 

9903 

9904 l_p(u, v) = \left( \int_{-\infty}^{+\infty} |U-V|^p \right)^{1/p} 

9905 

9906 p is a positive parameter; p = 1 gives the Wasserstein distance, p = 2 

9907 gives the energy distance. 

9908 

9909 Parameters 

9910 ---------- 

9911 u_values, v_values : array_like 

9912 Values observed in the (empirical) distribution. 

9913 u_weights, v_weights : array_like, optional 

9914 Weight for each value. If unspecified, each value is assigned the same 

9915 weight. 

9916 `u_weights` (resp. `v_weights`) must have the same length as 

9917 `u_values` (resp. `v_values`). If the weight sum differs from 1, it 

9918 must still be positive and finite so that the weights can be normalized 

9919 to sum to 1. 

9920 

9921 Returns 

9922 ------- 

9923 distance : float 

9924 The computed distance between the distributions. 

9925 

9926 Notes 

9927 ----- 

9928 The input distributions can be empirical, therefore coming from samples 

9929 whose values are effectively inputs of the function, or they can be seen as 

9930 generalized functions, in which case they are weighted sums of Dirac delta 

9931 functions located at the specified values. 

9932 

9933 References 

9934 ---------- 

9935 .. [1] Bellemare, Danihelka, Dabney, Mohamed, Lakshminarayanan, Hoyer, 

9936 Munos "The Cramer Distance as a Solution to Biased Wasserstein 

9937 Gradients" (2017). :arXiv:`1705.10743`. 

9938 

9939 """ 

9940 u_values, u_weights = _validate_distribution(u_values, u_weights) 

9941 v_values, v_weights = _validate_distribution(v_values, v_weights) 

9942 

9943 u_sorter = np.argsort(u_values) 

9944 v_sorter = np.argsort(v_values) 

9945 

9946 all_values = np.concatenate((u_values, v_values)) 

9947 all_values.sort(kind='mergesort') 

9948 

9949 # Compute the differences between pairs of successive values of u and v. 

9950 deltas = np.diff(all_values) 

9951 

9952 # Get the respective positions of the values of u and v among the values of 

9953 # both distributions. 

9954 u_cdf_indices = u_values[u_sorter].searchsorted(all_values[:-1], 'right') 

9955 v_cdf_indices = v_values[v_sorter].searchsorted(all_values[:-1], 'right') 

9956 

9957 # Calculate the CDFs of u and v using their weights, if specified. 

9958 if u_weights is None: 

9959 u_cdf = u_cdf_indices / u_values.size 

9960 else: 

9961 u_sorted_cumweights = np.concatenate(([0], 

9962 np.cumsum(u_weights[u_sorter]))) 

9963 u_cdf = u_sorted_cumweights[u_cdf_indices] / u_sorted_cumweights[-1] 

9964 

9965 if v_weights is None: 

9966 v_cdf = v_cdf_indices / v_values.size 

9967 else: 

9968 v_sorted_cumweights = np.concatenate(([0], 

9969 np.cumsum(v_weights[v_sorter]))) 

9970 v_cdf = v_sorted_cumweights[v_cdf_indices] / v_sorted_cumweights[-1] 

9971 

9972 # Compute the value of the integral based on the CDFs. 

9973 # If p = 1 or p = 2, we avoid using np.power, which introduces an overhead 

9974 # of about 15%. 

9975 if p == 1: 

9976 return np.sum(np.multiply(np.abs(u_cdf - v_cdf), deltas)) 

9977 if p == 2: 

9978 return np.sqrt(np.sum(np.multiply(np.square(u_cdf - v_cdf), deltas))) 

9979 return np.power(np.sum(np.multiply(np.power(np.abs(u_cdf - v_cdf), p), 

9980 deltas)), 1/p) 

9981 

9982 

9983def _validate_distribution(values, weights): 

9984 """ 

9985 Validate the values and weights from a distribution input of `cdf_distance` 

9986 and return them as ndarray objects. 

9987 

9988 Parameters 

9989 ---------- 

9990 values : array_like 

9991 Values observed in the (empirical) distribution. 

9992 weights : array_like 

9993 Weight for each value. 

9994 

9995 Returns 

9996 ------- 

9997 values : ndarray 

9998 Values as ndarray. 

9999 weights : ndarray 

10000 Weights as ndarray. 

10001 

10002 """ 

10003 # Validate the value array. 

10004 values = np.asarray(values, dtype=float) 

10005 if len(values) == 0: 

10006 raise ValueError("Distribution can't be empty.") 

10007 

10008 # Validate the weight array, if specified. 

10009 if weights is not None: 

10010 weights = np.asarray(weights, dtype=float) 

10011 if len(weights) != len(values): 

10012 raise ValueError('Value and weight array-likes for the same ' 

10013 'empirical distribution must be of the same size.') 

10014 if np.any(weights < 0): 

10015 raise ValueError('All weights must be non-negative.') 

10016 if not 0 < np.sum(weights) < np.inf: 

10017 raise ValueError('Weight array-like sum must be positive and ' 

10018 'finite. Set as None for an equal distribution of ' 

10019 'weight.') 

10020 

10021 return values, weights 

10022 

10023 return values, None 

10024 

10025 

10026##################################### 

10027# SUPPORT FUNCTIONS # 

10028##################################### 

10029 

10030RepeatedResults = namedtuple('RepeatedResults', ('values', 'counts')) 

10031 

10032 

10033def find_repeats(arr): 

10034 """Find repeats and repeat counts. 

10035 

10036 Parameters 

10037 ---------- 

10038 arr : array_like 

10039 Input array. This is cast to float64. 

10040 

10041 Returns 

10042 ------- 

10043 values : ndarray 

10044 The unique values from the (flattened) input that are repeated. 

10045 

10046 counts : ndarray 

10047 Number of times the corresponding 'value' is repeated. 

10048 

10049 Notes 

10050 ----- 

10051 In numpy >= 1.9 `numpy.unique` provides similar functionality. The main 

10052 difference is that `find_repeats` only returns repeated values. 

10053 

10054 Examples 

10055 -------- 

10056 >>> from scipy import stats 

10057 >>> stats.find_repeats([2, 1, 2, 3, 2, 2, 5]) 

10058 RepeatedResults(values=array([2.]), counts=array([4])) 

10059 

10060 >>> stats.find_repeats([[10, 20, 1, 2], [5, 5, 4, 4]]) 

10061 RepeatedResults(values=array([4., 5.]), counts=array([2, 2])) 

10062 

10063 """ 

10064 # Note: always copies. 

10065 return RepeatedResults(*_find_repeats(np.array(arr, dtype=np.float64))) 

10066 

10067 

10068def _sum_of_squares(a, axis=0): 

10069 """Square each element of the input array, and return the sum(s) of that. 

10070 

10071 Parameters 

10072 ---------- 

10073 a : array_like 

10074 Input array. 

10075 axis : int or None, optional 

10076 Axis along which to calculate. Default is 0. If None, compute over 

10077 the whole array `a`. 

10078 

10079 Returns 

10080 ------- 

10081 sum_of_squares : ndarray 

10082 The sum along the given axis for (a**2). 

10083 

10084 See Also 

10085 -------- 

10086 _square_of_sums : The square(s) of the sum(s) (the opposite of 

10087 `_sum_of_squares`). 

10088 

10089 """ 

10090 a, axis = _chk_asarray(a, axis) 

10091 return np.sum(a*a, axis) 

10092 

10093 

10094def _square_of_sums(a, axis=0): 

10095 """Sum elements of the input array, and return the square(s) of that sum. 

10096 

10097 Parameters 

10098 ---------- 

10099 a : array_like 

10100 Input array. 

10101 axis : int or None, optional 

10102 Axis along which to calculate. Default is 0. If None, compute over 

10103 the whole array `a`. 

10104 

10105 Returns 

10106 ------- 

10107 square_of_sums : float or ndarray 

10108 The square of the sum over `axis`. 

10109 

10110 See Also 

10111 -------- 

10112 _sum_of_squares : The sum of squares (the opposite of `square_of_sums`). 

10113 

10114 """ 

10115 a, axis = _chk_asarray(a, axis) 

10116 s = np.sum(a, axis) 

10117 if not np.isscalar(s): 

10118 return s.astype(float) * s 

10119 else: 

10120 return float(s) * s 

10121 

10122 

10123def rankdata(a, method='average', *, axis=None, nan_policy='propagate'): 

10124 """Assign ranks to data, dealing with ties appropriately. 

10125 

10126 By default (``axis=None``), the data array is first flattened, and a flat 

10127 array of ranks is returned. Separately reshape the rank array to the 

10128 shape of the data array if desired (see Examples). 

10129 

10130 Ranks begin at 1. The `method` argument controls how ranks are assigned 

10131 to equal values. See [1]_ for further discussion of ranking methods. 

10132 

10133 Parameters 

10134 ---------- 

10135 a : array_like 

10136 The array of values to be ranked. 

10137 method : {'average', 'min', 'max', 'dense', 'ordinal'}, optional 

10138 The method used to assign ranks to tied elements. 

10139 The following methods are available (default is 'average'): 

10140 

10141 * 'average': The average of the ranks that would have been assigned to 

10142 all the tied values is assigned to each value. 

10143 * 'min': The minimum of the ranks that would have been assigned to all 

10144 the tied values is assigned to each value. (This is also 

10145 referred to as "competition" ranking.) 

10146 * 'max': The maximum of the ranks that would have been assigned to all 

10147 the tied values is assigned to each value. 

10148 * 'dense': Like 'min', but the rank of the next highest element is 

10149 assigned the rank immediately after those assigned to the tied 

10150 elements. 

10151 * 'ordinal': All values are given a distinct rank, corresponding to 

10152 the order that the values occur in `a`. 

10153 axis : {None, int}, optional 

10154 Axis along which to perform the ranking. If ``None``, the data array 

10155 is first flattened. 

10156 nan_policy : {'propagate', 'omit', 'raise'}, optional 

10157 Defines how to handle when input contains nan. 

10158 The following options are available (default is 'propagate'): 

10159 

10160 * 'propagate': propagates nans through the rank calculation 

10161 * 'omit': performs the calculations ignoring nan values 

10162 * 'raise': raises an error 

10163 

10164 .. note:: 

10165 

10166 When `nan_policy` is 'propagate', the output is an array of *all* 

10167 nans because ranks relative to nans in the input are undefined. 

10168 When `nan_policy` is 'omit', nans in `a` are ignored when ranking 

10169 the other values, and the corresponding locations of the output 

10170 are nan. 

10171 

10172 .. versionadded:: 1.10 

10173 

10174 Returns 

10175 ------- 

10176 ranks : ndarray 

10177 An array of size equal to the size of `a`, containing rank 

10178 scores. 

10179 

10180 References 

10181 ---------- 

10182 .. [1] "Ranking", https://en.wikipedia.org/wiki/Ranking 

10183 

10184 Examples 

10185 -------- 

10186 >>> import numpy as np 

10187 >>> from scipy.stats import rankdata 

10188 >>> rankdata([0, 2, 3, 2]) 

10189 array([ 1. , 2.5, 4. , 2.5]) 

10190 >>> rankdata([0, 2, 3, 2], method='min') 

10191 array([ 1, 2, 4, 2]) 

10192 >>> rankdata([0, 2, 3, 2], method='max') 

10193 array([ 1, 3, 4, 3]) 

10194 >>> rankdata([0, 2, 3, 2], method='dense') 

10195 array([ 1, 2, 3, 2]) 

10196 >>> rankdata([0, 2, 3, 2], method='ordinal') 

10197 array([ 1, 2, 4, 3]) 

10198 >>> rankdata([[0, 2], [3, 2]]).reshape(2,2) 

10199 array([[1. , 2.5], 

10200 [4. , 2.5]]) 

10201 >>> rankdata([[0, 2, 2], [3, 2, 5]], axis=1) 

10202 array([[1. , 2.5, 2.5], 

10203 [2. , 1. , 3. ]]) 

10204 >>> rankdata([0, 2, 3, np.nan, -2, np.nan], nan_policy="propagate") 

10205 array([nan, nan, nan, nan, nan, nan]) 

10206 >>> rankdata([0, 2, 3, np.nan, -2, np.nan], nan_policy="omit") 

10207 array([ 2., 3., 4., nan, 1., nan]) 

10208 

10209 """ 

10210 if method not in ('average', 'min', 'max', 'dense', 'ordinal'): 

10211 raise ValueError(f'unknown method "{method}"') 

10212 

10213 a = np.asarray(a) 

10214 

10215 if axis is not None: 

10216 if a.size == 0: 

10217 # The return values of `normalize_axis_index` are ignored. The 

10218 # call validates `axis`, even though we won't use it. 

10219 # use scipy._lib._util._normalize_axis_index when available 

10220 np.core.multiarray.normalize_axis_index(axis, a.ndim) 

10221 dt = np.float64 if method == 'average' else np.int_ 

10222 return np.empty(a.shape, dtype=dt) 

10223 return np.apply_along_axis(rankdata, axis, a, method, 

10224 nan_policy=nan_policy) 

10225 

10226 arr = np.ravel(a) 

10227 contains_nan, nan_policy = _contains_nan(arr, nan_policy) 

10228 nan_indexes = None 

10229 if contains_nan: 

10230 if nan_policy == 'omit': 

10231 nan_indexes = np.isnan(arr) 

10232 if nan_policy == 'propagate': 

10233 return np.full_like(arr, np.nan) 

10234 

10235 algo = 'mergesort' if method == 'ordinal' else 'quicksort' 

10236 sorter = np.argsort(arr, kind=algo) 

10237 

10238 inv = np.empty(sorter.size, dtype=np.intp) 

10239 inv[sorter] = np.arange(sorter.size, dtype=np.intp) 

10240 

10241 if method == 'ordinal': 

10242 result = inv + 1 

10243 else: 

10244 arr = arr[sorter] 

10245 obs = np.r_[True, arr[1:] != arr[:-1]] 

10246 dense = obs.cumsum()[inv] 

10247 

10248 if method == 'dense': 

10249 result = dense 

10250 else: 

10251 # cumulative counts of each unique value 

10252 count = np.r_[np.nonzero(obs)[0], len(obs)] 

10253 

10254 if method == 'max': 

10255 result = count[dense] 

10256 

10257 if method == 'min': 

10258 result = count[dense - 1] + 1 

10259 

10260 if method == 'average': 

10261 result = .5 * (count[dense] + count[dense - 1] + 1) 

10262 

10263 if nan_indexes is not None: 

10264 result = result.astype('float64') 

10265 result[nan_indexes] = np.nan 

10266 

10267 return result 

10268 

10269 

10270def expectile(a, alpha=0.5, *, weights=None): 

10271 r"""Compute the expectile at the specified level. 

10272 

10273 Expectiles are a generalization of the expectation in the same way as 

10274 quantiles are a generalization of the median. The expectile at level 

10275 `alpha = 0.5` is the mean (average). See Notes for more details. 

10276 

10277 Parameters 

10278 ---------- 

10279 a : array_like 

10280 Array containing numbers whose expectile is desired. 

10281 alpha : float, default: 0.5 

10282 The level of the expectile; e.g., `alpha=0.5` gives the mean. 

10283 weights : array_like, optional 

10284 An array of weights associated with the values in `a`. 

10285 The `weights` must be broadcastable to the same shape as `a`. 

10286 Default is None, which gives each value a weight of 1.0. 

10287 An integer valued weight element acts like repeating the corresponding 

10288 observation in `a` that many times. See Notes for more details. 

10289 

10290 Returns 

10291 ------- 

10292 expectile : ndarray 

10293 The empirical expectile at level `alpha`. 

10294 

10295 See Also 

10296 -------- 

10297 numpy.mean : Arithmetic average 

10298 numpy.quantile : Quantile 

10299 

10300 Notes 

10301 ----- 

10302 In general, the expectile at level :math:`\alpha` of a random variable 

10303 :math:`X` with cumulative distribution function (CDF) :math:`F` is given 

10304 by the unique solution :math:`t` of: 

10305 

10306 .. math:: 

10307 

10308 \alpha E((X - t)_+) = (1 - \alpha) E((t - X)_+) \,. 

10309 

10310 Here, :math:`(x)_+ = \max(0, x)` is the positive part of :math:`x`. 

10311 This equation can be equivalently written as: 

10312 

10313 .. math:: 

10314 

10315 \alpha \int_t^\infty (x - t)\mathrm{d}F(x) 

10316 = (1 - \alpha) \int_{-\infty}^t (t - x)\mathrm{d}F(x) \,. 

10317 

10318 The empirical expectile at level :math:`\alpha` (`alpha`) of a sample 

10319 :math:`a_i` (the array `a`) is defined by plugging in the empirical CDF of 

10320 `a`. Given sample or case weights :math:`w` (the array `weights`), it 

10321 reads :math:`F_a(x) = \frac{1}{\sum_i w_i} \sum_i w_i 1_{a_i \leq x}` 

10322 with indicator function :math:`1_{A}`. This leads to the definition of the 

10323 empirical expectile at level `alpha` as the unique solution :math:`t` of: 

10324 

10325 .. math:: 

10326 

10327 \alpha \sum_{i=1}^n w_i (a_i - t)_+ = 

10328 (1 - \alpha) \sum_{i=1}^n w_i (t - a_i)_+ \,. 

10329 

10330 For :math:`\alpha=0.5`, this simplifies to the weighted average. 

10331 Furthermore, the larger :math:`\alpha`, the larger the value of the 

10332 expectile. 

10333 

10334 As a final remark, the expectile at level :math:`\alpha` can also be 

10335 written as a minimization problem. One often used choice is 

10336 

10337 .. math:: 

10338 

10339 \operatorname{argmin}_t 

10340 E(\lvert 1_{t\geq X} - \alpha\rvert(t - X)^2) \,. 

10341 

10342 References 

10343 ---------- 

10344 .. [1] W. K. Newey and J. L. Powell (1987), "Asymmetric Least Squares 

10345 Estimation and Testing," Econometrica, 55, 819-847. 

10346 .. [2] T. Gneiting (2009). "Making and Evaluating Point Forecasts," 

10347 Journal of the American Statistical Association, 106, 746 - 762. 

10348 :doi:`10.48550/arXiv.0912.0902` 

10349 

10350 Examples 

10351 -------- 

10352 >>> import numpy as np 

10353 >>> from scipy.stats import expectile 

10354 >>> a = [1, 4, 2, -1] 

10355 >>> expectile(a, alpha=0.5) == np.mean(a) 

10356 True 

10357 >>> expectile(a, alpha=0.2) 

10358 0.42857142857142855 

10359 >>> expectile(a, alpha=0.8) 

10360 2.5714285714285716 

10361 >>> weights = [1, 3, 1, 1] 

10362 

10363 """ 

10364 if alpha < 0 or alpha > 1: 

10365 raise ValueError( 

10366 "The expectile level alpha must be in the range [0, 1]." 

10367 ) 

10368 a = np.asarray(a) 

10369 

10370 if weights is not None: 

10371 weights = np.broadcast_to(weights, a.shape) 

10372 

10373 # This is the empirical equivalent of Eq. (13) with identification 

10374 # function from Table 9 (omitting a factor of 2) in [2] (their y is our 

10375 # data a, their x is our t) 

10376 def first_order(t): 

10377 return np.average(np.abs((a <= t) - alpha) * (t - a), weights=weights) 

10378 

10379 if alpha >= 0.5: 

10380 x0 = np.average(a, weights=weights) 

10381 x1 = np.amax(a) 

10382 else: 

10383 x1 = np.average(a, weights=weights) 

10384 x0 = np.amin(a) 

10385 

10386 if x0 == x1: 

10387 # a has a single unique element 

10388 return x0 

10389 

10390 # Note that the expectile is the unique solution, so no worries about 

10391 # finding a wrong root. 

10392 res = root_scalar(first_order, x0=x0, x1=x1) 

10393 return res.root