Coverage for /usr/lib/python3/dist-packages/scipy/stats/_mstats_basic.py: 11%

991 statements  

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

1""" 

2An extension of scipy.stats._stats_py to support masked arrays 

3 

4""" 

5# Original author (2007): Pierre GF Gerard-Marchant 

6 

7 

8__all__ = ['argstoarray', 

9 'count_tied_groups', 

10 'describe', 

11 'f_oneway', 'find_repeats','friedmanchisquare', 

12 'kendalltau','kendalltau_seasonal','kruskal','kruskalwallis', 

13 'ks_twosamp', 'ks_2samp', 'kurtosis', 'kurtosistest', 

14 'ks_1samp', 'kstest', 

15 'linregress', 

16 'mannwhitneyu', 'meppf','mode','moment','mquantiles','msign', 

17 'normaltest', 

18 'obrientransform', 

19 'pearsonr','plotting_positions','pointbiserialr', 

20 'rankdata', 

21 'scoreatpercentile','sem', 

22 'sen_seasonal_slopes','skew','skewtest','spearmanr', 

23 'siegelslopes', 'theilslopes', 

24 'tmax','tmean','tmin','trim','trimboth', 

25 'trimtail','trima','trimr','trimmed_mean','trimmed_std', 

26 'trimmed_stde','trimmed_var','tsem','ttest_1samp','ttest_onesamp', 

27 'ttest_ind','ttest_rel','tvar', 

28 'variation', 

29 'winsorize', 

30 'brunnermunzel', 

31 ] 

32 

33import numpy as np 

34from numpy import ndarray 

35import numpy.ma as ma 

36from numpy.ma import masked, nomask 

37import math 

38 

39import itertools 

40import warnings 

41from collections import namedtuple 

42 

43from . import distributions 

44from scipy._lib._util import _rename_parameter, _contains_nan 

45from scipy._lib._bunch import _make_tuple_bunch 

46import scipy.special as special 

47import scipy.stats._stats_py 

48 

49from ._stats_mstats_common import ( 

50 _find_repeats, 

51 linregress as stats_linregress, 

52 LinregressResult as stats_LinregressResult, 

53 theilslopes as stats_theilslopes, 

54 siegelslopes as stats_siegelslopes 

55 ) 

56 

57 

58def _chk_asarray(a, axis): 

59 # Always returns a masked array, raveled for axis=None 

60 a = ma.asanyarray(a) 

61 if axis is None: 

62 a = ma.ravel(a) 

63 outaxis = 0 

64 else: 

65 outaxis = axis 

66 return a, outaxis 

67 

68 

69def _chk2_asarray(a, b, axis): 

70 a = ma.asanyarray(a) 

71 b = ma.asanyarray(b) 

72 if axis is None: 

73 a = ma.ravel(a) 

74 b = ma.ravel(b) 

75 outaxis = 0 

76 else: 

77 outaxis = axis 

78 return a, b, outaxis 

79 

80 

81def _chk_size(a, b): 

82 a = ma.asanyarray(a) 

83 b = ma.asanyarray(b) 

84 (na, nb) = (a.size, b.size) 

85 if na != nb: 

86 raise ValueError("The size of the input array should match!" 

87 " ({} <> {})".format(na, nb)) 

88 return (a, b, na) 

89 

90 

91def argstoarray(*args): 

92 """ 

93 Constructs a 2D array from a group of sequences. 

94 

95 Sequences are filled with missing values to match the length of the longest 

96 sequence. 

97 

98 Parameters 

99 ---------- 

100 *args : sequences 

101 Group of sequences. 

102 

103 Returns 

104 ------- 

105 argstoarray : MaskedArray 

106 A ( `m` x `n` ) masked array, where `m` is the number of arguments and 

107 `n` the length of the longest argument. 

108 

109 Notes 

110 ----- 

111 `numpy.ma.vstack` has identical behavior, but is called with a sequence 

112 of sequences. 

113 

114 Examples 

115 -------- 

116 A 2D masked array constructed from a group of sequences is returned. 

117 

118 >>> from scipy.stats.mstats import argstoarray 

119 >>> argstoarray([1, 2, 3], [4, 5, 6]) 

120 masked_array( 

121 data=[[1.0, 2.0, 3.0], 

122 [4.0, 5.0, 6.0]], 

123 mask=[[False, False, False], 

124 [False, False, False]], 

125 fill_value=1e+20) 

126 

127 The returned masked array filled with missing values when the lengths of 

128 sequences are different. 

129 

130 >>> argstoarray([1, 3], [4, 5, 6]) 

131 masked_array( 

132 data=[[1.0, 3.0, --], 

133 [4.0, 5.0, 6.0]], 

134 mask=[[False, False, True], 

135 [False, False, False]], 

136 fill_value=1e+20) 

137 

138 """ 

139 if len(args) == 1 and not isinstance(args[0], ndarray): 

140 output = ma.asarray(args[0]) 

141 if output.ndim != 2: 

142 raise ValueError("The input should be 2D") 

143 else: 

144 n = len(args) 

145 m = max([len(k) for k in args]) 

146 output = ma.array(np.empty((n,m), dtype=float), mask=True) 

147 for (k,v) in enumerate(args): 

148 output[k,:len(v)] = v 

149 

150 output[np.logical_not(np.isfinite(output._data))] = masked 

151 return output 

152 

153 

154def find_repeats(arr): 

155 """Find repeats in arr and return a tuple (repeats, repeat_count). 

156 

157 The input is cast to float64. Masked values are discarded. 

158 

159 Parameters 

160 ---------- 

161 arr : sequence 

162 Input array. The array is flattened if it is not 1D. 

163 

164 Returns 

165 ------- 

166 repeats : ndarray 

167 Array of repeated values. 

168 counts : ndarray 

169 Array of counts. 

170 

171 Examples 

172 -------- 

173 >>> from scipy.stats import mstats 

174 >>> mstats.find_repeats([2, 1, 2, 3, 2, 2, 5]) 

175 (array([2.]), array([4])) 

176 

177 In the above example, 2 repeats 4 times. 

178 

179 >>> mstats.find_repeats([[10, 20, 1, 2], [5, 5, 4, 4]]) 

180 (array([4., 5.]), array([2, 2])) 

181 

182 In the above example, both 4 and 5 repeat 2 times. 

183 

184 """ 

185 # Make sure we get a copy. ma.compressed promises a "new array", but can 

186 # actually return a reference. 

187 compr = np.asarray(ma.compressed(arr), dtype=np.float64) 

188 try: 

189 need_copy = np.may_share_memory(compr, arr) 

190 except AttributeError: 

191 # numpy < 1.8.2 bug: np.may_share_memory([], []) raises, 

192 # while in numpy 1.8.2 and above it just (correctly) returns False. 

193 need_copy = False 

194 if need_copy: 

195 compr = compr.copy() 

196 return _find_repeats(compr) 

197 

198 

199def count_tied_groups(x, use_missing=False): 

200 """ 

201 Counts the number of tied values. 

202 

203 Parameters 

204 ---------- 

205 x : sequence 

206 Sequence of data on which to counts the ties 

207 use_missing : bool, optional 

208 Whether to consider missing values as tied. 

209 

210 Returns 

211 ------- 

212 count_tied_groups : dict 

213 Returns a dictionary (nb of ties: nb of groups). 

214 

215 Examples 

216 -------- 

217 >>> from scipy.stats import mstats 

218 >>> import numpy as np 

219 >>> z = [0, 0, 0, 2, 2, 2, 3, 3, 4, 5, 6] 

220 >>> mstats.count_tied_groups(z) 

221 {2: 1, 3: 2} 

222 

223 In the above example, the ties were 0 (3x), 2 (3x) and 3 (2x). 

224 

225 >>> z = np.ma.array([0, 0, 1, 2, 2, 2, 3, 3, 4, 5, 6]) 

226 >>> mstats.count_tied_groups(z) 

227 {2: 2, 3: 1} 

228 >>> z[[1,-1]] = np.ma.masked 

229 >>> mstats.count_tied_groups(z, use_missing=True) 

230 {2: 2, 3: 1} 

231 

232 """ 

233 nmasked = ma.getmask(x).sum() 

234 # We need the copy as find_repeats will overwrite the initial data 

235 data = ma.compressed(x).copy() 

236 (ties, counts) = find_repeats(data) 

237 nties = {} 

238 if len(ties): 

239 nties = dict(zip(np.unique(counts), itertools.repeat(1))) 

240 nties.update(dict(zip(*find_repeats(counts)))) 

241 

242 if nmasked and use_missing: 

243 try: 

244 nties[nmasked] += 1 

245 except KeyError: 

246 nties[nmasked] = 1 

247 

248 return nties 

249 

250 

251def rankdata(data, axis=None, use_missing=False): 

252 """Returns the rank (also known as order statistics) of each data point 

253 along the given axis. 

254 

255 If some values are tied, their rank is averaged. 

256 If some values are masked, their rank is set to 0 if use_missing is False, 

257 or set to the average rank of the unmasked values if use_missing is True. 

258 

259 Parameters 

260 ---------- 

261 data : sequence 

262 Input data. The data is transformed to a masked array 

263 axis : {None,int}, optional 

264 Axis along which to perform the ranking. 

265 If None, the array is first flattened. An exception is raised if 

266 the axis is specified for arrays with a dimension larger than 2 

267 use_missing : bool, optional 

268 Whether the masked values have a rank of 0 (False) or equal to the 

269 average rank of the unmasked values (True). 

270 

271 """ 

272 def _rank1d(data, use_missing=False): 

273 n = data.count() 

274 rk = np.empty(data.size, dtype=float) 

275 idx = data.argsort() 

276 rk[idx[:n]] = np.arange(1,n+1) 

277 

278 if use_missing: 

279 rk[idx[n:]] = (n+1)/2. 

280 else: 

281 rk[idx[n:]] = 0 

282 

283 repeats = find_repeats(data.copy()) 

284 for r in repeats[0]: 

285 condition = (data == r).filled(False) 

286 rk[condition] = rk[condition].mean() 

287 return rk 

288 

289 data = ma.array(data, copy=False) 

290 if axis is None: 

291 if data.ndim > 1: 

292 return _rank1d(data.ravel(), use_missing).reshape(data.shape) 

293 else: 

294 return _rank1d(data, use_missing) 

295 else: 

296 return ma.apply_along_axis(_rank1d,axis,data,use_missing).view(ndarray) 

297 

298 

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

300 

301 

302def mode(a, axis=0): 

303 """ 

304 Returns an array of the modal (most common) value in the passed array. 

305 

306 Parameters 

307 ---------- 

308 a : array_like 

309 n-dimensional array of which to find mode(s). 

310 axis : int or None, optional 

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

312 the whole array `a`. 

313 

314 Returns 

315 ------- 

316 mode : ndarray 

317 Array of modal values. 

318 count : ndarray 

319 Array of counts for each mode. 

320 

321 Notes 

322 ----- 

323 For more details, see `scipy.stats.mode`. 

324 

325 Examples 

326 -------- 

327 >>> import numpy as np 

328 >>> from scipy import stats 

329 >>> from scipy.stats import mstats 

330 >>> m_arr = np.ma.array([1, 1, 0, 0, 0, 0], mask=[0, 0, 1, 1, 1, 0]) 

331 >>> mstats.mode(m_arr) # note that most zeros are masked 

332 ModeResult(mode=array([1.]), count=array([2.])) 

333 

334 """ 

335 return _mode(a, axis=axis, keepdims=True) 

336 

337 

338def _mode(a, axis=0, keepdims=True): 

339 # Don't want to expose `keepdims` from the public `mstats.mode` 

340 a, axis = _chk_asarray(a, axis) 

341 

342 def _mode1D(a): 

343 (rep,cnt) = find_repeats(a) 

344 if not cnt.ndim: 

345 return (0, 0) 

346 elif cnt.size: 

347 return (rep[cnt.argmax()], cnt.max()) 

348 else: 

349 return (a.min(), 1) 

350 

351 if axis is None: 

352 output = _mode1D(ma.ravel(a)) 

353 output = (ma.array(output[0]), ma.array(output[1])) 

354 else: 

355 output = ma.apply_along_axis(_mode1D, axis, a) 

356 if keepdims is None or keepdims: 

357 newshape = list(a.shape) 

358 newshape[axis] = 1 

359 slices = [slice(None)] * output.ndim 

360 slices[axis] = 0 

361 modes = output[tuple(slices)].reshape(newshape) 

362 slices[axis] = 1 

363 counts = output[tuple(slices)].reshape(newshape) 

364 output = (modes, counts) 

365 else: 

366 output = np.moveaxis(output, axis, 0) 

367 

368 return ModeResult(*output) 

369 

370 

371def _betai(a, b, x): 

372 x = np.asanyarray(x) 

373 x = ma.where(x < 1.0, x, 1.0) # if x > 1 then return 1.0 

374 return special.betainc(a, b, x) 

375 

376 

377def msign(x): 

378 """Returns the sign of x, or 0 if x is masked.""" 

379 return ma.filled(np.sign(x), 0) 

380 

381 

382def pearsonr(x, y): 

383 r""" 

384 Pearson correlation coefficient and p-value for testing non-correlation. 

385 

386 The Pearson correlation coefficient [1]_ measures the linear relationship 

387 between two datasets. The calculation of the p-value relies on the 

388 assumption that each dataset is normally distributed. (See Kowalski [3]_ 

389 for a discussion of the effects of non-normality of the input on the 

390 distribution of the correlation coefficient.) Like other correlation 

391 coefficients, this one varies between -1 and +1 with 0 implying no 

392 correlation. Correlations of -1 or +1 imply an exact linear relationship. 

393 

394 Parameters 

395 ---------- 

396 x : (N,) array_like 

397 Input array. 

398 y : (N,) array_like 

399 Input array. 

400 

401 Returns 

402 ------- 

403 r : float 

404 Pearson's correlation coefficient. 

405 p-value : float 

406 Two-tailed p-value. 

407 

408 Warns 

409 ----- 

410 PearsonRConstantInputWarning 

411 Raised if an input is a constant array. The correlation coefficient 

412 is not defined in this case, so ``np.nan`` is returned. 

413 

414 PearsonRNearConstantInputWarning 

415 Raised if an input is "nearly" constant. The array ``x`` is considered 

416 nearly constant if ``norm(x - mean(x)) < 1e-13 * abs(mean(x))``. 

417 Numerical errors in the calculation ``x - mean(x)`` in this case might 

418 result in an inaccurate calculation of r. 

419 

420 See Also 

421 -------- 

422 spearmanr : Spearman rank-order correlation coefficient. 

423 kendalltau : Kendall's tau, a correlation measure for ordinal data. 

424 

425 Notes 

426 ----- 

427 The correlation coefficient is calculated as follows: 

428 

429 .. math:: 

430 

431 r = \frac{\sum (x - m_x) (y - m_y)} 

432 {\sqrt{\sum (x - m_x)^2 \sum (y - m_y)^2}} 

433 

434 where :math:`m_x` is the mean of the vector x and :math:`m_y` is 

435 the mean of the vector y. 

436 

437 Under the assumption that x and y are drawn from 

438 independent normal distributions (so the population correlation coefficient 

439 is 0), the probability density function of the sample correlation 

440 coefficient r is ([1]_, [2]_): 

441 

442 .. math:: 

443 

444 f(r) = \frac{{(1-r^2)}^{n/2-2}}{\mathrm{B}(\frac{1}{2},\frac{n}{2}-1)} 

445 

446 where n is the number of samples, and B is the beta function. This 

447 is sometimes referred to as the exact distribution of r. This is 

448 the distribution that is used in `pearsonr` to compute the p-value. 

449 The distribution is a beta distribution on the interval [-1, 1], 

450 with equal shape parameters a = b = n/2 - 1. In terms of SciPy's 

451 implementation of the beta distribution, the distribution of r is:: 

452 

453 dist = scipy.stats.beta(n/2 - 1, n/2 - 1, loc=-1, scale=2) 

454 

455 The p-value returned by `pearsonr` is a two-sided p-value. The p-value 

456 roughly indicates the probability of an uncorrelated system 

457 producing datasets that have a Pearson correlation at least as extreme 

458 as the one computed from these datasets. More precisely, for a 

459 given sample with correlation coefficient r, the p-value is 

460 the probability that abs(r') of a random sample x' and y' drawn from 

461 the population with zero correlation would be greater than or equal 

462 to abs(r). In terms of the object ``dist`` shown above, the p-value 

463 for a given r and length n can be computed as:: 

464 

465 p = 2*dist.cdf(-abs(r)) 

466 

467 When n is 2, the above continuous distribution is not well-defined. 

468 One can interpret the limit of the beta distribution as the shape 

469 parameters a and b approach a = b = 0 as a discrete distribution with 

470 equal probability masses at r = 1 and r = -1. More directly, one 

471 can observe that, given the data x = [x1, x2] and y = [y1, y2], and 

472 assuming x1 != x2 and y1 != y2, the only possible values for r are 1 

473 and -1. Because abs(r') for any sample x' and y' with length 2 will 

474 be 1, the two-sided p-value for a sample of length 2 is always 1. 

475 

476 References 

477 ---------- 

478 .. [1] "Pearson correlation coefficient", Wikipedia, 

479 https://en.wikipedia.org/wiki/Pearson_correlation_coefficient 

480 .. [2] Student, "Probable error of a correlation coefficient", 

481 Biometrika, Volume 6, Issue 2-3, 1 September 1908, pp. 302-310. 

482 .. [3] C. J. Kowalski, "On the Effects of Non-Normality on the Distribution 

483 of the Sample Product-Moment Correlation Coefficient" 

484 Journal of the Royal Statistical Society. Series C (Applied 

485 Statistics), Vol. 21, No. 1 (1972), pp. 1-12. 

486 

487 Examples 

488 -------- 

489 >>> import numpy as np 

490 >>> from scipy import stats 

491 >>> from scipy.stats import mstats 

492 >>> mstats.pearsonr([1, 2, 3, 4, 5], [10, 9, 2.5, 6, 4]) 

493 (-0.7426106572325057, 0.1505558088534455) 

494 

495 There is a linear dependence between x and y if y = a + b*x + e, where 

496 a,b are constants and e is a random error term, assumed to be independent 

497 of x. For simplicity, assume that x is standard normal, a=0, b=1 and let 

498 e follow a normal distribution with mean zero and standard deviation s>0. 

499 

500 >>> s = 0.5 

501 >>> x = stats.norm.rvs(size=500) 

502 >>> e = stats.norm.rvs(scale=s, size=500) 

503 >>> y = x + e 

504 >>> mstats.pearsonr(x, y) 

505 (0.9029601878969703, 8.428978827629898e-185) # may vary 

506 

507 This should be close to the exact value given by 

508 

509 >>> 1/np.sqrt(1 + s**2) 

510 0.8944271909999159 

511 

512 For s=0.5, we observe a high level of correlation. In general, a large 

513 variance of the noise reduces the correlation, while the correlation 

514 approaches one as the variance of the error goes to zero. 

515 

516 It is important to keep in mind that no correlation does not imply 

517 independence unless (x, y) is jointly normal. Correlation can even be zero 

518 when there is a very simple dependence structure: if X follows a 

519 standard normal distribution, let y = abs(x). Note that the correlation 

520 between x and y is zero. Indeed, since the expectation of x is zero, 

521 cov(x, y) = E[x*y]. By definition, this equals E[x*abs(x)] which is zero 

522 by symmetry. The following lines of code illustrate this observation: 

523 

524 >>> y = np.abs(x) 

525 >>> mstats.pearsonr(x, y) 

526 (-0.016172891856853524, 0.7182823678751942) # may vary 

527 

528 A non-zero correlation coefficient can be misleading. For example, if X has 

529 a standard normal distribution, define y = x if x < 0 and y = 0 otherwise. 

530 A simple calculation shows that corr(x, y) = sqrt(2/Pi) = 0.797..., 

531 implying a high level of correlation: 

532 

533 >>> y = np.where(x < 0, x, 0) 

534 >>> mstats.pearsonr(x, y) 

535 (0.8537091583771509, 3.183461621422181e-143) # may vary 

536 

537 This is unintuitive since there is no dependence of x and y if x is larger 

538 than zero which happens in about half of the cases if we sample x and y. 

539 """ 

540 (x, y, n) = _chk_size(x, y) 

541 (x, y) = (x.ravel(), y.ravel()) 

542 # Get the common mask and the total nb of unmasked elements 

543 m = ma.mask_or(ma.getmask(x), ma.getmask(y)) 

544 n -= m.sum() 

545 df = n-2 

546 if df < 0: 

547 return (masked, masked) 

548 

549 return scipy.stats._stats_py.pearsonr( 

550 ma.masked_array(x, mask=m).compressed(), 

551 ma.masked_array(y, mask=m).compressed()) 

552 

553 

554def spearmanr(x, y=None, use_ties=True, axis=None, nan_policy='propagate', 

555 alternative='two-sided'): 

556 """ 

557 Calculates a Spearman rank-order correlation coefficient and the p-value 

558 to test for non-correlation. 

559 

560 The Spearman correlation is a nonparametric measure of the linear 

561 relationship between two datasets. Unlike the Pearson correlation, the 

562 Spearman correlation does not assume that both datasets are normally 

563 distributed. Like other correlation coefficients, this one varies 

564 between -1 and +1 with 0 implying no correlation. Correlations of -1 or 

565 +1 imply a monotonic relationship. Positive correlations imply that 

566 as `x` increases, so does `y`. Negative correlations imply that as `x` 

567 increases, `y` decreases. 

568 

569 Missing values are discarded pair-wise: if a value is missing in `x`, the 

570 corresponding value in `y` is masked. 

571 

572 The p-value roughly indicates the probability of an uncorrelated system 

573 producing datasets that have a Spearman correlation at least as extreme 

574 as the one computed from these datasets. The p-values are not entirely 

575 reliable but are probably reasonable for datasets larger than 500 or so. 

576 

577 Parameters 

578 ---------- 

579 x, y : 1D or 2D array_like, y is optional 

580 One or two 1-D or 2-D arrays containing multiple variables and 

581 observations. When these are 1-D, each represents a vector of 

582 observations of a single variable. For the behavior in the 2-D case, 

583 see under ``axis``, below. 

584 use_ties : bool, optional 

585 DO NOT USE. Does not do anything, keyword is only left in place for 

586 backwards compatibility reasons. 

587 axis : int or None, optional 

588 If axis=0 (default), then each column represents a variable, with 

589 observations in the rows. If axis=1, the relationship is transposed: 

590 each row represents a variable, while the columns contain observations. 

591 If axis=None, then both arrays will be raveled. 

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

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

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

595 values. Default is 'propagate'. 

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

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

598 The following options are available: 

599 

600 * 'two-sided': the correlation is nonzero 

601 * 'less': the correlation is negative (less than zero) 

602 * 'greater': the correlation is positive (greater than zero) 

603 

604 .. versionadded:: 1.7.0 

605 

606 Returns 

607 ------- 

608 res : SignificanceResult 

609 An object containing attributes: 

610 

611 statistic : float or ndarray (2-D square) 

612 Spearman correlation matrix or correlation coefficient (if only 2 

613 variables are given as parameters). Correlation matrix is square 

614 with length equal to total number of variables (columns or rows) in 

615 ``a`` and ``b`` combined. 

616 pvalue : float 

617 The p-value for a hypothesis test whose null hypothesis 

618 is that two sets of data are linearly uncorrelated. See 

619 `alternative` above for alternative hypotheses. `pvalue` has the 

620 same shape as `statistic`. 

621 

622 References 

623 ---------- 

624 [CRCProbStat2000] section 14.7 

625 

626 """ 

627 if not use_ties: 

628 raise ValueError("`use_ties=False` is not supported in SciPy >= 1.2.0") 

629 

630 # Always returns a masked array, raveled if axis=None 

631 x, axisout = _chk_asarray(x, axis) 

632 if y is not None: 

633 # Deal only with 2-D `x` case. 

634 y, _ = _chk_asarray(y, axis) 

635 if axisout == 0: 

636 x = ma.column_stack((x, y)) 

637 else: 

638 x = ma.row_stack((x, y)) 

639 

640 if axisout == 1: 

641 # To simplify the code that follow (always use `n_obs, n_vars` shape) 

642 x = x.T 

643 

644 if nan_policy == 'omit': 

645 x = ma.masked_invalid(x) 

646 

647 def _spearmanr_2cols(x): 

648 # Mask the same observations for all variables, and then drop those 

649 # observations (can't leave them masked, rankdata is weird). 

650 x = ma.mask_rowcols(x, axis=0) 

651 x = x[~x.mask.any(axis=1), :] 

652 

653 # If either column is entirely NaN or Inf 

654 if not np.any(x.data): 

655 res = scipy.stats._stats_py.SignificanceResult(np.nan, np.nan) 

656 res.correlation = np.nan 

657 return res 

658 

659 m = ma.getmask(x) 

660 n_obs = x.shape[0] 

661 dof = n_obs - 2 - int(m.sum(axis=0)[0]) 

662 if dof < 0: 

663 raise ValueError("The input must have at least 3 entries!") 

664 

665 # Gets the ranks and rank differences 

666 x_ranked = rankdata(x, axis=0) 

667 rs = ma.corrcoef(x_ranked, rowvar=False).data 

668 

669 # rs can have elements equal to 1, so avoid zero division warnings 

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

671 # clip the small negative values possibly caused by rounding 

672 # errors before taking the square root 

673 t = rs * np.sqrt((dof / ((rs+1.0) * (1.0-rs))).clip(0)) 

674 

675 t, prob = scipy.stats._stats_py._ttest_finish(dof, t, alternative) 

676 

677 # For backwards compatibility, return scalars when comparing 2 columns 

678 if rs.shape == (2, 2): 

679 res = scipy.stats._stats_py.SignificanceResult(rs[1, 0], 

680 prob[1, 0]) 

681 res.correlation = rs[1, 0] 

682 return res 

683 else: 

684 res = scipy.stats._stats_py.SignificanceResult(rs, prob) 

685 res.correlation = rs 

686 return res 

687 

688 # Need to do this per pair of variables, otherwise the dropped observations 

689 # in a third column mess up the result for a pair. 

690 n_vars = x.shape[1] 

691 if n_vars == 2: 

692 return _spearmanr_2cols(x) 

693 else: 

694 rs = np.ones((n_vars, n_vars), dtype=float) 

695 prob = np.zeros((n_vars, n_vars), dtype=float) 

696 for var1 in range(n_vars - 1): 

697 for var2 in range(var1+1, n_vars): 

698 result = _spearmanr_2cols(x[:, [var1, var2]]) 

699 rs[var1, var2] = result.correlation 

700 rs[var2, var1] = result.correlation 

701 prob[var1, var2] = result.pvalue 

702 prob[var2, var1] = result.pvalue 

703 

704 res = scipy.stats._stats_py.SignificanceResult(rs, prob) 

705 res.correlation = rs 

706 return res 

707 

708 

709def _kendall_p_exact(n, c, alternative='two-sided'): 

710 

711 # Use the fact that distribution is symmetric: always calculate a CDF in 

712 # the left tail. 

713 # This will be the one-sided p-value if `c` is on the side of 

714 # the null distribution predicted by the alternative hypothesis. 

715 # The two-sided p-value will be twice this value. 

716 # If `c` is on the other side of the null distribution, we'll need to 

717 # take the complement and add back the probability mass at `c`. 

718 in_right_tail = (c >= (n*(n-1))//2 - c) 

719 alternative_greater = (alternative == 'greater') 

720 c = int(min(c, (n*(n-1))//2 - c)) 

721 

722 # Exact p-value, see Maurice G. Kendall, "Rank Correlation Methods" 

723 # (4th Edition), Charles Griffin & Co., 1970. 

724 if n <= 0: 

725 raise ValueError(f'n ({n}) must be positive') 

726 elif c < 0 or 4*c > n*(n-1): 

727 raise ValueError(f'c ({c}) must satisfy 0 <= 4c <= n(n-1) = {n*(n-1)}.') 

728 elif n == 1: 

729 prob = 1.0 

730 p_mass_at_c = 1 

731 elif n == 2: 

732 prob = 1.0 

733 p_mass_at_c = 0.5 

734 elif c == 0: 

735 prob = 2.0/math.factorial(n) if n < 171 else 0.0 

736 p_mass_at_c = prob/2 

737 elif c == 1: 

738 prob = 2.0/math.factorial(n-1) if n < 172 else 0.0 

739 p_mass_at_c = (n-1)/math.factorial(n) 

740 elif 4*c == n*(n-1) and alternative == 'two-sided': 

741 # I'm sure there's a simple formula for p_mass_at_c in this 

742 # case, but I don't know it. Use generic formula for one-sided p-value. 

743 prob = 1.0 

744 elif n < 171: 

745 new = np.zeros(c+1) 

746 new[0:2] = 1.0 

747 for j in range(3,n+1): 

748 new = np.cumsum(new) 

749 if j <= c: 

750 new[j:] -= new[:c+1-j] 

751 prob = 2.0*np.sum(new)/math.factorial(n) 

752 p_mass_at_c = new[-1]/math.factorial(n) 

753 else: 

754 new = np.zeros(c+1) 

755 new[0:2] = 1.0 

756 for j in range(3, n+1): 

757 new = np.cumsum(new)/j 

758 if j <= c: 

759 new[j:] -= new[:c+1-j] 

760 prob = np.sum(new) 

761 p_mass_at_c = new[-1]/2 

762 

763 if alternative != 'two-sided': 

764 # if the alternative hypothesis and alternative agree, 

765 # one-sided p-value is half the two-sided p-value 

766 if in_right_tail == alternative_greater: 

767 prob /= 2 

768 else: 

769 prob = 1 - prob/2 + p_mass_at_c 

770 

771 prob = np.clip(prob, 0, 1) 

772 

773 return prob 

774 

775 

776def kendalltau(x, y, use_ties=True, use_missing=False, method='auto', 

777 alternative='two-sided'): 

778 """ 

779 Computes Kendall's rank correlation tau on two variables *x* and *y*. 

780 

781 Parameters 

782 ---------- 

783 x : sequence 

784 First data list (for example, time). 

785 y : sequence 

786 Second data list. 

787 use_ties : {True, False}, optional 

788 Whether ties correction should be performed. 

789 use_missing : {False, True}, optional 

790 Whether missing data should be allocated a rank of 0 (False) or the 

791 average rank (True) 

792 method : {'auto', 'asymptotic', 'exact'}, optional 

793 Defines which method is used to calculate the p-value [1]_. 

794 'asymptotic' uses a normal approximation valid for large samples. 

795 'exact' computes the exact p-value, but can only be used if no ties 

796 are present. As the sample size increases, the 'exact' computation 

797 time may grow and the result may lose some precision. 

798 'auto' is the default and selects the appropriate 

799 method based on a trade-off between speed and accuracy. 

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

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

802 The following options are available: 

803 

804 * 'two-sided': the rank correlation is nonzero 

805 * 'less': the rank correlation is negative (less than zero) 

806 * 'greater': the rank correlation is positive (greater than zero) 

807 

808 Returns 

809 ------- 

810 res : SignificanceResult 

811 An object containing attributes: 

812 

813 statistic : float 

814 The tau statistic. 

815 pvalue : float 

816 The p-value for a hypothesis test whose null hypothesis is 

817 an absence of association, tau = 0. 

818 

819 References 

820 ---------- 

821 .. [1] Maurice G. Kendall, "Rank Correlation Methods" (4th Edition), 

822 Charles Griffin & Co., 1970. 

823 

824 """ 

825 (x, y, n) = _chk_size(x, y) 

826 (x, y) = (x.flatten(), y.flatten()) 

827 m = ma.mask_or(ma.getmask(x), ma.getmask(y)) 

828 if m is not nomask: 

829 x = ma.array(x, mask=m, copy=True) 

830 y = ma.array(y, mask=m, copy=True) 

831 # need int() here, otherwise numpy defaults to 32 bit 

832 # integer on all Windows architectures, causing overflow. 

833 # int() will keep it infinite precision. 

834 n -= int(m.sum()) 

835 

836 if n < 2: 

837 res = scipy.stats._stats_py.SignificanceResult(np.nan, np.nan) 

838 res.correlation = np.nan 

839 return res 

840 

841 rx = ma.masked_equal(rankdata(x, use_missing=use_missing), 0) 

842 ry = ma.masked_equal(rankdata(y, use_missing=use_missing), 0) 

843 idx = rx.argsort() 

844 (rx, ry) = (rx[idx], ry[idx]) 

845 C = np.sum([((ry[i+1:] > ry[i]) * (rx[i+1:] > rx[i])).filled(0).sum() 

846 for i in range(len(ry)-1)], dtype=float) 

847 D = np.sum([((ry[i+1:] < ry[i])*(rx[i+1:] > rx[i])).filled(0).sum() 

848 for i in range(len(ry)-1)], dtype=float) 

849 xties = count_tied_groups(x) 

850 yties = count_tied_groups(y) 

851 if use_ties: 

852 corr_x = np.sum([v*k*(k-1) for (k,v) in xties.items()], dtype=float) 

853 corr_y = np.sum([v*k*(k-1) for (k,v) in yties.items()], dtype=float) 

854 denom = ma.sqrt((n*(n-1)-corr_x)/2. * (n*(n-1)-corr_y)/2.) 

855 else: 

856 denom = n*(n-1)/2. 

857 tau = (C-D) / denom 

858 

859 if method == 'exact' and (xties or yties): 

860 raise ValueError("Ties found, exact method cannot be used.") 

861 

862 if method == 'auto': 

863 if (not xties and not yties) and (n <= 33 or min(C, n*(n-1)/2.0-C) <= 1): 

864 method = 'exact' 

865 else: 

866 method = 'asymptotic' 

867 

868 if not xties and not yties and method == 'exact': 

869 prob = _kendall_p_exact(n, C, alternative) 

870 

871 elif method == 'asymptotic': 

872 var_s = n*(n-1)*(2*n+5) 

873 if use_ties: 

874 var_s -= np.sum([v*k*(k-1)*(2*k+5)*1. for (k,v) in xties.items()]) 

875 var_s -= np.sum([v*k*(k-1)*(2*k+5)*1. for (k,v) in yties.items()]) 

876 v1 = (np.sum([v*k*(k-1) for (k, v) in xties.items()], dtype=float) * 

877 np.sum([v*k*(k-1) for (k, v) in yties.items()], dtype=float)) 

878 v1 /= 2.*n*(n-1) 

879 if n > 2: 

880 v2 = np.sum([v*k*(k-1)*(k-2) for (k,v) in xties.items()], 

881 dtype=float) * \ 

882 np.sum([v*k*(k-1)*(k-2) for (k,v) in yties.items()], 

883 dtype=float) 

884 v2 /= 9.*n*(n-1)*(n-2) 

885 else: 

886 v2 = 0 

887 else: 

888 v1 = v2 = 0 

889 

890 var_s /= 18. 

891 var_s += (v1 + v2) 

892 z = (C-D)/np.sqrt(var_s) 

893 _, prob = scipy.stats._stats_py._normtest_finish(z, alternative) 

894 else: 

895 raise ValueError("Unknown method "+str(method)+" specified, please " 

896 "use auto, exact or asymptotic.") 

897 

898 res = scipy.stats._stats_py.SignificanceResult(tau, prob) 

899 res.correlation = tau 

900 return res 

901 

902 

903def kendalltau_seasonal(x): 

904 """ 

905 Computes a multivariate Kendall's rank correlation tau, for seasonal data. 

906 

907 Parameters 

908 ---------- 

909 x : 2-D ndarray 

910 Array of seasonal data, with seasons in columns. 

911 

912 """ 

913 x = ma.array(x, subok=True, copy=False, ndmin=2) 

914 (n,m) = x.shape 

915 n_p = x.count(0) 

916 

917 S_szn = sum(msign(x[i:]-x[i]).sum(0) for i in range(n)) 

918 S_tot = S_szn.sum() 

919 

920 n_tot = x.count() 

921 ties = count_tied_groups(x.compressed()) 

922 corr_ties = sum(v*k*(k-1) for (k,v) in ties.items()) 

923 denom_tot = ma.sqrt(1.*n_tot*(n_tot-1)*(n_tot*(n_tot-1)-corr_ties))/2. 

924 

925 R = rankdata(x, axis=0, use_missing=True) 

926 K = ma.empty((m,m), dtype=int) 

927 covmat = ma.empty((m,m), dtype=float) 

928 denom_szn = ma.empty(m, dtype=float) 

929 for j in range(m): 

930 ties_j = count_tied_groups(x[:,j].compressed()) 

931 corr_j = sum(v*k*(k-1) for (k,v) in ties_j.items()) 

932 cmb = n_p[j]*(n_p[j]-1) 

933 for k in range(j,m,1): 

934 K[j,k] = sum(msign((x[i:,j]-x[i,j])*(x[i:,k]-x[i,k])).sum() 

935 for i in range(n)) 

936 covmat[j,k] = (K[j,k] + 4*(R[:,j]*R[:,k]).sum() - 

937 n*(n_p[j]+1)*(n_p[k]+1))/3. 

938 K[k,j] = K[j,k] 

939 covmat[k,j] = covmat[j,k] 

940 

941 denom_szn[j] = ma.sqrt(cmb*(cmb-corr_j)) / 2. 

942 

943 var_szn = covmat.diagonal() 

944 

945 z_szn = msign(S_szn) * (abs(S_szn)-1) / ma.sqrt(var_szn) 

946 z_tot_ind = msign(S_tot) * (abs(S_tot)-1) / ma.sqrt(var_szn.sum()) 

947 z_tot_dep = msign(S_tot) * (abs(S_tot)-1) / ma.sqrt(covmat.sum()) 

948 

949 prob_szn = special.erfc(abs(z_szn)/np.sqrt(2)) 

950 prob_tot_ind = special.erfc(abs(z_tot_ind)/np.sqrt(2)) 

951 prob_tot_dep = special.erfc(abs(z_tot_dep)/np.sqrt(2)) 

952 

953 chi2_tot = (z_szn*z_szn).sum() 

954 chi2_trd = m * z_szn.mean()**2 

955 output = {'seasonal tau': S_szn/denom_szn, 

956 'global tau': S_tot/denom_tot, 

957 'global tau (alt)': S_tot/denom_szn.sum(), 

958 'seasonal p-value': prob_szn, 

959 'global p-value (indep)': prob_tot_ind, 

960 'global p-value (dep)': prob_tot_dep, 

961 'chi2 total': chi2_tot, 

962 'chi2 trend': chi2_trd, 

963 } 

964 return output 

965 

966 

967PointbiserialrResult = namedtuple('PointbiserialrResult', ('correlation', 

968 'pvalue')) 

969 

970 

971def pointbiserialr(x, y): 

972 """Calculates a point biserial correlation coefficient and its p-value. 

973 

974 Parameters 

975 ---------- 

976 x : array_like of bools 

977 Input array. 

978 y : array_like 

979 Input array. 

980 

981 Returns 

982 ------- 

983 correlation : float 

984 R value 

985 pvalue : float 

986 2-tailed p-value 

987 

988 Notes 

989 ----- 

990 Missing values are considered pair-wise: if a value is missing in x, 

991 the corresponding value in y is masked. 

992 

993 For more details on `pointbiserialr`, see `scipy.stats.pointbiserialr`. 

994 

995 """ 

996 x = ma.fix_invalid(x, copy=True).astype(bool) 

997 y = ma.fix_invalid(y, copy=True).astype(float) 

998 # Get rid of the missing data 

999 m = ma.mask_or(ma.getmask(x), ma.getmask(y)) 

1000 if m is not nomask: 

1001 unmask = np.logical_not(m) 

1002 x = x[unmask] 

1003 y = y[unmask] 

1004 

1005 n = len(x) 

1006 # phat is the fraction of x values that are True 

1007 phat = x.sum() / float(n) 

1008 y0 = y[~x] # y-values where x is False 

1009 y1 = y[x] # y-values where x is True 

1010 y0m = y0.mean() 

1011 y1m = y1.mean() 

1012 

1013 rpb = (y1m - y0m)*np.sqrt(phat * (1-phat)) / y.std() 

1014 

1015 df = n-2 

1016 t = rpb*ma.sqrt(df/(1.0-rpb**2)) 

1017 prob = _betai(0.5*df, 0.5, df/(df+t*t)) 

1018 

1019 return PointbiserialrResult(rpb, prob) 

1020 

1021 

1022def linregress(x, y=None): 

1023 r""" 

1024 Linear regression calculation 

1025 

1026 Note that the non-masked version is used, and that this docstring is 

1027 replaced by the non-masked docstring + some info on missing data. 

1028 

1029 """ 

1030 if y is None: 

1031 x = ma.array(x) 

1032 if x.shape[0] == 2: 

1033 x, y = x 

1034 elif x.shape[1] == 2: 

1035 x, y = x.T 

1036 else: 

1037 raise ValueError("If only `x` is given as input, " 

1038 "it has to be of shape (2, N) or (N, 2), " 

1039 f"provided shape was {x.shape}") 

1040 else: 

1041 x = ma.array(x) 

1042 y = ma.array(y) 

1043 

1044 x = x.flatten() 

1045 y = y.flatten() 

1046 

1047 if np.amax(x) == np.amin(x) and len(x) > 1: 

1048 raise ValueError("Cannot calculate a linear regression " 

1049 "if all x values are identical") 

1050 

1051 m = ma.mask_or(ma.getmask(x), ma.getmask(y), shrink=False) 

1052 if m is not nomask: 

1053 x = ma.array(x, mask=m) 

1054 y = ma.array(y, mask=m) 

1055 if np.any(~m): 

1056 result = stats_linregress(x.data[~m], y.data[~m]) 

1057 else: 

1058 # All data is masked 

1059 result = stats_LinregressResult(slope=None, intercept=None, 

1060 rvalue=None, pvalue=None, 

1061 stderr=None, 

1062 intercept_stderr=None) 

1063 else: 

1064 result = stats_linregress(x.data, y.data) 

1065 

1066 return result 

1067 

1068 

1069def theilslopes(y, x=None, alpha=0.95, method='separate'): 

1070 r""" 

1071 Computes the Theil-Sen estimator for a set of points (x, y). 

1072 

1073 `theilslopes` implements a method for robust linear regression. It 

1074 computes the slope as the median of all slopes between paired values. 

1075 

1076 Parameters 

1077 ---------- 

1078 y : array_like 

1079 Dependent variable. 

1080 x : array_like or None, optional 

1081 Independent variable. If None, use ``arange(len(y))`` instead. 

1082 alpha : float, optional 

1083 Confidence degree between 0 and 1. Default is 95% confidence. 

1084 Note that `alpha` is symmetric around 0.5, i.e. both 0.1 and 0.9 are 

1085 interpreted as "find the 90% confidence interval". 

1086 method : {'joint', 'separate'}, optional 

1087 Method to be used for computing estimate for intercept. 

1088 Following methods are supported, 

1089 

1090 * 'joint': Uses np.median(y - slope * x) as intercept. 

1091 * 'separate': Uses np.median(y) - slope * np.median(x) 

1092 as intercept. 

1093 

1094 The default is 'separate'. 

1095 

1096 .. versionadded:: 1.8.0 

1097 

1098 Returns 

1099 ------- 

1100 result : ``TheilslopesResult`` instance 

1101 The return value is an object with the following attributes: 

1102 

1103 slope : float 

1104 Theil slope. 

1105 intercept : float 

1106 Intercept of the Theil line. 

1107 low_slope : float 

1108 Lower bound of the confidence interval on `slope`. 

1109 high_slope : float 

1110 Upper bound of the confidence interval on `slope`. 

1111 

1112 See Also 

1113 -------- 

1114 siegelslopes : a similar technique using repeated medians 

1115 

1116 

1117 Notes 

1118 ----- 

1119 For more details on `theilslopes`, see `scipy.stats.theilslopes`. 

1120 

1121 """ 

1122 y = ma.asarray(y).flatten() 

1123 if x is None: 

1124 x = ma.arange(len(y), dtype=float) 

1125 else: 

1126 x = ma.asarray(x).flatten() 

1127 if len(x) != len(y): 

1128 raise ValueError(f"Incompatible lengths ! ({len(y)}<>{len(x)})") 

1129 

1130 m = ma.mask_or(ma.getmask(x), ma.getmask(y)) 

1131 y._mask = x._mask = m 

1132 # Disregard any masked elements of x or y 

1133 y = y.compressed() 

1134 x = x.compressed().astype(float) 

1135 # We now have unmasked arrays so can use `scipy.stats.theilslopes` 

1136 return stats_theilslopes(y, x, alpha=alpha, method=method) 

1137 

1138 

1139def siegelslopes(y, x=None, method="hierarchical"): 

1140 r""" 

1141 Computes the Siegel estimator for a set of points (x, y). 

1142 

1143 `siegelslopes` implements a method for robust linear regression 

1144 using repeated medians to fit a line to the points (x, y). 

1145 The method is robust to outliers with an asymptotic breakdown point 

1146 of 50%. 

1147 

1148 Parameters 

1149 ---------- 

1150 y : array_like 

1151 Dependent variable. 

1152 x : array_like or None, optional 

1153 Independent variable. If None, use ``arange(len(y))`` instead. 

1154 method : {'hierarchical', 'separate'} 

1155 If 'hierarchical', estimate the intercept using the estimated 

1156 slope ``slope`` (default option). 

1157 If 'separate', estimate the intercept independent of the estimated 

1158 slope. See Notes for details. 

1159 

1160 Returns 

1161 ------- 

1162 result : ``SiegelslopesResult`` instance 

1163 The return value is an object with the following attributes: 

1164 

1165 slope : float 

1166 Estimate of the slope of the regression line. 

1167 intercept : float 

1168 Estimate of the intercept of the regression line. 

1169 

1170 See Also 

1171 -------- 

1172 theilslopes : a similar technique without repeated medians 

1173 

1174 Notes 

1175 ----- 

1176 For more details on `siegelslopes`, see `scipy.stats.siegelslopes`. 

1177 

1178 """ 

1179 y = ma.asarray(y).ravel() 

1180 if x is None: 

1181 x = ma.arange(len(y), dtype=float) 

1182 else: 

1183 x = ma.asarray(x).ravel() 

1184 if len(x) != len(y): 

1185 raise ValueError(f"Incompatible lengths ! ({len(y)}<>{len(x)})") 

1186 

1187 m = ma.mask_or(ma.getmask(x), ma.getmask(y)) 

1188 y._mask = x._mask = m 

1189 # Disregard any masked elements of x or y 

1190 y = y.compressed() 

1191 x = x.compressed().astype(float) 

1192 # We now have unmasked arrays so can use `scipy.stats.siegelslopes` 

1193 return stats_siegelslopes(y, x, method=method) 

1194 

1195 

1196SenSeasonalSlopesResult = _make_tuple_bunch('SenSeasonalSlopesResult', 

1197 ['intra_slope', 'inter_slope']) 

1198 

1199 

1200def sen_seasonal_slopes(x): 

1201 r""" 

1202 Computes seasonal Theil-Sen and Kendall slope estimators. 

1203 

1204 The seasonal generalization of Sen's slope computes the slopes between all 

1205 pairs of values within a "season" (column) of a 2D array. It returns an 

1206 array containing the median of these "within-season" slopes for each 

1207 season (the Theil-Sen slope estimator of each season), and it returns the 

1208 median of the within-season slopes across all seasons (the seasonal Kendall 

1209 slope estimator). 

1210 

1211 Parameters 

1212 ---------- 

1213 x : 2D array_like 

1214 Each column of `x` contains measurements of the dependent variable 

1215 within a season. The independent variable (usually time) of each season 

1216 is assumed to be ``np.arange(x.shape[0])``. 

1217 

1218 Returns 

1219 ------- 

1220 result : ``SenSeasonalSlopesResult`` instance 

1221 The return value is an object with the following attributes: 

1222 

1223 intra_slope : ndarray 

1224 For each season, the Theil-Sen slope estimator: the median of 

1225 within-season slopes. 

1226 inter_slope : float 

1227 The seasonal Kendall slope estimateor: the median of within-season 

1228 slopes *across all* seasons. 

1229 

1230 See Also 

1231 -------- 

1232 theilslopes : the analogous function for non-seasonal data 

1233 scipy.stats.theilslopes : non-seasonal slopes for non-masked arrays 

1234 

1235 Notes 

1236 ----- 

1237 The slopes :math:`d_{ijk}` within season :math:`i` are: 

1238 

1239 .. math:: 

1240 

1241 d_{ijk} = \frac{x_{ij} - x_{ik}} 

1242 {j - k} 

1243 

1244 for pairs of distinct integer indices :math:`j, k` of :math:`x`. 

1245 

1246 Element :math:`i` of the returned `intra_slope` array is the median of the 

1247 :math:`d_{ijk}` over all :math:`j < k`; this is the Theil-Sen slope 

1248 estimator of season :math:`i`. The returned `inter_slope` value, better 

1249 known as the seasonal Kendall slope estimator, is the median of the 

1250 :math:`d_{ijk}` over all :math:`i, j, k`. 

1251 

1252 References 

1253 ---------- 

1254 .. [1] Hirsch, Robert M., James R. Slack, and Richard A. Smith. 

1255 "Techniques of trend analysis for monthly water quality data." 

1256 *Water Resources Research* 18.1 (1982): 107-121. 

1257 

1258 Examples 

1259 -------- 

1260 Suppose we have 100 observations of a dependent variable for each of four 

1261 seasons: 

1262 

1263 >>> import numpy as np 

1264 >>> rng = np.random.default_rng() 

1265 >>> x = rng.random(size=(100, 4)) 

1266 

1267 We compute the seasonal slopes as: 

1268 

1269 >>> from scipy import stats 

1270 >>> intra_slope, inter_slope = stats.mstats.sen_seasonal_slopes(x) 

1271 

1272 If we define a function to compute all slopes between observations within 

1273 a season: 

1274 

1275 >>> def dijk(yi): 

1276 ... n = len(yi) 

1277 ... x = np.arange(n) 

1278 ... dy = yi - yi[:, np.newaxis] 

1279 ... dx = x - x[:, np.newaxis] 

1280 ... # we only want unique pairs of distinct indices 

1281 ... mask = np.triu(np.ones((n, n), dtype=bool), k=1) 

1282 ... return dy[mask]/dx[mask] 

1283 

1284 then element ``i`` of ``intra_slope`` is the median of ``dijk[x[:, i]]``: 

1285 

1286 >>> i = 2 

1287 >>> np.allclose(np.median(dijk(x[:, i])), intra_slope[i]) 

1288 True 

1289 

1290 and ``inter_slope`` is the median of the values returned by ``dijk`` for 

1291 all seasons: 

1292 

1293 >>> all_slopes = np.concatenate([dijk(x[:, i]) for i in range(x.shape[1])]) 

1294 >>> np.allclose(np.median(all_slopes), inter_slope) 

1295 True 

1296 

1297 Because the data are randomly generated, we would expect the median slopes 

1298 to be nearly zero both within and across all seasons, and indeed they are: 

1299 

1300 >>> intra_slope.data 

1301 array([ 0.00124504, -0.00277761, -0.00221245, -0.00036338]) 

1302 >>> inter_slope 

1303 -0.0010511779872922058 

1304 

1305 """ 

1306 x = ma.array(x, subok=True, copy=False, ndmin=2) 

1307 (n,_) = x.shape 

1308 # Get list of slopes per season 

1309 szn_slopes = ma.vstack([(x[i+1:]-x[i])/np.arange(1,n-i)[:,None] 

1310 for i in range(n)]) 

1311 szn_medslopes = ma.median(szn_slopes, axis=0) 

1312 medslope = ma.median(szn_slopes, axis=None) 

1313 return SenSeasonalSlopesResult(szn_medslopes, medslope) 

1314 

1315 

1316Ttest_1sampResult = namedtuple('Ttest_1sampResult', ('statistic', 'pvalue')) 

1317 

1318 

1319def ttest_1samp(a, popmean, axis=0, alternative='two-sided'): 

1320 """ 

1321 Calculates the T-test for the mean of ONE group of scores. 

1322 

1323 Parameters 

1324 ---------- 

1325 a : array_like 

1326 sample observation 

1327 popmean : float or array_like 

1328 expected value in null hypothesis, if array_like than it must have the 

1329 same shape as `a` excluding the axis dimension 

1330 axis : int or None, optional 

1331 Axis along which to compute test. If None, compute over the whole 

1332 array `a`. 

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

1334 Defines the alternative hypothesis. 

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

1336 

1337 * 'two-sided': the mean of the underlying distribution of the sample 

1338 is different than the given population mean (`popmean`) 

1339 * 'less': the mean of the underlying distribution of the sample is 

1340 less than the given population mean (`popmean`) 

1341 * 'greater': the mean of the underlying distribution of the sample is 

1342 greater than the given population mean (`popmean`) 

1343 

1344 .. versionadded:: 1.7.0 

1345 

1346 Returns 

1347 ------- 

1348 statistic : float or array 

1349 t-statistic 

1350 pvalue : float or array 

1351 The p-value 

1352 

1353 Notes 

1354 ----- 

1355 For more details on `ttest_1samp`, see `scipy.stats.ttest_1samp`. 

1356 

1357 """ 

1358 a, axis = _chk_asarray(a, axis) 

1359 if a.size == 0: 

1360 return (np.nan, np.nan) 

1361 

1362 x = a.mean(axis=axis) 

1363 v = a.var(axis=axis, ddof=1) 

1364 n = a.count(axis=axis) 

1365 # force df to be an array for masked division not to throw a warning 

1366 df = ma.asanyarray(n - 1.0) 

1367 svar = ((n - 1.0) * v) / df 

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

1369 t = (x - popmean) / ma.sqrt(svar / n) 

1370 

1371 t, prob = scipy.stats._stats_py._ttest_finish(df, t, alternative) 

1372 return Ttest_1sampResult(t, prob) 

1373 

1374 

1375ttest_onesamp = ttest_1samp 

1376 

1377 

1378Ttest_indResult = namedtuple('Ttest_indResult', ('statistic', 'pvalue')) 

1379 

1380 

1381def ttest_ind(a, b, axis=0, equal_var=True, alternative='two-sided'): 

1382 """ 

1383 Calculates the T-test for the means of TWO INDEPENDENT samples of scores. 

1384 

1385 Parameters 

1386 ---------- 

1387 a, b : array_like 

1388 The arrays must have the same shape, except in the dimension 

1389 corresponding to `axis` (the first, by default). 

1390 axis : int or None, optional 

1391 Axis along which to compute test. If None, compute over the whole 

1392 arrays, `a`, and `b`. 

1393 equal_var : bool, optional 

1394 If True, perform a standard independent 2 sample test that assumes equal 

1395 population variances. 

1396 If False, perform Welch's t-test, which does not assume equal population 

1397 variance. 

1398 

1399 .. versionadded:: 0.17.0 

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

1401 Defines the alternative hypothesis. 

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

1403 

1404 * 'two-sided': the means of the distributions underlying the samples 

1405 are unequal. 

1406 * 'less': the mean of the distribution underlying the first sample 

1407 is less than the mean of the distribution underlying the second 

1408 sample. 

1409 * 'greater': the mean of the distribution underlying the first 

1410 sample is greater than the mean of the distribution underlying 

1411 the second sample. 

1412 

1413 .. versionadded:: 1.7.0 

1414 

1415 Returns 

1416 ------- 

1417 statistic : float or array 

1418 The calculated t-statistic. 

1419 pvalue : float or array 

1420 The p-value. 

1421 

1422 Notes 

1423 ----- 

1424 For more details on `ttest_ind`, see `scipy.stats.ttest_ind`. 

1425 

1426 """ 

1427 a, b, axis = _chk2_asarray(a, b, axis) 

1428 

1429 if a.size == 0 or b.size == 0: 

1430 return Ttest_indResult(np.nan, np.nan) 

1431 

1432 (x1, x2) = (a.mean(axis), b.mean(axis)) 

1433 (v1, v2) = (a.var(axis=axis, ddof=1), b.var(axis=axis, ddof=1)) 

1434 (n1, n2) = (a.count(axis), b.count(axis)) 

1435 

1436 if equal_var: 

1437 # force df to be an array for masked division not to throw a warning 

1438 df = ma.asanyarray(n1 + n2 - 2.0) 

1439 svar = ((n1-1)*v1+(n2-1)*v2) / df 

1440 denom = ma.sqrt(svar*(1.0/n1 + 1.0/n2)) # n-D computation here! 

1441 else: 

1442 vn1 = v1/n1 

1443 vn2 = v2/n2 

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

1445 df = (vn1 + vn2)**2 / (vn1**2 / (n1 - 1) + vn2**2 / (n2 - 1)) 

1446 

1447 # If df is undefined, variances are zero. 

1448 # It doesn't matter what df is as long as it is not NaN. 

1449 df = np.where(np.isnan(df), 1, df) 

1450 denom = ma.sqrt(vn1 + vn2) 

1451 

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

1453 t = (x1-x2) / denom 

1454 

1455 t, prob = scipy.stats._stats_py._ttest_finish(df, t, alternative) 

1456 return Ttest_indResult(t, prob) 

1457 

1458 

1459Ttest_relResult = namedtuple('Ttest_relResult', ('statistic', 'pvalue')) 

1460 

1461 

1462def ttest_rel(a, b, axis=0, alternative='two-sided'): 

1463 """ 

1464 Calculates the T-test on TWO RELATED samples of scores, a and b. 

1465 

1466 Parameters 

1467 ---------- 

1468 a, b : array_like 

1469 The arrays must have the same shape. 

1470 axis : int or None, optional 

1471 Axis along which to compute test. If None, compute over the whole 

1472 arrays, `a`, and `b`. 

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

1474 Defines the alternative hypothesis. 

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

1476 

1477 * 'two-sided': the means of the distributions underlying the samples 

1478 are unequal. 

1479 * 'less': the mean of the distribution underlying the first sample 

1480 is less than the mean of the distribution underlying the second 

1481 sample. 

1482 * 'greater': the mean of the distribution underlying the first 

1483 sample is greater than the mean of the distribution underlying 

1484 the second sample. 

1485 

1486 .. versionadded:: 1.7.0 

1487 

1488 Returns 

1489 ------- 

1490 statistic : float or array 

1491 t-statistic 

1492 pvalue : float or array 

1493 two-tailed p-value 

1494 

1495 Notes 

1496 ----- 

1497 For more details on `ttest_rel`, see `scipy.stats.ttest_rel`. 

1498 

1499 """ 

1500 a, b, axis = _chk2_asarray(a, b, axis) 

1501 if len(a) != len(b): 

1502 raise ValueError('unequal length arrays') 

1503 

1504 if a.size == 0 or b.size == 0: 

1505 return Ttest_relResult(np.nan, np.nan) 

1506 

1507 n = a.count(axis) 

1508 df = ma.asanyarray(n-1.0) 

1509 d = (a-b).astype('d') 

1510 dm = d.mean(axis) 

1511 v = d.var(axis=axis, ddof=1) 

1512 denom = ma.sqrt(v / n) 

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

1514 t = dm / denom 

1515 

1516 t, prob = scipy.stats._stats_py._ttest_finish(df, t, alternative) 

1517 return Ttest_relResult(t, prob) 

1518 

1519 

1520MannwhitneyuResult = namedtuple('MannwhitneyuResult', ('statistic', 

1521 'pvalue')) 

1522 

1523 

1524def mannwhitneyu(x,y, use_continuity=True): 

1525 """ 

1526 Computes the Mann-Whitney statistic 

1527 

1528 Missing values in `x` and/or `y` are discarded. 

1529 

1530 Parameters 

1531 ---------- 

1532 x : sequence 

1533 Input 

1534 y : sequence 

1535 Input 

1536 use_continuity : {True, False}, optional 

1537 Whether a continuity correction (1/2.) should be taken into account. 

1538 

1539 Returns 

1540 ------- 

1541 statistic : float 

1542 The minimum of the Mann-Whitney statistics 

1543 pvalue : float 

1544 Approximate two-sided p-value assuming a normal distribution. 

1545 

1546 """ 

1547 x = ma.asarray(x).compressed().view(ndarray) 

1548 y = ma.asarray(y).compressed().view(ndarray) 

1549 ranks = rankdata(np.concatenate([x,y])) 

1550 (nx, ny) = (len(x), len(y)) 

1551 nt = nx + ny 

1552 U = ranks[:nx].sum() - nx*(nx+1)/2. 

1553 U = max(U, nx*ny - U) 

1554 u = nx*ny - U 

1555 

1556 mu = (nx*ny)/2. 

1557 sigsq = (nt**3 - nt)/12. 

1558 ties = count_tied_groups(ranks) 

1559 sigsq -= sum(v*(k**3-k) for (k,v) in ties.items())/12. 

1560 sigsq *= nx*ny/float(nt*(nt-1)) 

1561 

1562 if use_continuity: 

1563 z = (U - 1/2. - mu) / ma.sqrt(sigsq) 

1564 else: 

1565 z = (U - mu) / ma.sqrt(sigsq) 

1566 

1567 prob = special.erfc(abs(z)/np.sqrt(2)) 

1568 return MannwhitneyuResult(u, prob) 

1569 

1570 

1571KruskalResult = namedtuple('KruskalResult', ('statistic', 'pvalue')) 

1572 

1573 

1574def kruskal(*args): 

1575 """ 

1576 Compute the Kruskal-Wallis H-test for independent samples 

1577 

1578 Parameters 

1579 ---------- 

1580 sample1, sample2, ... : array_like 

1581 Two or more arrays with the sample measurements can be given as 

1582 arguments. 

1583 

1584 Returns 

1585 ------- 

1586 statistic : float 

1587 The Kruskal-Wallis H statistic, corrected for ties 

1588 pvalue : float 

1589 The p-value for the test using the assumption that H has a chi 

1590 square distribution 

1591 

1592 Notes 

1593 ----- 

1594 For more details on `kruskal`, see `scipy.stats.kruskal`. 

1595 

1596 Examples 

1597 -------- 

1598 >>> from scipy.stats.mstats import kruskal 

1599 

1600 Random samples from three different brands of batteries were tested 

1601 to see how long the charge lasted. Results were as follows: 

1602 

1603 >>> a = [6.3, 5.4, 5.7, 5.2, 5.0] 

1604 >>> b = [6.9, 7.0, 6.1, 7.9] 

1605 >>> c = [7.2, 6.9, 6.1, 6.5] 

1606 

1607 Test the hypotesis that the distribution functions for all of the brands' 

1608 durations are identical. Use 5% level of significance. 

1609 

1610 >>> kruskal(a, b, c) 

1611 KruskalResult(statistic=7.113812154696133, pvalue=0.028526948491942164) 

1612 

1613 The null hypothesis is rejected at the 5% level of significance 

1614 because the returned p-value is less than the critical value of 5%. 

1615 

1616 """ 

1617 output = argstoarray(*args) 

1618 ranks = ma.masked_equal(rankdata(output, use_missing=False), 0) 

1619 sumrk = ranks.sum(-1) 

1620 ngrp = ranks.count(-1) 

1621 ntot = ranks.count() 

1622 H = 12./(ntot*(ntot+1)) * (sumrk**2/ngrp).sum() - 3*(ntot+1) 

1623 # Tie correction 

1624 ties = count_tied_groups(ranks) 

1625 T = 1. - sum(v*(k**3-k) for (k,v) in ties.items())/float(ntot**3-ntot) 

1626 if T == 0: 

1627 raise ValueError('All numbers are identical in kruskal') 

1628 

1629 H /= T 

1630 df = len(output) - 1 

1631 prob = distributions.chi2.sf(H, df) 

1632 return KruskalResult(H, prob) 

1633 

1634 

1635kruskalwallis = kruskal 

1636 

1637 

1638@_rename_parameter("mode", "method") 

1639def ks_1samp(x, cdf, args=(), alternative="two-sided", method='auto'): 

1640 """ 

1641 Computes the Kolmogorov-Smirnov test on one sample of masked values. 

1642 

1643 Missing values in `x` are discarded. 

1644 

1645 Parameters 

1646 ---------- 

1647 x : array_like 

1648 a 1-D array of observations of random variables. 

1649 cdf : str or callable 

1650 If a string, it should be the name of a distribution in `scipy.stats`. 

1651 If a callable, that callable is used to calculate the cdf. 

1652 args : tuple, sequence, optional 

1653 Distribution parameters, used if `cdf` is a string. 

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

1655 Indicates the alternative hypothesis. Default is 'two-sided'. 

1656 method : {'auto', 'exact', 'asymp'}, optional 

1657 Defines the method used for calculating the p-value. 

1658 The following options are available (default is 'auto'): 

1659 

1660 * 'auto' : use 'exact' for small size arrays, 'asymp' for large 

1661 * 'exact' : use approximation to exact distribution of test statistic 

1662 * 'asymp' : use asymptotic distribution of test statistic 

1663 

1664 Returns 

1665 ------- 

1666 d : float 

1667 Value of the Kolmogorov Smirnov test 

1668 p : float 

1669 Corresponding p-value. 

1670 

1671 """ 

1672 alternative = {'t': 'two-sided', 'g': 'greater', 'l': 'less'}.get( 

1673 alternative.lower()[0], alternative) 

1674 return scipy.stats._stats_py.ks_1samp( 

1675 x, cdf, args=args, alternative=alternative, method=method) 

1676 

1677 

1678@_rename_parameter("mode", "method") 

1679def ks_2samp(data1, data2, alternative="two-sided", method='auto'): 

1680 """ 

1681 Computes the Kolmogorov-Smirnov test on two samples. 

1682 

1683 Missing values in `x` and/or `y` are discarded. 

1684 

1685 Parameters 

1686 ---------- 

1687 data1 : array_like 

1688 First data set 

1689 data2 : array_like 

1690 Second data set 

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

1692 Indicates the alternative hypothesis. Default is 'two-sided'. 

1693 method : {'auto', 'exact', 'asymp'}, optional 

1694 Defines the method used for calculating the p-value. 

1695 The following options are available (default is 'auto'): 

1696 

1697 * 'auto' : use 'exact' for small size arrays, 'asymp' for large 

1698 * 'exact' : use approximation to exact distribution of test statistic 

1699 * 'asymp' : use asymptotic distribution of test statistic 

1700 

1701 Returns 

1702 ------- 

1703 d : float 

1704 Value of the Kolmogorov Smirnov test 

1705 p : float 

1706 Corresponding p-value. 

1707 

1708 """ 

1709 # Ideally this would be accomplished by 

1710 # ks_2samp = scipy.stats._stats_py.ks_2samp 

1711 # but the circular dependencies between _mstats_basic and stats prevent that. 

1712 alternative = {'t': 'two-sided', 'g': 'greater', 'l': 'less'}.get( 

1713 alternative.lower()[0], alternative) 

1714 return scipy.stats._stats_py.ks_2samp(data1, data2, 

1715 alternative=alternative, 

1716 method=method) 

1717 

1718 

1719ks_twosamp = ks_2samp 

1720 

1721 

1722@_rename_parameter("mode", "method") 

1723def kstest(data1, data2, args=(), alternative='two-sided', method='auto'): 

1724 """ 

1725 

1726 Parameters 

1727 ---------- 

1728 data1 : array_like 

1729 data2 : str, callable or array_like 

1730 args : tuple, sequence, optional 

1731 Distribution parameters, used if `data1` or `data2` are strings. 

1732 alternative : str, as documented in stats.kstest 

1733 method : str, as documented in stats.kstest 

1734 

1735 Returns 

1736 ------- 

1737 tuple of (K-S statistic, probability) 

1738 

1739 """ 

1740 return scipy.stats._stats_py.kstest(data1, data2, args, 

1741 alternative=alternative, method=method) 

1742 

1743 

1744def trima(a, limits=None, inclusive=(True,True)): 

1745 """ 

1746 Trims an array by masking the data outside some given limits. 

1747 

1748 Returns a masked version of the input array. 

1749 

1750 Parameters 

1751 ---------- 

1752 a : array_like 

1753 Input array. 

1754 limits : {None, tuple}, optional 

1755 Tuple of (lower limit, upper limit) in absolute values. 

1756 Values of the input array lower (greater) than the lower (upper) limit 

1757 will be masked. A limit is None indicates an open interval. 

1758 inclusive : (bool, bool) tuple, optional 

1759 Tuple of (lower flag, upper flag), indicating whether values exactly 

1760 equal to the lower (upper) limit are allowed. 

1761 

1762 Examples 

1763 -------- 

1764 >>> from scipy.stats.mstats import trima 

1765 >>> import numpy as np 

1766 

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

1768 

1769 The interval is left-closed and right-open, i.e., `[2, 8)`. 

1770 Trim the array by keeping only values in the interval. 

1771 

1772 >>> trima(a, limits=(2, 8), inclusive=(True, False)) 

1773 masked_array(data=[--, --, 2, 3, 4, 5, 6, 7, --, --], 

1774 mask=[ True, True, False, False, False, False, False, False, 

1775 True, True], 

1776 fill_value=999999) 

1777 

1778 """ 

1779 a = ma.asarray(a) 

1780 a.unshare_mask() 

1781 if (limits is None) or (limits == (None, None)): 

1782 return a 

1783 

1784 (lower_lim, upper_lim) = limits 

1785 (lower_in, upper_in) = inclusive 

1786 condition = False 

1787 if lower_lim is not None: 

1788 if lower_in: 

1789 condition |= (a < lower_lim) 

1790 else: 

1791 condition |= (a <= lower_lim) 

1792 

1793 if upper_lim is not None: 

1794 if upper_in: 

1795 condition |= (a > upper_lim) 

1796 else: 

1797 condition |= (a >= upper_lim) 

1798 

1799 a[condition.filled(True)] = masked 

1800 return a 

1801 

1802 

1803def trimr(a, limits=None, inclusive=(True, True), axis=None): 

1804 """ 

1805 Trims an array by masking some proportion of the data on each end. 

1806 Returns a masked version of the input array. 

1807 

1808 Parameters 

1809 ---------- 

1810 a : sequence 

1811 Input array. 

1812 limits : {None, tuple}, optional 

1813 Tuple of the percentages to cut on each side of the array, with respect 

1814 to the number of unmasked data, as floats between 0. and 1. 

1815 Noting n the number of unmasked data before trimming, the 

1816 (n*limits[0])th smallest data and the (n*limits[1])th largest data are 

1817 masked, and the total number of unmasked data after trimming is 

1818 n*(1.-sum(limits)). The value of one limit can be set to None to 

1819 indicate an open interval. 

1820 inclusive : {(True,True) tuple}, optional 

1821 Tuple of flags indicating whether the number of data being masked on 

1822 the left (right) end should be truncated (True) or rounded (False) to 

1823 integers. 

1824 axis : {None,int}, optional 

1825 Axis along which to trim. If None, the whole array is trimmed, but its 

1826 shape is maintained. 

1827 

1828 """ 

1829 def _trimr1D(a, low_limit, up_limit, low_inclusive, up_inclusive): 

1830 n = a.count() 

1831 idx = a.argsort() 

1832 if low_limit: 

1833 if low_inclusive: 

1834 lowidx = int(low_limit*n) 

1835 else: 

1836 lowidx = int(np.round(low_limit*n)) 

1837 a[idx[:lowidx]] = masked 

1838 if up_limit is not None: 

1839 if up_inclusive: 

1840 upidx = n - int(n*up_limit) 

1841 else: 

1842 upidx = n - int(np.round(n*up_limit)) 

1843 a[idx[upidx:]] = masked 

1844 return a 

1845 

1846 a = ma.asarray(a) 

1847 a.unshare_mask() 

1848 if limits is None: 

1849 return a 

1850 

1851 # Check the limits 

1852 (lolim, uplim) = limits 

1853 errmsg = "The proportion to cut from the %s should be between 0. and 1." 

1854 if lolim is not None: 

1855 if lolim > 1. or lolim < 0: 

1856 raise ValueError(errmsg % 'beginning' + "(got %s)" % lolim) 

1857 if uplim is not None: 

1858 if uplim > 1. or uplim < 0: 

1859 raise ValueError(errmsg % 'end' + "(got %s)" % uplim) 

1860 

1861 (loinc, upinc) = inclusive 

1862 

1863 if axis is None: 

1864 shp = a.shape 

1865 return _trimr1D(a.ravel(),lolim,uplim,loinc,upinc).reshape(shp) 

1866 else: 

1867 return ma.apply_along_axis(_trimr1D, axis, a, lolim,uplim,loinc,upinc) 

1868 

1869 

1870trimdoc = """ 

1871 Parameters 

1872 ---------- 

1873 a : sequence 

1874 Input array 

1875 limits : {None, tuple}, optional 

1876 If `relative` is False, tuple (lower limit, upper limit) in absolute values. 

1877 Values of the input array lower (greater) than the lower (upper) limit are 

1878 masked. 

1879 

1880 If `relative` is True, tuple (lower percentage, upper percentage) to cut 

1881 on each side of the array, with respect to the number of unmasked data. 

1882 

1883 Noting n the number of unmasked data before trimming, the (n*limits[0])th 

1884 smallest data and the (n*limits[1])th largest data are masked, and the 

1885 total number of unmasked data after trimming is n*(1.-sum(limits)) 

1886 In each case, the value of one limit can be set to None to indicate an 

1887 open interval. 

1888 

1889 If limits is None, no trimming is performed 

1890 inclusive : {(bool, bool) tuple}, optional 

1891 If `relative` is False, tuple indicating whether values exactly equal 

1892 to the absolute limits are allowed. 

1893 If `relative` is True, tuple indicating whether the number of data 

1894 being masked on each side should be rounded (True) or truncated 

1895 (False). 

1896 relative : bool, optional 

1897 Whether to consider the limits as absolute values (False) or proportions 

1898 to cut (True). 

1899 axis : int, optional 

1900 Axis along which to trim. 

1901""" 

1902 

1903 

1904def trim(a, limits=None, inclusive=(True,True), relative=False, axis=None): 

1905 """ 

1906 Trims an array by masking the data outside some given limits. 

1907 

1908 Returns a masked version of the input array. 

1909 

1910 %s 

1911 

1912 Examples 

1913 -------- 

1914 >>> from scipy.stats.mstats import trim 

1915 >>> z = [ 1, 2, 3, 4, 5, 6, 7, 8, 9,10] 

1916 >>> print(trim(z,(3,8))) 

1917 [-- -- 3 4 5 6 7 8 -- --] 

1918 >>> print(trim(z,(0.1,0.2),relative=True)) 

1919 [-- 2 3 4 5 6 7 8 -- --] 

1920 

1921 """ 

1922 if relative: 

1923 return trimr(a, limits=limits, inclusive=inclusive, axis=axis) 

1924 else: 

1925 return trima(a, limits=limits, inclusive=inclusive) 

1926 

1927 

1928if trim.__doc__: 

1929 trim.__doc__ = trim.__doc__ % trimdoc 

1930 

1931 

1932def trimboth(data, proportiontocut=0.2, inclusive=(True,True), axis=None): 

1933 """ 

1934 Trims the smallest and largest data values. 

1935 

1936 Trims the `data` by masking the ``int(proportiontocut * n)`` smallest and 

1937 ``int(proportiontocut * n)`` largest values of data along the given axis, 

1938 where n is the number of unmasked values before trimming. 

1939 

1940 Parameters 

1941 ---------- 

1942 data : ndarray 

1943 Data to trim. 

1944 proportiontocut : float, optional 

1945 Percentage of trimming (as a float between 0 and 1). 

1946 If n is the number of unmasked values before trimming, the number of 

1947 values after trimming is ``(1 - 2*proportiontocut) * n``. 

1948 Default is 0.2. 

1949 inclusive : {(bool, bool) tuple}, optional 

1950 Tuple indicating whether the number of data being masked on each side 

1951 should be rounded (True) or truncated (False). 

1952 axis : int, optional 

1953 Axis along which to perform the trimming. 

1954 If None, the input array is first flattened. 

1955 

1956 """ 

1957 return trimr(data, limits=(proportiontocut,proportiontocut), 

1958 inclusive=inclusive, axis=axis) 

1959 

1960 

1961def trimtail(data, proportiontocut=0.2, tail='left', inclusive=(True,True), 

1962 axis=None): 

1963 """ 

1964 Trims the data by masking values from one tail. 

1965 

1966 Parameters 

1967 ---------- 

1968 data : array_like 

1969 Data to trim. 

1970 proportiontocut : float, optional 

1971 Percentage of trimming. If n is the number of unmasked values 

1972 before trimming, the number of values after trimming is 

1973 ``(1 - proportiontocut) * n``. Default is 0.2. 

1974 tail : {'left','right'}, optional 

1975 If 'left' the `proportiontocut` lowest values will be masked. 

1976 If 'right' the `proportiontocut` highest values will be masked. 

1977 Default is 'left'. 

1978 inclusive : {(bool, bool) tuple}, optional 

1979 Tuple indicating whether the number of data being masked on each side 

1980 should be rounded (True) or truncated (False). Default is 

1981 (True, True). 

1982 axis : int, optional 

1983 Axis along which to perform the trimming. 

1984 If None, the input array is first flattened. Default is None. 

1985 

1986 Returns 

1987 ------- 

1988 trimtail : ndarray 

1989 Returned array of same shape as `data` with masked tail values. 

1990 

1991 """ 

1992 tail = str(tail).lower()[0] 

1993 if tail == 'l': 

1994 limits = (proportiontocut,None) 

1995 elif tail == 'r': 

1996 limits = (None, proportiontocut) 

1997 else: 

1998 raise TypeError("The tail argument should be in ('left','right')") 

1999 

2000 return trimr(data, limits=limits, axis=axis, inclusive=inclusive) 

2001 

2002 

2003trim1 = trimtail 

2004 

2005 

2006def trimmed_mean(a, limits=(0.1,0.1), inclusive=(1,1), relative=True, 

2007 axis=None): 

2008 """Returns the trimmed mean of the data along the given axis. 

2009 

2010 %s 

2011 

2012 """ 

2013 if (not isinstance(limits,tuple)) and isinstance(limits,float): 

2014 limits = (limits, limits) 

2015 if relative: 

2016 return trimr(a,limits=limits,inclusive=inclusive,axis=axis).mean(axis=axis) 

2017 else: 

2018 return trima(a,limits=limits,inclusive=inclusive).mean(axis=axis) 

2019 

2020 

2021if trimmed_mean.__doc__: 

2022 trimmed_mean.__doc__ = trimmed_mean.__doc__ % trimdoc 

2023 

2024 

2025def trimmed_var(a, limits=(0.1,0.1), inclusive=(1,1), relative=True, 

2026 axis=None, ddof=0): 

2027 """Returns the trimmed variance of the data along the given axis. 

2028 

2029 %s 

2030 ddof : {0,integer}, optional 

2031 Means Delta Degrees of Freedom. The denominator used during computations 

2032 is (n-ddof). DDOF=0 corresponds to a biased estimate, DDOF=1 to an un- 

2033 biased estimate of the variance. 

2034 

2035 """ 

2036 if (not isinstance(limits,tuple)) and isinstance(limits,float): 

2037 limits = (limits, limits) 

2038 if relative: 

2039 out = trimr(a,limits=limits, inclusive=inclusive,axis=axis) 

2040 else: 

2041 out = trima(a,limits=limits,inclusive=inclusive) 

2042 

2043 return out.var(axis=axis, ddof=ddof) 

2044 

2045 

2046if trimmed_var.__doc__: 

2047 trimmed_var.__doc__ = trimmed_var.__doc__ % trimdoc 

2048 

2049 

2050def trimmed_std(a, limits=(0.1,0.1), inclusive=(1,1), relative=True, 

2051 axis=None, ddof=0): 

2052 """Returns the trimmed standard deviation of the data along the given axis. 

2053 

2054 %s 

2055 ddof : {0,integer}, optional 

2056 Means Delta Degrees of Freedom. The denominator used during computations 

2057 is (n-ddof). DDOF=0 corresponds to a biased estimate, DDOF=1 to an un- 

2058 biased estimate of the variance. 

2059 

2060 """ 

2061 if (not isinstance(limits,tuple)) and isinstance(limits,float): 

2062 limits = (limits, limits) 

2063 if relative: 

2064 out = trimr(a,limits=limits,inclusive=inclusive,axis=axis) 

2065 else: 

2066 out = trima(a,limits=limits,inclusive=inclusive) 

2067 return out.std(axis=axis,ddof=ddof) 

2068 

2069 

2070if trimmed_std.__doc__: 

2071 trimmed_std.__doc__ = trimmed_std.__doc__ % trimdoc 

2072 

2073 

2074def trimmed_stde(a, limits=(0.1,0.1), inclusive=(1,1), axis=None): 

2075 """ 

2076 Returns the standard error of the trimmed mean along the given axis. 

2077 

2078 Parameters 

2079 ---------- 

2080 a : sequence 

2081 Input array 

2082 limits : {(0.1,0.1), tuple of float}, optional 

2083 tuple (lower percentage, upper percentage) to cut on each side of the 

2084 array, with respect to the number of unmasked data. 

2085 

2086 If n is the number of unmasked data before trimming, the values 

2087 smaller than ``n * limits[0]`` and the values larger than 

2088 ``n * `limits[1]`` are masked, and the total number of unmasked 

2089 data after trimming is ``n * (1.-sum(limits))``. In each case, 

2090 the value of one limit can be set to None to indicate an open interval. 

2091 If `limits` is None, no trimming is performed. 

2092 inclusive : {(bool, bool) tuple} optional 

2093 Tuple indicating whether the number of data being masked on each side 

2094 should be rounded (True) or truncated (False). 

2095 axis : int, optional 

2096 Axis along which to trim. 

2097 

2098 Returns 

2099 ------- 

2100 trimmed_stde : scalar or ndarray 

2101 

2102 """ 

2103 def _trimmed_stde_1D(a, low_limit, up_limit, low_inclusive, up_inclusive): 

2104 "Returns the standard error of the trimmed mean for a 1D input data." 

2105 n = a.count() 

2106 idx = a.argsort() 

2107 if low_limit: 

2108 if low_inclusive: 

2109 lowidx = int(low_limit*n) 

2110 else: 

2111 lowidx = np.round(low_limit*n) 

2112 a[idx[:lowidx]] = masked 

2113 if up_limit is not None: 

2114 if up_inclusive: 

2115 upidx = n - int(n*up_limit) 

2116 else: 

2117 upidx = n - np.round(n*up_limit) 

2118 a[idx[upidx:]] = masked 

2119 a[idx[:lowidx]] = a[idx[lowidx]] 

2120 a[idx[upidx:]] = a[idx[upidx-1]] 

2121 winstd = a.std(ddof=1) 

2122 return winstd / ((1-low_limit-up_limit)*np.sqrt(len(a))) 

2123 

2124 a = ma.array(a, copy=True, subok=True) 

2125 a.unshare_mask() 

2126 if limits is None: 

2127 return a.std(axis=axis,ddof=1)/ma.sqrt(a.count(axis)) 

2128 if (not isinstance(limits,tuple)) and isinstance(limits,float): 

2129 limits = (limits, limits) 

2130 

2131 # Check the limits 

2132 (lolim, uplim) = limits 

2133 errmsg = "The proportion to cut from the %s should be between 0. and 1." 

2134 if lolim is not None: 

2135 if lolim > 1. or lolim < 0: 

2136 raise ValueError(errmsg % 'beginning' + "(got %s)" % lolim) 

2137 if uplim is not None: 

2138 if uplim > 1. or uplim < 0: 

2139 raise ValueError(errmsg % 'end' + "(got %s)" % uplim) 

2140 

2141 (loinc, upinc) = inclusive 

2142 if (axis is None): 

2143 return _trimmed_stde_1D(a.ravel(),lolim,uplim,loinc,upinc) 

2144 else: 

2145 if a.ndim > 2: 

2146 raise ValueError("Array 'a' must be at most two dimensional, " 

2147 "but got a.ndim = %d" % a.ndim) 

2148 return ma.apply_along_axis(_trimmed_stde_1D, axis, a, 

2149 lolim,uplim,loinc,upinc) 

2150 

2151 

2152def _mask_to_limits(a, limits, inclusive): 

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

2154 

2155 This is primarily a utility function. 

2156 

2157 Parameters 

2158 ---------- 

2159 a : array 

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

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

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

2163 will be masked out. None implies no limit. 

2164 inclusive : (bool, bool) 

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

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

2167 

2168 Returns 

2169 ------- 

2170 A MaskedArray. 

2171 

2172 Raises 

2173 ------ 

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

2175 """ 

2176 lower_limit, upper_limit = limits 

2177 lower_include, upper_include = inclusive 

2178 am = ma.MaskedArray(a) 

2179 if lower_limit is not None: 

2180 if lower_include: 

2181 am = ma.masked_less(am, lower_limit) 

2182 else: 

2183 am = ma.masked_less_equal(am, lower_limit) 

2184 

2185 if upper_limit is not None: 

2186 if upper_include: 

2187 am = ma.masked_greater(am, upper_limit) 

2188 else: 

2189 am = ma.masked_greater_equal(am, upper_limit) 

2190 

2191 if am.count() == 0: 

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

2193 

2194 return am 

2195 

2196 

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

2198 """ 

2199 Compute the trimmed mean. 

2200 

2201 Parameters 

2202 ---------- 

2203 a : array_like 

2204 Array of values. 

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

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

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

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

2209 None representing a half-open interval. 

2210 inclusive : (bool, bool), optional 

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

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

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

2214 axis : int or None, optional 

2215 Axis along which to operate. If None, compute over the 

2216 whole array. Default is None. 

2217 

2218 Returns 

2219 ------- 

2220 tmean : float 

2221 

2222 Notes 

2223 ----- 

2224 For more details on `tmean`, see `scipy.stats.tmean`. 

2225 

2226 Examples 

2227 -------- 

2228 >>> import numpy as np 

2229 >>> from scipy.stats import mstats 

2230 >>> a = np.array([[6, 8, 3, 0], 

2231 ... [3, 9, 1, 2], 

2232 ... [8, 7, 8, 2], 

2233 ... [5, 6, 0, 2], 

2234 ... [4, 5, 5, 2]]) 

2235 ... 

2236 ... 

2237 >>> mstats.tmean(a, (2,5)) 

2238 3.3 

2239 >>> mstats.tmean(a, (2,5), axis=0) 

2240 masked_array(data=[4.0, 5.0, 4.0, 2.0], 

2241 mask=[False, False, False, False], 

2242 fill_value=1e+20) 

2243 

2244 """ 

2245 return trima(a, limits=limits, inclusive=inclusive).mean(axis=axis) 

2246 

2247 

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

2249 """ 

2250 Compute the trimmed variance 

2251 

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

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

2254 

2255 Parameters 

2256 ---------- 

2257 a : array_like 

2258 Array of values. 

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

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

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

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

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

2264 inclusive : (bool, bool), optional 

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

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

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

2268 axis : int or None, optional 

2269 Axis along which to operate. If None, compute over the 

2270 whole array. Default is zero. 

2271 ddof : int, optional 

2272 Delta degrees of freedom. Default is 1. 

2273 

2274 Returns 

2275 ------- 

2276 tvar : float 

2277 Trimmed variance. 

2278 

2279 Notes 

2280 ----- 

2281 For more details on `tvar`, see `scipy.stats.tvar`. 

2282 

2283 """ 

2284 a = a.astype(float).ravel() 

2285 if limits is None: 

2286 n = (~a.mask).sum() # todo: better way to do that? 

2287 return np.ma.var(a) * n/(n-1.) 

2288 am = _mask_to_limits(a, limits=limits, inclusive=inclusive) 

2289 

2290 return np.ma.var(am, axis=axis, ddof=ddof) 

2291 

2292 

2293def tmin(a, lowerlimit=None, axis=0, inclusive=True): 

2294 """ 

2295 Compute the trimmed minimum 

2296 

2297 Parameters 

2298 ---------- 

2299 a : array_like 

2300 array of values 

2301 lowerlimit : None or float, optional 

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

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

2304 is None. 

2305 axis : int or None, optional 

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

2307 whole array `a`. 

2308 inclusive : {True, False}, optional 

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

2310 are included. The default value is True. 

2311 

2312 Returns 

2313 ------- 

2314 tmin : float, int or ndarray 

2315 

2316 Notes 

2317 ----- 

2318 For more details on `tmin`, see `scipy.stats.tmin`. 

2319 

2320 Examples 

2321 -------- 

2322 >>> import numpy as np 

2323 >>> from scipy.stats import mstats 

2324 >>> a = np.array([[6, 8, 3, 0], 

2325 ... [3, 2, 1, 2], 

2326 ... [8, 1, 8, 2], 

2327 ... [5, 3, 0, 2], 

2328 ... [4, 7, 5, 2]]) 

2329 ... 

2330 >>> mstats.tmin(a, 5) 

2331 masked_array(data=[5, 7, 5, --], 

2332 mask=[False, False, False, True], 

2333 fill_value=999999) 

2334 

2335 """ 

2336 a, axis = _chk_asarray(a, axis) 

2337 am = trima(a, (lowerlimit, None), (inclusive, False)) 

2338 return ma.minimum.reduce(am, axis) 

2339 

2340 

2341def tmax(a, upperlimit=None, axis=0, inclusive=True): 

2342 """ 

2343 Compute the trimmed maximum 

2344 

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

2346 while ignoring values larger than a specified upper limit. 

2347 

2348 Parameters 

2349 ---------- 

2350 a : array_like 

2351 array of values 

2352 upperlimit : None or float, optional 

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

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

2355 is None. 

2356 axis : int or None, optional 

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

2358 whole array `a`. 

2359 inclusive : {True, False}, optional 

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

2361 are included. The default value is True. 

2362 

2363 Returns 

2364 ------- 

2365 tmax : float, int or ndarray 

2366 

2367 Notes 

2368 ----- 

2369 For more details on `tmax`, see `scipy.stats.tmax`. 

2370 

2371 Examples 

2372 -------- 

2373 >>> import numpy as np 

2374 >>> from scipy.stats import mstats 

2375 >>> a = np.array([[6, 8, 3, 0], 

2376 ... [3, 9, 1, 2], 

2377 ... [8, 7, 8, 2], 

2378 ... [5, 6, 0, 2], 

2379 ... [4, 5, 5, 2]]) 

2380 ... 

2381 ... 

2382 >>> mstats.tmax(a, 4) 

2383 masked_array(data=[4, --, 3, 2], 

2384 mask=[False, True, False, False], 

2385 fill_value=999999) 

2386 

2387 """ 

2388 a, axis = _chk_asarray(a, axis) 

2389 am = trima(a, (None, upperlimit), (False, inclusive)) 

2390 return ma.maximum.reduce(am, axis) 

2391 

2392 

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

2394 """ 

2395 Compute the trimmed standard error of the mean. 

2396 

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

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

2399 

2400 Parameters 

2401 ---------- 

2402 a : array_like 

2403 array of values 

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

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

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

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

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

2409 inclusive : (bool, bool), optional 

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

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

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

2413 axis : int or None, optional 

2414 Axis along which to operate. If None, compute over the 

2415 whole array. Default is zero. 

2416 ddof : int, optional 

2417 Delta degrees of freedom. Default is 1. 

2418 

2419 Returns 

2420 ------- 

2421 tsem : float 

2422 

2423 Notes 

2424 ----- 

2425 For more details on `tsem`, see `scipy.stats.tsem`. 

2426 

2427 """ 

2428 a = ma.asarray(a).ravel() 

2429 if limits is None: 

2430 n = float(a.count()) 

2431 return a.std(axis=axis, ddof=ddof)/ma.sqrt(n) 

2432 

2433 am = trima(a.ravel(), limits, inclusive) 

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

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

2436 

2437 

2438def winsorize(a, limits=None, inclusive=(True, True), inplace=False, 

2439 axis=None, nan_policy='propagate'): 

2440 """Returns a Winsorized version of the input array. 

2441 

2442 The (limits[0])th lowest values are set to the (limits[0])th percentile, 

2443 and the (limits[1])th highest values are set to the (1 - limits[1])th 

2444 percentile. 

2445 Masked values are skipped. 

2446 

2447 

2448 Parameters 

2449 ---------- 

2450 a : sequence 

2451 Input array. 

2452 limits : {None, tuple of float}, optional 

2453 Tuple of the percentages to cut on each side of the array, with respect 

2454 to the number of unmasked data, as floats between 0. and 1. 

2455 Noting n the number of unmasked data before trimming, the 

2456 (n*limits[0])th smallest data and the (n*limits[1])th largest data are 

2457 masked, and the total number of unmasked data after trimming 

2458 is n*(1.-sum(limits)) The value of one limit can be set to None to 

2459 indicate an open interval. 

2460 inclusive : {(True, True) tuple}, optional 

2461 Tuple indicating whether the number of data being masked on each side 

2462 should be truncated (True) or rounded (False). 

2463 inplace : {False, True}, optional 

2464 Whether to winsorize in place (True) or to use a copy (False) 

2465 axis : {None, int}, optional 

2466 Axis along which to trim. If None, the whole array is trimmed, but its 

2467 shape is maintained. 

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

2469 Defines how to handle when input contains nan. 

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

2471 

2472 * 'propagate': allows nan values and may overwrite or propagate them 

2473 * 'raise': throws an error 

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

2475 

2476 Notes 

2477 ----- 

2478 This function is applied to reduce the effect of possibly spurious outliers 

2479 by limiting the extreme values. 

2480 

2481 Examples 

2482 -------- 

2483 >>> import numpy as np 

2484 >>> from scipy.stats.mstats import winsorize 

2485 

2486 A shuffled array contains integers from 1 to 10. 

2487 

2488 >>> a = np.array([10, 4, 9, 8, 5, 3, 7, 2, 1, 6]) 

2489 

2490 The 10% of the lowest value (i.e., `1`) and the 20% of the highest 

2491 values (i.e., `9` and `10`) are replaced. 

2492 

2493 >>> winsorize(a, limits=[0.1, 0.2]) 

2494 masked_array(data=[8, 4, 8, 8, 5, 3, 7, 2, 2, 6], 

2495 mask=False, 

2496 fill_value=999999) 

2497 

2498 """ 

2499 def _winsorize1D(a, low_limit, up_limit, low_include, up_include, 

2500 contains_nan, nan_policy): 

2501 n = a.count() 

2502 idx = a.argsort() 

2503 if contains_nan: 

2504 nan_count = np.count_nonzero(np.isnan(a)) 

2505 if low_limit: 

2506 if low_include: 

2507 lowidx = int(low_limit * n) 

2508 else: 

2509 lowidx = np.round(low_limit * n).astype(int) 

2510 if contains_nan and nan_policy == 'omit': 

2511 lowidx = min(lowidx, n-nan_count-1) 

2512 a[idx[:lowidx]] = a[idx[lowidx]] 

2513 if up_limit is not None: 

2514 if up_include: 

2515 upidx = n - int(n * up_limit) 

2516 else: 

2517 upidx = n - np.round(n * up_limit).astype(int) 

2518 if contains_nan and nan_policy == 'omit': 

2519 a[idx[upidx:-nan_count]] = a[idx[upidx - 1]] 

2520 else: 

2521 a[idx[upidx:]] = a[idx[upidx - 1]] 

2522 return a 

2523 

2524 contains_nan, nan_policy = _contains_nan(a, nan_policy) 

2525 # We are going to modify a: better make a copy 

2526 a = ma.array(a, copy=np.logical_not(inplace)) 

2527 

2528 if limits is None: 

2529 return a 

2530 if (not isinstance(limits, tuple)) and isinstance(limits, float): 

2531 limits = (limits, limits) 

2532 

2533 # Check the limits 

2534 (lolim, uplim) = limits 

2535 errmsg = "The proportion to cut from the %s should be between 0. and 1." 

2536 if lolim is not None: 

2537 if lolim > 1. or lolim < 0: 

2538 raise ValueError(errmsg % 'beginning' + "(got %s)" % lolim) 

2539 if uplim is not None: 

2540 if uplim > 1. or uplim < 0: 

2541 raise ValueError(errmsg % 'end' + "(got %s)" % uplim) 

2542 

2543 (loinc, upinc) = inclusive 

2544 

2545 if axis is None: 

2546 shp = a.shape 

2547 return _winsorize1D(a.ravel(), lolim, uplim, loinc, upinc, 

2548 contains_nan, nan_policy).reshape(shp) 

2549 else: 

2550 return ma.apply_along_axis(_winsorize1D, axis, a, lolim, uplim, loinc, 

2551 upinc, contains_nan, nan_policy) 

2552 

2553 

2554def moment(a, moment=1, axis=0): 

2555 """ 

2556 Calculates the nth moment about the mean for a sample. 

2557 

2558 Parameters 

2559 ---------- 

2560 a : array_like 

2561 data 

2562 moment : int, optional 

2563 order of central moment that is returned 

2564 axis : int or None, optional 

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

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

2567 

2568 Returns 

2569 ------- 

2570 n-th central moment : ndarray or float 

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

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

2573 observations, no degrees of freedom correction is done. 

2574 

2575 Notes 

2576 ----- 

2577 For more details about `moment`, see `scipy.stats.moment`. 

2578 

2579 """ 

2580 a, axis = _chk_asarray(a, axis) 

2581 if a.size == 0: 

2582 moment_shape = list(a.shape) 

2583 del moment_shape[axis] 

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

2585 # empty array, return nan(s) with shape matching `moment` 

2586 out_shape = (moment_shape if np.isscalar(moment) 

2587 else [len(moment)] + moment_shape) 

2588 if len(out_shape) == 0: 

2589 return dtype(np.nan) 

2590 else: 

2591 return ma.array(np.full(out_shape, np.nan, dtype=dtype)) 

2592 

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

2594 if not np.isscalar(moment): 

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

2596 mmnt = [_moment(a, i, axis, mean=mean) for i in moment] 

2597 return ma.array(mmnt) 

2598 else: 

2599 return _moment(a, moment, axis) 

2600 

2601 

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

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

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

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

2606 

2607 if moment == 0 or moment == 1: 

2608 # By definition the zeroth moment about the mean is 1, and the first 

2609 # moment is 0. 

2610 shape = list(a.shape) 

2611 del shape[axis] 

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

2613 

2614 if len(shape) == 0: 

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

2616 else: 

2617 return (ma.ones(shape, dtype=dtype) if moment == 0 

2618 else ma.zeros(shape, dtype=dtype)) 

2619 else: 

2620 # Exponentiation by squares: form exponent sequence 

2621 n_list = [moment] 

2622 current_n = moment 

2623 while current_n > 2: 

2624 if current_n % 2: 

2625 current_n = (current_n-1)/2 

2626 else: 

2627 current_n /= 2 

2628 n_list.append(current_n) 

2629 

2630 # Starting point for exponentiation by squares 

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

2632 a_zero_mean = a - mean 

2633 if n_list[-1] == 1: 

2634 s = a_zero_mean.copy() 

2635 else: 

2636 s = a_zero_mean**2 

2637 

2638 # Perform multiplications 

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

2640 s = s**2 

2641 if n % 2: 

2642 s *= a_zero_mean 

2643 return s.mean(axis) 

2644 

2645 

2646def variation(a, axis=0, ddof=0): 

2647 """ 

2648 Compute the coefficient of variation. 

2649 

2650 The coefficient of variation is the standard deviation divided by the 

2651 mean. This function is equivalent to:: 

2652 

2653 np.std(x, axis=axis, ddof=ddof) / np.mean(x) 

2654 

2655 The default for ``ddof`` is 0, but many definitions of the coefficient 

2656 of variation use the square root of the unbiased sample variance 

2657 for the sample standard deviation, which corresponds to ``ddof=1``. 

2658 

2659 Parameters 

2660 ---------- 

2661 a : array_like 

2662 Input array. 

2663 axis : int or None, optional 

2664 Axis along which to calculate the coefficient of variation. Default 

2665 is 0. If None, compute over the whole array `a`. 

2666 ddof : int, optional 

2667 Delta degrees of freedom. Default is 0. 

2668 

2669 Returns 

2670 ------- 

2671 variation : ndarray 

2672 The calculated variation along the requested axis. 

2673 

2674 Notes 

2675 ----- 

2676 For more details about `variation`, see `scipy.stats.variation`. 

2677 

2678 Examples 

2679 -------- 

2680 >>> import numpy as np 

2681 >>> from scipy.stats.mstats import variation 

2682 >>> a = np.array([2,8,4]) 

2683 >>> variation(a) 

2684 0.5345224838248487 

2685 >>> b = np.array([2,8,3,4]) 

2686 >>> c = np.ma.masked_array(b, mask=[0,0,1,0]) 

2687 >>> variation(c) 

2688 0.5345224838248487 

2689 

2690 In the example above, it can be seen that this works the same as 

2691 `scipy.stats.variation` except 'stats.mstats.variation' ignores masked 

2692 array elements. 

2693 

2694 """ 

2695 a, axis = _chk_asarray(a, axis) 

2696 return a.std(axis, ddof=ddof)/a.mean(axis) 

2697 

2698 

2699def skew(a, axis=0, bias=True): 

2700 """ 

2701 Computes the skewness of a data set. 

2702 

2703 Parameters 

2704 ---------- 

2705 a : ndarray 

2706 data 

2707 axis : int or None, optional 

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

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

2710 bias : bool, optional 

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

2712 

2713 Returns 

2714 ------- 

2715 skewness : ndarray 

2716 The skewness of values along an axis, returning 0 where all values are 

2717 equal. 

2718 

2719 Notes 

2720 ----- 

2721 For more details about `skew`, see `scipy.stats.skew`. 

2722 

2723 """ 

2724 a, axis = _chk_asarray(a,axis) 

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

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

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

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

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

2730 vals = ma.where(zero, 0, m3 / m2**1.5) 

2731 

2732 if not bias and zero is not ma.masked and m2 is not ma.masked: 

2733 n = a.count(axis) 

2734 can_correct = ~zero & (n > 2) 

2735 if can_correct.any(): 

2736 n = np.extract(can_correct, n) 

2737 m2 = np.extract(can_correct, m2) 

2738 m3 = np.extract(can_correct, m3) 

2739 nval = ma.sqrt((n-1.0)*n)/(n-2.0)*m3/m2**1.5 

2740 np.place(vals, can_correct, nval) 

2741 return vals 

2742 

2743 

2744def kurtosis(a, axis=0, fisher=True, bias=True): 

2745 """ 

2746 Computes the kurtosis (Fisher or Pearson) of a dataset. 

2747 

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

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

2750 the result to give 0.0 for a normal distribution. 

2751 

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

2753 eliminate bias coming from biased moment estimators 

2754 

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

2756 

2757 Parameters 

2758 ---------- 

2759 a : array 

2760 data for which the kurtosis is calculated 

2761 axis : int or None, optional 

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

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

2764 fisher : bool, optional 

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

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

2767 bias : bool, optional 

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

2769 

2770 Returns 

2771 ------- 

2772 kurtosis : array 

2773 The kurtosis of values along an axis. If all values are equal, 

2774 return -3 for Fisher's definition and 0 for Pearson's definition. 

2775 

2776 Notes 

2777 ----- 

2778 For more details about `kurtosis`, see `scipy.stats.kurtosis`. 

2779 

2780 """ 

2781 a, axis = _chk_asarray(a, axis) 

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

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

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

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

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

2787 vals = ma.where(zero, 0, m4 / m2**2.0) 

2788 

2789 if not bias and zero is not ma.masked and m2 is not ma.masked: 

2790 n = a.count(axis) 

2791 can_correct = ~zero & (n > 3) 

2792 if can_correct.any(): 

2793 n = np.extract(can_correct, n) 

2794 m2 = np.extract(can_correct, m2) 

2795 m4 = np.extract(can_correct, m4) 

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

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

2798 if fisher: 

2799 return vals - 3 

2800 else: 

2801 return vals 

2802 

2803 

2804DescribeResult = namedtuple('DescribeResult', ('nobs', 'minmax', 'mean', 

2805 'variance', 'skewness', 

2806 'kurtosis')) 

2807 

2808 

2809def describe(a, axis=0, ddof=0, bias=True): 

2810 """ 

2811 Computes several descriptive statistics of the passed array. 

2812 

2813 Parameters 

2814 ---------- 

2815 a : array_like 

2816 Data array 

2817 axis : int or None, optional 

2818 Axis along which to calculate statistics. Default 0. If None, 

2819 compute over the whole array `a`. 

2820 ddof : int, optional 

2821 degree of freedom (default 0); note that default ddof is different 

2822 from the same routine in stats.describe 

2823 bias : bool, optional 

2824 If False, then the skewness and kurtosis calculations are corrected for 

2825 statistical bias. 

2826 

2827 Returns 

2828 ------- 

2829 nobs : int 

2830 (size of the data (discarding missing values) 

2831 

2832 minmax : (int, int) 

2833 min, max 

2834 

2835 mean : float 

2836 arithmetic mean 

2837 

2838 variance : float 

2839 unbiased variance 

2840 

2841 skewness : float 

2842 biased skewness 

2843 

2844 kurtosis : float 

2845 biased kurtosis 

2846 

2847 Examples 

2848 -------- 

2849 >>> import numpy as np 

2850 >>> from scipy.stats.mstats import describe 

2851 >>> ma = np.ma.array(range(6), mask=[0, 0, 0, 1, 1, 1]) 

2852 >>> describe(ma) 

2853 DescribeResult(nobs=3, minmax=(masked_array(data=0, 

2854 mask=False, 

2855 fill_value=999999), masked_array(data=2, 

2856 mask=False, 

2857 fill_value=999999)), mean=1.0, variance=0.6666666666666666, 

2858 skewness=masked_array(data=0., mask=False, fill_value=1e+20), 

2859 kurtosis=-1.5) 

2860 

2861 """ 

2862 a, axis = _chk_asarray(a, axis) 

2863 n = a.count(axis) 

2864 mm = (ma.minimum.reduce(a, axis=axis), ma.maximum.reduce(a, axis=axis)) 

2865 m = a.mean(axis) 

2866 v = a.var(axis, ddof=ddof) 

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

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

2869 

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

2871 

2872 

2873def stde_median(data, axis=None): 

2874 """Returns the McKean-Schrader estimate of the standard error of the sample 

2875 median along the given axis. masked values are discarded. 

2876 

2877 Parameters 

2878 ---------- 

2879 data : ndarray 

2880 Data to trim. 

2881 axis : {None,int}, optional 

2882 Axis along which to perform the trimming. 

2883 If None, the input array is first flattened. 

2884 

2885 """ 

2886 def _stdemed_1D(data): 

2887 data = np.sort(data.compressed()) 

2888 n = len(data) 

2889 z = 2.5758293035489004 

2890 k = int(np.round((n+1)/2. - z * np.sqrt(n/4.),0)) 

2891 return ((data[n-k] - data[k-1])/(2.*z)) 

2892 

2893 data = ma.array(data, copy=False, subok=True) 

2894 if (axis is None): 

2895 return _stdemed_1D(data) 

2896 else: 

2897 if data.ndim > 2: 

2898 raise ValueError("Array 'data' must be at most two dimensional, " 

2899 "but got data.ndim = %d" % data.ndim) 

2900 return ma.apply_along_axis(_stdemed_1D, axis, data) 

2901 

2902 

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

2904 

2905 

2906def skewtest(a, axis=0, alternative='two-sided'): 

2907 """ 

2908 Tests whether the skew is different from the normal distribution. 

2909 

2910 Parameters 

2911 ---------- 

2912 a : array_like 

2913 The data to be tested 

2914 axis : int or None, optional 

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

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

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

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

2919 The following options are available: 

2920 

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

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

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

2924 is less than that of the normal distribution 

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

2926 is greater than that of the normal distribution 

2927 

2928 .. versionadded:: 1.7.0 

2929 

2930 Returns 

2931 ------- 

2932 statistic : array_like 

2933 The computed z-score for this test. 

2934 pvalue : array_like 

2935 A p-value for the hypothesis test 

2936 

2937 Notes 

2938 ----- 

2939 For more details about `skewtest`, see `scipy.stats.skewtest`. 

2940 

2941 """ 

2942 a, axis = _chk_asarray(a, axis) 

2943 if axis is None: 

2944 a = a.ravel() 

2945 axis = 0 

2946 b2 = skew(a,axis) 

2947 n = a.count(axis) 

2948 if np.min(n) < 8: 

2949 raise ValueError( 

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

2951 " were given." % np.min(n)) 

2952 

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

2954 beta2 = (3.0*(n*n+27*n-70)*(n+1)*(n+3)) / ((n-2.0)*(n+5)*(n+7)*(n+9)) 

2955 W2 = -1 + ma.sqrt(2*(beta2-1)) 

2956 delta = 1/ma.sqrt(0.5*ma.log(W2)) 

2957 alpha = ma.sqrt(2.0/(W2-1)) 

2958 y = ma.where(y == 0, 1, y) 

2959 Z = delta*ma.log(y/alpha + ma.sqrt((y/alpha)**2+1)) 

2960 

2961 return SkewtestResult(*scipy.stats._stats_py._normtest_finish(Z, alternative)) 

2962 

2963 

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

2965 

2966 

2967def kurtosistest(a, axis=0, alternative='two-sided'): 

2968 """ 

2969 Tests whether a dataset has normal kurtosis 

2970 

2971 Parameters 

2972 ---------- 

2973 a : array_like 

2974 array of the sample data 

2975 axis : int or None, optional 

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

2977 compute over the whole array `a`. 

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

2979 Defines the alternative hypothesis. 

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

2981 

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

2983 is different from that of the normal distribution 

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

2985 is less than that of the normal distribution 

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

2987 is greater than that of the normal distribution 

2988 

2989 .. versionadded:: 1.7.0 

2990 

2991 Returns 

2992 ------- 

2993 statistic : array_like 

2994 The computed z-score for this test. 

2995 pvalue : array_like 

2996 The p-value for the hypothesis test 

2997 

2998 Notes 

2999 ----- 

3000 For more details about `kurtosistest`, see `scipy.stats.kurtosistest`. 

3001 

3002 """ 

3003 a, axis = _chk_asarray(a, axis) 

3004 n = a.count(axis=axis) 

3005 if np.min(n) < 5: 

3006 raise ValueError( 

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

3008 " were given." % np.min(n)) 

3009 if np.min(n) < 20: 

3010 warnings.warn( 

3011 "kurtosistest only valid for n>=20 ... continuing anyway, n=%i" % 

3012 np.min(n)) 

3013 

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

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

3016 varb2 = 24.0*n*(n-2.)*(n-3) / ((n+1)*(n+1.)*(n+3)*(n+5)) 

3017 x = (b2-E)/ma.sqrt(varb2) 

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

3019 (n*(n-2)*(n-3))) 

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

3021 term1 = 1 - 2./(9.0*A) 

3022 denom = 1 + x*ma.sqrt(2/(A-4.0)) 

3023 if np.ma.isMaskedArray(denom): 

3024 # For multi-dimensional array input 

3025 denom[denom == 0.0] = masked 

3026 elif denom == 0.0: 

3027 denom = masked 

3028 

3029 term2 = np.ma.where(denom > 0, ma.power((1-2.0/A)/denom, 1/3.0), 

3030 -ma.power(-(1-2.0/A)/denom, 1/3.0)) 

3031 Z = (term1 - term2) / np.sqrt(2/(9.0*A)) 

3032 

3033 return KurtosistestResult( 

3034 *scipy.stats._stats_py._normtest_finish(Z, alternative) 

3035 ) 

3036 

3037 

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

3039 

3040 

3041def normaltest(a, axis=0): 

3042 """ 

3043 Tests whether a sample differs from a normal distribution. 

3044 

3045 Parameters 

3046 ---------- 

3047 a : array_like 

3048 The array containing the data to be tested. 

3049 axis : int or None, optional 

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

3051 compute over the whole array `a`. 

3052 

3053 Returns 

3054 ------- 

3055 statistic : float or array 

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

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

3058 pvalue : float or array 

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

3060 

3061 Notes 

3062 ----- 

3063 For more details about `normaltest`, see `scipy.stats.normaltest`. 

3064 

3065 """ 

3066 a, axis = _chk_asarray(a, axis) 

3067 s, _ = skewtest(a, axis) 

3068 k, _ = kurtosistest(a, axis) 

3069 k2 = s*s + k*k 

3070 

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

3072 

3073 

3074def mquantiles(a, prob=list([.25,.5,.75]), alphap=.4, betap=.4, axis=None, 

3075 limit=()): 

3076 """ 

3077 Computes empirical quantiles for a data array. 

3078 

3079 Samples quantile are defined by ``Q(p) = (1-gamma)*x[j] + gamma*x[j+1]``, 

3080 where ``x[j]`` is the j-th order statistic, and gamma is a function of 

3081 ``j = floor(n*p + m)``, ``m = alphap + p*(1 - alphap - betap)`` and 

3082 ``g = n*p + m - j``. 

3083 

3084 Reinterpreting the above equations to compare to **R** lead to the 

3085 equation: ``p(k) = (k - alphap)/(n + 1 - alphap - betap)`` 

3086 

3087 Typical values of (alphap,betap) are: 

3088 - (0,1) : ``p(k) = k/n`` : linear interpolation of cdf 

3089 (**R** type 4) 

3090 - (.5,.5) : ``p(k) = (k - 1/2.)/n`` : piecewise linear function 

3091 (**R** type 5) 

3092 - (0,0) : ``p(k) = k/(n+1)`` : 

3093 (**R** type 6) 

3094 - (1,1) : ``p(k) = (k-1)/(n-1)``: p(k) = mode[F(x[k])]. 

3095 (**R** type 7, **R** default) 

3096 - (1/3,1/3): ``p(k) = (k-1/3)/(n+1/3)``: Then p(k) ~ median[F(x[k])]. 

3097 The resulting quantile estimates are approximately median-unbiased 

3098 regardless of the distribution of x. 

3099 (**R** type 8) 

3100 - (3/8,3/8): ``p(k) = (k-3/8)/(n+1/4)``: Blom. 

3101 The resulting quantile estimates are approximately unbiased 

3102 if x is normally distributed 

3103 (**R** type 9) 

3104 - (.4,.4) : approximately quantile unbiased (Cunnane) 

3105 - (.35,.35): APL, used with PWM 

3106 

3107 Parameters 

3108 ---------- 

3109 a : array_like 

3110 Input data, as a sequence or array of dimension at most 2. 

3111 prob : array_like, optional 

3112 List of quantiles to compute. 

3113 alphap : float, optional 

3114 Plotting positions parameter, default is 0.4. 

3115 betap : float, optional 

3116 Plotting positions parameter, default is 0.4. 

3117 axis : int, optional 

3118 Axis along which to perform the trimming. 

3119 If None (default), the input array is first flattened. 

3120 limit : tuple, optional 

3121 Tuple of (lower, upper) values. 

3122 Values of `a` outside this open interval are ignored. 

3123 

3124 Returns 

3125 ------- 

3126 mquantiles : MaskedArray 

3127 An array containing the calculated quantiles. 

3128 

3129 Notes 

3130 ----- 

3131 This formulation is very similar to **R** except the calculation of 

3132 ``m`` from ``alphap`` and ``betap``, where in **R** ``m`` is defined 

3133 with each type. 

3134 

3135 References 

3136 ---------- 

3137 .. [1] *R* statistical software: https://www.r-project.org/ 

3138 .. [2] *R* ``quantile`` function: 

3139 http://stat.ethz.ch/R-manual/R-devel/library/stats/html/quantile.html 

3140 

3141 Examples 

3142 -------- 

3143 >>> import numpy as np 

3144 >>> from scipy.stats.mstats import mquantiles 

3145 >>> a = np.array([6., 47., 49., 15., 42., 41., 7., 39., 43., 40., 36.]) 

3146 >>> mquantiles(a) 

3147 array([ 19.2, 40. , 42.8]) 

3148 

3149 Using a 2D array, specifying axis and limit. 

3150 

3151 >>> data = np.array([[ 6., 7., 1.], 

3152 ... [ 47., 15., 2.], 

3153 ... [ 49., 36., 3.], 

3154 ... [ 15., 39., 4.], 

3155 ... [ 42., 40., -999.], 

3156 ... [ 41., 41., -999.], 

3157 ... [ 7., -999., -999.], 

3158 ... [ 39., -999., -999.], 

3159 ... [ 43., -999., -999.], 

3160 ... [ 40., -999., -999.], 

3161 ... [ 36., -999., -999.]]) 

3162 >>> print(mquantiles(data, axis=0, limit=(0, 50))) 

3163 [[19.2 14.6 1.45] 

3164 [40. 37.5 2.5 ] 

3165 [42.8 40.05 3.55]] 

3166 

3167 >>> data[:, 2] = -999. 

3168 >>> print(mquantiles(data, axis=0, limit=(0, 50))) 

3169 [[19.200000000000003 14.6 --] 

3170 [40.0 37.5 --] 

3171 [42.800000000000004 40.05 --]] 

3172 

3173 """ 

3174 def _quantiles1D(data,m,p): 

3175 x = np.sort(data.compressed()) 

3176 n = len(x) 

3177 if n == 0: 

3178 return ma.array(np.empty(len(p), dtype=float), mask=True) 

3179 elif n == 1: 

3180 return ma.array(np.resize(x, p.shape), mask=nomask) 

3181 aleph = (n*p + m) 

3182 k = np.floor(aleph.clip(1, n-1)).astype(int) 

3183 gamma = (aleph-k).clip(0,1) 

3184 return (1.-gamma)*x[(k-1).tolist()] + gamma*x[k.tolist()] 

3185 

3186 data = ma.array(a, copy=False) 

3187 if data.ndim > 2: 

3188 raise TypeError("Array should be 2D at most !") 

3189 

3190 if limit: 

3191 condition = (limit[0] < data) & (data < limit[1]) 

3192 data[~condition.filled(True)] = masked 

3193 

3194 p = np.array(prob, copy=False, ndmin=1) 

3195 m = alphap + p*(1.-alphap-betap) 

3196 # Computes quantiles along axis (or globally) 

3197 if (axis is None): 

3198 return _quantiles1D(data, m, p) 

3199 

3200 return ma.apply_along_axis(_quantiles1D, axis, data, m, p) 

3201 

3202 

3203def scoreatpercentile(data, per, limit=(), alphap=.4, betap=.4): 

3204 """Calculate the score at the given 'per' percentile of the 

3205 sequence a. For example, the score at per=50 is the median. 

3206 

3207 This function is a shortcut to mquantile 

3208 

3209 """ 

3210 if (per < 0) or (per > 100.): 

3211 raise ValueError("The percentile should be between 0. and 100. !" 

3212 " (got %s)" % per) 

3213 

3214 return mquantiles(data, prob=[per/100.], alphap=alphap, betap=betap, 

3215 limit=limit, axis=0).squeeze() 

3216 

3217 

3218def plotting_positions(data, alpha=0.4, beta=0.4): 

3219 """ 

3220 Returns plotting positions (or empirical percentile points) for the data. 

3221 

3222 Plotting positions are defined as ``(i-alpha)/(n+1-alpha-beta)``, where: 

3223 - i is the rank order statistics 

3224 - n is the number of unmasked values along the given axis 

3225 - `alpha` and `beta` are two parameters. 

3226 

3227 Typical values for `alpha` and `beta` are: 

3228 - (0,1) : ``p(k) = k/n``, linear interpolation of cdf (R, type 4) 

3229 - (.5,.5) : ``p(k) = (k-1/2.)/n``, piecewise linear function 

3230 (R, type 5) 

3231 - (0,0) : ``p(k) = k/(n+1)``, Weibull (R type 6) 

3232 - (1,1) : ``p(k) = (k-1)/(n-1)``, in this case, 

3233 ``p(k) = mode[F(x[k])]``. That's R default (R type 7) 

3234 - (1/3,1/3): ``p(k) = (k-1/3)/(n+1/3)``, then 

3235 ``p(k) ~ median[F(x[k])]``. 

3236 The resulting quantile estimates are approximately median-unbiased 

3237 regardless of the distribution of x. (R type 8) 

3238 - (3/8,3/8): ``p(k) = (k-3/8)/(n+1/4)``, Blom. 

3239 The resulting quantile estimates are approximately unbiased 

3240 if x is normally distributed (R type 9) 

3241 - (.4,.4) : approximately quantile unbiased (Cunnane) 

3242 - (.35,.35): APL, used with PWM 

3243 - (.3175, .3175): used in scipy.stats.probplot 

3244 

3245 Parameters 

3246 ---------- 

3247 data : array_like 

3248 Input data, as a sequence or array of dimension at most 2. 

3249 alpha : float, optional 

3250 Plotting positions parameter. Default is 0.4. 

3251 beta : float, optional 

3252 Plotting positions parameter. Default is 0.4. 

3253 

3254 Returns 

3255 ------- 

3256 positions : MaskedArray 

3257 The calculated plotting positions. 

3258 

3259 """ 

3260 data = ma.array(data, copy=False).reshape(1,-1) 

3261 n = data.count() 

3262 plpos = np.empty(data.size, dtype=float) 

3263 plpos[n:] = 0 

3264 plpos[data.argsort(axis=None)[:n]] = ((np.arange(1, n+1) - alpha) / 

3265 (n + 1.0 - alpha - beta)) 

3266 return ma.array(plpos, mask=data._mask) 

3267 

3268 

3269meppf = plotting_positions 

3270 

3271 

3272def obrientransform(*args): 

3273 """ 

3274 Computes a transform on input data (any number of columns). Used to 

3275 test for homogeneity of variance prior to running one-way stats. Each 

3276 array in ``*args`` is one level of a factor. If an `f_oneway()` run on 

3277 the transformed data and found significant, variances are unequal. From 

3278 Maxwell and Delaney, p.112. 

3279 

3280 Returns: transformed data for use in an ANOVA 

3281 """ 

3282 data = argstoarray(*args).T 

3283 v = data.var(axis=0,ddof=1) 

3284 m = data.mean(0) 

3285 n = data.count(0).astype(float) 

3286 # result = ((N-1.5)*N*(a-m)**2 - 0.5*v*(n-1))/((n-1)*(n-2)) 

3287 data -= m 

3288 data **= 2 

3289 data *= (n-1.5)*n 

3290 data -= 0.5*v*(n-1) 

3291 data /= (n-1.)*(n-2.) 

3292 if not ma.allclose(v,data.mean(0)): 

3293 raise ValueError("Lack of convergence in obrientransform.") 

3294 

3295 return data 

3296 

3297 

3298def sem(a, axis=0, ddof=1): 

3299 """ 

3300 Calculates the standard error of the mean of the input array. 

3301 

3302 Also sometimes called standard error of measurement. 

3303 

3304 Parameters 

3305 ---------- 

3306 a : array_like 

3307 An array containing the values for which the standard error is 

3308 returned. 

3309 axis : int or None, optional 

3310 If axis is None, ravel `a` first. If axis is an integer, this will be 

3311 the axis over which to operate. Defaults to 0. 

3312 ddof : int, optional 

3313 Delta degrees-of-freedom. How many degrees of freedom to adjust 

3314 for bias in limited samples relative to the population estimate 

3315 of variance. Defaults to 1. 

3316 

3317 Returns 

3318 ------- 

3319 s : ndarray or float 

3320 The standard error of the mean in the sample(s), along the input axis. 

3321 

3322 Notes 

3323 ----- 

3324 The default value for `ddof` changed in scipy 0.15.0 to be consistent with 

3325 `scipy.stats.sem` as well as with the most common definition used (like in 

3326 the R documentation). 

3327 

3328 Examples 

3329 -------- 

3330 Find standard error along the first axis: 

3331 

3332 >>> import numpy as np 

3333 >>> from scipy import stats 

3334 >>> a = np.arange(20).reshape(5,4) 

3335 >>> print(stats.mstats.sem(a)) 

3336 [2.8284271247461903 2.8284271247461903 2.8284271247461903 

3337 2.8284271247461903] 

3338 

3339 Find standard error across the whole array, using n degrees of freedom: 

3340 

3341 >>> print(stats.mstats.sem(a, axis=None, ddof=0)) 

3342 1.2893796958227628 

3343 

3344 """ 

3345 a, axis = _chk_asarray(a, axis) 

3346 n = a.count(axis=axis) 

3347 s = a.std(axis=axis, ddof=ddof) / ma.sqrt(n) 

3348 return s 

3349 

3350 

3351F_onewayResult = namedtuple('F_onewayResult', ('statistic', 'pvalue')) 

3352 

3353 

3354def f_oneway(*args): 

3355 """ 

3356 Performs a 1-way ANOVA, returning an F-value and probability given 

3357 any number of groups. From Heiman, pp.394-7. 

3358 

3359 Usage: ``f_oneway(*args)``, where ``*args`` is 2 or more arrays, 

3360 one per treatment group. 

3361 

3362 Returns 

3363 ------- 

3364 statistic : float 

3365 The computed F-value of the test. 

3366 pvalue : float 

3367 The associated p-value from the F-distribution. 

3368 

3369 """ 

3370 # Construct a single array of arguments: each row is a group 

3371 data = argstoarray(*args) 

3372 ngroups = len(data) 

3373 ntot = data.count() 

3374 sstot = (data**2).sum() - (data.sum())**2/float(ntot) 

3375 ssbg = (data.count(-1) * (data.mean(-1)-data.mean())**2).sum() 

3376 sswg = sstot-ssbg 

3377 dfbg = ngroups-1 

3378 dfwg = ntot - ngroups 

3379 msb = ssbg/float(dfbg) 

3380 msw = sswg/float(dfwg) 

3381 f = msb/msw 

3382 prob = special.fdtrc(dfbg, dfwg, f) # equivalent to stats.f.sf 

3383 

3384 return F_onewayResult(f, prob) 

3385 

3386 

3387FriedmanchisquareResult = namedtuple('FriedmanchisquareResult', 

3388 ('statistic', 'pvalue')) 

3389 

3390 

3391def friedmanchisquare(*args): 

3392 """Friedman Chi-Square is a non-parametric, one-way within-subjects ANOVA. 

3393 This function calculates the Friedman Chi-square test for repeated measures 

3394 and returns the result, along with the associated probability value. 

3395 

3396 Each input is considered a given group. Ideally, the number of treatments 

3397 among each group should be equal. If this is not the case, only the first 

3398 n treatments are taken into account, where n is the number of treatments 

3399 of the smallest group. 

3400 If a group has some missing values, the corresponding treatments are masked 

3401 in the other groups. 

3402 The test statistic is corrected for ties. 

3403 

3404 Masked values in one group are propagated to the other groups. 

3405 

3406 Returns 

3407 ------- 

3408 statistic : float 

3409 the test statistic. 

3410 pvalue : float 

3411 the associated p-value. 

3412 

3413 """ 

3414 data = argstoarray(*args).astype(float) 

3415 k = len(data) 

3416 if k < 3: 

3417 raise ValueError("Less than 3 groups (%i): " % k + 

3418 "the Friedman test is NOT appropriate.") 

3419 

3420 ranked = ma.masked_values(rankdata(data, axis=0), 0) 

3421 if ranked._mask is not nomask: 

3422 ranked = ma.mask_cols(ranked) 

3423 ranked = ranked.compressed().reshape(k,-1).view(ndarray) 

3424 else: 

3425 ranked = ranked._data 

3426 (k,n) = ranked.shape 

3427 # Ties correction 

3428 repeats = [find_repeats(row) for row in ranked.T] 

3429 ties = np.array([y for x, y in repeats if x.size > 0]) 

3430 tie_correction = 1 - (ties**3-ties).sum()/float(n*(k**3-k)) 

3431 

3432 ssbg = np.sum((ranked.sum(-1) - n*(k+1)/2.)**2) 

3433 chisq = ssbg * 12./(n*k*(k+1)) * 1./tie_correction 

3434 

3435 return FriedmanchisquareResult(chisq, 

3436 distributions.chi2.sf(chisq, k-1)) 

3437 

3438 

3439BrunnerMunzelResult = namedtuple('BrunnerMunzelResult', ('statistic', 'pvalue')) 

3440 

3441 

3442def brunnermunzel(x, y, alternative="two-sided", distribution="t"): 

3443 """ 

3444 Computes the Brunner-Munzel test on samples x and y 

3445 

3446 Missing values in `x` and/or `y` are discarded. 

3447 

3448 Parameters 

3449 ---------- 

3450 x, y : array_like 

3451 Array of samples, should be one-dimensional. 

3452 alternative : 'less', 'two-sided', or 'greater', optional 

3453 Whether to get the p-value for the one-sided hypothesis ('less' 

3454 or 'greater') or for the two-sided hypothesis ('two-sided'). 

3455 Defaults value is 'two-sided' . 

3456 distribution : 't' or 'normal', optional 

3457 Whether to get the p-value by t-distribution or by standard normal 

3458 distribution. 

3459 Defaults value is 't' . 

3460 

3461 Returns 

3462 ------- 

3463 statistic : float 

3464 The Brunner-Munzer W statistic. 

3465 pvalue : float 

3466 p-value assuming an t distribution. One-sided or 

3467 two-sided, depending on the choice of `alternative` and `distribution`. 

3468 

3469 See Also 

3470 -------- 

3471 mannwhitneyu : Mann-Whitney rank test on two samples. 

3472 

3473 Notes 

3474 ----- 

3475 For more details on `brunnermunzel`, see `scipy.stats.brunnermunzel`. 

3476 

3477 """ 

3478 x = ma.asarray(x).compressed().view(ndarray) 

3479 y = ma.asarray(y).compressed().view(ndarray) 

3480 nx = len(x) 

3481 ny = len(y) 

3482 if nx == 0 or ny == 0: 

3483 return BrunnerMunzelResult(np.nan, np.nan) 

3484 rankc = rankdata(np.concatenate((x,y))) 

3485 rankcx = rankc[0:nx] 

3486 rankcy = rankc[nx:nx+ny] 

3487 rankcx_mean = np.mean(rankcx) 

3488 rankcy_mean = np.mean(rankcy) 

3489 rankx = rankdata(x) 

3490 ranky = rankdata(y) 

3491 rankx_mean = np.mean(rankx) 

3492 ranky_mean = np.mean(ranky) 

3493 

3494 Sx = np.sum(np.power(rankcx - rankx - rankcx_mean + rankx_mean, 2.0)) 

3495 Sx /= nx - 1 

3496 Sy = np.sum(np.power(rankcy - ranky - rankcy_mean + ranky_mean, 2.0)) 

3497 Sy /= ny - 1 

3498 

3499 wbfn = nx * ny * (rankcy_mean - rankcx_mean) 

3500 wbfn /= (nx + ny) * np.sqrt(nx * Sx + ny * Sy) 

3501 

3502 if distribution == "t": 

3503 df_numer = np.power(nx * Sx + ny * Sy, 2.0) 

3504 df_denom = np.power(nx * Sx, 2.0) / (nx - 1) 

3505 df_denom += np.power(ny * Sy, 2.0) / (ny - 1) 

3506 df = df_numer / df_denom 

3507 p = distributions.t.cdf(wbfn, df) 

3508 elif distribution == "normal": 

3509 p = distributions.norm.cdf(wbfn) 

3510 else: 

3511 raise ValueError( 

3512 "distribution should be 't' or 'normal'") 

3513 

3514 if alternative == "greater": 

3515 pass 

3516 elif alternative == "less": 

3517 p = 1 - p 

3518 elif alternative == "two-sided": 

3519 p = 2 * np.min([p, 1-p]) 

3520 else: 

3521 raise ValueError( 

3522 "alternative should be 'less', 'greater' or 'two-sided'") 

3523 

3524 return BrunnerMunzelResult(wbfn, p)