Coverage for /usr/lib/python3/dist-packages/scipy/stats/_stats_py.py: 10%
2010 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1# Copyright 2002 Gary Strangman. All rights reserved
2# Copyright 2002-2016 The SciPy Developers
3#
4# The original code from Gary Strangman was heavily adapted for
5# use in SciPy by Travis Oliphant. The original code came with the
6# following disclaimer:
7#
8# This software is provided "as-is". There are no expressed or implied
9# warranties of any kind, including, but not limited to, the warranties
10# of merchantability and fitness for a given application. In no event
11# shall Gary Strangman be liable for any direct, indirect, incidental,
12# special, exemplary or consequential damages (including, but not limited
13# to, loss of use, data or profits, or business interruption) however
14# caused and on any theory of liability, whether in contract, strict
15# liability or tort (including negligence or otherwise) arising in any way
16# out of the use of this software, even if advised of the possibility of
17# such damage.
19"""
20A collection of basic statistical functions for Python.
22References
23----------
24.. [CRCProbStat2000] Zwillinger, D. and Kokoska, S. (2000). CRC Standard
25 Probability and Statistics Tables and Formulae. Chapman & Hall: New
26 York. 2000.
28"""
29import warnings
30import math
31from math import gcd
32from collections import namedtuple
34import numpy as np
35from numpy import array, asarray, ma
36from numpy.lib import NumpyVersion
37from numpy.testing import suppress_warnings
39from scipy.spatial.distance import cdist
40from scipy.ndimage import _measurements
41from scipy._lib._util import (check_random_state, MapWrapper, _get_nan,
42 rng_integers, _rename_parameter, _contains_nan)
44import scipy.special as special
45from scipy import linalg
46from . import distributions
47from . import _mstats_basic as mstats_basic
48from ._stats_mstats_common import (_find_repeats, linregress, theilslopes,
49 siegelslopes)
50from ._stats import (_kendall_dis, _toint64, _weightedrankedtau,
51 _local_correlations)
52from dataclasses import dataclass
53from ._hypotests import _all_partitions
54from ._stats_pythran import _compute_outer_prob_inside_method
55from ._resampling import (MonteCarloMethod, PermutationMethod, BootstrapMethod,
56 monte_carlo_test, permutation_test, bootstrap,
57 _batch_generator)
58from ._axis_nan_policy import (_axis_nan_policy_factory,
59 _broadcast_concatenate)
60from ._binomtest import _binary_search_for_binom_tst as _binary_search
61from scipy._lib._bunch import _make_tuple_bunch
62from scipy import stats
63from scipy.optimize import root_scalar
66# Functions/classes in other files should be added in `__init__.py`, not here
67__all__ = ['find_repeats', 'gmean', 'hmean', 'pmean', 'mode', 'tmean', 'tvar',
68 'tmin', 'tmax', 'tstd', 'tsem', 'moment',
69 'skew', 'kurtosis', 'describe', 'skewtest', 'kurtosistest',
70 'normaltest', 'jarque_bera',
71 'scoreatpercentile', 'percentileofscore',
72 'cumfreq', 'relfreq', 'obrientransform',
73 'sem', 'zmap', 'zscore', 'gzscore', 'iqr', 'gstd',
74 'median_abs_deviation',
75 'sigmaclip', 'trimboth', 'trim1', 'trim_mean',
76 'f_oneway', 'pearsonr', 'fisher_exact',
77 'spearmanr', 'pointbiserialr',
78 'kendalltau', 'weightedtau', 'multiscale_graphcorr',
79 'linregress', 'siegelslopes', 'theilslopes', 'ttest_1samp',
80 'ttest_ind', 'ttest_ind_from_stats', 'ttest_rel',
81 'kstest', 'ks_1samp', 'ks_2samp',
82 'chisquare', 'power_divergence',
83 'tiecorrect', 'ranksums', 'kruskal', 'friedmanchisquare',
84 'rankdata',
85 'combine_pvalues', 'wasserstein_distance', 'energy_distance',
86 'brunnermunzel', 'alexandergovern',
87 'expectile', ]
90def _chk_asarray(a, axis):
91 if axis is None:
92 a = np.ravel(a)
93 outaxis = 0
94 else:
95 a = np.asarray(a)
96 outaxis = axis
98 if a.ndim == 0:
99 a = np.atleast_1d(a)
101 return a, outaxis
104def _chk2_asarray(a, b, axis):
105 if axis is None:
106 a = np.ravel(a)
107 b = np.ravel(b)
108 outaxis = 0
109 else:
110 a = np.asarray(a)
111 b = np.asarray(b)
112 outaxis = axis
114 if a.ndim == 0:
115 a = np.atleast_1d(a)
116 if b.ndim == 0:
117 b = np.atleast_1d(b)
119 return a, b, outaxis
122SignificanceResult = _make_tuple_bunch('SignificanceResult',
123 ['statistic', 'pvalue'], [])
126# note that `weights` are paired with `x`
127@_axis_nan_policy_factory(
128 lambda x: x, n_samples=1, n_outputs=1, too_small=0, paired=True,
129 result_to_tuple=lambda x: (x,), kwd_samples=['weights'])
130def gmean(a, axis=0, dtype=None, weights=None):
131 r"""Compute the weighted geometric mean along the specified axis.
133 The weighted geometric mean of the array :math:`a_i` associated to weights
134 :math:`w_i` is:
136 .. math::
138 \exp \left( \frac{ \sum_{i=1}^n w_i \ln a_i }{ \sum_{i=1}^n w_i }
139 \right) \, ,
141 and, with equal weights, it gives:
143 .. math::
145 \sqrt[n]{ \prod_{i=1}^n a_i } \, .
147 Parameters
148 ----------
149 a : array_like
150 Input array or object that can be converted to an array.
151 axis : int or None, optional
152 Axis along which the geometric mean is computed. Default is 0.
153 If None, compute over the whole array `a`.
154 dtype : dtype, optional
155 Type to which the input arrays are cast before the calculation is
156 performed.
157 weights : array_like, optional
158 The `weights` array must be broadcastable to the same shape as `a`.
159 Default is None, which gives each value a weight of 1.0.
161 Returns
162 -------
163 gmean : ndarray
164 See `dtype` parameter above.
166 See Also
167 --------
168 numpy.mean : Arithmetic average
169 numpy.average : Weighted average
170 hmean : Harmonic mean
172 References
173 ----------
174 .. [1] "Weighted Geometric Mean", *Wikipedia*,
175 https://en.wikipedia.org/wiki/Weighted_geometric_mean.
176 .. [2] Grossman, J., Grossman, M., Katz, R., "Averages: A New Approach",
177 Archimedes Foundation, 1983
179 Examples
180 --------
181 >>> from scipy.stats import gmean
182 >>> gmean([1, 4])
183 2.0
184 >>> gmean([1, 2, 3, 4, 5, 6, 7])
185 3.3800151591412964
186 >>> gmean([1, 4, 7], weights=[3, 1, 3])
187 2.80668351922014
189 """
191 a = np.asarray(a, dtype=dtype)
193 if weights is not None:
194 weights = np.asarray(weights, dtype=dtype)
196 with np.errstate(divide='ignore'):
197 log_a = np.log(a)
199 return np.exp(np.average(log_a, axis=axis, weights=weights))
202@_axis_nan_policy_factory(
203 lambda x: x, n_samples=1, n_outputs=1, too_small=0, paired=True,
204 result_to_tuple=lambda x: (x,), kwd_samples=['weights'])
205def hmean(a, axis=0, dtype=None, *, weights=None):
206 r"""Calculate the weighted harmonic mean along the specified axis.
208 The weighted harmonic mean of the array :math:`a_i` associated to weights
209 :math:`w_i` is:
211 .. math::
213 \frac{ \sum_{i=1}^n w_i }{ \sum_{i=1}^n \frac{w_i}{a_i} } \, ,
215 and, with equal weights, it gives:
217 .. math::
219 \frac{ n }{ \sum_{i=1}^n \frac{1}{a_i} } \, .
221 Parameters
222 ----------
223 a : array_like
224 Input array, masked array or object that can be converted to an array.
225 axis : int or None, optional
226 Axis along which the harmonic mean is computed. Default is 0.
227 If None, compute over the whole array `a`.
228 dtype : dtype, optional
229 Type of the returned array and of the accumulator in which the
230 elements are summed. If `dtype` is not specified, it defaults to the
231 dtype of `a`, unless `a` has an integer `dtype` with a precision less
232 than that of the default platform integer. In that case, the default
233 platform integer is used.
234 weights : array_like, optional
235 The weights array can either be 1-D (in which case its length must be
236 the size of `a` along the given `axis`) or of the same shape as `a`.
237 Default is None, which gives each value a weight of 1.0.
239 .. versionadded:: 1.9
241 Returns
242 -------
243 hmean : ndarray
244 See `dtype` parameter above.
246 See Also
247 --------
248 numpy.mean : Arithmetic average
249 numpy.average : Weighted average
250 gmean : Geometric mean
252 Notes
253 -----
254 The harmonic mean is computed over a single dimension of the input
255 array, axis=0 by default, or all values in the array if axis=None.
256 float64 intermediate and return values are used for integer inputs.
258 References
259 ----------
260 .. [1] "Weighted Harmonic Mean", *Wikipedia*,
261 https://en.wikipedia.org/wiki/Harmonic_mean#Weighted_harmonic_mean
262 .. [2] Ferger, F., "The nature and use of the harmonic mean", Journal of
263 the American Statistical Association, vol. 26, pp. 36-40, 1931
265 Examples
266 --------
267 >>> from scipy.stats import hmean
268 >>> hmean([1, 4])
269 1.6000000000000001
270 >>> hmean([1, 2, 3, 4, 5, 6, 7])
271 2.6997245179063363
272 >>> hmean([1, 4, 7], weights=[3, 1, 3])
273 1.9029126213592233
275 """
276 if not isinstance(a, np.ndarray):
277 a = np.array(a, dtype=dtype)
278 elif dtype:
279 # Must change the default dtype allowing array type
280 if isinstance(a, np.ma.MaskedArray):
281 a = np.ma.asarray(a, dtype=dtype)
282 else:
283 a = np.asarray(a, dtype=dtype)
285 if np.all(a >= 0):
286 # Harmonic mean only defined if greater than or equal to zero.
287 if weights is not None:
288 weights = np.asanyarray(weights, dtype=dtype)
290 with np.errstate(divide='ignore'):
291 return 1.0 / np.average(1.0 / a, axis=axis, weights=weights)
292 else:
293 raise ValueError("Harmonic mean only defined if all elements greater "
294 "than or equal to zero")
297@_axis_nan_policy_factory(
298 lambda x: x, n_samples=1, n_outputs=1, too_small=0, paired=True,
299 result_to_tuple=lambda x: (x,), kwd_samples=['weights'])
300def pmean(a, p, *, axis=0, dtype=None, weights=None):
301 r"""Calculate the weighted power mean along the specified axis.
303 The weighted power mean of the array :math:`a_i` associated to weights
304 :math:`w_i` is:
306 .. math::
308 \left( \frac{ \sum_{i=1}^n w_i a_i^p }{ \sum_{i=1}^n w_i }
309 \right)^{ 1 / p } \, ,
311 and, with equal weights, it gives:
313 .. math::
315 \left( \frac{ 1 }{ n } \sum_{i=1}^n a_i^p \right)^{ 1 / p } \, .
317 When ``p=0``, it returns the geometric mean.
319 This mean is also called generalized mean or Hölder mean, and must not be
320 confused with the Kolmogorov generalized mean, also called
321 quasi-arithmetic mean or generalized f-mean [3]_.
323 Parameters
324 ----------
325 a : array_like
326 Input array, masked array or object that can be converted to an array.
327 p : int or float
328 Exponent.
329 axis : int or None, optional
330 Axis along which the power mean is computed. Default is 0.
331 If None, compute over the whole array `a`.
332 dtype : dtype, optional
333 Type of the returned array and of the accumulator in which the
334 elements are summed. If `dtype` is not specified, it defaults to the
335 dtype of `a`, unless `a` has an integer `dtype` with a precision less
336 than that of the default platform integer. In that case, the default
337 platform integer is used.
338 weights : array_like, optional
339 The weights array can either be 1-D (in which case its length must be
340 the size of `a` along the given `axis`) or of the same shape as `a`.
341 Default is None, which gives each value a weight of 1.0.
343 Returns
344 -------
345 pmean : ndarray, see `dtype` parameter above.
346 Output array containing the power mean values.
348 See Also
349 --------
350 numpy.average : Weighted average
351 gmean : Geometric mean
352 hmean : Harmonic mean
354 Notes
355 -----
356 The power mean is computed over a single dimension of the input
357 array, ``axis=0`` by default, or all values in the array if ``axis=None``.
358 float64 intermediate and return values are used for integer inputs.
360 .. versionadded:: 1.9
362 References
363 ----------
364 .. [1] "Generalized Mean", *Wikipedia*,
365 https://en.wikipedia.org/wiki/Generalized_mean
366 .. [2] Norris, N., "Convexity properties of generalized mean value
367 functions", The Annals of Mathematical Statistics, vol. 8,
368 pp. 118-120, 1937
369 .. [3] Bullen, P.S., Handbook of Means and Their Inequalities, 2003
371 Examples
372 --------
373 >>> from scipy.stats import pmean, hmean, gmean
374 >>> pmean([1, 4], 1.3)
375 2.639372938300652
376 >>> pmean([1, 2, 3, 4, 5, 6, 7], 1.3)
377 4.157111214492084
378 >>> pmean([1, 4, 7], -2, weights=[3, 1, 3])
379 1.4969684896631954
381 For p=-1, power mean is equal to harmonic mean:
383 >>> pmean([1, 4, 7], -1, weights=[3, 1, 3])
384 1.9029126213592233
385 >>> hmean([1, 4, 7], weights=[3, 1, 3])
386 1.9029126213592233
388 For p=0, power mean is defined as the geometric mean:
390 >>> pmean([1, 4, 7], 0, weights=[3, 1, 3])
391 2.80668351922014
392 >>> gmean([1, 4, 7], weights=[3, 1, 3])
393 2.80668351922014
395 """
396 if not isinstance(p, (int, float)):
397 raise ValueError("Power mean only defined for exponent of type int or "
398 "float.")
399 if p == 0:
400 return gmean(a, axis=axis, dtype=dtype, weights=weights)
402 if not isinstance(a, np.ndarray):
403 a = np.array(a, dtype=dtype)
404 elif dtype:
405 # Must change the default dtype allowing array type
406 if isinstance(a, np.ma.MaskedArray):
407 a = np.ma.asarray(a, dtype=dtype)
408 else:
409 a = np.asarray(a, dtype=dtype)
411 if np.all(a >= 0):
412 # Power mean only defined if greater than or equal to zero
413 if weights is not None:
414 weights = np.asanyarray(weights, dtype=dtype)
416 with np.errstate(divide='ignore'):
417 return np.float_power(
418 np.average(np.float_power(a, p), axis=axis, weights=weights),
419 1/p)
420 else:
421 raise ValueError("Power mean only defined if all elements greater "
422 "than or equal to zero")
425ModeResult = namedtuple('ModeResult', ('mode', 'count'))
428def _mode_result(mode, count):
429 # When a slice is empty, `_axis_nan_policy` automatically produces
430 # NaN for `mode` and `count`. This is a reasonable convention for `mode`,
431 # but `count` should not be NaN; it should be zero.
432 i = np.isnan(count)
433 if i.shape == ():
434 count = count.dtype(0) if i else count
435 else:
436 count[i] = 0
437 return ModeResult(mode, count)
440@_axis_nan_policy_factory(_mode_result, override={'vectorization': True,
441 'nan_propagation': False})
442def mode(a, axis=0, nan_policy='propagate', keepdims=False):
443 r"""Return an array of the modal (most common) value in the passed array.
445 If there is more than one such value, only one is returned.
446 The bin-count for the modal bins is also returned.
448 Parameters
449 ----------
450 a : array_like
451 Numeric, n-dimensional array of which to find mode(s).
452 axis : int or None, optional
453 Axis along which to operate. Default is 0. If None, compute over
454 the whole array `a`.
455 nan_policy : {'propagate', 'raise', 'omit'}, optional
456 Defines how to handle when input contains nan.
457 The following options are available (default is 'propagate'):
459 * 'propagate': treats nan as it would treat any other value
460 * 'raise': throws an error
461 * 'omit': performs the calculations ignoring nan values
462 keepdims : bool, optional
463 If set to ``False``, the `axis` over which the statistic is taken
464 is consumed (eliminated from the output array). If set to ``True``,
465 the `axis` is retained with size one, and the result will broadcast
466 correctly against the input array.
468 Returns
469 -------
470 mode : ndarray
471 Array of modal values.
472 count : ndarray
473 Array of counts for each mode.
475 Notes
476 -----
477 The mode is calculated using `numpy.unique`.
478 In NumPy versions 1.21 and after, all NaNs - even those with different
479 binary representations - are treated as equivalent and counted as separate
480 instances of the same value.
482 By convention, the mode of an empty array is NaN, and the associated count
483 is zero.
485 Examples
486 --------
487 >>> import numpy as np
488 >>> a = np.array([[3, 0, 3, 7],
489 ... [3, 2, 6, 2],
490 ... [1, 7, 2, 8],
491 ... [3, 0, 6, 1],
492 ... [3, 2, 5, 5]])
493 >>> from scipy import stats
494 >>> stats.mode(a, keepdims=True)
495 ModeResult(mode=array([[3, 0, 6, 1]]), count=array([[4, 2, 2, 1]]))
497 To get mode of whole array, specify ``axis=None``:
499 >>> stats.mode(a, axis=None, keepdims=True)
500 ModeResult(mode=[[3]], count=[[5]])
501 >>> stats.mode(a, axis=None, keepdims=False)
502 ModeResult(mode=3, count=5)
504 """ # noqa: E501
505 # `axis`, `nan_policy`, and `keepdims` are handled by `_axis_nan_policy`
506 if not np.issubdtype(a.dtype, np.number):
507 message = ("Argument `a` is not recognized as numeric. "
508 "Support for input that cannot be coerced to a numeric "
509 "array was deprecated in SciPy 1.9.0 and removed in SciPy "
510 "1.11.0. Please consider `np.unique`.")
511 raise TypeError(message)
513 if a.size == 0:
514 NaN = _get_nan(a)
515 return ModeResult(*np.array([NaN, 0], dtype=NaN.dtype))
517 vals, cnts = np.unique(a, return_counts=True)
518 modes, counts = vals[cnts.argmax()], cnts.max()
519 return ModeResult(modes[()], counts[()])
522def _mask_to_limits(a, limits, inclusive):
523 """Mask an array for values outside of given limits.
525 This is primarily a utility function.
527 Parameters
528 ----------
529 a : array
530 limits : (float or None, float or None)
531 A tuple consisting of the (lower limit, upper limit). Values in the
532 input array less than the lower limit or greater than the upper limit
533 will be masked out. None implies no limit.
534 inclusive : (bool, bool)
535 A tuple consisting of the (lower flag, upper flag). These flags
536 determine whether values exactly equal to lower or upper are allowed.
538 Returns
539 -------
540 A MaskedArray.
542 Raises
543 ------
544 A ValueError if there are no values within the given limits.
546 """
547 lower_limit, upper_limit = limits
548 lower_include, upper_include = inclusive
549 am = ma.MaskedArray(a)
550 if lower_limit is not None:
551 if lower_include:
552 am = ma.masked_less(am, lower_limit)
553 else:
554 am = ma.masked_less_equal(am, lower_limit)
556 if upper_limit is not None:
557 if upper_include:
558 am = ma.masked_greater(am, upper_limit)
559 else:
560 am = ma.masked_greater_equal(am, upper_limit)
562 if am.count() == 0:
563 raise ValueError("No array values within given limits")
565 return am
568def tmean(a, limits=None, inclusive=(True, True), axis=None):
569 """Compute the trimmed mean.
571 This function finds the arithmetic mean of given values, ignoring values
572 outside the given `limits`.
574 Parameters
575 ----------
576 a : array_like
577 Array of values.
578 limits : None or (lower limit, upper limit), optional
579 Values in the input array less than the lower limit or greater than the
580 upper limit will be ignored. When limits is None (default), then all
581 values are used. Either of the limit values in the tuple can also be
582 None representing a half-open interval.
583 inclusive : (bool, bool), optional
584 A tuple consisting of the (lower flag, upper flag). These flags
585 determine whether values exactly equal to the lower or upper limits
586 are included. The default value is (True, True).
587 axis : int or None, optional
588 Axis along which to compute test. Default is None.
590 Returns
591 -------
592 tmean : ndarray
593 Trimmed mean.
595 See Also
596 --------
597 trim_mean : Returns mean after trimming a proportion from both tails.
599 Examples
600 --------
601 >>> import numpy as np
602 >>> from scipy import stats
603 >>> x = np.arange(20)
604 >>> stats.tmean(x)
605 9.5
606 >>> stats.tmean(x, (3,17))
607 10.0
609 """
610 a = asarray(a)
611 if limits is None:
612 return np.mean(a, axis)
613 am = _mask_to_limits(a, limits, inclusive)
614 mean = np.ma.filled(am.mean(axis=axis), fill_value=np.nan)
615 return mean if mean.ndim > 0 else mean.item()
618def tvar(a, limits=None, inclusive=(True, True), axis=0, ddof=1):
619 """Compute the trimmed variance.
621 This function computes the sample variance of an array of values,
622 while ignoring values which are outside of given `limits`.
624 Parameters
625 ----------
626 a : array_like
627 Array of values.
628 limits : None or (lower limit, upper limit), optional
629 Values in the input array less than the lower limit or greater than the
630 upper limit will be ignored. When limits is None, then all values are
631 used. Either of the limit values in the tuple can also be None
632 representing a half-open interval. The default value is None.
633 inclusive : (bool, bool), optional
634 A tuple consisting of the (lower flag, upper flag). These flags
635 determine whether values exactly equal to the lower or upper limits
636 are included. The default value is (True, True).
637 axis : int or None, optional
638 Axis along which to operate. Default is 0. If None, compute over the
639 whole array `a`.
640 ddof : int, optional
641 Delta degrees of freedom. Default is 1.
643 Returns
644 -------
645 tvar : float
646 Trimmed variance.
648 Notes
649 -----
650 `tvar` computes the unbiased sample variance, i.e. it uses a correction
651 factor ``n / (n - 1)``.
653 Examples
654 --------
655 >>> import numpy as np
656 >>> from scipy import stats
657 >>> x = np.arange(20)
658 >>> stats.tvar(x)
659 35.0
660 >>> stats.tvar(x, (3,17))
661 20.0
663 """
664 a = asarray(a)
665 a = a.astype(float)
666 if limits is None:
667 return a.var(ddof=ddof, axis=axis)
668 am = _mask_to_limits(a, limits, inclusive)
669 amnan = am.filled(fill_value=np.nan)
670 return np.nanvar(amnan, ddof=ddof, axis=axis)
673def tmin(a, lowerlimit=None, axis=0, inclusive=True, nan_policy='propagate'):
674 """Compute the trimmed minimum.
676 This function finds the miminum value of an array `a` along the
677 specified axis, but only considering values greater than a specified
678 lower limit.
680 Parameters
681 ----------
682 a : array_like
683 Array of values.
684 lowerlimit : None or float, optional
685 Values in the input array less than the given limit will be ignored.
686 When lowerlimit is None, then all values are used. The default value
687 is None.
688 axis : int or None, optional
689 Axis along which to operate. Default is 0. If None, compute over the
690 whole array `a`.
691 inclusive : {True, False}, optional
692 This flag determines whether values exactly equal to the lower limit
693 are included. The default value is True.
694 nan_policy : {'propagate', 'raise', 'omit'}, optional
695 Defines how to handle when input contains nan.
696 The following options are available (default is 'propagate'):
698 * 'propagate': returns nan
699 * 'raise': throws an error
700 * 'omit': performs the calculations ignoring nan values
702 Returns
703 -------
704 tmin : float, int or ndarray
705 Trimmed minimum.
707 Examples
708 --------
709 >>> import numpy as np
710 >>> from scipy import stats
711 >>> x = np.arange(20)
712 >>> stats.tmin(x)
713 0
715 >>> stats.tmin(x, 13)
716 13
718 >>> stats.tmin(x, 13, inclusive=False)
719 14
721 """
722 a, axis = _chk_asarray(a, axis)
723 am = _mask_to_limits(a, (lowerlimit, None), (inclusive, False))
725 contains_nan, nan_policy = _contains_nan(am, nan_policy)
727 if contains_nan and nan_policy == 'omit':
728 am = ma.masked_invalid(am)
730 res = ma.minimum.reduce(am, axis).data
731 if res.ndim == 0:
732 return res[()]
733 return res
736def tmax(a, upperlimit=None, axis=0, inclusive=True, nan_policy='propagate'):
737 """Compute the trimmed maximum.
739 This function computes the maximum value of an array along a given axis,
740 while ignoring values larger than a specified upper limit.
742 Parameters
743 ----------
744 a : array_like
745 Array of values.
746 upperlimit : None or float, optional
747 Values in the input array greater than the given limit will be ignored.
748 When upperlimit is None, then all values are used. The default value
749 is None.
750 axis : int or None, optional
751 Axis along which to operate. Default is 0. If None, compute over the
752 whole array `a`.
753 inclusive : {True, False}, optional
754 This flag determines whether values exactly equal to the upper limit
755 are included. The default value is True.
756 nan_policy : {'propagate', 'raise', 'omit'}, optional
757 Defines how to handle when input contains nan.
758 The following options are available (default is 'propagate'):
760 * 'propagate': returns nan
761 * 'raise': throws an error
762 * 'omit': performs the calculations ignoring nan values
764 Returns
765 -------
766 tmax : float, int or ndarray
767 Trimmed maximum.
769 Examples
770 --------
771 >>> import numpy as np
772 >>> from scipy import stats
773 >>> x = np.arange(20)
774 >>> stats.tmax(x)
775 19
777 >>> stats.tmax(x, 13)
778 13
780 >>> stats.tmax(x, 13, inclusive=False)
781 12
783 """
784 a, axis = _chk_asarray(a, axis)
785 am = _mask_to_limits(a, (None, upperlimit), (False, inclusive))
787 contains_nan, nan_policy = _contains_nan(am, nan_policy)
789 if contains_nan and nan_policy == 'omit':
790 am = ma.masked_invalid(am)
792 res = ma.maximum.reduce(am, axis).data
793 if res.ndim == 0:
794 return res[()]
795 return res
798def tstd(a, limits=None, inclusive=(True, True), axis=0, ddof=1):
799 """Compute the trimmed sample standard deviation.
801 This function finds the sample standard deviation of given values,
802 ignoring values outside the given `limits`.
804 Parameters
805 ----------
806 a : array_like
807 Array of values.
808 limits : None or (lower limit, upper limit), optional
809 Values in the input array less than the lower limit or greater than the
810 upper limit will be ignored. When limits is None, then all values are
811 used. Either of the limit values in the tuple can also be None
812 representing a half-open interval. The default value is None.
813 inclusive : (bool, bool), optional
814 A tuple consisting of the (lower flag, upper flag). These flags
815 determine whether values exactly equal to the lower or upper limits
816 are included. The default value is (True, True).
817 axis : int or None, optional
818 Axis along which to operate. Default is 0. If None, compute over the
819 whole array `a`.
820 ddof : int, optional
821 Delta degrees of freedom. Default is 1.
823 Returns
824 -------
825 tstd : float
826 Trimmed sample standard deviation.
828 Notes
829 -----
830 `tstd` computes the unbiased sample standard deviation, i.e. it uses a
831 correction factor ``n / (n - 1)``.
833 Examples
834 --------
835 >>> import numpy as np
836 >>> from scipy import stats
837 >>> x = np.arange(20)
838 >>> stats.tstd(x)
839 5.9160797830996161
840 >>> stats.tstd(x, (3,17))
841 4.4721359549995796
843 """
844 return np.sqrt(tvar(a, limits, inclusive, axis, ddof))
847def tsem(a, limits=None, inclusive=(True, True), axis=0, ddof=1):
848 """Compute the trimmed standard error of the mean.
850 This function finds the standard error of the mean for given
851 values, ignoring values outside the given `limits`.
853 Parameters
854 ----------
855 a : array_like
856 Array of values.
857 limits : None or (lower limit, upper limit), optional
858 Values in the input array less than the lower limit or greater than the
859 upper limit will be ignored. When limits is None, then all values are
860 used. Either of the limit values in the tuple can also be None
861 representing a half-open interval. The default value is None.
862 inclusive : (bool, bool), optional
863 A tuple consisting of the (lower flag, upper flag). These flags
864 determine whether values exactly equal to the lower or upper limits
865 are included. The default value is (True, True).
866 axis : int or None, optional
867 Axis along which to operate. Default is 0. If None, compute over the
868 whole array `a`.
869 ddof : int, optional
870 Delta degrees of freedom. Default is 1.
872 Returns
873 -------
874 tsem : float
875 Trimmed standard error of the mean.
877 Notes
878 -----
879 `tsem` uses unbiased sample standard deviation, i.e. it uses a
880 correction factor ``n / (n - 1)``.
882 Examples
883 --------
884 >>> import numpy as np
885 >>> from scipy import stats
886 >>> x = np.arange(20)
887 >>> stats.tsem(x)
888 1.3228756555322954
889 >>> stats.tsem(x, (3,17))
890 1.1547005383792515
892 """
893 a = np.asarray(a).ravel()
894 if limits is None:
895 return a.std(ddof=ddof) / np.sqrt(a.size)
897 am = _mask_to_limits(a, limits, inclusive)
898 sd = np.sqrt(np.ma.var(am, ddof=ddof, axis=axis))
899 return sd / np.sqrt(am.count())
902#####################################
903# MOMENTS #
904#####################################
907def _moment_outputs(kwds):
908 moment = np.atleast_1d(kwds.get('moment', 1))
909 if moment.size == 0:
910 raise ValueError("'moment' must be a scalar or a non-empty 1D "
911 "list/array.")
912 return len(moment)
915def _moment_result_object(*args):
916 if len(args) == 1:
917 return args[0]
918 return np.asarray(args)
920# `moment` fits into the `_axis_nan_policy` pattern, but it is a bit unusual
921# because the number of outputs is variable. Specifically,
922# `result_to_tuple=lambda x: (x,)` may be surprising for a function that
923# can produce more than one output, but it is intended here.
924# When `moment is called to produce the output:
925# - `result_to_tuple` packs the returned array into a single-element tuple,
926# - `_moment_result_object` extracts and returns that single element.
927# However, when the input array is empty, `moment` is never called. Instead,
928# - `_check_empty_inputs` is used to produce an empty array with the
929# appropriate dimensions.
930# - A list comprehension creates the appropriate number of copies of this
931# array, depending on `n_outputs`.
932# - This list - which may have multiple elements - is passed into
933# `_moment_result_object`.
934# - If there is a single output, `_moment_result_object` extracts and returns
935# the single output from the list.
936# - If there are multiple outputs, and therefore multiple elements in the list,
937# `_moment_result_object` converts the list of arrays to a single array and
938# returns it.
939# Currently this leads to a slight inconsistency: when the input array is
940# empty, there is no distinction between the `moment` function being called
941# with parameter `moments=1` and `moments=[1]`; the latter *should* produce
942# the same as the former but with a singleton zeroth dimension.
943@_axis_nan_policy_factory( # noqa: E302
944 _moment_result_object, n_samples=1, result_to_tuple=lambda x: (x,),
945 n_outputs=_moment_outputs
946)
947def moment(a, moment=1, axis=0, nan_policy='propagate', *, center=None):
948 r"""Calculate the nth moment about the mean for a sample.
950 A moment is a specific quantitative measure of the shape of a set of
951 points. It is often used to calculate coefficients of skewness and kurtosis
952 due to its close relationship with them.
954 Parameters
955 ----------
956 a : array_like
957 Input array.
958 moment : int or array_like of ints, optional
959 Order of central moment that is returned. Default is 1.
960 axis : int or None, optional
961 Axis along which the central moment is computed. Default is 0.
962 If None, compute over the whole array `a`.
963 nan_policy : {'propagate', 'raise', 'omit'}, optional
964 Defines how to handle when input contains nan.
965 The following options are available (default is 'propagate'):
967 * 'propagate': returns nan
968 * 'raise': throws an error
969 * 'omit': performs the calculations ignoring nan values
971 center : float or None, optional
972 The point about which moments are taken. This can be the sample mean,
973 the origin, or any other be point. If `None` (default) compute the
974 center as the sample mean.
976 Returns
977 -------
978 n-th moment about the `center` : ndarray or float
979 The appropriate moment along the given axis or over all values if axis
980 is None. The denominator for the moment calculation is the number of
981 observations, no degrees of freedom correction is done.
983 See Also
984 --------
985 kurtosis, skew, describe
987 Notes
988 -----
989 The k-th moment of a data sample is:
991 .. math::
993 m_k = \frac{1}{n} \sum_{i = 1}^n (x_i - c)^k
995 Where `n` is the number of samples, and `c` is the center around which the
996 moment is calculated. This function uses exponentiation by squares [1]_ for
997 efficiency.
999 Note that, if `a` is an empty array (``a.size == 0``), array `moment` with
1000 one element (`moment.size == 1`) is treated the same as scalar `moment`
1001 (``np.isscalar(moment)``). This might produce arrays of unexpected shape.
1003 References
1004 ----------
1005 .. [1] https://eli.thegreenplace.net/2009/03/21/efficient-integer-exponentiation-algorithms
1007 Examples
1008 --------
1009 >>> from scipy.stats import moment
1010 >>> moment([1, 2, 3, 4, 5], moment=1)
1011 0.0
1012 >>> moment([1, 2, 3, 4, 5], moment=2)
1013 2.0
1015 """
1016 a, axis = _chk_asarray(a, axis)
1018 # for array_like moment input, return a value for each.
1019 if not np.isscalar(moment):
1020 # Calculated the mean once at most, and only if it will be used
1021 calculate_mean = center is None and np.any(np.asarray(moment) > 1)
1022 mean = a.mean(axis, keepdims=True) if calculate_mean else None
1023 mmnt = []
1024 for i in moment:
1025 if center is None and i > 1:
1026 mmnt.append(_moment(a, i, axis, mean=mean))
1027 else:
1028 mmnt.append(_moment(a, i, axis, mean=center))
1029 return np.array(mmnt)
1030 else:
1031 return _moment(a, moment, axis, mean=center)
1034# Moment with optional pre-computed mean, equal to a.mean(axis, keepdims=True)
1035def _moment(a, moment, axis, *, mean=None):
1036 if np.abs(moment - np.round(moment)) > 0:
1037 raise ValueError("All moment parameters must be integers")
1039 # moment of empty array is the same regardless of order
1040 if a.size == 0:
1041 return np.mean(a, axis=axis)
1043 dtype = a.dtype.type if a.dtype.kind in 'fc' else np.float64
1045 if moment == 0 or (moment == 1 and mean is None):
1046 # By definition the zeroth moment is always 1, and the first *central*
1047 # moment is 0.
1048 shape = list(a.shape)
1049 del shape[axis]
1051 if len(shape) == 0:
1052 return dtype(1.0 if moment == 0 else 0.0)
1053 else:
1054 return (np.ones(shape, dtype=dtype) if moment == 0
1055 else np.zeros(shape, dtype=dtype))
1056 else:
1057 # Exponentiation by squares: form exponent sequence
1058 n_list = [moment]
1059 current_n = moment
1060 while current_n > 2:
1061 if current_n % 2:
1062 current_n = (current_n - 1) / 2
1063 else:
1064 current_n /= 2
1065 n_list.append(current_n)
1067 # Starting point for exponentiation by squares
1068 mean = (a.mean(axis, keepdims=True) if mean is None
1069 else np.asarray(mean, dtype=dtype)[()])
1070 a_zero_mean = a - mean
1072 eps = np.finfo(a_zero_mean.dtype).resolution * 10
1073 with np.errstate(divide='ignore', invalid='ignore'):
1074 rel_diff = np.max(np.abs(a_zero_mean), axis=axis,
1075 keepdims=True) / np.abs(mean)
1076 with np.errstate(invalid='ignore'):
1077 precision_loss = np.any(rel_diff < eps)
1078 n = a.shape[axis] if axis is not None else a.size
1079 if precision_loss and n > 1:
1080 message = ("Precision loss occurred in moment calculation due to "
1081 "catastrophic cancellation. This occurs when the data "
1082 "are nearly identical. Results may be unreliable.")
1083 warnings.warn(message, RuntimeWarning, stacklevel=4)
1085 if n_list[-1] == 1:
1086 s = a_zero_mean.copy()
1087 else:
1088 s = a_zero_mean**2
1090 # Perform multiplications
1091 for n in n_list[-2::-1]:
1092 s = s**2
1093 if n % 2:
1094 s *= a_zero_mean
1095 return np.mean(s, axis)
1098def _var(x, axis=0, ddof=0, mean=None):
1099 # Calculate variance of sample, warning if precision is lost
1100 var = _moment(x, 2, axis, mean=mean)
1101 if ddof != 0:
1102 n = x.shape[axis] if axis is not None else x.size
1103 var *= np.divide(n, n-ddof) # to avoid error on division by zero
1104 return var
1107@_axis_nan_policy_factory(
1108 lambda x: x, result_to_tuple=lambda x: (x,), n_outputs=1
1109)
1110def skew(a, axis=0, bias=True, nan_policy='propagate'):
1111 r"""Compute the sample skewness of a data set.
1113 For normally distributed data, the skewness should be about zero. For
1114 unimodal continuous distributions, a skewness value greater than zero means
1115 that there is more weight in the right tail of the distribution. The
1116 function `skewtest` can be used to determine if the skewness value
1117 is close enough to zero, statistically speaking.
1119 Parameters
1120 ----------
1121 a : ndarray
1122 Input array.
1123 axis : int or None, optional
1124 Axis along which skewness is calculated. Default is 0.
1125 If None, compute over the whole array `a`.
1126 bias : bool, optional
1127 If False, then the calculations are corrected for statistical bias.
1128 nan_policy : {'propagate', 'raise', 'omit'}, optional
1129 Defines how to handle when input contains nan.
1130 The following options are available (default is 'propagate'):
1132 * 'propagate': returns nan
1133 * 'raise': throws an error
1134 * 'omit': performs the calculations ignoring nan values
1136 Returns
1137 -------
1138 skewness : ndarray
1139 The skewness of values along an axis, returning NaN where all values
1140 are equal.
1142 Notes
1143 -----
1144 The sample skewness is computed as the Fisher-Pearson coefficient
1145 of skewness, i.e.
1147 .. math::
1149 g_1=\frac{m_3}{m_2^{3/2}}
1151 where
1153 .. math::
1155 m_i=\frac{1}{N}\sum_{n=1}^N(x[n]-\bar{x})^i
1157 is the biased sample :math:`i\texttt{th}` central moment, and
1158 :math:`\bar{x}` is
1159 the sample mean. If ``bias`` is False, the calculations are
1160 corrected for bias and the value computed is the adjusted
1161 Fisher-Pearson standardized moment coefficient, i.e.
1163 .. math::
1165 G_1=\frac{k_3}{k_2^{3/2}}=
1166 \frac{\sqrt{N(N-1)}}{N-2}\frac{m_3}{m_2^{3/2}}.
1168 References
1169 ----------
1170 .. [1] Zwillinger, D. and Kokoska, S. (2000). CRC Standard
1171 Probability and Statistics Tables and Formulae. Chapman & Hall: New
1172 York. 2000.
1173 Section 2.2.24.1
1175 Examples
1176 --------
1177 >>> from scipy.stats import skew
1178 >>> skew([1, 2, 3, 4, 5])
1179 0.0
1180 >>> skew([2, 8, 0, 4, 1, 9, 9, 0])
1181 0.2650554122698573
1183 """
1184 a, axis = _chk_asarray(a, axis)
1185 n = a.shape[axis]
1187 contains_nan, nan_policy = _contains_nan(a, nan_policy)
1189 if contains_nan and nan_policy == 'omit':
1190 a = ma.masked_invalid(a)
1191 return mstats_basic.skew(a, axis, bias)
1193 mean = a.mean(axis, keepdims=True)
1194 m2 = _moment(a, 2, axis, mean=mean)
1195 m3 = _moment(a, 3, axis, mean=mean)
1196 with np.errstate(all='ignore'):
1197 zero = (m2 <= (np.finfo(m2.dtype).resolution * mean.squeeze(axis))**2)
1198 vals = np.where(zero, np.nan, m3 / m2**1.5)
1199 if not bias:
1200 can_correct = ~zero & (n > 2)
1201 if can_correct.any():
1202 m2 = np.extract(can_correct, m2)
1203 m3 = np.extract(can_correct, m3)
1204 nval = np.sqrt((n - 1.0) * n) / (n - 2.0) * m3 / m2**1.5
1205 np.place(vals, can_correct, nval)
1207 return vals[()]
1210@_axis_nan_policy_factory(
1211 lambda x: x, result_to_tuple=lambda x: (x,), n_outputs=1
1212)
1213def kurtosis(a, axis=0, fisher=True, bias=True, nan_policy='propagate'):
1214 """Compute the kurtosis (Fisher or Pearson) of a dataset.
1216 Kurtosis is the fourth central moment divided by the square of the
1217 variance. If Fisher's definition is used, then 3.0 is subtracted from
1218 the result to give 0.0 for a normal distribution.
1220 If bias is False then the kurtosis is calculated using k statistics to
1221 eliminate bias coming from biased moment estimators
1223 Use `kurtosistest` to see if result is close enough to normal.
1225 Parameters
1226 ----------
1227 a : array
1228 Data for which the kurtosis is calculated.
1229 axis : int or None, optional
1230 Axis along which the kurtosis is calculated. Default is 0.
1231 If None, compute over the whole array `a`.
1232 fisher : bool, optional
1233 If True, Fisher's definition is used (normal ==> 0.0). If False,
1234 Pearson's definition is used (normal ==> 3.0).
1235 bias : bool, optional
1236 If False, then the calculations are corrected for statistical bias.
1237 nan_policy : {'propagate', 'raise', 'omit'}, optional
1238 Defines how to handle when input contains nan. 'propagate' returns nan,
1239 'raise' throws an error, 'omit' performs the calculations ignoring nan
1240 values. Default is 'propagate'.
1242 Returns
1243 -------
1244 kurtosis : array
1245 The kurtosis of values along an axis, returning NaN where all values
1246 are equal.
1248 References
1249 ----------
1250 .. [1] Zwillinger, D. and Kokoska, S. (2000). CRC Standard
1251 Probability and Statistics Tables and Formulae. Chapman & Hall: New
1252 York. 2000.
1254 Examples
1255 --------
1256 In Fisher's definition, the kurtosis of the normal distribution is zero.
1257 In the following example, the kurtosis is close to zero, because it was
1258 calculated from the dataset, not from the continuous distribution.
1260 >>> import numpy as np
1261 >>> from scipy.stats import norm, kurtosis
1262 >>> data = norm.rvs(size=1000, random_state=3)
1263 >>> kurtosis(data)
1264 -0.06928694200380558
1266 The distribution with a higher kurtosis has a heavier tail.
1267 The zero valued kurtosis of the normal distribution in Fisher's definition
1268 can serve as a reference point.
1270 >>> import matplotlib.pyplot as plt
1271 >>> import scipy.stats as stats
1272 >>> from scipy.stats import kurtosis
1274 >>> x = np.linspace(-5, 5, 100)
1275 >>> ax = plt.subplot()
1276 >>> distnames = ['laplace', 'norm', 'uniform']
1278 >>> for distname in distnames:
1279 ... if distname == 'uniform':
1280 ... dist = getattr(stats, distname)(loc=-2, scale=4)
1281 ... else:
1282 ... dist = getattr(stats, distname)
1283 ... data = dist.rvs(size=1000)
1284 ... kur = kurtosis(data, fisher=True)
1285 ... y = dist.pdf(x)
1286 ... ax.plot(x, y, label="{}, {}".format(distname, round(kur, 3)))
1287 ... ax.legend()
1289 The Laplace distribution has a heavier tail than the normal distribution.
1290 The uniform distribution (which has negative kurtosis) has the thinnest
1291 tail.
1293 """
1294 a, axis = _chk_asarray(a, axis)
1296 contains_nan, nan_policy = _contains_nan(a, nan_policy)
1298 if contains_nan and nan_policy == 'omit':
1299 a = ma.masked_invalid(a)
1300 return mstats_basic.kurtosis(a, axis, fisher, bias)
1302 n = a.shape[axis]
1303 mean = a.mean(axis, keepdims=True)
1304 m2 = _moment(a, 2, axis, mean=mean)
1305 m4 = _moment(a, 4, axis, mean=mean)
1306 with np.errstate(all='ignore'):
1307 zero = (m2 <= (np.finfo(m2.dtype).resolution * mean.squeeze(axis))**2)
1308 vals = np.where(zero, np.nan, m4 / m2**2.0)
1310 if not bias:
1311 can_correct = ~zero & (n > 3)
1312 if can_correct.any():
1313 m2 = np.extract(can_correct, m2)
1314 m4 = np.extract(can_correct, m4)
1315 nval = 1.0/(n-2)/(n-3) * ((n**2-1.0)*m4/m2**2.0 - 3*(n-1)**2.0)
1316 np.place(vals, can_correct, nval + 3.0)
1318 return vals[()] - 3 if fisher else vals[()]
1321DescribeResult = namedtuple('DescribeResult',
1322 ('nobs', 'minmax', 'mean', 'variance', 'skewness',
1323 'kurtosis'))
1326def describe(a, axis=0, ddof=1, bias=True, nan_policy='propagate'):
1327 """Compute several descriptive statistics of the passed array.
1329 Parameters
1330 ----------
1331 a : array_like
1332 Input data.
1333 axis : int or None, optional
1334 Axis along which statistics are calculated. Default is 0.
1335 If None, compute over the whole array `a`.
1336 ddof : int, optional
1337 Delta degrees of freedom (only for variance). Default is 1.
1338 bias : bool, optional
1339 If False, then the skewness and kurtosis calculations are corrected
1340 for statistical bias.
1341 nan_policy : {'propagate', 'raise', 'omit'}, optional
1342 Defines how to handle when input contains nan.
1343 The following options are available (default is 'propagate'):
1345 * 'propagate': returns nan
1346 * 'raise': throws an error
1347 * 'omit': performs the calculations ignoring nan values
1349 Returns
1350 -------
1351 nobs : int or ndarray of ints
1352 Number of observations (length of data along `axis`).
1353 When 'omit' is chosen as nan_policy, the length along each axis
1354 slice is counted separately.
1355 minmax: tuple of ndarrays or floats
1356 Minimum and maximum value of `a` along the given axis.
1357 mean : ndarray or float
1358 Arithmetic mean of `a` along the given axis.
1359 variance : ndarray or float
1360 Unbiased variance of `a` along the given axis; denominator is number
1361 of observations minus one.
1362 skewness : ndarray or float
1363 Skewness of `a` along the given axis, based on moment calculations
1364 with denominator equal to the number of observations, i.e. no degrees
1365 of freedom correction.
1366 kurtosis : ndarray or float
1367 Kurtosis (Fisher) of `a` along the given axis. The kurtosis is
1368 normalized so that it is zero for the normal distribution. No
1369 degrees of freedom are used.
1371 See Also
1372 --------
1373 skew, kurtosis
1375 Examples
1376 --------
1377 >>> import numpy as np
1378 >>> from scipy import stats
1379 >>> a = np.arange(10)
1380 >>> stats.describe(a)
1381 DescribeResult(nobs=10, minmax=(0, 9), mean=4.5,
1382 variance=9.166666666666666, skewness=0.0,
1383 kurtosis=-1.2242424242424244)
1384 >>> b = [[1, 2], [3, 4]]
1385 >>> stats.describe(b)
1386 DescribeResult(nobs=2, minmax=(array([1, 2]), array([3, 4])),
1387 mean=array([2., 3.]), variance=array([2., 2.]),
1388 skewness=array([0., 0.]), kurtosis=array([-2., -2.]))
1390 """
1391 a, axis = _chk_asarray(a, axis)
1393 contains_nan, nan_policy = _contains_nan(a, nan_policy)
1395 if contains_nan and nan_policy == 'omit':
1396 a = ma.masked_invalid(a)
1397 return mstats_basic.describe(a, axis, ddof, bias)
1399 if a.size == 0:
1400 raise ValueError("The input must not be empty.")
1401 n = a.shape[axis]
1402 mm = (np.min(a, axis=axis), np.max(a, axis=axis))
1403 m = np.mean(a, axis=axis)
1404 v = _var(a, axis=axis, ddof=ddof)
1405 sk = skew(a, axis, bias=bias)
1406 kurt = kurtosis(a, axis, bias=bias)
1408 return DescribeResult(n, mm, m, v, sk, kurt)
1410#####################################
1411# NORMALITY TESTS #
1412#####################################
1415def _normtest_finish(z, alternative):
1416 """Common code between all the normality-test functions."""
1417 if alternative == 'less':
1418 prob = distributions.norm.cdf(z)
1419 elif alternative == 'greater':
1420 prob = distributions.norm.sf(z)
1421 elif alternative == 'two-sided':
1422 prob = 2 * distributions.norm.sf(np.abs(z))
1423 else:
1424 raise ValueError("alternative must be "
1425 "'less', 'greater' or 'two-sided'")
1427 if z.ndim == 0:
1428 z = z[()]
1430 return z, prob
1433SkewtestResult = namedtuple('SkewtestResult', ('statistic', 'pvalue'))
1436def skewtest(a, axis=0, nan_policy='propagate', alternative='two-sided'):
1437 r"""Test whether the skew is different from the normal distribution.
1439 This function tests the null hypothesis that the skewness of
1440 the population that the sample was drawn from is the same
1441 as that of a corresponding normal distribution.
1443 Parameters
1444 ----------
1445 a : array
1446 The data to be tested.
1447 axis : int or None, optional
1448 Axis along which statistics are calculated. Default is 0.
1449 If None, compute over the whole array `a`.
1450 nan_policy : {'propagate', 'raise', 'omit'}, optional
1451 Defines how to handle when input contains nan.
1452 The following options are available (default is 'propagate'):
1454 * 'propagate': returns nan
1455 * 'raise': throws an error
1456 * 'omit': performs the calculations ignoring nan values
1458 alternative : {'two-sided', 'less', 'greater'}, optional
1459 Defines the alternative hypothesis. Default is 'two-sided'.
1460 The following options are available:
1462 * 'two-sided': the skewness of the distribution underlying the sample
1463 is different from that of the normal distribution (i.e. 0)
1464 * 'less': the skewness of the distribution underlying the sample
1465 is less than that of the normal distribution
1466 * 'greater': the skewness of the distribution underlying the sample
1467 is greater than that of the normal distribution
1469 .. versionadded:: 1.7.0
1471 Returns
1472 -------
1473 statistic : float
1474 The computed z-score for this test.
1475 pvalue : float
1476 The p-value for the hypothesis test.
1478 Notes
1479 -----
1480 The sample size must be at least 8.
1482 References
1483 ----------
1484 .. [1] R. B. D'Agostino, A. J. Belanger and R. B. D'Agostino Jr.,
1485 "A suggestion for using powerful and informative tests of
1486 normality", American Statistician 44, pp. 316-321, 1990.
1487 .. [2] Shapiro, S. S., & Wilk, M. B. (1965). An analysis of variance test
1488 for normality (complete samples). Biometrika, 52(3/4), 591-611.
1489 .. [3] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be
1490 Zero: Calculating Exact P-values When Permutations Are Randomly
1491 Drawn." Statistical Applications in Genetics and Molecular Biology
1492 9.1 (2010).
1494 Examples
1495 --------
1496 Suppose we wish to infer from measurements whether the weights of adult
1497 human males in a medical study are not normally distributed [2]_.
1498 The weights (lbs) are recorded in the array ``x`` below.
1500 >>> import numpy as np
1501 >>> x = np.array([148, 154, 158, 160, 161, 162, 166, 170, 182, 195, 236])
1503 The skewness test from [1]_ begins by computing a statistic based on the
1504 sample skewness.
1506 >>> from scipy import stats
1507 >>> res = stats.skewtest(x)
1508 >>> res.statistic
1509 2.7788579769903414
1511 Because normal distributions have zero skewness, the magnitude of this
1512 statistic tends to be low for samples drawn from a normal distribution.
1514 The test is performed by comparing the observed value of the
1515 statistic against the null distribution: the distribution of statistic
1516 values derived under the null hypothesis that the weights were drawn from
1517 a normal distribution.
1519 For this test, the null distribution of the statistic for very large
1520 samples is the standard normal distribution.
1522 >>> import matplotlib.pyplot as plt
1523 >>> dist = stats.norm()
1524 >>> st_val = np.linspace(-5, 5, 100)
1525 >>> pdf = dist.pdf(st_val)
1526 >>> fig, ax = plt.subplots(figsize=(8, 5))
1527 >>> def st_plot(ax): # we'll re-use this
1528 ... ax.plot(st_val, pdf)
1529 ... ax.set_title("Skew Test Null Distribution")
1530 ... ax.set_xlabel("statistic")
1531 ... ax.set_ylabel("probability density")
1532 >>> st_plot(ax)
1533 >>> plt.show()
1535 The comparison is quantified by the p-value: the proportion of values in
1536 the null distribution as extreme or more extreme than the observed
1537 value of the statistic. In a two-sided test, elements of the null
1538 distribution greater than the observed statistic and elements of the null
1539 distribution less than the negative of the observed statistic are both
1540 considered "more extreme".
1542 >>> fig, ax = plt.subplots(figsize=(8, 5))
1543 >>> st_plot(ax)
1544 >>> pvalue = dist.cdf(-res.statistic) + dist.sf(res.statistic)
1545 >>> annotation = (f'p-value={pvalue:.3f}\n(shaded area)')
1546 >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8)
1547 >>> _ = ax.annotate(annotation, (3, 0.005), (3.25, 0.02), arrowprops=props)
1548 >>> i = st_val >= res.statistic
1549 >>> ax.fill_between(st_val[i], y1=0, y2=pdf[i], color='C0')
1550 >>> i = st_val <= -res.statistic
1551 >>> ax.fill_between(st_val[i], y1=0, y2=pdf[i], color='C0')
1552 >>> ax.set_xlim(-5, 5)
1553 >>> ax.set_ylim(0, 0.1)
1554 >>> plt.show()
1555 >>> res.pvalue
1556 0.005455036974740185
1558 If the p-value is "small" - that is, if there is a low probability of
1559 sampling data from a normally distributed population that produces such an
1560 extreme value of the statistic - this may be taken as evidence against
1561 the null hypothesis in favor of the alternative: the weights were not
1562 drawn from a normal distribution. Note that:
1564 - The inverse is not true; that is, the test is not used to provide
1565 evidence for the null hypothesis.
1566 - The threshold for values that will be considered "small" is a choice that
1567 should be made before the data is analyzed [3]_ with consideration of the
1568 risks of both false positives (incorrectly rejecting the null hypothesis)
1569 and false negatives (failure to reject a false null hypothesis).
1571 Note that the standard normal distribution provides an asymptotic
1572 approximation of the null distribution; it is only accurate for samples
1573 with many observations. For small samples like ours,
1574 `scipy.stats.monte_carlo_test` may provide a more accurate, albeit
1575 stochastic, approximation of the exact p-value.
1577 >>> def statistic(x, axis):
1578 ... # get just the skewtest statistic; ignore the p-value
1579 ... return stats.skewtest(x, axis=axis).statistic
1580 >>> res = stats.monte_carlo_test(x, stats.norm.rvs, statistic)
1581 >>> fig, ax = plt.subplots(figsize=(8, 5))
1582 >>> st_plot(ax)
1583 >>> ax.hist(res.null_distribution, np.linspace(-5, 5, 50),
1584 ... density=True)
1585 >>> ax.legend(['aymptotic approximation\n(many observations)',
1586 ... 'Monte Carlo approximation\n(11 observations)'])
1587 >>> plt.show()
1588 >>> res.pvalue
1589 0.0062 # may vary
1591 In this case, the asymptotic approximation and Monte Carlo approximation
1592 agree fairly closely, even for our small sample.
1594 """
1595 a, axis = _chk_asarray(a, axis)
1597 contains_nan, nan_policy = _contains_nan(a, nan_policy)
1599 if contains_nan and nan_policy == 'omit':
1600 a = ma.masked_invalid(a)
1601 return mstats_basic.skewtest(a, axis, alternative)
1603 if axis is None:
1604 a = np.ravel(a)
1605 axis = 0
1606 b2 = skew(a, axis)
1607 n = a.shape[axis]
1608 if n < 8:
1609 raise ValueError(
1610 "skewtest is not valid with less than 8 samples; %i samples"
1611 " were given." % int(n))
1612 y = b2 * math.sqrt(((n + 1) * (n + 3)) / (6.0 * (n - 2)))
1613 beta2 = (3.0 * (n**2 + 27*n - 70) * (n+1) * (n+3) /
1614 ((n-2.0) * (n+5) * (n+7) * (n+9)))
1615 W2 = -1 + math.sqrt(2 * (beta2 - 1))
1616 delta = 1 / math.sqrt(0.5 * math.log(W2))
1617 alpha = math.sqrt(2.0 / (W2 - 1))
1618 y = np.where(y == 0, 1, y)
1619 Z = delta * np.log(y / alpha + np.sqrt((y / alpha)**2 + 1))
1621 return SkewtestResult(*_normtest_finish(Z, alternative))
1624KurtosistestResult = namedtuple('KurtosistestResult', ('statistic', 'pvalue'))
1627def kurtosistest(a, axis=0, nan_policy='propagate', alternative='two-sided'):
1628 r"""Test whether a dataset has normal kurtosis.
1630 This function tests the null hypothesis that the kurtosis
1631 of the population from which the sample was drawn is that
1632 of the normal distribution.
1634 Parameters
1635 ----------
1636 a : array
1637 Array of the sample data.
1638 axis : int or None, optional
1639 Axis along which to compute test. Default is 0. If None,
1640 compute over the whole array `a`.
1641 nan_policy : {'propagate', 'raise', 'omit'}, optional
1642 Defines how to handle when input contains nan.
1643 The following options are available (default is 'propagate'):
1645 * 'propagate': returns nan
1646 * 'raise': throws an error
1647 * 'omit': performs the calculations ignoring nan values
1649 alternative : {'two-sided', 'less', 'greater'}, optional
1650 Defines the alternative hypothesis.
1651 The following options are available (default is 'two-sided'):
1653 * 'two-sided': the kurtosis of the distribution underlying the sample
1654 is different from that of the normal distribution
1655 * 'less': the kurtosis of the distribution underlying the sample
1656 is less than that of the normal distribution
1657 * 'greater': the kurtosis of the distribution underlying the sample
1658 is greater than that of the normal distribution
1660 .. versionadded:: 1.7.0
1662 Returns
1663 -------
1664 statistic : float
1665 The computed z-score for this test.
1666 pvalue : float
1667 The p-value for the hypothesis test.
1669 Notes
1670 -----
1671 Valid only for n>20. This function uses the method described in [1]_.
1673 References
1674 ----------
1675 .. [1] see e.g. F. J. Anscombe, W. J. Glynn, "Distribution of the kurtosis
1676 statistic b2 for normal samples", Biometrika, vol. 70, pp. 227-234, 1983.
1677 .. [2] Shapiro, S. S., & Wilk, M. B. (1965). An analysis of variance test
1678 for normality (complete samples). Biometrika, 52(3/4), 591-611.
1679 .. [3] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be
1680 Zero: Calculating Exact P-values When Permutations Are Randomly
1681 Drawn." Statistical Applications in Genetics and Molecular Biology
1682 9.1 (2010).
1683 .. [4] Panagiotakos, D. B. (2008). The value of p-value in biomedical
1684 research. The open cardiovascular medicine journal, 2, 97.
1686 Examples
1687 --------
1688 Suppose we wish to infer from measurements whether the weights of adult
1689 human males in a medical study are not normally distributed [2]_.
1690 The weights (lbs) are recorded in the array ``x`` below.
1692 >>> import numpy as np
1693 >>> x = np.array([148, 154, 158, 160, 161, 162, 166, 170, 182, 195, 236])
1695 The kurtosis test from [1]_ begins by computing a statistic based on the
1696 sample (excess/Fisher) kurtosis.
1698 >>> from scipy import stats
1699 >>> res = stats.kurtosistest(x)
1700 >>> res.statistic
1701 2.3048235214240873
1703 (The test warns that our sample has too few observations to perform the
1704 test. We'll return to this at the end of the example.)
1705 Because normal distributions have zero excess kurtosis (by definition),
1706 the magnitude of this statistic tends to be low for samples drawn from a
1707 normal distribution.
1709 The test is performed by comparing the observed value of the
1710 statistic against the null distribution: the distribution of statistic
1711 values derived under the null hypothesis that the weights were drawn from
1712 a normal distribution.
1714 For this test, the null distribution of the statistic for very large
1715 samples is the standard normal distribution.
1717 >>> import matplotlib.pyplot as plt
1718 >>> dist = stats.norm()
1719 >>> kt_val = np.linspace(-5, 5, 100)
1720 >>> pdf = dist.pdf(kt_val)
1721 >>> fig, ax = plt.subplots(figsize=(8, 5))
1722 >>> def kt_plot(ax): # we'll re-use this
1723 ... ax.plot(kt_val, pdf)
1724 ... ax.set_title("Kurtosis Test Null Distribution")
1725 ... ax.set_xlabel("statistic")
1726 ... ax.set_ylabel("probability density")
1727 >>> kt_plot(ax)
1728 >>> plt.show()
1730 The comparison is quantified by the p-value: the proportion of values in
1731 the null distribution as extreme or more extreme than the observed
1732 value of the statistic. In a two-sided test in which the statistic is
1733 positive, elements of the null distribution greater than the observed
1734 statistic and elements of the null distribution less than the negative of
1735 the observed statistic are both considered "more extreme".
1737 >>> fig, ax = plt.subplots(figsize=(8, 5))
1738 >>> kt_plot(ax)
1739 >>> pvalue = dist.cdf(-res.statistic) + dist.sf(res.statistic)
1740 >>> annotation = (f'p-value={pvalue:.3f}\n(shaded area)')
1741 >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8)
1742 >>> _ = ax.annotate(annotation, (3, 0.005), (3.25, 0.02), arrowprops=props)
1743 >>> i = kt_val >= res.statistic
1744 >>> ax.fill_between(kt_val[i], y1=0, y2=pdf[i], color='C0')
1745 >>> i = kt_val <= -res.statistic
1746 >>> ax.fill_between(kt_val[i], y1=0, y2=pdf[i], color='C0')
1747 >>> ax.set_xlim(-5, 5)
1748 >>> ax.set_ylim(0, 0.1)
1749 >>> plt.show()
1750 >>> res.pvalue
1751 0.0211764592113868
1753 If the p-value is "small" - that is, if there is a low probability of
1754 sampling data from a normally distributed population that produces such an
1755 extreme value of the statistic - this may be taken as evidence against
1756 the null hypothesis in favor of the alternative: the weights were not
1757 drawn from a normal distribution. Note that:
1759 - The inverse is not true; that is, the test is not used to provide
1760 evidence for the null hypothesis.
1761 - The threshold for values that will be considered "small" is a choice that
1762 should be made before the data is analyzed [3]_ with consideration of the
1763 risks of both false positives (incorrectly rejecting the null hypothesis)
1764 and false negatives (failure to reject a false null hypothesis).
1766 Note that the standard normal distribution provides an asymptotic
1767 approximation of the null distribution; it is only accurate for samples
1768 with many observations. This is the reason we received a warning at the
1769 beginning of the example; our sample is quite small. In this case,
1770 `scipy.stats.monte_carlo_test` may provide a more accurate, albeit
1771 stochastic, approximation of the exact p-value.
1773 >>> def statistic(x, axis):
1774 ... # get just the skewtest statistic; ignore the p-value
1775 ... return stats.kurtosistest(x, axis=axis).statistic
1776 >>> res = stats.monte_carlo_test(x, stats.norm.rvs, statistic)
1777 >>> fig, ax = plt.subplots(figsize=(8, 5))
1778 >>> kt_plot(ax)
1779 >>> ax.hist(res.null_distribution, np.linspace(-5, 5, 50),
1780 ... density=True)
1781 >>> ax.legend(['aymptotic approximation\n(many observations)',
1782 ... 'Monte Carlo approximation\n(11 observations)'])
1783 >>> plt.show()
1784 >>> res.pvalue
1785 0.0272 # may vary
1787 Furthermore, despite their stochastic nature, p-values computed in this way
1788 can be used to exactly control the rate of false rejections of the null
1789 hypothesis [4]_.
1791 """
1792 a, axis = _chk_asarray(a, axis)
1794 contains_nan, nan_policy = _contains_nan(a, nan_policy)
1796 if contains_nan and nan_policy == 'omit':
1797 a = ma.masked_invalid(a)
1798 return mstats_basic.kurtosistest(a, axis, alternative)
1800 n = a.shape[axis]
1801 if n < 5:
1802 raise ValueError(
1803 "kurtosistest requires at least 5 observations; %i observations"
1804 " were given." % int(n))
1805 if n < 20:
1806 warnings.warn("kurtosistest only valid for n>=20 ... continuing "
1807 "anyway, n=%i" % int(n))
1808 b2 = kurtosis(a, axis, fisher=False)
1810 E = 3.0*(n-1) / (n+1)
1811 varb2 = 24.0*n*(n-2)*(n-3) / ((n+1)*(n+1.)*(n+3)*(n+5)) # [1]_ Eq. 1
1812 x = (b2-E) / np.sqrt(varb2) # [1]_ Eq. 4
1813 # [1]_ Eq. 2:
1814 sqrtbeta1 = 6.0*(n*n-5*n+2)/((n+7)*(n+9)) * np.sqrt((6.0*(n+3)*(n+5)) /
1815 (n*(n-2)*(n-3)))
1816 # [1]_ Eq. 3:
1817 A = 6.0 + 8.0/sqrtbeta1 * (2.0/sqrtbeta1 + np.sqrt(1+4.0/(sqrtbeta1**2)))
1818 term1 = 1 - 2/(9.0*A)
1819 denom = 1 + x*np.sqrt(2/(A-4.0))
1820 term2 = np.sign(denom) * np.where(denom == 0.0, np.nan,
1821 np.power((1-2.0/A)/np.abs(denom), 1/3.0))
1822 if np.any(denom == 0):
1823 msg = "Test statistic not defined in some cases due to division by " \
1824 "zero. Return nan in that case..."
1825 warnings.warn(msg, RuntimeWarning)
1827 Z = (term1 - term2) / np.sqrt(2/(9.0*A)) # [1]_ Eq. 5
1829 # zprob uses upper tail, so Z needs to be positive
1830 return KurtosistestResult(*_normtest_finish(Z, alternative))
1833NormaltestResult = namedtuple('NormaltestResult', ('statistic', 'pvalue'))
1836def normaltest(a, axis=0, nan_policy='propagate'):
1837 r"""Test whether a sample differs from a normal distribution.
1839 This function tests the null hypothesis that a sample comes
1840 from a normal distribution. It is based on D'Agostino and
1841 Pearson's [1]_, [2]_ test that combines skew and kurtosis to
1842 produce an omnibus test of normality.
1844 Parameters
1845 ----------
1846 a : array_like
1847 The array containing the sample to be tested.
1848 axis : int or None, optional
1849 Axis along which to compute test. Default is 0. If None,
1850 compute over the whole array `a`.
1851 nan_policy : {'propagate', 'raise', 'omit'}, optional
1852 Defines how to handle when input contains nan.
1853 The following options are available (default is 'propagate'):
1855 * 'propagate': returns nan
1856 * 'raise': throws an error
1857 * 'omit': performs the calculations ignoring nan values
1859 Returns
1860 -------
1861 statistic : float or array
1862 ``s^2 + k^2``, where ``s`` is the z-score returned by `skewtest` and
1863 ``k`` is the z-score returned by `kurtosistest`.
1864 pvalue : float or array
1865 A 2-sided chi squared probability for the hypothesis test.
1867 References
1868 ----------
1869 .. [1] D'Agostino, R. B. (1971), "An omnibus test of normality for
1870 moderate and large sample size", Biometrika, 58, 341-348
1871 .. [2] D'Agostino, R. and Pearson, E. S. (1973), "Tests for departure from
1872 normality", Biometrika, 60, 613-622
1873 .. [3] Shapiro, S. S., & Wilk, M. B. (1965). An analysis of variance test
1874 for normality (complete samples). Biometrika, 52(3/4), 591-611.
1875 .. [4] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be
1876 Zero: Calculating Exact P-values When Permutations Are Randomly
1877 Drawn." Statistical Applications in Genetics and Molecular Biology
1878 9.1 (2010).
1879 .. [5] Panagiotakos, D. B. (2008). The value of p-value in biomedical
1880 research. The open cardiovascular medicine journal, 2, 97.
1882 Examples
1883 --------
1884 Suppose we wish to infer from measurements whether the weights of adult
1885 human males in a medical study are not normally distributed [3]_.
1886 The weights (lbs) are recorded in the array ``x`` below.
1888 >>> import numpy as np
1889 >>> x = np.array([148, 154, 158, 160, 161, 162, 166, 170, 182, 195, 236])
1891 The normality test of [1]_ and [2]_ begins by computing a statistic based
1892 on the sample skewness and kurtosis.
1894 >>> from scipy import stats
1895 >>> res = stats.normaltest(x)
1896 >>> res.statistic
1897 13.034263121192582
1899 (The test warns that our sample has too few observations to perform the
1900 test. We'll return to this at the end of the example.)
1901 Because the normal distribution has zero skewness and zero
1902 ("excess" or "Fisher") kurtosis, the value of this statistic tends to be
1903 low for samples drawn from a normal distribution.
1905 The test is performed by comparing the observed value of the statistic
1906 against the null distribution: the distribution of statistic values derived
1907 under the null hypothesis that the weights were drawn from a normal
1908 distribution.
1909 For this normality test, the null distribution for very large samples is
1910 the chi-squared distribution with two degrees of freedom.
1912 >>> import matplotlib.pyplot as plt
1913 >>> dist = stats.chi2(df=2)
1914 >>> stat_vals = np.linspace(0, 16, 100)
1915 >>> pdf = dist.pdf(stat_vals)
1916 >>> fig, ax = plt.subplots(figsize=(8, 5))
1917 >>> def plot(ax): # we'll re-use this
1918 ... ax.plot(stat_vals, pdf)
1919 ... ax.set_title("Normality Test Null Distribution")
1920 ... ax.set_xlabel("statistic")
1921 ... ax.set_ylabel("probability density")
1922 >>> plot(ax)
1923 >>> plt.show()
1925 The comparison is quantified by the p-value: the proportion of values in
1926 the null distribution greater than or equal to the observed value of the
1927 statistic.
1929 >>> fig, ax = plt.subplots(figsize=(8, 5))
1930 >>> plot(ax)
1931 >>> pvalue = dist.sf(res.statistic)
1932 >>> annotation = (f'p-value={pvalue:.6f}\n(shaded area)')
1933 >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8)
1934 >>> _ = ax.annotate(annotation, (13.5, 5e-4), (14, 5e-3), arrowprops=props)
1935 >>> i = stat_vals >= res.statistic # index more extreme statistic values
1936 >>> ax.fill_between(stat_vals[i], y1=0, y2=pdf[i])
1937 >>> ax.set_xlim(8, 16)
1938 >>> ax.set_ylim(0, 0.01)
1939 >>> plt.show()
1940 >>> res.pvalue
1941 0.0014779023013100172
1943 If the p-value is "small" - that is, if there is a low probability of
1944 sampling data from a normally distributed population that produces such an
1945 extreme value of the statistic - this may be taken as evidence against
1946 the null hypothesis in favor of the alternative: the weights were not
1947 drawn from a normal distribution. Note that:
1949 - The inverse is not true; that is, the test is not used to provide
1950 evidence for the null hypothesis.
1951 - The threshold for values that will be considered "small" is a choice that
1952 should be made before the data is analyzed [4]_ with consideration of the
1953 risks of both false positives (incorrectly rejecting the null hypothesis)
1954 and false negatives (failure to reject a false null hypothesis).
1956 Note that the chi-squared distribution provides an asymptotic
1957 approximation of the null distribution; it is only accurate for samples
1958 with many observations. This is the reason we received a warning at the
1959 beginning of the example; our sample is quite small. In this case,
1960 `scipy.stats.monte_carlo_test` may provide a more accurate, albeit
1961 stochastic, approximation of the exact p-value.
1963 >>> def statistic(x, axis):
1964 ... # Get only the `normaltest` statistic; ignore approximate p-value
1965 ... return stats.normaltest(x, axis=axis).statistic
1966 >>> res = stats.monte_carlo_test(x, stats.norm.rvs, statistic,
1967 ... alternative='greater')
1968 >>> fig, ax = plt.subplots(figsize=(8, 5))
1969 >>> plot(ax)
1970 >>> ax.hist(res.null_distribution, np.linspace(0, 25, 50),
1971 ... density=True)
1972 >>> ax.legend(['aymptotic approximation (many observations)',
1973 ... 'Monte Carlo approximation (11 observations)'])
1974 >>> ax.set_xlim(0, 14)
1975 >>> plt.show()
1976 >>> res.pvalue
1977 0.0082 # may vary
1979 Furthermore, despite their stochastic nature, p-values computed in this way
1980 can be used to exactly control the rate of false rejections of the null
1981 hypothesis [5]_.
1983 """
1984 a, axis = _chk_asarray(a, axis)
1986 contains_nan, nan_policy = _contains_nan(a, nan_policy)
1988 if contains_nan and nan_policy == 'omit':
1989 a = ma.masked_invalid(a)
1990 return mstats_basic.normaltest(a, axis)
1992 s, _ = skewtest(a, axis)
1993 k, _ = kurtosistest(a, axis)
1994 k2 = s*s + k*k
1996 return NormaltestResult(k2, distributions.chi2.sf(k2, 2))
1999@_axis_nan_policy_factory(SignificanceResult, default_axis=None)
2000def jarque_bera(x, *, axis=None):
2001 r"""Perform the Jarque-Bera goodness of fit test on sample data.
2003 The Jarque-Bera test tests whether the sample data has the skewness and
2004 kurtosis matching a normal distribution.
2006 Note that this test only works for a large enough number of data samples
2007 (>2000) as the test statistic asymptotically has a Chi-squared distribution
2008 with 2 degrees of freedom.
2010 Parameters
2011 ----------
2012 x : array_like
2013 Observations of a random variable.
2014 axis : int or None, default: 0
2015 If an int, the axis of the input along which to compute the statistic.
2016 The statistic of each axis-slice (e.g. row) of the input will appear in
2017 a corresponding element of the output.
2018 If ``None``, the input will be raveled before computing the statistic.
2020 Returns
2021 -------
2022 result : SignificanceResult
2023 An object with the following attributes:
2025 statistic : float
2026 The test statistic.
2027 pvalue : float
2028 The p-value for the hypothesis test.
2030 References
2031 ----------
2032 .. [1] Jarque, C. and Bera, A. (1980) "Efficient tests for normality,
2033 homoscedasticity and serial independence of regression residuals",
2034 6 Econometric Letters 255-259.
2035 .. [2] Shapiro, S. S., & Wilk, M. B. (1965). An analysis of variance test
2036 for normality (complete samples). Biometrika, 52(3/4), 591-611.
2037 .. [3] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be
2038 Zero: Calculating Exact P-values When Permutations Are Randomly
2039 Drawn." Statistical Applications in Genetics and Molecular Biology
2040 9.1 (2010).
2041 .. [4] Panagiotakos, D. B. (2008). The value of p-value in biomedical
2042 research. The open cardiovascular medicine journal, 2, 97.
2044 Examples
2045 --------
2046 Suppose we wish to infer from measurements whether the weights of adult
2047 human males in a medical study are not normally distributed [2]_.
2048 The weights (lbs) are recorded in the array ``x`` below.
2050 >>> import numpy as np
2051 >>> x = np.array([148, 154, 158, 160, 161, 162, 166, 170, 182, 195, 236])
2053 The Jarque-Bera test begins by computing a statistic based on the sample
2054 skewness and kurtosis.
2056 >>> from scipy import stats
2057 >>> res = stats.jarque_bera(x)
2058 >>> res.statistic
2059 6.982848237344646
2061 Because the normal distribution has zero skewness and zero
2062 ("excess" or "Fisher") kurtosis, the value of this statistic tends to be
2063 low for samples drawn from a normal distribution.
2065 The test is performed by comparing the observed value of the statistic
2066 against the null distribution: the distribution of statistic values derived
2067 under the null hypothesis that the weights were drawn from a normal
2068 distribution.
2069 For the Jarque-Bera test, the null distribution for very large samples is
2070 the chi-squared distribution with two degrees of freedom.
2072 >>> import matplotlib.pyplot as plt
2073 >>> dist = stats.chi2(df=2)
2074 >>> jb_val = np.linspace(0, 11, 100)
2075 >>> pdf = dist.pdf(jb_val)
2076 >>> fig, ax = plt.subplots(figsize=(8, 5))
2077 >>> def jb_plot(ax): # we'll re-use this
2078 ... ax.plot(jb_val, pdf)
2079 ... ax.set_title("Jarque-Bera Null Distribution")
2080 ... ax.set_xlabel("statistic")
2081 ... ax.set_ylabel("probability density")
2082 >>> jb_plot(ax)
2083 >>> plt.show()
2085 The comparison is quantified by the p-value: the proportion of values in
2086 the null distribution greater than or equal to the observed value of the
2087 statistic.
2089 >>> fig, ax = plt.subplots(figsize=(8, 5))
2090 >>> jb_plot(ax)
2091 >>> pvalue = dist.sf(res.statistic)
2092 >>> annotation = (f'p-value={pvalue:.6f}\n(shaded area)')
2093 >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8)
2094 >>> _ = ax.annotate(annotation, (7.5, 0.01), (8, 0.05), arrowprops=props)
2095 >>> i = jb_val >= res.statistic # indices of more extreme statistic values
2096 >>> ax.fill_between(jb_val[i], y1=0, y2=pdf[i])
2097 >>> ax.set_xlim(0, 11)
2098 >>> ax.set_ylim(0, 0.3)
2099 >>> plt.show()
2100 >>> res.pvalue
2101 0.03045746622458189
2103 If the p-value is "small" - that is, if there is a low probability of
2104 sampling data from a normally distributed population that produces such an
2105 extreme value of the statistic - this may be taken as evidence against
2106 the null hypothesis in favor of the alternative: the weights were not
2107 drawn from a normal distribution. Note that:
2109 - The inverse is not true; that is, the test is not used to provide
2110 evidence for the null hypothesis.
2111 - The threshold for values that will be considered "small" is a choice that
2112 should be made before the data is analyzed [3]_ with consideration of the
2113 risks of both false positives (incorrectly rejecting the null hypothesis)
2114 and false negatives (failure to reject a false null hypothesis).
2116 Note that the chi-squared distribution provides an asymptotic approximation
2117 of the null distribution; it is only accurate for samples with many
2118 observations. For small samples like ours, `scipy.stats.monte_carlo_test`
2119 may provide a more accurate, albeit stochastic, approximation of the
2120 exact p-value.
2122 >>> def statistic(x, axis):
2123 ... # underlying calculation of the Jarque Bera statistic
2124 ... s = stats.skew(x, axis=axis)
2125 ... k = stats.kurtosis(x, axis=axis)
2126 ... return x.shape[axis]/6 * (s**2 + k**2/4)
2127 >>> res = stats.monte_carlo_test(x, stats.norm.rvs, statistic,
2128 ... alternative='greater')
2129 >>> fig, ax = plt.subplots(figsize=(8, 5))
2130 >>> jb_plot(ax)
2131 >>> ax.hist(res.null_distribution, np.linspace(0, 10, 50),
2132 ... density=True)
2133 >>> ax.legend(['aymptotic approximation (many observations)',
2134 ... 'Monte Carlo approximation (11 observations)'])
2135 >>> plt.show()
2136 >>> res.pvalue
2137 0.0097 # may vary
2139 Furthermore, despite their stochastic nature, p-values computed in this way
2140 can be used to exactly control the rate of false rejections of the null
2141 hypothesis [4]_.
2143 """
2144 x = np.asarray(x)
2145 if axis is None:
2146 x = x.ravel()
2147 axis = 0
2149 n = x.shape[axis]
2150 if n == 0:
2151 raise ValueError('At least one observation is required.')
2153 mu = x.mean(axis=axis, keepdims=True)
2154 diffx = x - mu
2155 s = skew(diffx, axis=axis, _no_deco=True)
2156 k = kurtosis(diffx, axis=axis, _no_deco=True)
2157 statistic = n / 6 * (s**2 + k**2 / 4)
2158 pvalue = distributions.chi2.sf(statistic, df=2)
2160 return SignificanceResult(statistic, pvalue)
2163#####################################
2164# FREQUENCY FUNCTIONS #
2165#####################################
2168def scoreatpercentile(a, per, limit=(), interpolation_method='fraction',
2169 axis=None):
2170 """Calculate the score at a given percentile of the input sequence.
2172 For example, the score at `per=50` is the median. If the desired quantile
2173 lies between two data points, we interpolate between them, according to
2174 the value of `interpolation`. If the parameter `limit` is provided, it
2175 should be a tuple (lower, upper) of two values.
2177 Parameters
2178 ----------
2179 a : array_like
2180 A 1-D array of values from which to extract score.
2181 per : array_like
2182 Percentile(s) at which to extract score. Values should be in range
2183 [0,100].
2184 limit : tuple, optional
2185 Tuple of two scalars, the lower and upper limits within which to
2186 compute the percentile. Values of `a` outside
2187 this (closed) interval will be ignored.
2188 interpolation_method : {'fraction', 'lower', 'higher'}, optional
2189 Specifies the interpolation method to use,
2190 when the desired quantile lies between two data points `i` and `j`
2191 The following options are available (default is 'fraction'):
2193 * 'fraction': ``i + (j - i) * fraction`` where ``fraction`` is the
2194 fractional part of the index surrounded by ``i`` and ``j``
2195 * 'lower': ``i``
2196 * 'higher': ``j``
2198 axis : int, optional
2199 Axis along which the percentiles are computed. Default is None. If
2200 None, compute over the whole array `a`.
2202 Returns
2203 -------
2204 score : float or ndarray
2205 Score at percentile(s).
2207 See Also
2208 --------
2209 percentileofscore, numpy.percentile
2211 Notes
2212 -----
2213 This function will become obsolete in the future.
2214 For NumPy 1.9 and higher, `numpy.percentile` provides all the functionality
2215 that `scoreatpercentile` provides. And it's significantly faster.
2216 Therefore it's recommended to use `numpy.percentile` for users that have
2217 numpy >= 1.9.
2219 Examples
2220 --------
2221 >>> import numpy as np
2222 >>> from scipy import stats
2223 >>> a = np.arange(100)
2224 >>> stats.scoreatpercentile(a, 50)
2225 49.5
2227 """
2228 # adapted from NumPy's percentile function. When we require numpy >= 1.8,
2229 # the implementation of this function can be replaced by np.percentile.
2230 a = np.asarray(a)
2231 if a.size == 0:
2232 # empty array, return nan(s) with shape matching `per`
2233 if np.isscalar(per):
2234 return np.nan
2235 else:
2236 return np.full(np.asarray(per).shape, np.nan, dtype=np.float64)
2238 if limit:
2239 a = a[(limit[0] <= a) & (a <= limit[1])]
2241 sorted_ = np.sort(a, axis=axis)
2242 if axis is None:
2243 axis = 0
2245 return _compute_qth_percentile(sorted_, per, interpolation_method, axis)
2248# handle sequence of per's without calling sort multiple times
2249def _compute_qth_percentile(sorted_, per, interpolation_method, axis):
2250 if not np.isscalar(per):
2251 score = [_compute_qth_percentile(sorted_, i,
2252 interpolation_method, axis)
2253 for i in per]
2254 return np.array(score)
2256 if not (0 <= per <= 100):
2257 raise ValueError("percentile must be in the range [0, 100]")
2259 indexer = [slice(None)] * sorted_.ndim
2260 idx = per / 100. * (sorted_.shape[axis] - 1)
2262 if int(idx) != idx:
2263 # round fractional indices according to interpolation method
2264 if interpolation_method == 'lower':
2265 idx = int(np.floor(idx))
2266 elif interpolation_method == 'higher':
2267 idx = int(np.ceil(idx))
2268 elif interpolation_method == 'fraction':
2269 pass # keep idx as fraction and interpolate
2270 else:
2271 raise ValueError("interpolation_method can only be 'fraction', "
2272 "'lower' or 'higher'")
2274 i = int(idx)
2275 if i == idx:
2276 indexer[axis] = slice(i, i + 1)
2277 weights = array(1)
2278 sumval = 1.0
2279 else:
2280 indexer[axis] = slice(i, i + 2)
2281 j = i + 1
2282 weights = array([(j - idx), (idx - i)], float)
2283 wshape = [1] * sorted_.ndim
2284 wshape[axis] = 2
2285 weights.shape = wshape
2286 sumval = weights.sum()
2288 # Use np.add.reduce (== np.sum but a little faster) to coerce data type
2289 return np.add.reduce(sorted_[tuple(indexer)] * weights, axis=axis) / sumval
2292def percentileofscore(a, score, kind='rank', nan_policy='propagate'):
2293 """Compute the percentile rank of a score relative to a list of scores.
2295 A `percentileofscore` of, for example, 80% means that 80% of the
2296 scores in `a` are below the given score. In the case of gaps or
2297 ties, the exact definition depends on the optional keyword, `kind`.
2299 Parameters
2300 ----------
2301 a : array_like
2302 Array to which `score` is compared.
2303 score : array_like
2304 Scores to compute percentiles for.
2305 kind : {'rank', 'weak', 'strict', 'mean'}, optional
2306 Specifies the interpretation of the resulting score.
2307 The following options are available (default is 'rank'):
2309 * 'rank': Average percentage ranking of score. In case of multiple
2310 matches, average the percentage rankings of all matching scores.
2311 * 'weak': This kind corresponds to the definition of a cumulative
2312 distribution function. A percentileofscore of 80% means that 80%
2313 of values are less than or equal to the provided score.
2314 * 'strict': Similar to "weak", except that only values that are
2315 strictly less than the given score are counted.
2316 * 'mean': The average of the "weak" and "strict" scores, often used
2317 in testing. See https://en.wikipedia.org/wiki/Percentile_rank
2318 nan_policy : {'propagate', 'raise', 'omit'}, optional
2319 Specifies how to treat `nan` values in `a`.
2320 The following options are available (default is 'propagate'):
2322 * 'propagate': returns nan (for each value in `score`).
2323 * 'raise': throws an error
2324 * 'omit': performs the calculations ignoring nan values
2326 Returns
2327 -------
2328 pcos : float
2329 Percentile-position of score (0-100) relative to `a`.
2331 See Also
2332 --------
2333 numpy.percentile
2334 scipy.stats.scoreatpercentile, scipy.stats.rankdata
2336 Examples
2337 --------
2338 Three-quarters of the given values lie below a given score:
2340 >>> import numpy as np
2341 >>> from scipy import stats
2342 >>> stats.percentileofscore([1, 2, 3, 4], 3)
2343 75.0
2345 With multiple matches, note how the scores of the two matches, 0.6
2346 and 0.8 respectively, are averaged:
2348 >>> stats.percentileofscore([1, 2, 3, 3, 4], 3)
2349 70.0
2351 Only 2/5 values are strictly less than 3:
2353 >>> stats.percentileofscore([1, 2, 3, 3, 4], 3, kind='strict')
2354 40.0
2356 But 4/5 values are less than or equal to 3:
2358 >>> stats.percentileofscore([1, 2, 3, 3, 4], 3, kind='weak')
2359 80.0
2361 The average between the weak and the strict scores is:
2363 >>> stats.percentileofscore([1, 2, 3, 3, 4], 3, kind='mean')
2364 60.0
2366 Score arrays (of any dimensionality) are supported:
2368 >>> stats.percentileofscore([1, 2, 3, 3, 4], [2, 3])
2369 array([40., 70.])
2371 The inputs can be infinite:
2373 >>> stats.percentileofscore([-np.inf, 0, 1, np.inf], [1, 2, np.inf])
2374 array([75., 75., 100.])
2376 If `a` is empty, then the resulting percentiles are all `nan`:
2378 >>> stats.percentileofscore([], [1, 2])
2379 array([nan, nan])
2380 """
2382 a = np.asarray(a)
2383 n = len(a)
2384 score = np.asarray(score)
2386 # Nan treatment
2387 cna, npa = _contains_nan(a, nan_policy, use_summation=False)
2388 cns, nps = _contains_nan(score, nan_policy, use_summation=False)
2390 if (cna or cns) and nan_policy == 'raise':
2391 raise ValueError("The input contains nan values")
2393 if cns:
2394 # If a score is nan, then the output should be nan
2395 # (also if nan_policy is "omit", because it only applies to `a`)
2396 score = ma.masked_where(np.isnan(score), score)
2398 if cna:
2399 if nan_policy == "omit":
2400 # Don't count nans
2401 a = ma.masked_where(np.isnan(a), a)
2402 n = a.count()
2404 if nan_policy == "propagate":
2405 # All outputs should be nans
2406 n = 0
2408 # Cannot compare to empty list ==> nan
2409 if n == 0:
2410 perct = np.full_like(score, np.nan, dtype=np.float64)
2412 else:
2413 # Prepare broadcasting
2414 score = score[..., None]
2416 def count(x):
2417 return np.count_nonzero(x, -1)
2419 # Despite using masked_array to omit nan values from processing,
2420 # the CI tests on "Azure pipelines" (but not on the other CI servers)
2421 # emits warnings when there are nan values, contrarily to the purpose
2422 # of masked_arrays. As a fix, we simply suppress the warnings.
2423 with suppress_warnings() as sup:
2424 sup.filter(RuntimeWarning,
2425 "invalid value encountered in less")
2426 sup.filter(RuntimeWarning,
2427 "invalid value encountered in greater")
2429 # Main computations/logic
2430 if kind == 'rank':
2431 left = count(a < score)
2432 right = count(a <= score)
2433 plus1 = left < right
2434 perct = (left + right + plus1) * (50.0 / n)
2435 elif kind == 'strict':
2436 perct = count(a < score) * (100.0 / n)
2437 elif kind == 'weak':
2438 perct = count(a <= score) * (100.0 / n)
2439 elif kind == 'mean':
2440 left = count(a < score)
2441 right = count(a <= score)
2442 perct = (left + right) * (50.0 / n)
2443 else:
2444 raise ValueError(
2445 "kind can only be 'rank', 'strict', 'weak' or 'mean'")
2447 # Re-insert nan values
2448 perct = ma.filled(perct, np.nan)
2450 if perct.ndim == 0:
2451 return perct[()]
2452 return perct
2455HistogramResult = namedtuple('HistogramResult',
2456 ('count', 'lowerlimit', 'binsize', 'extrapoints'))
2459def _histogram(a, numbins=10, defaultlimits=None, weights=None,
2460 printextras=False):
2461 """Create a histogram.
2463 Separate the range into several bins and return the number of instances
2464 in each bin.
2466 Parameters
2467 ----------
2468 a : array_like
2469 Array of scores which will be put into bins.
2470 numbins : int, optional
2471 The number of bins to use for the histogram. Default is 10.
2472 defaultlimits : tuple (lower, upper), optional
2473 The lower and upper values for the range of the histogram.
2474 If no value is given, a range slightly larger than the range of the
2475 values in a is used. Specifically ``(a.min() - s, a.max() + s)``,
2476 where ``s = (1/2)(a.max() - a.min()) / (numbins - 1)``.
2477 weights : array_like, optional
2478 The weights for each value in `a`. Default is None, which gives each
2479 value a weight of 1.0
2480 printextras : bool, optional
2481 If True, if there are extra points (i.e. the points that fall outside
2482 the bin limits) a warning is raised saying how many of those points
2483 there are. Default is False.
2485 Returns
2486 -------
2487 count : ndarray
2488 Number of points (or sum of weights) in each bin.
2489 lowerlimit : float
2490 Lowest value of histogram, the lower limit of the first bin.
2491 binsize : float
2492 The size of the bins (all bins have the same size).
2493 extrapoints : int
2494 The number of points outside the range of the histogram.
2496 See Also
2497 --------
2498 numpy.histogram
2500 Notes
2501 -----
2502 This histogram is based on numpy's histogram but has a larger range by
2503 default if default limits is not set.
2505 """
2506 a = np.ravel(a)
2507 if defaultlimits is None:
2508 if a.size == 0:
2509 # handle empty arrays. Undetermined range, so use 0-1.
2510 defaultlimits = (0, 1)
2511 else:
2512 # no range given, so use values in `a`
2513 data_min = a.min()
2514 data_max = a.max()
2515 # Have bins extend past min and max values slightly
2516 s = (data_max - data_min) / (2. * (numbins - 1.))
2517 defaultlimits = (data_min - s, data_max + s)
2519 # use numpy's histogram method to compute bins
2520 hist, bin_edges = np.histogram(a, bins=numbins, range=defaultlimits,
2521 weights=weights)
2522 # hist are not always floats, convert to keep with old output
2523 hist = np.array(hist, dtype=float)
2524 # fixed width for bins is assumed, as numpy's histogram gives
2525 # fixed width bins for int values for 'bins'
2526 binsize = bin_edges[1] - bin_edges[0]
2527 # calculate number of extra points
2528 extrapoints = len([v for v in a
2529 if defaultlimits[0] > v or v > defaultlimits[1]])
2530 if extrapoints > 0 and printextras:
2531 warnings.warn("Points outside given histogram range = %s"
2532 % extrapoints)
2534 return HistogramResult(hist, defaultlimits[0], binsize, extrapoints)
2537CumfreqResult = namedtuple('CumfreqResult',
2538 ('cumcount', 'lowerlimit', 'binsize',
2539 'extrapoints'))
2542def cumfreq(a, numbins=10, defaultreallimits=None, weights=None):
2543 """Return a cumulative frequency histogram, using the histogram function.
2545 A cumulative histogram is a mapping that counts the cumulative number of
2546 observations in all of the bins up to the specified bin.
2548 Parameters
2549 ----------
2550 a : array_like
2551 Input array.
2552 numbins : int, optional
2553 The number of bins to use for the histogram. Default is 10.
2554 defaultreallimits : tuple (lower, upper), optional
2555 The lower and upper values for the range of the histogram.
2556 If no value is given, a range slightly larger than the range of the
2557 values in `a` is used. Specifically ``(a.min() - s, a.max() + s)``,
2558 where ``s = (1/2)(a.max() - a.min()) / (numbins - 1)``.
2559 weights : array_like, optional
2560 The weights for each value in `a`. Default is None, which gives each
2561 value a weight of 1.0
2563 Returns
2564 -------
2565 cumcount : ndarray
2566 Binned values of cumulative frequency.
2567 lowerlimit : float
2568 Lower real limit
2569 binsize : float
2570 Width of each bin.
2571 extrapoints : int
2572 Extra points.
2574 Examples
2575 --------
2576 >>> import numpy as np
2577 >>> import matplotlib.pyplot as plt
2578 >>> from scipy import stats
2579 >>> rng = np.random.default_rng()
2580 >>> x = [1, 4, 2, 1, 3, 1]
2581 >>> res = stats.cumfreq(x, numbins=4, defaultreallimits=(1.5, 5))
2582 >>> res.cumcount
2583 array([ 1., 2., 3., 3.])
2584 >>> res.extrapoints
2585 3
2587 Create a normal distribution with 1000 random values
2589 >>> samples = stats.norm.rvs(size=1000, random_state=rng)
2591 Calculate cumulative frequencies
2593 >>> res = stats.cumfreq(samples, numbins=25)
2595 Calculate space of values for x
2597 >>> x = res.lowerlimit + np.linspace(0, res.binsize*res.cumcount.size,
2598 ... res.cumcount.size)
2600 Plot histogram and cumulative histogram
2602 >>> fig = plt.figure(figsize=(10, 4))
2603 >>> ax1 = fig.add_subplot(1, 2, 1)
2604 >>> ax2 = fig.add_subplot(1, 2, 2)
2605 >>> ax1.hist(samples, bins=25)
2606 >>> ax1.set_title('Histogram')
2607 >>> ax2.bar(x, res.cumcount, width=res.binsize)
2608 >>> ax2.set_title('Cumulative histogram')
2609 >>> ax2.set_xlim([x.min(), x.max()])
2611 >>> plt.show()
2613 """
2614 h, l, b, e = _histogram(a, numbins, defaultreallimits, weights=weights)
2615 cumhist = np.cumsum(h * 1, axis=0)
2616 return CumfreqResult(cumhist, l, b, e)
2619RelfreqResult = namedtuple('RelfreqResult',
2620 ('frequency', 'lowerlimit', 'binsize',
2621 'extrapoints'))
2624def relfreq(a, numbins=10, defaultreallimits=None, weights=None):
2625 """Return a relative frequency histogram, using the histogram function.
2627 A relative frequency histogram is a mapping of the number of
2628 observations in each of the bins relative to the total of observations.
2630 Parameters
2631 ----------
2632 a : array_like
2633 Input array.
2634 numbins : int, optional
2635 The number of bins to use for the histogram. Default is 10.
2636 defaultreallimits : tuple (lower, upper), optional
2637 The lower and upper values for the range of the histogram.
2638 If no value is given, a range slightly larger than the range of the
2639 values in a is used. Specifically ``(a.min() - s, a.max() + s)``,
2640 where ``s = (1/2)(a.max() - a.min()) / (numbins - 1)``.
2641 weights : array_like, optional
2642 The weights for each value in `a`. Default is None, which gives each
2643 value a weight of 1.0
2645 Returns
2646 -------
2647 frequency : ndarray
2648 Binned values of relative frequency.
2649 lowerlimit : float
2650 Lower real limit.
2651 binsize : float
2652 Width of each bin.
2653 extrapoints : int
2654 Extra points.
2656 Examples
2657 --------
2658 >>> import numpy as np
2659 >>> import matplotlib.pyplot as plt
2660 >>> from scipy import stats
2661 >>> rng = np.random.default_rng()
2662 >>> a = np.array([2, 4, 1, 2, 3, 2])
2663 >>> res = stats.relfreq(a, numbins=4)
2664 >>> res.frequency
2665 array([ 0.16666667, 0.5 , 0.16666667, 0.16666667])
2666 >>> np.sum(res.frequency) # relative frequencies should add up to 1
2667 1.0
2669 Create a normal distribution with 1000 random values
2671 >>> samples = stats.norm.rvs(size=1000, random_state=rng)
2673 Calculate relative frequencies
2675 >>> res = stats.relfreq(samples, numbins=25)
2677 Calculate space of values for x
2679 >>> x = res.lowerlimit + np.linspace(0, res.binsize*res.frequency.size,
2680 ... res.frequency.size)
2682 Plot relative frequency histogram
2684 >>> fig = plt.figure(figsize=(5, 4))
2685 >>> ax = fig.add_subplot(1, 1, 1)
2686 >>> ax.bar(x, res.frequency, width=res.binsize)
2687 >>> ax.set_title('Relative frequency histogram')
2688 >>> ax.set_xlim([x.min(), x.max()])
2690 >>> plt.show()
2692 """
2693 a = np.asanyarray(a)
2694 h, l, b, e = _histogram(a, numbins, defaultreallimits, weights=weights)
2695 h = h / a.shape[0]
2697 return RelfreqResult(h, l, b, e)
2700#####################################
2701# VARIABILITY FUNCTIONS #
2702#####################################
2704def obrientransform(*samples):
2705 """Compute the O'Brien transform on input data (any number of arrays).
2707 Used to test for homogeneity of variance prior to running one-way stats.
2708 Each array in ``*samples`` is one level of a factor.
2709 If `f_oneway` is run on the transformed data and found significant,
2710 the variances are unequal. From Maxwell and Delaney [1]_, p.112.
2712 Parameters
2713 ----------
2714 sample1, sample2, ... : array_like
2715 Any number of arrays.
2717 Returns
2718 -------
2719 obrientransform : ndarray
2720 Transformed data for use in an ANOVA. The first dimension
2721 of the result corresponds to the sequence of transformed
2722 arrays. If the arrays given are all 1-D of the same length,
2723 the return value is a 2-D array; otherwise it is a 1-D array
2724 of type object, with each element being an ndarray.
2726 References
2727 ----------
2728 .. [1] S. E. Maxwell and H. D. Delaney, "Designing Experiments and
2729 Analyzing Data: A Model Comparison Perspective", Wadsworth, 1990.
2731 Examples
2732 --------
2733 We'll test the following data sets for differences in their variance.
2735 >>> x = [10, 11, 13, 9, 7, 12, 12, 9, 10]
2736 >>> y = [13, 21, 5, 10, 8, 14, 10, 12, 7, 15]
2738 Apply the O'Brien transform to the data.
2740 >>> from scipy.stats import obrientransform
2741 >>> tx, ty = obrientransform(x, y)
2743 Use `scipy.stats.f_oneway` to apply a one-way ANOVA test to the
2744 transformed data.
2746 >>> from scipy.stats import f_oneway
2747 >>> F, p = f_oneway(tx, ty)
2748 >>> p
2749 0.1314139477040335
2751 If we require that ``p < 0.05`` for significance, we cannot conclude
2752 that the variances are different.
2754 """
2755 TINY = np.sqrt(np.finfo(float).eps)
2757 # `arrays` will hold the transformed arguments.
2758 arrays = []
2759 sLast = None
2761 for sample in samples:
2762 a = np.asarray(sample)
2763 n = len(a)
2764 mu = np.mean(a)
2765 sq = (a - mu)**2
2766 sumsq = sq.sum()
2768 # The O'Brien transform.
2769 t = ((n - 1.5) * n * sq - 0.5 * sumsq) / ((n - 1) * (n - 2))
2771 # Check that the mean of the transformed data is equal to the
2772 # original variance.
2773 var = sumsq / (n - 1)
2774 if abs(var - np.mean(t)) > TINY:
2775 raise ValueError('Lack of convergence in obrientransform.')
2777 arrays.append(t)
2778 sLast = a.shape
2780 if sLast:
2781 for arr in arrays[:-1]:
2782 if sLast != arr.shape:
2783 return np.array(arrays, dtype=object)
2784 return np.array(arrays)
2787@_axis_nan_policy_factory(
2788 lambda x: x, result_to_tuple=lambda x: (x,), n_outputs=1, too_small=1
2789)
2790def sem(a, axis=0, ddof=1, nan_policy='propagate'):
2791 """Compute standard error of the mean.
2793 Calculate the standard error of the mean (or standard error of
2794 measurement) of the values in the input array.
2796 Parameters
2797 ----------
2798 a : array_like
2799 An array containing the values for which the standard error is
2800 returned.
2801 axis : int or None, optional
2802 Axis along which to operate. Default is 0. If None, compute over
2803 the whole array `a`.
2804 ddof : int, optional
2805 Delta degrees-of-freedom. How many degrees of freedom to adjust
2806 for bias in limited samples relative to the population estimate
2807 of variance. Defaults to 1.
2808 nan_policy : {'propagate', 'raise', 'omit'}, optional
2809 Defines how to handle when input contains nan.
2810 The following options are available (default is 'propagate'):
2812 * 'propagate': returns nan
2813 * 'raise': throws an error
2814 * 'omit': performs the calculations ignoring nan values
2816 Returns
2817 -------
2818 s : ndarray or float
2819 The standard error of the mean in the sample(s), along the input axis.
2821 Notes
2822 -----
2823 The default value for `ddof` is different to the default (0) used by other
2824 ddof containing routines, such as np.std and np.nanstd.
2826 Examples
2827 --------
2828 Find standard error along the first axis:
2830 >>> import numpy as np
2831 >>> from scipy import stats
2832 >>> a = np.arange(20).reshape(5,4)
2833 >>> stats.sem(a)
2834 array([ 2.8284, 2.8284, 2.8284, 2.8284])
2836 Find standard error across the whole array, using n degrees of freedom:
2838 >>> stats.sem(a, axis=None, ddof=0)
2839 1.2893796958227628
2841 """
2842 n = a.shape[axis]
2843 s = np.std(a, axis=axis, ddof=ddof) / np.sqrt(n)
2844 return s
2847def _isconst(x):
2848 """
2849 Check if all values in x are the same. nans are ignored.
2851 x must be a 1d array.
2853 The return value is a 1d array with length 1, so it can be used
2854 in np.apply_along_axis.
2855 """
2856 y = x[~np.isnan(x)]
2857 if y.size == 0:
2858 return np.array([True])
2859 else:
2860 return (y[0] == y).all(keepdims=True)
2863def _quiet_nanmean(x):
2864 """
2865 Compute nanmean for the 1d array x, but quietly return nan if x is all nan.
2867 The return value is a 1d array with length 1, so it can be used
2868 in np.apply_along_axis.
2869 """
2870 y = x[~np.isnan(x)]
2871 if y.size == 0:
2872 return np.array([np.nan])
2873 else:
2874 return np.mean(y, keepdims=True)
2877def _quiet_nanstd(x, ddof=0):
2878 """
2879 Compute nanstd for the 1d array x, but quietly return nan if x is all nan.
2881 The return value is a 1d array with length 1, so it can be used
2882 in np.apply_along_axis.
2883 """
2884 y = x[~np.isnan(x)]
2885 if y.size == 0:
2886 return np.array([np.nan])
2887 else:
2888 return np.std(y, keepdims=True, ddof=ddof)
2891def zscore(a, axis=0, ddof=0, nan_policy='propagate'):
2892 """
2893 Compute the z score.
2895 Compute the z score of each value in the sample, relative to the
2896 sample mean and standard deviation.
2898 Parameters
2899 ----------
2900 a : array_like
2901 An array like object containing the sample data.
2902 axis : int or None, optional
2903 Axis along which to operate. Default is 0. If None, compute over
2904 the whole array `a`.
2905 ddof : int, optional
2906 Degrees of freedom correction in the calculation of the
2907 standard deviation. Default is 0.
2908 nan_policy : {'propagate', 'raise', 'omit'}, optional
2909 Defines how to handle when input contains nan. 'propagate' returns nan,
2910 'raise' throws an error, 'omit' performs the calculations ignoring nan
2911 values. Default is 'propagate'. Note that when the value is 'omit',
2912 nans in the input also propagate to the output, but they do not affect
2913 the z-scores computed for the non-nan values.
2915 Returns
2916 -------
2917 zscore : array_like
2918 The z-scores, standardized by mean and standard deviation of
2919 input array `a`.
2921 See Also
2922 --------
2923 numpy.mean : Arithmetic average
2924 numpy.std : Arithmetic standard deviation
2925 scipy.stats.gzscore : Geometric standard score
2927 Notes
2928 -----
2929 This function preserves ndarray subclasses, and works also with
2930 matrices and masked arrays (it uses `asanyarray` instead of
2931 `asarray` for parameters).
2933 References
2934 ----------
2935 .. [1] "Standard score", *Wikipedia*,
2936 https://en.wikipedia.org/wiki/Standard_score.
2937 .. [2] Huck, S. W., Cross, T. L., Clark, S. B, "Overcoming misconceptions
2938 about Z-scores", Teaching Statistics, vol. 8, pp. 38-40, 1986
2940 Examples
2941 --------
2942 >>> import numpy as np
2943 >>> a = np.array([ 0.7972, 0.0767, 0.4383, 0.7866, 0.8091,
2944 ... 0.1954, 0.6307, 0.6599, 0.1065, 0.0508])
2945 >>> from scipy import stats
2946 >>> stats.zscore(a)
2947 array([ 1.1273, -1.247 , -0.0552, 1.0923, 1.1664, -0.8559, 0.5786,
2948 0.6748, -1.1488, -1.3324])
2950 Computing along a specified axis, using n-1 degrees of freedom
2951 (``ddof=1``) to calculate the standard deviation:
2953 >>> b = np.array([[ 0.3148, 0.0478, 0.6243, 0.4608],
2954 ... [ 0.7149, 0.0775, 0.6072, 0.9656],
2955 ... [ 0.6341, 0.1403, 0.9759, 0.4064],
2956 ... [ 0.5918, 0.6948, 0.904 , 0.3721],
2957 ... [ 0.0921, 0.2481, 0.1188, 0.1366]])
2958 >>> stats.zscore(b, axis=1, ddof=1)
2959 array([[-0.19264823, -1.28415119, 1.07259584, 0.40420358],
2960 [ 0.33048416, -1.37380874, 0.04251374, 1.00081084],
2961 [ 0.26796377, -1.12598418, 1.23283094, -0.37481053],
2962 [-0.22095197, 0.24468594, 1.19042819, -1.21416216],
2963 [-0.82780366, 1.4457416 , -0.43867764, -0.1792603 ]])
2965 An example with `nan_policy='omit'`:
2967 >>> x = np.array([[25.11, 30.10, np.nan, 32.02, 43.15],
2968 ... [14.95, 16.06, 121.25, 94.35, 29.81]])
2969 >>> stats.zscore(x, axis=1, nan_policy='omit')
2970 array([[-1.13490897, -0.37830299, nan, -0.08718406, 1.60039602],
2971 [-0.91611681, -0.89090508, 1.4983032 , 0.88731639, -0.5785977 ]])
2972 """
2973 return zmap(a, a, axis=axis, ddof=ddof, nan_policy=nan_policy)
2976def gzscore(a, *, axis=0, ddof=0, nan_policy='propagate'):
2977 """
2978 Compute the geometric standard score.
2980 Compute the geometric z score of each strictly positive value in the
2981 sample, relative to the geometric mean and standard deviation.
2982 Mathematically the geometric z score can be evaluated as::
2984 gzscore = log(a/gmu) / log(gsigma)
2986 where ``gmu`` (resp. ``gsigma``) is the geometric mean (resp. standard
2987 deviation).
2989 Parameters
2990 ----------
2991 a : array_like
2992 Sample data.
2993 axis : int or None, optional
2994 Axis along which to operate. Default is 0. If None, compute over
2995 the whole array `a`.
2996 ddof : int, optional
2997 Degrees of freedom correction in the calculation of the
2998 standard deviation. Default is 0.
2999 nan_policy : {'propagate', 'raise', 'omit'}, optional
3000 Defines how to handle when input contains nan. 'propagate' returns nan,
3001 'raise' throws an error, 'omit' performs the calculations ignoring nan
3002 values. Default is 'propagate'. Note that when the value is 'omit',
3003 nans in the input also propagate to the output, but they do not affect
3004 the geometric z scores computed for the non-nan values.
3006 Returns
3007 -------
3008 gzscore : array_like
3009 The geometric z scores, standardized by geometric mean and geometric
3010 standard deviation of input array `a`.
3012 See Also
3013 --------
3014 gmean : Geometric mean
3015 gstd : Geometric standard deviation
3016 zscore : Standard score
3018 Notes
3019 -----
3020 This function preserves ndarray subclasses, and works also with
3021 matrices and masked arrays (it uses ``asanyarray`` instead of
3022 ``asarray`` for parameters).
3024 .. versionadded:: 1.8
3026 References
3027 ----------
3028 .. [1] "Geometric standard score", *Wikipedia*,
3029 https://en.wikipedia.org/wiki/Geometric_standard_deviation#Geometric_standard_score.
3031 Examples
3032 --------
3033 Draw samples from a log-normal distribution:
3035 >>> import numpy as np
3036 >>> from scipy.stats import zscore, gzscore
3037 >>> import matplotlib.pyplot as plt
3039 >>> rng = np.random.default_rng()
3040 >>> mu, sigma = 3., 1. # mean and standard deviation
3041 >>> x = rng.lognormal(mu, sigma, size=500)
3043 Display the histogram of the samples:
3045 >>> fig, ax = plt.subplots()
3046 >>> ax.hist(x, 50)
3047 >>> plt.show()
3049 Display the histogram of the samples standardized by the classical zscore.
3050 Distribution is rescaled but its shape is unchanged.
3052 >>> fig, ax = plt.subplots()
3053 >>> ax.hist(zscore(x), 50)
3054 >>> plt.show()
3056 Demonstrate that the distribution of geometric zscores is rescaled and
3057 quasinormal:
3059 >>> fig, ax = plt.subplots()
3060 >>> ax.hist(gzscore(x), 50)
3061 >>> plt.show()
3063 """
3064 a = np.asanyarray(a)
3065 log = ma.log if isinstance(a, ma.MaskedArray) else np.log
3067 return zscore(log(a), axis=axis, ddof=ddof, nan_policy=nan_policy)
3070def zmap(scores, compare, axis=0, ddof=0, nan_policy='propagate'):
3071 """
3072 Calculate the relative z-scores.
3074 Return an array of z-scores, i.e., scores that are standardized to
3075 zero mean and unit variance, where mean and variance are calculated
3076 from the comparison array.
3078 Parameters
3079 ----------
3080 scores : array_like
3081 The input for which z-scores are calculated.
3082 compare : array_like
3083 The input from which the mean and standard deviation of the
3084 normalization are taken; assumed to have the same dimension as
3085 `scores`.
3086 axis : int or None, optional
3087 Axis over which mean and variance of `compare` are calculated.
3088 Default is 0. If None, compute over the whole array `scores`.
3089 ddof : int, optional
3090 Degrees of freedom correction in the calculation of the
3091 standard deviation. Default is 0.
3092 nan_policy : {'propagate', 'raise', 'omit'}, optional
3093 Defines how to handle the occurrence of nans in `compare`.
3094 'propagate' returns nan, 'raise' raises an exception, 'omit'
3095 performs the calculations ignoring nan values. Default is
3096 'propagate'. Note that when the value is 'omit', nans in `scores`
3097 also propagate to the output, but they do not affect the z-scores
3098 computed for the non-nan values.
3100 Returns
3101 -------
3102 zscore : array_like
3103 Z-scores, in the same shape as `scores`.
3105 Notes
3106 -----
3107 This function preserves ndarray subclasses, and works also with
3108 matrices and masked arrays (it uses `asanyarray` instead of
3109 `asarray` for parameters).
3111 Examples
3112 --------
3113 >>> from scipy.stats import zmap
3114 >>> a = [0.5, 2.0, 2.5, 3]
3115 >>> b = [0, 1, 2, 3, 4]
3116 >>> zmap(a, b)
3117 array([-1.06066017, 0. , 0.35355339, 0.70710678])
3119 """
3120 a = np.asanyarray(compare)
3122 if a.size == 0:
3123 return np.empty(a.shape)
3125 contains_nan, nan_policy = _contains_nan(a, nan_policy)
3127 if contains_nan and nan_policy == 'omit':
3128 if axis is None:
3129 mn = _quiet_nanmean(a.ravel())
3130 std = _quiet_nanstd(a.ravel(), ddof=ddof)
3131 isconst = _isconst(a.ravel())
3132 else:
3133 mn = np.apply_along_axis(_quiet_nanmean, axis, a)
3134 std = np.apply_along_axis(_quiet_nanstd, axis, a, ddof=ddof)
3135 isconst = np.apply_along_axis(_isconst, axis, a)
3136 else:
3137 mn = a.mean(axis=axis, keepdims=True)
3138 std = a.std(axis=axis, ddof=ddof, keepdims=True)
3139 if axis is None:
3140 isconst = (a.item(0) == a).all()
3141 else:
3142 isconst = (_first(a, axis) == a).all(axis=axis, keepdims=True)
3144 # Set std deviations that are 0 to 1 to avoid division by 0.
3145 std[isconst] = 1.0
3146 z = (scores - mn) / std
3147 # Set the outputs associated with a constant input to nan.
3148 z[np.broadcast_to(isconst, z.shape)] = np.nan
3149 return z
3152def gstd(a, axis=0, ddof=1):
3153 """
3154 Calculate the geometric standard deviation of an array.
3156 The geometric standard deviation describes the spread of a set of numbers
3157 where the geometric mean is preferred. It is a multiplicative factor, and
3158 so a dimensionless quantity.
3160 It is defined as the exponent of the standard deviation of ``log(a)``.
3161 Mathematically the population geometric standard deviation can be
3162 evaluated as::
3164 gstd = exp(std(log(a)))
3166 .. versionadded:: 1.3.0
3168 Parameters
3169 ----------
3170 a : array_like
3171 An array like object containing the sample data.
3172 axis : int, tuple or None, optional
3173 Axis along which to operate. Default is 0. If None, compute over
3174 the whole array `a`.
3175 ddof : int, optional
3176 Degree of freedom correction in the calculation of the
3177 geometric standard deviation. Default is 1.
3179 Returns
3180 -------
3181 gstd : ndarray or float
3182 An array of the geometric standard deviation. If `axis` is None or `a`
3183 is a 1d array a float is returned.
3185 See Also
3186 --------
3187 gmean : Geometric mean
3188 numpy.std : Standard deviation
3189 gzscore : Geometric standard score
3191 Notes
3192 -----
3193 As the calculation requires the use of logarithms the geometric standard
3194 deviation only supports strictly positive values. Any non-positive or
3195 infinite values will raise a `ValueError`.
3196 The geometric standard deviation is sometimes confused with the exponent of
3197 the standard deviation, ``exp(std(a))``. Instead the geometric standard
3198 deviation is ``exp(std(log(a)))``.
3199 The default value for `ddof` is different to the default value (0) used
3200 by other ddof containing functions, such as ``np.std`` and ``np.nanstd``.
3202 References
3203 ----------
3204 .. [1] "Geometric standard deviation", *Wikipedia*,
3205 https://en.wikipedia.org/wiki/Geometric_standard_deviation.
3206 .. [2] Kirkwood, T. B., "Geometric means and measures of dispersion",
3207 Biometrics, vol. 35, pp. 908-909, 1979
3209 Examples
3210 --------
3211 Find the geometric standard deviation of a log-normally distributed sample.
3212 Note that the standard deviation of the distribution is one, on a
3213 log scale this evaluates to approximately ``exp(1)``.
3215 >>> import numpy as np
3216 >>> from scipy.stats import gstd
3217 >>> rng = np.random.default_rng()
3218 >>> sample = rng.lognormal(mean=0, sigma=1, size=1000)
3219 >>> gstd(sample)
3220 2.810010162475324
3222 Compute the geometric standard deviation of a multidimensional array and
3223 of a given axis.
3225 >>> a = np.arange(1, 25).reshape(2, 3, 4)
3226 >>> gstd(a, axis=None)
3227 2.2944076136018947
3228 >>> gstd(a, axis=2)
3229 array([[1.82424757, 1.22436866, 1.13183117],
3230 [1.09348306, 1.07244798, 1.05914985]])
3231 >>> gstd(a, axis=(1,2))
3232 array([2.12939215, 1.22120169])
3234 The geometric standard deviation further handles masked arrays.
3236 >>> a = np.arange(1, 25).reshape(2, 3, 4)
3237 >>> ma = np.ma.masked_where(a > 16, a)
3238 >>> ma
3239 masked_array(
3240 data=[[[1, 2, 3, 4],
3241 [5, 6, 7, 8],
3242 [9, 10, 11, 12]],
3243 [[13, 14, 15, 16],
3244 [--, --, --, --],
3245 [--, --, --, --]]],
3246 mask=[[[False, False, False, False],
3247 [False, False, False, False],
3248 [False, False, False, False]],
3249 [[False, False, False, False],
3250 [ True, True, True, True],
3251 [ True, True, True, True]]],
3252 fill_value=999999)
3253 >>> gstd(ma, axis=2)
3254 masked_array(
3255 data=[[1.8242475707663655, 1.2243686572447428, 1.1318311657788478],
3256 [1.0934830582350938, --, --]],
3257 mask=[[False, False, False],
3258 [False, True, True]],
3259 fill_value=999999)
3261 """
3262 a = np.asanyarray(a)
3263 log = ma.log if isinstance(a, ma.MaskedArray) else np.log
3265 try:
3266 with warnings.catch_warnings():
3267 warnings.simplefilter("error", RuntimeWarning)
3268 return np.exp(np.std(log(a), axis=axis, ddof=ddof))
3269 except RuntimeWarning as w:
3270 if np.isinf(a).any():
3271 raise ValueError(
3272 'Infinite value encountered. The geometric standard deviation '
3273 'is defined for strictly positive values only.'
3274 ) from w
3275 a_nan = np.isnan(a)
3276 a_nan_any = a_nan.any()
3277 # exclude NaN's from negativity check, but
3278 # avoid expensive masking for arrays with no NaN
3279 if ((a_nan_any and np.less_equal(np.nanmin(a), 0)) or
3280 (not a_nan_any and np.less_equal(a, 0).any())):
3281 raise ValueError(
3282 'Non positive value encountered. The geometric standard '
3283 'deviation is defined for strictly positive values only.'
3284 ) from w
3285 elif 'Degrees of freedom <= 0 for slice' == str(w):
3286 raise ValueError(w) from w
3287 else:
3288 # Remaining warnings don't need to be exceptions.
3289 return np.exp(np.std(log(a, where=~a_nan), axis=axis, ddof=ddof))
3290 except TypeError as e:
3291 raise ValueError(
3292 'Invalid array input. The inputs could not be '
3293 'safely coerced to any supported types') from e
3296# Private dictionary initialized only once at module level
3297# See https://en.wikipedia.org/wiki/Robust_measures_of_scale
3298_scale_conversions = {'raw': 1.0,
3299 'normal': special.erfinv(0.5) * 2.0 * math.sqrt(2.0)}
3302@_axis_nan_policy_factory(
3303 lambda x: x, result_to_tuple=lambda x: (x,), n_outputs=1,
3304 default_axis=None, override={'nan_propagation': False}
3305)
3306def iqr(x, axis=None, rng=(25, 75), scale=1.0, nan_policy='propagate',
3307 interpolation='linear', keepdims=False):
3308 r"""
3309 Compute the interquartile range of the data along the specified axis.
3311 The interquartile range (IQR) is the difference between the 75th and
3312 25th percentile of the data. It is a measure of the dispersion
3313 similar to standard deviation or variance, but is much more robust
3314 against outliers [2]_.
3316 The ``rng`` parameter allows this function to compute other
3317 percentile ranges than the actual IQR. For example, setting
3318 ``rng=(0, 100)`` is equivalent to `numpy.ptp`.
3320 The IQR of an empty array is `np.nan`.
3322 .. versionadded:: 0.18.0
3324 Parameters
3325 ----------
3326 x : array_like
3327 Input array or object that can be converted to an array.
3328 axis : int or sequence of int, optional
3329 Axis along which the range is computed. The default is to
3330 compute the IQR for the entire array.
3331 rng : Two-element sequence containing floats in range of [0,100] optional
3332 Percentiles over which to compute the range. Each must be
3333 between 0 and 100, inclusive. The default is the true IQR:
3334 ``(25, 75)``. The order of the elements is not important.
3335 scale : scalar or str, optional
3336 The numerical value of scale will be divided out of the final
3337 result. The following string values are recognized:
3339 * 'raw' : No scaling, just return the raw IQR.
3340 **Deprecated!** Use ``scale=1`` instead.
3341 * 'normal' : Scale by
3342 :math:`2 \sqrt{2} erf^{-1}(\frac{1}{2}) \approx 1.349`.
3344 The default is 1.0. The use of ``scale='raw'`` is deprecated infavor
3345 of ``scale=1`` and will raise an error in SciPy 1.12.0.
3346 Array-like `scale` is also allowed, as long
3347 as it broadcasts correctly to the output such that
3348 ``out / scale`` is a valid operation. The output dimensions
3349 depend on the input array, `x`, the `axis` argument, and the
3350 `keepdims` flag.
3351 nan_policy : {'propagate', 'raise', 'omit'}, optional
3352 Defines how to handle when input contains nan.
3353 The following options are available (default is 'propagate'):
3355 * 'propagate': returns nan
3356 * 'raise': throws an error
3357 * 'omit': performs the calculations ignoring nan values
3358 interpolation : str, optional
3360 Specifies the interpolation method to use when the percentile
3361 boundaries lie between two data points ``i`` and ``j``.
3362 The following options are available (default is 'linear'):
3364 * 'linear': ``i + (j - i)*fraction``, where ``fraction`` is the
3365 fractional part of the index surrounded by ``i`` and ``j``.
3366 * 'lower': ``i``.
3367 * 'higher': ``j``.
3368 * 'nearest': ``i`` or ``j`` whichever is nearest.
3369 * 'midpoint': ``(i + j)/2``.
3371 For NumPy >= 1.22.0, the additional options provided by the ``method``
3372 keyword of `numpy.percentile` are also valid.
3374 keepdims : bool, optional
3375 If this is set to True, the reduced axes are left in the
3376 result as dimensions with size one. With this option, the result
3377 will broadcast correctly against the original array `x`.
3379 Returns
3380 -------
3381 iqr : scalar or ndarray
3382 If ``axis=None``, a scalar is returned. If the input contains
3383 integers or floats of smaller precision than ``np.float64``, then the
3384 output data-type is ``np.float64``. Otherwise, the output data-type is
3385 the same as that of the input.
3387 See Also
3388 --------
3389 numpy.std, numpy.var
3391 References
3392 ----------
3393 .. [1] "Interquartile range" https://en.wikipedia.org/wiki/Interquartile_range
3394 .. [2] "Robust measures of scale" https://en.wikipedia.org/wiki/Robust_measures_of_scale
3395 .. [3] "Quantile" https://en.wikipedia.org/wiki/Quantile
3397 Examples
3398 --------
3399 >>> import numpy as np
3400 >>> from scipy.stats import iqr
3401 >>> x = np.array([[10, 7, 4], [3, 2, 1]])
3402 >>> x
3403 array([[10, 7, 4],
3404 [ 3, 2, 1]])
3405 >>> iqr(x)
3406 4.0
3407 >>> iqr(x, axis=0)
3408 array([ 3.5, 2.5, 1.5])
3409 >>> iqr(x, axis=1)
3410 array([ 3., 1.])
3411 >>> iqr(x, axis=1, keepdims=True)
3412 array([[ 3.],
3413 [ 1.]])
3415 """
3416 x = asarray(x)
3418 # This check prevents percentile from raising an error later. Also, it is
3419 # consistent with `np.var` and `np.std`.
3420 if not x.size:
3421 return _get_nan(x)
3423 # An error may be raised here, so fail-fast, before doing lengthy
3424 # computations, even though `scale` is not used until later
3425 if isinstance(scale, str):
3426 scale_key = scale.lower()
3427 if scale_key not in _scale_conversions:
3428 raise ValueError(f"{scale} not a valid scale for `iqr`")
3429 if scale_key == 'raw':
3430 msg = ("The use of 'scale=\"raw\"' is deprecated infavor of "
3431 "'scale=1' and will raise an error in SciPy 1.12.0.")
3432 warnings.warn(msg, DeprecationWarning, stacklevel=2)
3433 scale = _scale_conversions[scale_key]
3435 # Select the percentile function to use based on nans and policy
3436 contains_nan, nan_policy = _contains_nan(x, nan_policy)
3438 if contains_nan and nan_policy == 'omit':
3439 percentile_func = np.nanpercentile
3440 else:
3441 percentile_func = np.percentile
3443 if len(rng) != 2:
3444 raise TypeError("quantile range must be two element sequence")
3446 if np.isnan(rng).any():
3447 raise ValueError("range must not contain NaNs")
3449 rng = sorted(rng)
3450 if NumpyVersion(np.__version__) >= '1.22.0':
3451 pct = percentile_func(x, rng, axis=axis, method=interpolation,
3452 keepdims=keepdims)
3453 else:
3454 pct = percentile_func(x, rng, axis=axis, interpolation=interpolation,
3455 keepdims=keepdims)
3456 out = np.subtract(pct[1], pct[0])
3458 if scale != 1.0:
3459 out /= scale
3461 return out
3464def _mad_1d(x, center, nan_policy):
3465 # Median absolute deviation for 1-d array x.
3466 # This is a helper function for `median_abs_deviation`; it assumes its
3467 # arguments have been validated already. In particular, x must be a
3468 # 1-d numpy array, center must be callable, and if nan_policy is not
3469 # 'propagate', it is assumed to be 'omit', because 'raise' is handled
3470 # in `median_abs_deviation`.
3471 # No warning is generated if x is empty or all nan.
3472 isnan = np.isnan(x)
3473 if isnan.any():
3474 if nan_policy == 'propagate':
3475 return np.nan
3476 x = x[~isnan]
3477 if x.size == 0:
3478 # MAD of an empty array is nan.
3479 return np.nan
3480 # Edge cases have been handled, so do the basic MAD calculation.
3481 med = center(x)
3482 mad = np.median(np.abs(x - med))
3483 return mad
3486def median_abs_deviation(x, axis=0, center=np.median, scale=1.0,
3487 nan_policy='propagate'):
3488 r"""
3489 Compute the median absolute deviation of the data along the given axis.
3491 The median absolute deviation (MAD, [1]_) computes the median over the
3492 absolute deviations from the median. It is a measure of dispersion
3493 similar to the standard deviation but more robust to outliers [2]_.
3495 The MAD of an empty array is ``np.nan``.
3497 .. versionadded:: 1.5.0
3499 Parameters
3500 ----------
3501 x : array_like
3502 Input array or object that can be converted to an array.
3503 axis : int or None, optional
3504 Axis along which the range is computed. Default is 0. If None, compute
3505 the MAD over the entire array.
3506 center : callable, optional
3507 A function that will return the central value. The default is to use
3508 np.median. Any user defined function used will need to have the
3509 function signature ``func(arr, axis)``.
3510 scale : scalar or str, optional
3511 The numerical value of scale will be divided out of the final
3512 result. The default is 1.0. The string "normal" is also accepted,
3513 and results in `scale` being the inverse of the standard normal
3514 quantile function at 0.75, which is approximately 0.67449.
3515 Array-like scale is also allowed, as long as it broadcasts correctly
3516 to the output such that ``out / scale`` is a valid operation. The
3517 output dimensions depend on the input array, `x`, and the `axis`
3518 argument.
3519 nan_policy : {'propagate', 'raise', 'omit'}, optional
3520 Defines how to handle when input contains nan.
3521 The following options are available (default is 'propagate'):
3523 * 'propagate': returns nan
3524 * 'raise': throws an error
3525 * 'omit': performs the calculations ignoring nan values
3527 Returns
3528 -------
3529 mad : scalar or ndarray
3530 If ``axis=None``, a scalar is returned. If the input contains
3531 integers or floats of smaller precision than ``np.float64``, then the
3532 output data-type is ``np.float64``. Otherwise, the output data-type is
3533 the same as that of the input.
3535 See Also
3536 --------
3537 numpy.std, numpy.var, numpy.median, scipy.stats.iqr, scipy.stats.tmean,
3538 scipy.stats.tstd, scipy.stats.tvar
3540 Notes
3541 -----
3542 The `center` argument only affects the calculation of the central value
3543 around which the MAD is calculated. That is, passing in ``center=np.mean``
3544 will calculate the MAD around the mean - it will not calculate the *mean*
3545 absolute deviation.
3547 The input array may contain `inf`, but if `center` returns `inf`, the
3548 corresponding MAD for that data will be `nan`.
3550 References
3551 ----------
3552 .. [1] "Median absolute deviation",
3553 https://en.wikipedia.org/wiki/Median_absolute_deviation
3554 .. [2] "Robust measures of scale",
3555 https://en.wikipedia.org/wiki/Robust_measures_of_scale
3557 Examples
3558 --------
3559 When comparing the behavior of `median_abs_deviation` with ``np.std``,
3560 the latter is affected when we change a single value of an array to have an
3561 outlier value while the MAD hardly changes:
3563 >>> import numpy as np
3564 >>> from scipy import stats
3565 >>> x = stats.norm.rvs(size=100, scale=1, random_state=123456)
3566 >>> x.std()
3567 0.9973906394005013
3568 >>> stats.median_abs_deviation(x)
3569 0.82832610097857
3570 >>> x[0] = 345.6
3571 >>> x.std()
3572 34.42304872314415
3573 >>> stats.median_abs_deviation(x)
3574 0.8323442311590675
3576 Axis handling example:
3578 >>> x = np.array([[10, 7, 4], [3, 2, 1]])
3579 >>> x
3580 array([[10, 7, 4],
3581 [ 3, 2, 1]])
3582 >>> stats.median_abs_deviation(x)
3583 array([3.5, 2.5, 1.5])
3584 >>> stats.median_abs_deviation(x, axis=None)
3585 2.0
3587 Scale normal example:
3589 >>> x = stats.norm.rvs(size=1000000, scale=2, random_state=123456)
3590 >>> stats.median_abs_deviation(x)
3591 1.3487398527041636
3592 >>> stats.median_abs_deviation(x, scale='normal')
3593 1.9996446978061115
3595 """
3596 if not callable(center):
3597 raise TypeError("The argument 'center' must be callable. The given "
3598 f"value {repr(center)} is not callable.")
3600 # An error may be raised here, so fail-fast, before doing lengthy
3601 # computations, even though `scale` is not used until later
3602 if isinstance(scale, str):
3603 if scale.lower() == 'normal':
3604 scale = 0.6744897501960817 # special.ndtri(0.75)
3605 else:
3606 raise ValueError(f"{scale} is not a valid scale value.")
3608 x = asarray(x)
3610 # Consistent with `np.var` and `np.std`.
3611 if not x.size:
3612 if axis is None:
3613 return np.nan
3614 nan_shape = tuple(item for i, item in enumerate(x.shape) if i != axis)
3615 if nan_shape == ():
3616 # Return nan, not array(nan)
3617 return np.nan
3618 return np.full(nan_shape, np.nan)
3620 contains_nan, nan_policy = _contains_nan(x, nan_policy)
3622 if contains_nan:
3623 if axis is None:
3624 mad = _mad_1d(x.ravel(), center, nan_policy)
3625 else:
3626 mad = np.apply_along_axis(_mad_1d, axis, x, center, nan_policy)
3627 else:
3628 if axis is None:
3629 med = center(x, axis=None)
3630 mad = np.median(np.abs(x - med))
3631 else:
3632 # Wrap the call to center() in expand_dims() so it acts like
3633 # keepdims=True was used.
3634 med = np.expand_dims(center(x, axis=axis), axis)
3635 mad = np.median(np.abs(x - med), axis=axis)
3637 return mad / scale
3640#####################################
3641# TRIMMING FUNCTIONS #
3642#####################################
3645SigmaclipResult = namedtuple('SigmaclipResult', ('clipped', 'lower', 'upper'))
3648def sigmaclip(a, low=4., high=4.):
3649 """Perform iterative sigma-clipping of array elements.
3651 Starting from the full sample, all elements outside the critical range are
3652 removed, i.e. all elements of the input array `c` that satisfy either of
3653 the following conditions::
3655 c < mean(c) - std(c)*low
3656 c > mean(c) + std(c)*high
3658 The iteration continues with the updated sample until no
3659 elements are outside the (updated) range.
3661 Parameters
3662 ----------
3663 a : array_like
3664 Data array, will be raveled if not 1-D.
3665 low : float, optional
3666 Lower bound factor of sigma clipping. Default is 4.
3667 high : float, optional
3668 Upper bound factor of sigma clipping. Default is 4.
3670 Returns
3671 -------
3672 clipped : ndarray
3673 Input array with clipped elements removed.
3674 lower : float
3675 Lower threshold value use for clipping.
3676 upper : float
3677 Upper threshold value use for clipping.
3679 Examples
3680 --------
3681 >>> import numpy as np
3682 >>> from scipy.stats import sigmaclip
3683 >>> a = np.concatenate((np.linspace(9.5, 10.5, 31),
3684 ... np.linspace(0, 20, 5)))
3685 >>> fact = 1.5
3686 >>> c, low, upp = sigmaclip(a, fact, fact)
3687 >>> c
3688 array([ 9.96666667, 10. , 10.03333333, 10. ])
3689 >>> c.var(), c.std()
3690 (0.00055555555555555165, 0.023570226039551501)
3691 >>> low, c.mean() - fact*c.std(), c.min()
3692 (9.9646446609406727, 9.9646446609406727, 9.9666666666666668)
3693 >>> upp, c.mean() + fact*c.std(), c.max()
3694 (10.035355339059327, 10.035355339059327, 10.033333333333333)
3696 >>> a = np.concatenate((np.linspace(9.5, 10.5, 11),
3697 ... np.linspace(-100, -50, 3)))
3698 >>> c, low, upp = sigmaclip(a, 1.8, 1.8)
3699 >>> (c == np.linspace(9.5, 10.5, 11)).all()
3700 True
3702 """
3703 c = np.asarray(a).ravel()
3704 delta = 1
3705 while delta:
3706 c_std = c.std()
3707 c_mean = c.mean()
3708 size = c.size
3709 critlower = c_mean - c_std * low
3710 critupper = c_mean + c_std * high
3711 c = c[(c >= critlower) & (c <= critupper)]
3712 delta = size - c.size
3714 return SigmaclipResult(c, critlower, critupper)
3717def trimboth(a, proportiontocut, axis=0):
3718 """Slice off a proportion of items from both ends of an array.
3720 Slice off the passed proportion of items from both ends of the passed
3721 array (i.e., with `proportiontocut` = 0.1, slices leftmost 10% **and**
3722 rightmost 10% of scores). The trimmed values are the lowest and
3723 highest ones.
3724 Slice off less if proportion results in a non-integer slice index (i.e.
3725 conservatively slices off `proportiontocut`).
3727 Parameters
3728 ----------
3729 a : array_like
3730 Data to trim.
3731 proportiontocut : float
3732 Proportion (in range 0-1) of total data set to trim of each end.
3733 axis : int or None, optional
3734 Axis along which to trim data. Default is 0. If None, compute over
3735 the whole array `a`.
3737 Returns
3738 -------
3739 out : ndarray
3740 Trimmed version of array `a`. The order of the trimmed content
3741 is undefined.
3743 See Also
3744 --------
3745 trim_mean
3747 Examples
3748 --------
3749 Create an array of 10 values and trim 10% of those values from each end:
3751 >>> import numpy as np
3752 >>> from scipy import stats
3753 >>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
3754 >>> stats.trimboth(a, 0.1)
3755 array([1, 3, 2, 4, 5, 6, 7, 8])
3757 Note that the elements of the input array are trimmed by value, but the
3758 output array is not necessarily sorted.
3760 The proportion to trim is rounded down to the nearest integer. For
3761 instance, trimming 25% of the values from each end of an array of 10
3762 values will return an array of 6 values:
3764 >>> b = np.arange(10)
3765 >>> stats.trimboth(b, 1/4).shape
3766 (6,)
3768 Multidimensional arrays can be trimmed along any axis or across the entire
3769 array:
3771 >>> c = [2, 4, 6, 8, 0, 1, 3, 5, 7, 9]
3772 >>> d = np.array([a, b, c])
3773 >>> stats.trimboth(d, 0.4, axis=0).shape
3774 (1, 10)
3775 >>> stats.trimboth(d, 0.4, axis=1).shape
3776 (3, 2)
3777 >>> stats.trimboth(d, 0.4, axis=None).shape
3778 (6,)
3780 """
3781 a = np.asarray(a)
3783 if a.size == 0:
3784 return a
3786 if axis is None:
3787 a = a.ravel()
3788 axis = 0
3790 nobs = a.shape[axis]
3791 lowercut = int(proportiontocut * nobs)
3792 uppercut = nobs - lowercut
3793 if (lowercut >= uppercut):
3794 raise ValueError("Proportion too big.")
3796 atmp = np.partition(a, (lowercut, uppercut - 1), axis)
3798 sl = [slice(None)] * atmp.ndim
3799 sl[axis] = slice(lowercut, uppercut)
3800 return atmp[tuple(sl)]
3803def trim1(a, proportiontocut, tail='right', axis=0):
3804 """Slice off a proportion from ONE end of the passed array distribution.
3806 If `proportiontocut` = 0.1, slices off 'leftmost' or 'rightmost'
3807 10% of scores. The lowest or highest values are trimmed (depending on
3808 the tail).
3809 Slice off less if proportion results in a non-integer slice index
3810 (i.e. conservatively slices off `proportiontocut` ).
3812 Parameters
3813 ----------
3814 a : array_like
3815 Input array.
3816 proportiontocut : float
3817 Fraction to cut off of 'left' or 'right' of distribution.
3818 tail : {'left', 'right'}, optional
3819 Defaults to 'right'.
3820 axis : int or None, optional
3821 Axis along which to trim data. Default is 0. If None, compute over
3822 the whole array `a`.
3824 Returns
3825 -------
3826 trim1 : ndarray
3827 Trimmed version of array `a`. The order of the trimmed content is
3828 undefined.
3830 Examples
3831 --------
3832 Create an array of 10 values and trim 20% of its lowest values:
3834 >>> import numpy as np
3835 >>> from scipy import stats
3836 >>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
3837 >>> stats.trim1(a, 0.2, 'left')
3838 array([2, 4, 3, 5, 6, 7, 8, 9])
3840 Note that the elements of the input array are trimmed by value, but the
3841 output array is not necessarily sorted.
3843 The proportion to trim is rounded down to the nearest integer. For
3844 instance, trimming 25% of the values from an array of 10 values will
3845 return an array of 8 values:
3847 >>> b = np.arange(10)
3848 >>> stats.trim1(b, 1/4).shape
3849 (8,)
3851 Multidimensional arrays can be trimmed along any axis or across the entire
3852 array:
3854 >>> c = [2, 4, 6, 8, 0, 1, 3, 5, 7, 9]
3855 >>> d = np.array([a, b, c])
3856 >>> stats.trim1(d, 0.8, axis=0).shape
3857 (1, 10)
3858 >>> stats.trim1(d, 0.8, axis=1).shape
3859 (3, 2)
3860 >>> stats.trim1(d, 0.8, axis=None).shape
3861 (6,)
3863 """
3864 a = np.asarray(a)
3865 if axis is None:
3866 a = a.ravel()
3867 axis = 0
3869 nobs = a.shape[axis]
3871 # avoid possible corner case
3872 if proportiontocut >= 1:
3873 return []
3875 if tail.lower() == 'right':
3876 lowercut = 0
3877 uppercut = nobs - int(proportiontocut * nobs)
3879 elif tail.lower() == 'left':
3880 lowercut = int(proportiontocut * nobs)
3881 uppercut = nobs
3883 atmp = np.partition(a, (lowercut, uppercut - 1), axis)
3885 sl = [slice(None)] * atmp.ndim
3886 sl[axis] = slice(lowercut, uppercut)
3887 return atmp[tuple(sl)]
3890def trim_mean(a, proportiontocut, axis=0):
3891 """Return mean of array after trimming distribution from both tails.
3893 If `proportiontocut` = 0.1, slices off 'leftmost' and 'rightmost' 10% of
3894 scores. The input is sorted before slicing. Slices off less if proportion
3895 results in a non-integer slice index (i.e., conservatively slices off
3896 `proportiontocut` ).
3898 Parameters
3899 ----------
3900 a : array_like
3901 Input array.
3902 proportiontocut : float
3903 Fraction to cut off of both tails of the distribution.
3904 axis : int or None, optional
3905 Axis along which the trimmed means are computed. Default is 0.
3906 If None, compute over the whole array `a`.
3908 Returns
3909 -------
3910 trim_mean : ndarray
3911 Mean of trimmed array.
3913 See Also
3914 --------
3915 trimboth
3916 tmean : Compute the trimmed mean ignoring values outside given `limits`.
3918 Examples
3919 --------
3920 >>> import numpy as np
3921 >>> from scipy import stats
3922 >>> x = np.arange(20)
3923 >>> stats.trim_mean(x, 0.1)
3924 9.5
3925 >>> x2 = x.reshape(5, 4)
3926 >>> x2
3927 array([[ 0, 1, 2, 3],
3928 [ 4, 5, 6, 7],
3929 [ 8, 9, 10, 11],
3930 [12, 13, 14, 15],
3931 [16, 17, 18, 19]])
3932 >>> stats.trim_mean(x2, 0.25)
3933 array([ 8., 9., 10., 11.])
3934 >>> stats.trim_mean(x2, 0.25, axis=1)
3935 array([ 1.5, 5.5, 9.5, 13.5, 17.5])
3937 """
3938 a = np.asarray(a)
3940 if a.size == 0:
3941 return np.nan
3943 if axis is None:
3944 a = a.ravel()
3945 axis = 0
3947 nobs = a.shape[axis]
3948 lowercut = int(proportiontocut * nobs)
3949 uppercut = nobs - lowercut
3950 if (lowercut > uppercut):
3951 raise ValueError("Proportion too big.")
3953 atmp = np.partition(a, (lowercut, uppercut - 1), axis)
3955 sl = [slice(None)] * atmp.ndim
3956 sl[axis] = slice(lowercut, uppercut)
3957 return np.mean(atmp[tuple(sl)], axis=axis)
3960F_onewayResult = namedtuple('F_onewayResult', ('statistic', 'pvalue'))
3963def _create_f_oneway_nan_result(shape, axis):
3964 """
3965 This is a helper function for f_oneway for creating the return values
3966 in certain degenerate conditions. It creates return values that are
3967 all nan with the appropriate shape for the given `shape` and `axis`.
3968 """
3969 axis = np.core.multiarray.normalize_axis_index(axis, len(shape))
3970 shp = shape[:axis] + shape[axis+1:]
3971 if shp == ():
3972 f = np.nan
3973 prob = np.nan
3974 else:
3975 f = np.full(shp, fill_value=np.nan)
3976 prob = f.copy()
3977 return F_onewayResult(f, prob)
3980def _first(arr, axis):
3981 """Return arr[..., 0:1, ...] where 0:1 is in the `axis` position."""
3982 return np.take_along_axis(arr, np.array(0, ndmin=arr.ndim), axis)
3985def f_oneway(*samples, axis=0):
3986 """Perform one-way ANOVA.
3988 The one-way ANOVA tests the null hypothesis that two or more groups have
3989 the same population mean. The test is applied to samples from two or
3990 more groups, possibly with differing sizes.
3992 Parameters
3993 ----------
3994 sample1, sample2, ... : array_like
3995 The sample measurements for each group. There must be at least
3996 two arguments. If the arrays are multidimensional, then all the
3997 dimensions of the array must be the same except for `axis`.
3998 axis : int, optional
3999 Axis of the input arrays along which the test is applied.
4000 Default is 0.
4002 Returns
4003 -------
4004 statistic : float
4005 The computed F statistic of the test.
4006 pvalue : float
4007 The associated p-value from the F distribution.
4009 Warns
4010 -----
4011 `~scipy.stats.ConstantInputWarning`
4012 Raised if all values within each of the input arrays are identical.
4013 In this case the F statistic is either infinite or isn't defined,
4014 so ``np.inf`` or ``np.nan`` is returned.
4016 `~scipy.stats.DegenerateDataWarning`
4017 Raised if the length of any input array is 0, or if all the input
4018 arrays have length 1. ``np.nan`` is returned for the F statistic
4019 and the p-value in these cases.
4021 Notes
4022 -----
4023 The ANOVA test has important assumptions that must be satisfied in order
4024 for the associated p-value to be valid.
4026 1. The samples are independent.
4027 2. Each sample is from a normally distributed population.
4028 3. The population standard deviations of the groups are all equal. This
4029 property is known as homoscedasticity.
4031 If these assumptions are not true for a given set of data, it may still
4032 be possible to use the Kruskal-Wallis H-test (`scipy.stats.kruskal`) or
4033 the Alexander-Govern test (`scipy.stats.alexandergovern`) although with
4034 some loss of power.
4036 The length of each group must be at least one, and there must be at
4037 least one group with length greater than one. If these conditions
4038 are not satisfied, a warning is generated and (``np.nan``, ``np.nan``)
4039 is returned.
4041 If all values in each group are identical, and there exist at least two
4042 groups with different values, the function generates a warning and
4043 returns (``np.inf``, 0).
4045 If all values in all groups are the same, function generates a warning
4046 and returns (``np.nan``, ``np.nan``).
4048 The algorithm is from Heiman [2]_, pp.394-7.
4050 References
4051 ----------
4052 .. [1] R. Lowry, "Concepts and Applications of Inferential Statistics",
4053 Chapter 14, 2014, http://vassarstats.net/textbook/
4055 .. [2] G.W. Heiman, "Understanding research methods and statistics: An
4056 integrated introduction for psychology", Houghton, Mifflin and
4057 Company, 2001.
4059 .. [3] G.H. McDonald, "Handbook of Biological Statistics", One-way ANOVA.
4060 http://www.biostathandbook.com/onewayanova.html
4062 Examples
4063 --------
4064 >>> import numpy as np
4065 >>> from scipy.stats import f_oneway
4067 Here are some data [3]_ on a shell measurement (the length of the anterior
4068 adductor muscle scar, standardized by dividing by length) in the mussel
4069 Mytilus trossulus from five locations: Tillamook, Oregon; Newport, Oregon;
4070 Petersburg, Alaska; Magadan, Russia; and Tvarminne, Finland, taken from a
4071 much larger data set used in McDonald et al. (1991).
4073 >>> tillamook = [0.0571, 0.0813, 0.0831, 0.0976, 0.0817, 0.0859, 0.0735,
4074 ... 0.0659, 0.0923, 0.0836]
4075 >>> newport = [0.0873, 0.0662, 0.0672, 0.0819, 0.0749, 0.0649, 0.0835,
4076 ... 0.0725]
4077 >>> petersburg = [0.0974, 0.1352, 0.0817, 0.1016, 0.0968, 0.1064, 0.105]
4078 >>> magadan = [0.1033, 0.0915, 0.0781, 0.0685, 0.0677, 0.0697, 0.0764,
4079 ... 0.0689]
4080 >>> tvarminne = [0.0703, 0.1026, 0.0956, 0.0973, 0.1039, 0.1045]
4081 >>> f_oneway(tillamook, newport, petersburg, magadan, tvarminne)
4082 F_onewayResult(statistic=7.121019471642447, pvalue=0.0002812242314534544)
4084 `f_oneway` accepts multidimensional input arrays. When the inputs
4085 are multidimensional and `axis` is not given, the test is performed
4086 along the first axis of the input arrays. For the following data, the
4087 test is performed three times, once for each column.
4089 >>> a = np.array([[9.87, 9.03, 6.81],
4090 ... [7.18, 8.35, 7.00],
4091 ... [8.39, 7.58, 7.68],
4092 ... [7.45, 6.33, 9.35],
4093 ... [6.41, 7.10, 9.33],
4094 ... [8.00, 8.24, 8.44]])
4095 >>> b = np.array([[6.35, 7.30, 7.16],
4096 ... [6.65, 6.68, 7.63],
4097 ... [5.72, 7.73, 6.72],
4098 ... [7.01, 9.19, 7.41],
4099 ... [7.75, 7.87, 8.30],
4100 ... [6.90, 7.97, 6.97]])
4101 >>> c = np.array([[3.31, 8.77, 1.01],
4102 ... [8.25, 3.24, 3.62],
4103 ... [6.32, 8.81, 5.19],
4104 ... [7.48, 8.83, 8.91],
4105 ... [8.59, 6.01, 6.07],
4106 ... [3.07, 9.72, 7.48]])
4107 >>> F, p = f_oneway(a, b, c)
4108 >>> F
4109 array([1.75676344, 0.03701228, 3.76439349])
4110 >>> p
4111 array([0.20630784, 0.96375203, 0.04733157])
4113 """
4114 if len(samples) < 2:
4115 raise TypeError('at least two inputs are required;'
4116 f' got {len(samples)}.')
4118 samples = [np.asarray(sample, dtype=float) for sample in samples]
4120 # ANOVA on N groups, each in its own array
4121 num_groups = len(samples)
4123 # We haven't explicitly validated axis, but if it is bad, this call of
4124 # np.concatenate will raise np.AxisError. The call will raise ValueError
4125 # if the dimensions of all the arrays, except the axis dimension, are not
4126 # the same.
4127 alldata = np.concatenate(samples, axis=axis)
4128 bign = alldata.shape[axis]
4130 # Check this after forming alldata, so shape errors are detected
4131 # and reported before checking for 0 length inputs.
4132 if any(sample.shape[axis] == 0 for sample in samples):
4133 warnings.warn(stats.DegenerateDataWarning('at least one input '
4134 'has length 0'))
4135 return _create_f_oneway_nan_result(alldata.shape, axis)
4137 # Must have at least one group with length greater than 1.
4138 if all(sample.shape[axis] == 1 for sample in samples):
4139 msg = ('all input arrays have length 1. f_oneway requires that at '
4140 'least one input has length greater than 1.')
4141 warnings.warn(stats.DegenerateDataWarning(msg))
4142 return _create_f_oneway_nan_result(alldata.shape, axis)
4144 # Check if all values within each group are identical, and if the common
4145 # value in at least one group is different from that in another group.
4146 # Based on https://github.com/scipy/scipy/issues/11669
4148 # If axis=0, say, and the groups have shape (n0, ...), (n1, ...), ...,
4149 # then is_const is a boolean array with shape (num_groups, ...).
4150 # It is True if the values within the groups along the axis slice are
4151 # identical. In the typical case where each input array is 1-d, is_const is
4152 # a 1-d array with length num_groups.
4153 is_const = np.concatenate(
4154 [(_first(sample, axis) == sample).all(axis=axis,
4155 keepdims=True)
4156 for sample in samples],
4157 axis=axis
4158 )
4160 # all_const is a boolean array with shape (...) (see previous comment).
4161 # It is True if the values within each group along the axis slice are
4162 # the same (e.g. [[3, 3, 3], [5, 5, 5, 5], [4, 4, 4]]).
4163 all_const = is_const.all(axis=axis)
4164 if all_const.any():
4165 msg = ("Each of the input arrays is constant;"
4166 "the F statistic is not defined or infinite")
4167 warnings.warn(stats.ConstantInputWarning(msg))
4169 # all_same_const is True if all the values in the groups along the axis=0
4170 # slice are the same (e.g. [[3, 3, 3], [3, 3, 3, 3], [3, 3, 3]]).
4171 all_same_const = (_first(alldata, axis) == alldata).all(axis=axis)
4173 # Determine the mean of the data, and subtract that from all inputs to a
4174 # variance (via sum_of_sq / sq_of_sum) calculation. Variance is invariant
4175 # to a shift in location, and centering all data around zero vastly
4176 # improves numerical stability.
4177 offset = alldata.mean(axis=axis, keepdims=True)
4178 alldata -= offset
4180 normalized_ss = _square_of_sums(alldata, axis=axis) / bign
4182 sstot = _sum_of_squares(alldata, axis=axis) - normalized_ss
4184 ssbn = 0
4185 for sample in samples:
4186 ssbn += _square_of_sums(sample - offset,
4187 axis=axis) / sample.shape[axis]
4189 # Naming: variables ending in bn/b are for "between treatments", wn/w are
4190 # for "within treatments"
4191 ssbn -= normalized_ss
4192 sswn = sstot - ssbn
4193 dfbn = num_groups - 1
4194 dfwn = bign - num_groups
4195 msb = ssbn / dfbn
4196 msw = sswn / dfwn
4197 with np.errstate(divide='ignore', invalid='ignore'):
4198 f = msb / msw
4200 prob = special.fdtrc(dfbn, dfwn, f) # equivalent to stats.f.sf
4202 # Fix any f values that should be inf or nan because the corresponding
4203 # inputs were constant.
4204 if np.isscalar(f):
4205 if all_same_const:
4206 f = np.nan
4207 prob = np.nan
4208 elif all_const:
4209 f = np.inf
4210 prob = 0.0
4211 else:
4212 f[all_const] = np.inf
4213 prob[all_const] = 0.0
4214 f[all_same_const] = np.nan
4215 prob[all_same_const] = np.nan
4217 return F_onewayResult(f, prob)
4220def alexandergovern(*samples, nan_policy='propagate'):
4221 """Performs the Alexander Govern test.
4223 The Alexander-Govern approximation tests the equality of k independent
4224 means in the face of heterogeneity of variance. The test is applied to
4225 samples from two or more groups, possibly with differing sizes.
4227 Parameters
4228 ----------
4229 sample1, sample2, ... : array_like
4230 The sample measurements for each group. There must be at least
4231 two samples.
4232 nan_policy : {'propagate', 'raise', 'omit'}, optional
4233 Defines how to handle when input contains nan.
4234 The following options are available (default is 'propagate'):
4236 * 'propagate': returns nan
4237 * 'raise': throws an error
4238 * 'omit': performs the calculations ignoring nan values
4240 Returns
4241 -------
4242 res : AlexanderGovernResult
4243 An object with attributes:
4245 statistic : float
4246 The computed A statistic of the test.
4247 pvalue : float
4248 The associated p-value from the chi-squared distribution.
4250 Warns
4251 -----
4252 `~scipy.stats.ConstantInputWarning`
4253 Raised if an input is a constant array. The statistic is not defined
4254 in this case, so ``np.nan`` is returned.
4256 See Also
4257 --------
4258 f_oneway : one-way ANOVA
4260 Notes
4261 -----
4262 The use of this test relies on several assumptions.
4264 1. The samples are independent.
4265 2. Each sample is from a normally distributed population.
4266 3. Unlike `f_oneway`, this test does not assume on homoscedasticity,
4267 instead relaxing the assumption of equal variances.
4269 Input samples must be finite, one dimensional, and with size greater than
4270 one.
4272 References
4273 ----------
4274 .. [1] Alexander, Ralph A., and Diane M. Govern. "A New and Simpler
4275 Approximation for ANOVA under Variance Heterogeneity." Journal
4276 of Educational Statistics, vol. 19, no. 2, 1994, pp. 91-101.
4277 JSTOR, www.jstor.org/stable/1165140. Accessed 12 Sept. 2020.
4279 Examples
4280 --------
4281 >>> from scipy.stats import alexandergovern
4283 Here are some data on annual percentage rate of interest charged on
4284 new car loans at nine of the largest banks in four American cities
4285 taken from the National Institute of Standards and Technology's
4286 ANOVA dataset.
4288 We use `alexandergovern` to test the null hypothesis that all cities
4289 have the same mean APR against the alternative that the cities do not
4290 all have the same mean APR. We decide that a significance level of 5%
4291 is required to reject the null hypothesis in favor of the alternative.
4293 >>> atlanta = [13.75, 13.75, 13.5, 13.5, 13.0, 13.0, 13.0, 12.75, 12.5]
4294 >>> chicago = [14.25, 13.0, 12.75, 12.5, 12.5, 12.4, 12.3, 11.9, 11.9]
4295 >>> houston = [14.0, 14.0, 13.51, 13.5, 13.5, 13.25, 13.0, 12.5, 12.5]
4296 >>> memphis = [15.0, 14.0, 13.75, 13.59, 13.25, 12.97, 12.5, 12.25,
4297 ... 11.89]
4298 >>> alexandergovern(atlanta, chicago, houston, memphis)
4299 AlexanderGovernResult(statistic=4.65087071883494,
4300 pvalue=0.19922132490385214)
4302 The p-value is 0.1992, indicating a nearly 20% chance of observing
4303 such an extreme value of the test statistic under the null hypothesis.
4304 This exceeds 5%, so we do not reject the null hypothesis in favor of
4305 the alternative.
4307 """
4308 samples = _alexandergovern_input_validation(samples, nan_policy)
4310 if np.any([(sample == sample[0]).all() for sample in samples]):
4311 msg = "An input array is constant; the statistic is not defined."
4312 warnings.warn(stats.ConstantInputWarning(msg))
4313 return AlexanderGovernResult(np.nan, np.nan)
4315 # The following formula numbers reference the equation described on
4316 # page 92 by Alexander, Govern. Formulas 5, 6, and 7 describe other
4317 # tests that serve as the basis for equation (8) but are not needed
4318 # to perform the test.
4320 # precalculate mean and length of each sample
4321 lengths = np.array([ma.count(sample) if nan_policy == 'omit'
4322 else len(sample) for sample in samples])
4323 means = np.array([np.mean(sample) for sample in samples])
4325 # (1) determine standard error of the mean for each sample
4326 standard_errors = [np.std(sample, ddof=1) / np.sqrt(length)
4327 for sample, length in zip(samples, lengths)]
4329 # (2) define a weight for each sample
4330 inv_sq_se = 1 / np.square(standard_errors)
4331 weights = inv_sq_se / np.sum(inv_sq_se)
4333 # (3) determine variance-weighted estimate of the common mean
4334 var_w = np.sum(weights * means)
4336 # (4) determine one-sample t statistic for each group
4337 t_stats = (means - var_w)/standard_errors
4339 # calculate parameters to be used in transformation
4340 v = lengths - 1
4341 a = v - .5
4342 b = 48 * a**2
4343 c = (a * np.log(1 + (t_stats ** 2)/v))**.5
4345 # (8) perform a normalizing transformation on t statistic
4346 z = (c + ((c**3 + 3*c)/b) -
4347 ((4*c**7 + 33*c**5 + 240*c**3 + 855*c) /
4348 (b**2*10 + 8*b*c**4 + 1000*b)))
4350 # (9) calculate statistic
4351 A = np.sum(np.square(z))
4353 # "[the p value is determined from] central chi-square random deviates
4354 # with k - 1 degrees of freedom". Alexander, Govern (94)
4355 p = distributions.chi2.sf(A, len(samples) - 1)
4356 return AlexanderGovernResult(A, p)
4359def _alexandergovern_input_validation(samples, nan_policy):
4360 if len(samples) < 2:
4361 raise TypeError(f"2 or more inputs required, got {len(samples)}")
4363 # input arrays are flattened
4364 samples = [np.asarray(sample, dtype=float) for sample in samples]
4366 for i, sample in enumerate(samples):
4367 if np.size(sample) <= 1:
4368 raise ValueError("Input sample size must be greater than one.")
4369 if sample.ndim != 1:
4370 raise ValueError("Input samples must be one-dimensional")
4371 if np.isinf(sample).any():
4372 raise ValueError("Input samples must be finite.")
4374 contains_nan, nan_policy = _contains_nan(sample,
4375 nan_policy=nan_policy)
4376 if contains_nan and nan_policy == 'omit':
4377 samples[i] = ma.masked_invalid(sample)
4378 return samples
4381@dataclass
4382class AlexanderGovernResult:
4383 statistic: float
4384 pvalue: float
4387def _pearsonr_fisher_ci(r, n, confidence_level, alternative):
4388 """
4389 Compute the confidence interval for Pearson's R.
4391 Fisher's transformation is used to compute the confidence interval
4392 (https://en.wikipedia.org/wiki/Fisher_transformation).
4393 """
4394 if r == 1:
4395 zr = np.inf
4396 elif r == -1:
4397 zr = -np.inf
4398 else:
4399 zr = np.arctanh(r)
4401 if n > 3:
4402 se = np.sqrt(1 / (n - 3))
4403 if alternative == "two-sided":
4404 h = special.ndtri(0.5 + confidence_level/2)
4405 zlo = zr - h*se
4406 zhi = zr + h*se
4407 rlo = np.tanh(zlo)
4408 rhi = np.tanh(zhi)
4409 elif alternative == "less":
4410 h = special.ndtri(confidence_level)
4411 zhi = zr + h*se
4412 rhi = np.tanh(zhi)
4413 rlo = -1.0
4414 else:
4415 # alternative == "greater":
4416 h = special.ndtri(confidence_level)
4417 zlo = zr - h*se
4418 rlo = np.tanh(zlo)
4419 rhi = 1.0
4420 else:
4421 rlo, rhi = -1.0, 1.0
4423 return ConfidenceInterval(low=rlo, high=rhi)
4426def _pearsonr_bootstrap_ci(confidence_level, method, x, y, alternative):
4427 """
4428 Compute the confidence interval for Pearson's R using the bootstrap.
4429 """
4430 def statistic(x, y):
4431 statistic, _ = pearsonr(x, y)
4432 return statistic
4434 res = bootstrap((x, y), statistic, confidence_level=confidence_level,
4435 paired=True, alternative=alternative, **method._asdict())
4436 # for one-sided confidence intervals, bootstrap gives +/- inf on one side
4437 res.confidence_interval = np.clip(res.confidence_interval, -1, 1)
4439 return ConfidenceInterval(*res.confidence_interval)
4442ConfidenceInterval = namedtuple('ConfidenceInterval', ['low', 'high'])
4444PearsonRResultBase = _make_tuple_bunch('PearsonRResultBase',
4445 ['statistic', 'pvalue'], [])
4448class PearsonRResult(PearsonRResultBase):
4449 """
4450 Result of `scipy.stats.pearsonr`
4452 Attributes
4453 ----------
4454 statistic : float
4455 Pearson product-moment correlation coefficient.
4456 pvalue : float
4457 The p-value associated with the chosen alternative.
4459 Methods
4460 -------
4461 confidence_interval
4462 Computes the confidence interval of the correlation
4463 coefficient `statistic` for the given confidence level.
4465 """
4466 def __init__(self, statistic, pvalue, alternative, n, x, y):
4467 super().__init__(statistic, pvalue)
4468 self._alternative = alternative
4469 self._n = n
4470 self._x = x
4471 self._y = y
4473 # add alias for consistency with other correlation functions
4474 self.correlation = statistic
4476 def confidence_interval(self, confidence_level=0.95, method=None):
4477 """
4478 The confidence interval for the correlation coefficient.
4480 Compute the confidence interval for the correlation coefficient
4481 ``statistic`` with the given confidence level.
4483 If `method` is not provided,
4484 The confidence interval is computed using the Fisher transformation
4485 F(r) = arctanh(r) [1]_. When the sample pairs are drawn from a
4486 bivariate normal distribution, F(r) approximately follows a normal
4487 distribution with standard error ``1/sqrt(n - 3)``, where ``n`` is the
4488 length of the original samples along the calculation axis. When
4489 ``n <= 3``, this approximation does not yield a finite, real standard
4490 error, so we define the confidence interval to be -1 to 1.
4492 If `method` is an instance of `BootstrapMethod`, the confidence
4493 interval is computed using `scipy.stats.bootstrap` with the provided
4494 configuration options and other appropriate settings. In some cases,
4495 confidence limits may be NaN due to a degenerate resample, and this is
4496 typical for very small samples (~6 observations).
4498 Parameters
4499 ----------
4500 confidence_level : float
4501 The confidence level for the calculation of the correlation
4502 coefficient confidence interval. Default is 0.95.
4504 method : BootstrapMethod, optional
4505 Defines the method used to compute the confidence interval. See
4506 method description for details.
4508 .. versionadded:: 1.11.0
4510 Returns
4511 -------
4512 ci : namedtuple
4513 The confidence interval is returned in a ``namedtuple`` with
4514 fields `low` and `high`.
4516 References
4517 ----------
4518 .. [1] "Pearson correlation coefficient", Wikipedia,
4519 https://en.wikipedia.org/wiki/Pearson_correlation_coefficient
4520 """
4521 if isinstance(method, BootstrapMethod):
4522 ci = _pearsonr_bootstrap_ci(confidence_level, method,
4523 self._x, self._y, self._alternative)
4524 elif method is None:
4525 ci = _pearsonr_fisher_ci(self.statistic, self._n, confidence_level,
4526 self._alternative)
4527 else:
4528 message = ('`method` must be an instance of `BootstrapMethod` '
4529 'or None.')
4530 raise ValueError(message)
4531 return ci
4533def pearsonr(x, y, *, alternative='two-sided', method=None):
4534 r"""
4535 Pearson correlation coefficient and p-value for testing non-correlation.
4537 The Pearson correlation coefficient [1]_ measures the linear relationship
4538 between two datasets. Like other correlation
4539 coefficients, this one varies between -1 and +1 with 0 implying no
4540 correlation. Correlations of -1 or +1 imply an exact linear relationship.
4541 Positive correlations imply that as x increases, so does y. Negative
4542 correlations imply that as x increases, y decreases.
4544 This function also performs a test of the null hypothesis that the
4545 distributions underlying the samples are uncorrelated and normally
4546 distributed. (See Kowalski [3]_
4547 for a discussion of the effects of non-normality of the input on the
4548 distribution of the correlation coefficient.)
4549 The p-value roughly indicates the probability of an uncorrelated system
4550 producing datasets that have a Pearson correlation at least as extreme
4551 as the one computed from these datasets.
4553 Parameters
4554 ----------
4555 x : (N,) array_like
4556 Input array.
4557 y : (N,) array_like
4558 Input array.
4559 alternative : {'two-sided', 'greater', 'less'}, optional
4560 Defines the alternative hypothesis. Default is 'two-sided'.
4561 The following options are available:
4563 * 'two-sided': the correlation is nonzero
4564 * 'less': the correlation is negative (less than zero)
4565 * 'greater': the correlation is positive (greater than zero)
4567 .. versionadded:: 1.9.0
4568 method : ResamplingMethod, optional
4569 Defines the method used to compute the p-value. If `method` is an
4570 instance of `PermutationMethod`/`MonteCarloMethod`, the p-value is
4571 computed using
4572 `scipy.stats.permutation_test`/`scipy.stats.monte_carlo_test` with the
4573 provided configuration options and other appropriate settings.
4574 Otherwise, the p-value is computed as documented in the notes.
4576 .. versionadded:: 1.11.0
4578 Returns
4579 -------
4580 result : `~scipy.stats._result_classes.PearsonRResult`
4581 An object with the following attributes:
4583 statistic : float
4584 Pearson product-moment correlation coefficient.
4585 pvalue : float
4586 The p-value associated with the chosen alternative.
4588 The object has the following method:
4590 confidence_interval(confidence_level, method)
4591 This computes the confidence interval of the correlation
4592 coefficient `statistic` for the given confidence level.
4593 The confidence interval is returned in a ``namedtuple`` with
4594 fields `low` and `high`. If `method` is not provided, the
4595 confidence interval is computed using the Fisher transformation
4596 [1]_. If `method` is an instance of `BootstrapMethod`, the
4597 confidence interval is computed using `scipy.stats.bootstrap` with
4598 the provided configuration options and other appropriate settings.
4599 In some cases, confidence limits may be NaN due to a degenerate
4600 resample, and this is typical for very small samples (~6
4601 observations).
4603 Warns
4604 -----
4605 `~scipy.stats.ConstantInputWarning`
4606 Raised if an input is a constant array. The correlation coefficient
4607 is not defined in this case, so ``np.nan`` is returned.
4609 `~scipy.stats.NearConstantInputWarning`
4610 Raised if an input is "nearly" constant. The array ``x`` is considered
4611 nearly constant if ``norm(x - mean(x)) < 1e-13 * abs(mean(x))``.
4612 Numerical errors in the calculation ``x - mean(x)`` in this case might
4613 result in an inaccurate calculation of r.
4615 See Also
4616 --------
4617 spearmanr : Spearman rank-order correlation coefficient.
4618 kendalltau : Kendall's tau, a correlation measure for ordinal data.
4620 Notes
4621 -----
4622 The correlation coefficient is calculated as follows:
4624 .. math::
4626 r = \frac{\sum (x - m_x) (y - m_y)}
4627 {\sqrt{\sum (x - m_x)^2 \sum (y - m_y)^2}}
4629 where :math:`m_x` is the mean of the vector x and :math:`m_y` is
4630 the mean of the vector y.
4632 Under the assumption that x and y are drawn from
4633 independent normal distributions (so the population correlation coefficient
4634 is 0), the probability density function of the sample correlation
4635 coefficient r is ([1]_, [2]_):
4637 .. math::
4638 f(r) = \frac{{(1-r^2)}^{n/2-2}}{\mathrm{B}(\frac{1}{2},\frac{n}{2}-1)}
4640 where n is the number of samples, and B is the beta function. This
4641 is sometimes referred to as the exact distribution of r. This is
4642 the distribution that is used in `pearsonr` to compute the p-value when
4643 the `method` parameter is left at its default value (None).
4644 The distribution is a beta distribution on the interval [-1, 1],
4645 with equal shape parameters a = b = n/2 - 1. In terms of SciPy's
4646 implementation of the beta distribution, the distribution of r is::
4648 dist = scipy.stats.beta(n/2 - 1, n/2 - 1, loc=-1, scale=2)
4650 The default p-value returned by `pearsonr` is a two-sided p-value. For a
4651 given sample with correlation coefficient r, the p-value is
4652 the probability that abs(r') of a random sample x' and y' drawn from
4653 the population with zero correlation would be greater than or equal
4654 to abs(r). In terms of the object ``dist`` shown above, the p-value
4655 for a given r and length n can be computed as::
4657 p = 2*dist.cdf(-abs(r))
4659 When n is 2, the above continuous distribution is not well-defined.
4660 One can interpret the limit of the beta distribution as the shape
4661 parameters a and b approach a = b = 0 as a discrete distribution with
4662 equal probability masses at r = 1 and r = -1. More directly, one
4663 can observe that, given the data x = [x1, x2] and y = [y1, y2], and
4664 assuming x1 != x2 and y1 != y2, the only possible values for r are 1
4665 and -1. Because abs(r') for any sample x' and y' with length 2 will
4666 be 1, the two-sided p-value for a sample of length 2 is always 1.
4668 For backwards compatibility, the object that is returned also behaves
4669 like a tuple of length two that holds the statistic and the p-value.
4671 References
4672 ----------
4673 .. [1] "Pearson correlation coefficient", Wikipedia,
4674 https://en.wikipedia.org/wiki/Pearson_correlation_coefficient
4675 .. [2] Student, "Probable error of a correlation coefficient",
4676 Biometrika, Volume 6, Issue 2-3, 1 September 1908, pp. 302-310.
4677 .. [3] C. J. Kowalski, "On the Effects of Non-Normality on the Distribution
4678 of the Sample Product-Moment Correlation Coefficient"
4679 Journal of the Royal Statistical Society. Series C (Applied
4680 Statistics), Vol. 21, No. 1 (1972), pp. 1-12.
4682 Examples
4683 --------
4684 >>> import numpy as np
4685 >>> from scipy import stats
4686 >>> x, y = [1, 2, 3, 4, 5, 6, 7], [10, 9, 2.5, 6, 4, 3, 2]
4687 >>> res = stats.pearsonr(x, y)
4688 >>> res
4689 PearsonRResult(statistic=-0.828503883588428, pvalue=0.021280260007523286)
4691 To perform an exact permutation version of the test:
4693 >>> rng = np.random.default_rng(7796654889291491997)
4694 >>> method = stats.PermutationMethod(n_resamples=np.inf, random_state=rng)
4695 >>> stats.pearsonr(x, y, method=method)
4696 PearsonRResult(statistic=-0.828503883588428, pvalue=0.028174603174603175)
4698 To perform the test under the null hypothesis that the data were drawn from
4699 *uniform* distributions:
4701 >>> method = stats.MonteCarloMethod(rvs=(rng.uniform, rng.uniform))
4702 >>> stats.pearsonr(x, y, method=method)
4703 PearsonRResult(statistic=-0.828503883588428, pvalue=0.0188)
4705 To produce an asymptotic 90% confidence interval:
4707 >>> res.confidence_interval(confidence_level=0.9)
4708 ConfidenceInterval(low=-0.9644331982722841, high=-0.3460237473272273)
4710 And for a bootstrap confidence interval:
4712 >>> method = stats.BootstrapMethod(method='BCa', random_state=rng)
4713 >>> res.confidence_interval(confidence_level=0.9, method=method)
4714 ConfidenceInterval(low=-0.9983163756488651, high=-0.22771001702132443) # may vary
4716 There is a linear dependence between x and y if y = a + b*x + e, where
4717 a,b are constants and e is a random error term, assumed to be independent
4718 of x. For simplicity, assume that x is standard normal, a=0, b=1 and let
4719 e follow a normal distribution with mean zero and standard deviation s>0.
4721 >>> rng = np.random.default_rng()
4722 >>> s = 0.5
4723 >>> x = stats.norm.rvs(size=500, random_state=rng)
4724 >>> e = stats.norm.rvs(scale=s, size=500, random_state=rng)
4725 >>> y = x + e
4726 >>> stats.pearsonr(x, y).statistic
4727 0.9001942438244763
4729 This should be close to the exact value given by
4731 >>> 1/np.sqrt(1 + s**2)
4732 0.8944271909999159
4734 For s=0.5, we observe a high level of correlation. In general, a large
4735 variance of the noise reduces the correlation, while the correlation
4736 approaches one as the variance of the error goes to zero.
4738 It is important to keep in mind that no correlation does not imply
4739 independence unless (x, y) is jointly normal. Correlation can even be zero
4740 when there is a very simple dependence structure: if X follows a
4741 standard normal distribution, let y = abs(x). Note that the correlation
4742 between x and y is zero. Indeed, since the expectation of x is zero,
4743 cov(x, y) = E[x*y]. By definition, this equals E[x*abs(x)] which is zero
4744 by symmetry. The following lines of code illustrate this observation:
4746 >>> y = np.abs(x)
4747 >>> stats.pearsonr(x, y)
4748 PearsonRResult(statistic=-0.05444919272687482, pvalue=0.22422294836207743)
4750 A non-zero correlation coefficient can be misleading. For example, if X has
4751 a standard normal distribution, define y = x if x < 0 and y = 0 otherwise.
4752 A simple calculation shows that corr(x, y) = sqrt(2/Pi) = 0.797...,
4753 implying a high level of correlation:
4755 >>> y = np.where(x < 0, x, 0)
4756 >>> stats.pearsonr(x, y)
4757 PearsonRResult(statistic=0.861985781588, pvalue=4.813432002751103e-149)
4759 This is unintuitive since there is no dependence of x and y if x is larger
4760 than zero which happens in about half of the cases if we sample x and y.
4762 """
4763 n = len(x)
4764 if n != len(y):
4765 raise ValueError('x and y must have the same length.')
4767 if n < 2:
4768 raise ValueError('x and y must have length at least 2.')
4770 x = np.asarray(x)
4771 y = np.asarray(y)
4773 if (np.issubdtype(x.dtype, np.complexfloating)
4774 or np.issubdtype(y.dtype, np.complexfloating)):
4775 raise ValueError('This function does not support complex data')
4777 # If an input is constant, the correlation coefficient is not defined.
4778 if (x == x[0]).all() or (y == y[0]).all():
4779 msg = ("An input array is constant; the correlation coefficient "
4780 "is not defined.")
4781 warnings.warn(stats.ConstantInputWarning(msg))
4782 result = PearsonRResult(statistic=np.nan, pvalue=np.nan, n=n,
4783 alternative=alternative, x=x, y=y)
4784 return result
4786 if isinstance(method, PermutationMethod):
4787 def statistic(y):
4788 statistic, _ = pearsonr(x, y, alternative=alternative)
4789 return statistic
4791 res = permutation_test((y,), statistic, permutation_type='pairings',
4792 alternative=alternative, **method._asdict())
4794 return PearsonRResult(statistic=res.statistic, pvalue=res.pvalue, n=n,
4795 alternative=alternative, x=x, y=y)
4796 elif isinstance(method, MonteCarloMethod):
4797 def statistic(x, y):
4798 statistic, _ = pearsonr(x, y, alternative=alternative)
4799 return statistic
4801 if method.rvs is None:
4802 rng = np.random.default_rng()
4803 method.rvs = rng.normal, rng.normal
4805 res = monte_carlo_test((x, y,), statistic=statistic,
4806 alternative=alternative, **method._asdict())
4808 return PearsonRResult(statistic=res.statistic, pvalue=res.pvalue, n=n,
4809 alternative=alternative, x=x, y=y)
4810 elif method is not None:
4811 message = ('`method` must be an instance of `PermutationMethod`,'
4812 '`MonteCarloMethod`, or None.')
4813 raise ValueError(message)
4815 # dtype is the data type for the calculations. This expression ensures
4816 # that the data type is at least 64 bit floating point. It might have
4817 # more precision if the input is, for example, np.longdouble.
4818 dtype = type(1.0 + x[0] + y[0])
4820 if n == 2:
4821 r = dtype(np.sign(x[1] - x[0])*np.sign(y[1] - y[0]))
4822 result = PearsonRResult(statistic=r, pvalue=1.0, n=n,
4823 alternative=alternative, x=x, y=y)
4824 return result
4826 xmean = x.mean(dtype=dtype)
4827 ymean = y.mean(dtype=dtype)
4829 # By using `astype(dtype)`, we ensure that the intermediate calculations
4830 # use at least 64 bit floating point.
4831 xm = x.astype(dtype) - xmean
4832 ym = y.astype(dtype) - ymean
4834 # Unlike np.linalg.norm or the expression sqrt((xm*xm).sum()),
4835 # scipy.linalg.norm(xm) does not overflow if xm is, for example,
4836 # [-5e210, 5e210, 3e200, -3e200]
4837 normxm = linalg.norm(xm)
4838 normym = linalg.norm(ym)
4840 threshold = 1e-13
4841 if normxm < threshold*abs(xmean) or normym < threshold*abs(ymean):
4842 # If all the values in x (likewise y) are very close to the mean,
4843 # the loss of precision that occurs in the subtraction xm = x - xmean
4844 # might result in large errors in r.
4845 msg = ("An input array is nearly constant; the computed "
4846 "correlation coefficient may be inaccurate.")
4847 warnings.warn(stats.NearConstantInputWarning(msg))
4849 r = np.dot(xm/normxm, ym/normym)
4851 # Presumably, if abs(r) > 1, then it is only some small artifact of
4852 # floating point arithmetic.
4853 r = max(min(r, 1.0), -1.0)
4855 # As explained in the docstring, the distribution of `r` under the null
4856 # hypothesis is the beta distribution on (-1, 1) with a = b = n/2 - 1.
4857 ab = n/2 - 1
4858 dist = stats.beta(ab, ab, loc=-1, scale=2)
4859 if alternative == 'two-sided':
4860 prob = 2*dist.sf(abs(r))
4861 elif alternative == 'less':
4862 prob = dist.cdf(r)
4863 elif alternative == 'greater':
4864 prob = dist.sf(r)
4865 else:
4866 raise ValueError('alternative must be one of '
4867 '["two-sided", "less", "greater"]')
4869 return PearsonRResult(statistic=r, pvalue=prob, n=n,
4870 alternative=alternative, x=x, y=y)
4873def fisher_exact(table, alternative='two-sided'):
4874 """Perform a Fisher exact test on a 2x2 contingency table.
4876 The null hypothesis is that the true odds ratio of the populations
4877 underlying the observations is one, and the observations were sampled
4878 from these populations under a condition: the marginals of the
4879 resulting table must equal those of the observed table. The statistic
4880 returned is the unconditional maximum likelihood estimate of the odds
4881 ratio, and the p-value is the probability under the null hypothesis of
4882 obtaining a table at least as extreme as the one that was actually
4883 observed. There are other possible choices of statistic and two-sided
4884 p-value definition associated with Fisher's exact test; please see the
4885 Notes for more information.
4887 Parameters
4888 ----------
4889 table : array_like of ints
4890 A 2x2 contingency table. Elements must be non-negative integers.
4891 alternative : {'two-sided', 'less', 'greater'}, optional
4892 Defines the alternative hypothesis.
4893 The following options are available (default is 'two-sided'):
4895 * 'two-sided': the odds ratio of the underlying population is not one
4896 * 'less': the odds ratio of the underlying population is less than one
4897 * 'greater': the odds ratio of the underlying population is greater
4898 than one
4900 See the Notes for more details.
4902 Returns
4903 -------
4904 res : SignificanceResult
4905 An object containing attributes:
4907 statistic : float
4908 This is the prior odds ratio, not a posterior estimate.
4909 pvalue : float
4910 The probability under the null hypothesis of obtaining a
4911 table at least as extreme as the one that was actually observed.
4913 See Also
4914 --------
4915 chi2_contingency : Chi-square test of independence of variables in a
4916 contingency table. This can be used as an alternative to
4917 `fisher_exact` when the numbers in the table are large.
4918 contingency.odds_ratio : Compute the odds ratio (sample or conditional
4919 MLE) for a 2x2 contingency table.
4920 barnard_exact : Barnard's exact test, which is a more powerful alternative
4921 than Fisher's exact test for 2x2 contingency tables.
4922 boschloo_exact : Boschloo's exact test, which is a more powerful
4923 alternative than Fisher's exact test for 2x2 contingency tables.
4925 Notes
4926 -----
4927 *Null hypothesis and p-values*
4929 The null hypothesis is that the true odds ratio of the populations
4930 underlying the observations is one, and the observations were sampled at
4931 random from these populations under a condition: the marginals of the
4932 resulting table must equal those of the observed table. Equivalently,
4933 the null hypothesis is that the input table is from the hypergeometric
4934 distribution with parameters (as used in `hypergeom`)
4935 ``M = a + b + c + d``, ``n = a + b`` and ``N = a + c``, where the
4936 input table is ``[[a, b], [c, d]]``. This distribution has support
4937 ``max(0, N + n - M) <= x <= min(N, n)``, or, in terms of the values
4938 in the input table, ``min(0, a - d) <= x <= a + min(b, c)``. ``x``
4939 can be interpreted as the upper-left element of a 2x2 table, so the
4940 tables in the distribution have form::
4942 [ x n - x ]
4943 [N - x M - (n + N) + x]
4945 For example, if::
4947 table = [6 2]
4948 [1 4]
4950 then the support is ``2 <= x <= 7``, and the tables in the distribution
4951 are::
4953 [2 6] [3 5] [4 4] [5 3] [6 2] [7 1]
4954 [5 0] [4 1] [3 2] [2 3] [1 4] [0 5]
4956 The probability of each table is given by the hypergeometric distribution
4957 ``hypergeom.pmf(x, M, n, N)``. For this example, these are (rounded to
4958 three significant digits)::
4960 x 2 3 4 5 6 7
4961 p 0.0163 0.163 0.408 0.326 0.0816 0.00466
4963 These can be computed with::
4965 >>> import numpy as np
4966 >>> from scipy.stats import hypergeom
4967 >>> table = np.array([[6, 2], [1, 4]])
4968 >>> M = table.sum()
4969 >>> n = table[0].sum()
4970 >>> N = table[:, 0].sum()
4971 >>> start, end = hypergeom.support(M, n, N)
4972 >>> hypergeom.pmf(np.arange(start, end+1), M, n, N)
4973 array([0.01631702, 0.16317016, 0.40792541, 0.32634033, 0.08158508,
4974 0.004662 ])
4976 The two-sided p-value is the probability that, under the null hypothesis,
4977 a random table would have a probability equal to or less than the
4978 probability of the input table. For our example, the probability of
4979 the input table (where ``x = 6``) is 0.0816. The x values where the
4980 probability does not exceed this are 2, 6 and 7, so the two-sided p-value
4981 is ``0.0163 + 0.0816 + 0.00466 ~= 0.10256``::
4983 >>> from scipy.stats import fisher_exact
4984 >>> res = fisher_exact(table, alternative='two-sided')
4985 >>> res.pvalue
4986 0.10256410256410257
4988 The one-sided p-value for ``alternative='greater'`` is the probability
4989 that a random table has ``x >= a``, which in our example is ``x >= 6``,
4990 or ``0.0816 + 0.00466 ~= 0.08626``::
4992 >>> res = fisher_exact(table, alternative='greater')
4993 >>> res.pvalue
4994 0.08624708624708627
4996 This is equivalent to computing the survival function of the
4997 distribution at ``x = 5`` (one less than ``x`` from the input table,
4998 because we want to include the probability of ``x = 6`` in the sum)::
5000 >>> hypergeom.sf(5, M, n, N)
5001 0.08624708624708627
5003 For ``alternative='less'``, the one-sided p-value is the probability
5004 that a random table has ``x <= a``, (i.e. ``x <= 6`` in our example),
5005 or ``0.0163 + 0.163 + 0.408 + 0.326 + 0.0816 ~= 0.9949``::
5007 >>> res = fisher_exact(table, alternative='less')
5008 >>> res.pvalue
5009 0.9953379953379957
5011 This is equivalent to computing the cumulative distribution function
5012 of the distribution at ``x = 6``:
5014 >>> hypergeom.cdf(6, M, n, N)
5015 0.9953379953379957
5017 *Odds ratio*
5019 The calculated odds ratio is different from the value computed by the
5020 R function ``fisher.test``. This implementation returns the "sample"
5021 or "unconditional" maximum likelihood estimate, while ``fisher.test``
5022 in R uses the conditional maximum likelihood estimate. To compute the
5023 conditional maximum likelihood estimate of the odds ratio, use
5024 `scipy.stats.contingency.odds_ratio`.
5026 References
5027 ----------
5028 .. [1] Fisher, Sir Ronald A, "The Design of Experiments:
5029 Mathematics of a Lady Tasting Tea." ISBN 978-0-486-41151-4, 1935.
5030 .. [2] "Fisher's exact test",
5031 https://en.wikipedia.org/wiki/Fisher's_exact_test
5032 .. [3] Emma V. Low et al. "Identifying the lowest effective dose of
5033 acetazolamide for the prophylaxis of acute mountain sickness:
5034 systematic review and meta-analysis."
5035 BMJ, 345, :doi:`10.1136/bmj.e6779`, 2012.
5037 Examples
5038 --------
5039 In [3]_, the effective dose of acetazolamide for the prophylaxis of acute
5040 mountain sickness was investigated. The study notably concluded:
5042 Acetazolamide 250 mg, 500 mg, and 750 mg daily were all efficacious for
5043 preventing acute mountain sickness. Acetazolamide 250 mg was the lowest
5044 effective dose with available evidence for this indication.
5046 The following table summarizes the results of the experiment in which
5047 some participants took a daily dose of acetazolamide 250 mg while others
5048 took a placebo.
5049 Cases of acute mountain sickness were recorded::
5051 Acetazolamide Control/Placebo
5052 Acute mountain sickness 7 17
5053 No 15 5
5056 Is there evidence that the acetazolamide 250 mg reduces the risk of
5057 acute mountain sickness?
5058 We begin by formulating a null hypothesis :math:`H_0`:
5060 The odds of experiencing acute mountain sickness are the same with
5061 the acetazolamide treatment as they are with placebo.
5063 Let's assess the plausibility of this hypothesis with
5064 Fisher's test.
5066 >>> from scipy.stats import fisher_exact
5067 >>> res = fisher_exact([[7, 17], [15, 5]], alternative='less')
5068 >>> res.statistic
5069 0.13725490196078433
5070 >>> res.pvalue
5071 0.0028841933752349743
5073 Using a significance level of 5%, we would reject the null hypothesis in
5074 favor of the alternative hypothesis: "The odds of experiencing acute
5075 mountain sickness with acetazolamide treatment are less than the odds of
5076 experiencing acute mountain sickness with placebo."
5078 .. note::
5080 Because the null distribution of Fisher's exact test is formed under
5081 the assumption that both row and column sums are fixed, the result of
5082 the test are conservative when applied to an experiment in which the
5083 row sums are not fixed.
5085 In this case, the column sums are fixed; there are 22 subjects in each
5086 group. But the number of cases of acute mountain sickness is not
5087 (and cannot be) fixed before conducting the experiment. It is a
5088 consequence.
5090 Boschloo's test does not depend on the assumption that the row sums
5091 are fixed, and consequently, it provides a more powerful test in this
5092 situation.
5094 >>> from scipy.stats import boschloo_exact
5095 >>> res = boschloo_exact([[7, 17], [15, 5]], alternative='less')
5096 >>> res.statistic
5097 0.0028841933752349743
5098 >>> res.pvalue
5099 0.0015141406667567101
5101 We verify that the p-value is less than with `fisher_exact`.
5103 """
5104 hypergeom = distributions.hypergeom
5105 # int32 is not enough for the algorithm
5106 c = np.asarray(table, dtype=np.int64)
5107 if not c.shape == (2, 2):
5108 raise ValueError("The input `table` must be of shape (2, 2).")
5110 if np.any(c < 0):
5111 raise ValueError("All values in `table` must be nonnegative.")
5113 if 0 in c.sum(axis=0) or 0 in c.sum(axis=1):
5114 # If both values in a row or column are zero, the p-value is 1 and
5115 # the odds ratio is NaN.
5116 return SignificanceResult(np.nan, 1.0)
5118 if c[1, 0] > 0 and c[0, 1] > 0:
5119 oddsratio = c[0, 0] * c[1, 1] / (c[1, 0] * c[0, 1])
5120 else:
5121 oddsratio = np.inf
5123 n1 = c[0, 0] + c[0, 1]
5124 n2 = c[1, 0] + c[1, 1]
5125 n = c[0, 0] + c[1, 0]
5127 def pmf(x):
5128 return hypergeom.pmf(x, n1 + n2, n1, n)
5130 if alternative == 'less':
5131 pvalue = hypergeom.cdf(c[0, 0], n1 + n2, n1, n)
5132 elif alternative == 'greater':
5133 # Same formula as the 'less' case, but with the second column.
5134 pvalue = hypergeom.cdf(c[0, 1], n1 + n2, n1, c[0, 1] + c[1, 1])
5135 elif alternative == 'two-sided':
5136 mode = int((n + 1) * (n1 + 1) / (n1 + n2 + 2))
5137 pexact = hypergeom.pmf(c[0, 0], n1 + n2, n1, n)
5138 pmode = hypergeom.pmf(mode, n1 + n2, n1, n)
5140 epsilon = 1e-14
5141 gamma = 1 + epsilon
5143 if np.abs(pexact - pmode) / np.maximum(pexact, pmode) <= epsilon:
5144 return SignificanceResult(oddsratio, 1.)
5146 elif c[0, 0] < mode:
5147 plower = hypergeom.cdf(c[0, 0], n1 + n2, n1, n)
5148 if hypergeom.pmf(n, n1 + n2, n1, n) > pexact * gamma:
5149 return SignificanceResult(oddsratio, plower)
5151 guess = _binary_search(lambda x: -pmf(x), -pexact * gamma, mode, n)
5152 pvalue = plower + hypergeom.sf(guess, n1 + n2, n1, n)
5153 else:
5154 pupper = hypergeom.sf(c[0, 0] - 1, n1 + n2, n1, n)
5155 if hypergeom.pmf(0, n1 + n2, n1, n) > pexact * gamma:
5156 return SignificanceResult(oddsratio, pupper)
5158 guess = _binary_search(pmf, pexact * gamma, 0, mode)
5159 pvalue = pupper + hypergeom.cdf(guess, n1 + n2, n1, n)
5160 else:
5161 msg = "`alternative` should be one of {'two-sided', 'less', 'greater'}"
5162 raise ValueError(msg)
5164 pvalue = min(pvalue, 1.0)
5166 return SignificanceResult(oddsratio, pvalue)
5169def spearmanr(a, b=None, axis=0, nan_policy='propagate',
5170 alternative='two-sided'):
5171 r"""Calculate a Spearman correlation coefficient with associated p-value.
5173 The Spearman rank-order correlation coefficient is a nonparametric measure
5174 of the monotonicity of the relationship between two datasets.
5175 Like other correlation coefficients,
5176 this one varies between -1 and +1 with 0 implying no correlation.
5177 Correlations of -1 or +1 imply an exact monotonic relationship. Positive
5178 correlations imply that as x increases, so does y. Negative correlations
5179 imply that as x increases, y decreases.
5181 The p-value roughly indicates the probability of an uncorrelated system
5182 producing datasets that have a Spearman correlation at least as extreme
5183 as the one computed from these datasets. Although calculation of the
5184 p-value does not make strong assumptions about the distributions underlying
5185 the samples, it is only accurate for very large samples (>500
5186 observations). For smaller sample sizes, consider a permutation test (see
5187 Examples section below).
5189 Parameters
5190 ----------
5191 a, b : 1D or 2D array_like, b is optional
5192 One or two 1-D or 2-D arrays containing multiple variables and
5193 observations. When these are 1-D, each represents a vector of
5194 observations of a single variable. For the behavior in the 2-D case,
5195 see under ``axis``, below.
5196 Both arrays need to have the same length in the ``axis`` dimension.
5197 axis : int or None, optional
5198 If axis=0 (default), then each column represents a variable, with
5199 observations in the rows. If axis=1, the relationship is transposed:
5200 each row represents a variable, while the columns contain observations.
5201 If axis=None, then both arrays will be raveled.
5202 nan_policy : {'propagate', 'raise', 'omit'}, optional
5203 Defines how to handle when input contains nan.
5204 The following options are available (default is 'propagate'):
5206 * 'propagate': returns nan
5207 * 'raise': throws an error
5208 * 'omit': performs the calculations ignoring nan values
5210 alternative : {'two-sided', 'less', 'greater'}, optional
5211 Defines the alternative hypothesis. Default is 'two-sided'.
5212 The following options are available:
5214 * 'two-sided': the correlation is nonzero
5215 * 'less': the correlation is negative (less than zero)
5216 * 'greater': the correlation is positive (greater than zero)
5218 .. versionadded:: 1.7.0
5220 Returns
5221 -------
5222 res : SignificanceResult
5223 An object containing attributes:
5225 statistic : float or ndarray (2-D square)
5226 Spearman correlation matrix or correlation coefficient (if only 2
5227 variables are given as parameters). Correlation matrix is square
5228 with length equal to total number of variables (columns or rows) in
5229 ``a`` and ``b`` combined.
5230 pvalue : float
5231 The p-value for a hypothesis test whose null hypothesis
5232 is that two samples have no ordinal correlation. See
5233 `alternative` above for alternative hypotheses. `pvalue` has the
5234 same shape as `statistic`.
5236 Warns
5237 -----
5238 `~scipy.stats.ConstantInputWarning`
5239 Raised if an input is a constant array. The correlation coefficient
5240 is not defined in this case, so ``np.nan`` is returned.
5242 References
5243 ----------
5244 .. [1] Zwillinger, D. and Kokoska, S. (2000). CRC Standard
5245 Probability and Statistics Tables and Formulae. Chapman & Hall: New
5246 York. 2000.
5247 Section 14.7
5248 .. [2] Kendall, M. G. and Stuart, A. (1973).
5249 The Advanced Theory of Statistics, Volume 2: Inference and Relationship.
5250 Griffin. 1973.
5251 Section 31.18
5252 .. [3] Kershenobich, D., Fierro, F. J., & Rojkind, M. (1970). The
5253 relationship between the free pool of proline and collagen content in
5254 human liver cirrhosis. The Journal of Clinical Investigation, 49(12),
5255 2246-2249.
5256 .. [4] Hollander, M., Wolfe, D. A., & Chicken, E. (2013). Nonparametric
5257 statistical methods. John Wiley & Sons.
5258 .. [5] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be
5259 Zero: Calculating Exact P-values When Permutations Are Randomly Drawn."
5260 Statistical Applications in Genetics and Molecular Biology 9.1 (2010).
5261 .. [6] Ludbrook, J., & Dudley, H. (1998). Why permutation tests are
5262 superior to t and F tests in biomedical research. The American
5263 Statistician, 52(2), 127-132.
5265 Examples
5266 --------
5267 Consider the following data from [3]_, which studied the relationship
5268 between free proline (an amino acid) and total collagen (a protein often
5269 found in connective tissue) in unhealthy human livers.
5271 The ``x`` and ``y`` arrays below record measurements of the two compounds.
5272 The observations are paired: each free proline measurement was taken from
5273 the same liver as the total collagen measurement at the same index.
5275 >>> import numpy as np
5276 >>> # total collagen (mg/g dry weight of liver)
5277 >>> x = np.array([7.1, 7.1, 7.2, 8.3, 9.4, 10.5, 11.4])
5278 >>> # free proline (μ mole/g dry weight of liver)
5279 >>> y = np.array([2.8, 2.9, 2.8, 2.6, 3.5, 4.6, 5.0])
5281 These data were analyzed in [4]_ using Spearman's correlation coefficient,
5282 a statistic sensitive to monotonic correlation between the samples.
5284 >>> from scipy import stats
5285 >>> res = stats.spearmanr(x, y)
5286 >>> res.statistic
5287 0.7000000000000001
5289 The value of this statistic tends to be high (close to 1) for samples with
5290 a strongly positive ordinal correlation, low (close to -1) for samples with
5291 a strongly negative ordinal correlation, and small in magnitude (close to
5292 zero) for samples with weak ordinal correlation.
5294 The test is performed by comparing the observed value of the
5295 statistic against the null distribution: the distribution of statistic
5296 values derived under the null hypothesis that total collagen and free
5297 proline measurements are independent.
5299 For this test, the statistic can be transformed such that the null
5300 distribution for large samples is Student's t distribution with
5301 ``len(x) - 2`` degrees of freedom.
5303 >>> import matplotlib.pyplot as plt
5304 >>> dof = len(x)-2 # len(x) == len(y)
5305 >>> dist = stats.t(df=dof)
5306 >>> t_vals = np.linspace(-5, 5, 100)
5307 >>> pdf = dist.pdf(t_vals)
5308 >>> fig, ax = plt.subplots(figsize=(8, 5))
5309 >>> def plot(ax): # we'll re-use this
5310 ... ax.plot(t_vals, pdf)
5311 ... ax.set_title("Spearman's Rho Test Null Distribution")
5312 ... ax.set_xlabel("statistic")
5313 ... ax.set_ylabel("probability density")
5314 >>> plot(ax)
5315 >>> plt.show()
5317 The comparison is quantified by the p-value: the proportion of values in
5318 the null distribution as extreme or more extreme than the observed
5319 value of the statistic. In a two-sided test in which the statistic is
5320 positive, elements of the null distribution greater than the transformed
5321 statistic and elements of the null distribution less than the negative of
5322 the observed statistic are both considered "more extreme".
5324 >>> fig, ax = plt.subplots(figsize=(8, 5))
5325 >>> plot(ax)
5326 >>> rs = res.statistic # original statistic
5327 >>> transformed = rs * np.sqrt(dof / ((rs+1.0)*(1.0-rs)))
5328 >>> pvalue = dist.cdf(-transformed) + dist.sf(transformed)
5329 >>> annotation = (f'p-value={pvalue:.4f}\n(shaded area)')
5330 >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8)
5331 >>> _ = ax.annotate(annotation, (2.7, 0.025), (3, 0.03), arrowprops=props)
5332 >>> i = t_vals >= transformed
5333 >>> ax.fill_between(t_vals[i], y1=0, y2=pdf[i], color='C0')
5334 >>> i = t_vals <= -transformed
5335 >>> ax.fill_between(t_vals[i], y1=0, y2=pdf[i], color='C0')
5336 >>> ax.set_xlim(-5, 5)
5337 >>> ax.set_ylim(0, 0.1)
5338 >>> plt.show()
5339 >>> res.pvalue
5340 0.07991669030889909 # two-sided p-value
5342 If the p-value is "small" - that is, if there is a low probability of
5343 sampling data from independent distributions that produces such an extreme
5344 value of the statistic - this may be taken as evidence against the null
5345 hypothesis in favor of the alternative: the distribution of total collagen
5346 and free proline are *not* independent. Note that:
5348 - The inverse is not true; that is, the test is not used to provide
5349 evidence for the null hypothesis.
5350 - The threshold for values that will be considered "small" is a choice that
5351 should be made before the data is analyzed [5]_ with consideration of the
5352 risks of both false positives (incorrectly rejecting the null hypothesis)
5353 and false negatives (failure to reject a false null hypothesis).
5354 - Small p-values are not evidence for a *large* effect; rather, they can
5355 only provide evidence for a "significant" effect, meaning that they are
5356 unlikely to have occurred under the null hypothesis.
5358 Suppose that before performing the experiment, the authors had reason
5359 to predict a positive correlation between the total collagen and free
5360 proline measurements, and that they had chosen to assess the plausibility
5361 of the null hypothesis against a one-sided alternative: free proline has a
5362 positive ordinal correlation with total collagen. In this case, only those
5363 values in the null distribution that are as great or greater than the
5364 observed statistic are considered to be more extreme.
5366 >>> res = stats.spearmanr(x, y, alternative='greater')
5367 >>> res.statistic
5368 0.7000000000000001 # same statistic
5369 >>> fig, ax = plt.subplots(figsize=(8, 5))
5370 >>> plot(ax)
5371 >>> pvalue = dist.sf(transformed)
5372 >>> annotation = (f'p-value={pvalue:.6f}\n(shaded area)')
5373 >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8)
5374 >>> _ = ax.annotate(annotation, (3, 0.018), (3.5, 0.03), arrowprops=props)
5375 >>> i = t_vals >= transformed
5376 >>> ax.fill_between(t_vals[i], y1=0, y2=pdf[i], color='C0')
5377 >>> ax.set_xlim(1, 5)
5378 >>> ax.set_ylim(0, 0.1)
5379 >>> plt.show()
5380 >>> res.pvalue
5381 0.03995834515444954 # one-sided p-value; half of the two-sided p-value
5383 Note that the t-distribution provides an asymptotic approximation of the
5384 null distribution; it is only accurate for samples with many observations.
5385 For small samples, it may be more appropriate to perform a permutation
5386 test: Under the null hypothesis that total collagen and free proline are
5387 independent, each of the free proline measurements were equally likely to
5388 have been observed with any of the total collagen measurements. Therefore,
5389 we can form an *exact* null distribution by calculating the statistic under
5390 each possible pairing of elements between ``x`` and ``y``.
5392 >>> def statistic(x): # explore all possible pairings by permuting `x`
5393 ... rs = stats.spearmanr(x, y).statistic # ignore pvalue
5394 ... transformed = rs * np.sqrt(dof / ((rs+1.0)*(1.0-rs)))
5395 ... return transformed
5396 >>> ref = stats.permutation_test((x,), statistic, alternative='greater',
5397 ... permutation_type='pairings')
5398 >>> fig, ax = plt.subplots(figsize=(8, 5))
5399 >>> plot(ax)
5400 >>> ax.hist(ref.null_distribution, np.linspace(-5, 5, 26),
5401 ... density=True)
5402 >>> ax.legend(['aymptotic approximation\n(many observations)',
5403 ... f'exact \n({len(ref.null_distribution)} permutations)'])
5404 >>> plt.show()
5405 >>> ref.pvalue
5406 0.04563492063492063 # exact one-sided p-value
5408 """
5409 if axis is not None and axis > 1:
5410 raise ValueError("spearmanr only handles 1-D or 2-D arrays, "
5411 "supplied axis argument {}, please use only "
5412 "values 0, 1 or None for axis".format(axis))
5414 a, axisout = _chk_asarray(a, axis)
5415 if a.ndim > 2:
5416 raise ValueError("spearmanr only handles 1-D or 2-D arrays")
5418 if b is None:
5419 if a.ndim < 2:
5420 raise ValueError("`spearmanr` needs at least 2 "
5421 "variables to compare")
5422 else:
5423 # Concatenate a and b, so that we now only have to handle the case
5424 # of a 2-D `a`.
5425 b, _ = _chk_asarray(b, axis)
5426 if axisout == 0:
5427 a = np.column_stack((a, b))
5428 else:
5429 a = np.row_stack((a, b))
5431 n_vars = a.shape[1 - axisout]
5432 n_obs = a.shape[axisout]
5433 if n_obs <= 1:
5434 # Handle empty arrays or single observations.
5435 res = SignificanceResult(np.nan, np.nan)
5436 res.correlation = np.nan
5437 return res
5439 warn_msg = ("An input array is constant; the correlation coefficient "
5440 "is not defined.")
5441 if axisout == 0:
5442 if (a[:, 0][0] == a[:, 0]).all() or (a[:, 1][0] == a[:, 1]).all():
5443 # If an input is constant, the correlation coefficient
5444 # is not defined.
5445 warnings.warn(stats.ConstantInputWarning(warn_msg))
5446 res = SignificanceResult(np.nan, np.nan)
5447 res.correlation = np.nan
5448 return res
5449 else: # case when axisout == 1 b/c a is 2 dim only
5450 if (a[0, :][0] == a[0, :]).all() or (a[1, :][0] == a[1, :]).all():
5451 # If an input is constant, the correlation coefficient
5452 # is not defined.
5453 warnings.warn(stats.ConstantInputWarning(warn_msg))
5454 res = SignificanceResult(np.nan, np.nan)
5455 res.correlation = np.nan
5456 return res
5458 a_contains_nan, nan_policy = _contains_nan(a, nan_policy)
5459 variable_has_nan = np.zeros(n_vars, dtype=bool)
5460 if a_contains_nan:
5461 if nan_policy == 'omit':
5462 return mstats_basic.spearmanr(a, axis=axis, nan_policy=nan_policy,
5463 alternative=alternative)
5464 elif nan_policy == 'propagate':
5465 if a.ndim == 1 or n_vars <= 2:
5466 res = SignificanceResult(np.nan, np.nan)
5467 res.correlation = np.nan
5468 return res
5469 else:
5470 # Keep track of variables with NaNs, set the outputs to NaN
5471 # only for those variables
5472 variable_has_nan = np.isnan(a).any(axis=axisout)
5474 a_ranked = np.apply_along_axis(rankdata, axisout, a)
5475 rs = np.corrcoef(a_ranked, rowvar=axisout)
5476 dof = n_obs - 2 # degrees of freedom
5478 # rs can have elements equal to 1, so avoid zero division warnings
5479 with np.errstate(divide='ignore'):
5480 # clip the small negative values possibly caused by rounding
5481 # errors before taking the square root
5482 t = rs * np.sqrt((dof/((rs+1.0)*(1.0-rs))).clip(0))
5484 t, prob = _ttest_finish(dof, t, alternative)
5486 # For backwards compatibility, return scalars when comparing 2 columns
5487 if rs.shape == (2, 2):
5488 res = SignificanceResult(rs[1, 0], prob[1, 0])
5489 res.correlation = rs[1, 0]
5490 return res
5491 else:
5492 rs[variable_has_nan, :] = np.nan
5493 rs[:, variable_has_nan] = np.nan
5494 res = SignificanceResult(rs, prob)
5495 res.correlation = rs
5496 return res
5499def pointbiserialr(x, y):
5500 r"""Calculate a point biserial correlation coefficient and its p-value.
5502 The point biserial correlation is used to measure the relationship
5503 between a binary variable, x, and a continuous variable, y. Like other
5504 correlation coefficients, this one varies between -1 and +1 with 0
5505 implying no correlation. Correlations of -1 or +1 imply a determinative
5506 relationship.
5508 This function may be computed using a shortcut formula but produces the
5509 same result as `pearsonr`.
5511 Parameters
5512 ----------
5513 x : array_like of bools
5514 Input array.
5515 y : array_like
5516 Input array.
5518 Returns
5519 -------
5520 res: SignificanceResult
5521 An object containing attributes:
5523 statistic : float
5524 The R value.
5525 pvalue : float
5526 The two-sided p-value.
5528 Notes
5529 -----
5530 `pointbiserialr` uses a t-test with ``n-1`` degrees of freedom.
5531 It is equivalent to `pearsonr`.
5533 The value of the point-biserial correlation can be calculated from:
5535 .. math::
5537 r_{pb} = \frac{\overline{Y_1} - \overline{Y_0}}
5538 {s_y}
5539 \sqrt{\frac{N_0 N_1}
5540 {N (N - 1)}}
5542 Where :math:`\overline{Y_{0}}` and :math:`\overline{Y_{1}}` are means
5543 of the metric observations coded 0 and 1 respectively; :math:`N_{0}` and
5544 :math:`N_{1}` are number of observations coded 0 and 1 respectively;
5545 :math:`N` is the total number of observations and :math:`s_{y}` is the
5546 standard deviation of all the metric observations.
5548 A value of :math:`r_{pb}` that is significantly different from zero is
5549 completely equivalent to a significant difference in means between the two
5550 groups. Thus, an independent groups t Test with :math:`N-2` degrees of
5551 freedom may be used to test whether :math:`r_{pb}` is nonzero. The
5552 relation between the t-statistic for comparing two independent groups and
5553 :math:`r_{pb}` is given by:
5555 .. math::
5557 t = \sqrt{N - 2}\frac{r_{pb}}{\sqrt{1 - r^{2}_{pb}}}
5559 References
5560 ----------
5561 .. [1] J. Lev, "The Point Biserial Coefficient of Correlation", Ann. Math.
5562 Statist., Vol. 20, no.1, pp. 125-126, 1949.
5564 .. [2] R.F. Tate, "Correlation Between a Discrete and a Continuous
5565 Variable. Point-Biserial Correlation.", Ann. Math. Statist., Vol. 25,
5566 np. 3, pp. 603-607, 1954.
5568 .. [3] D. Kornbrot "Point Biserial Correlation", In Wiley StatsRef:
5569 Statistics Reference Online (eds N. Balakrishnan, et al.), 2014.
5570 :doi:`10.1002/9781118445112.stat06227`
5572 Examples
5573 --------
5574 >>> import numpy as np
5575 >>> from scipy import stats
5576 >>> a = np.array([0, 0, 0, 1, 1, 1, 1])
5577 >>> b = np.arange(7)
5578 >>> stats.pointbiserialr(a, b)
5579 (0.8660254037844386, 0.011724811003954652)
5580 >>> stats.pearsonr(a, b)
5581 (0.86602540378443871, 0.011724811003954626)
5582 >>> np.corrcoef(a, b)
5583 array([[ 1. , 0.8660254],
5584 [ 0.8660254, 1. ]])
5586 """
5587 rpb, prob = pearsonr(x, y)
5588 # create result object with alias for backward compatibility
5589 res = SignificanceResult(rpb, prob)
5590 res.correlation = rpb
5591 return res
5594def kendalltau(x, y, initial_lexsort=None, nan_policy='propagate',
5595 method='auto', variant='b', alternative='two-sided'):
5596 r"""Calculate Kendall's tau, a correlation measure for ordinal data.
5598 Kendall's tau is a measure of the correspondence between two rankings.
5599 Values close to 1 indicate strong agreement, and values close to -1
5600 indicate strong disagreement. This implements two variants of Kendall's
5601 tau: tau-b (the default) and tau-c (also known as Stuart's tau-c). These
5602 differ only in how they are normalized to lie within the range -1 to 1;
5603 the hypothesis tests (their p-values) are identical. Kendall's original
5604 tau-a is not implemented separately because both tau-b and tau-c reduce
5605 to tau-a in the absence of ties.
5607 Parameters
5608 ----------
5609 x, y : array_like
5610 Arrays of rankings, of the same shape. If arrays are not 1-D, they
5611 will be flattened to 1-D.
5612 initial_lexsort : bool, optional, deprecated
5613 This argument is unused.
5615 .. deprecated:: 1.10.0
5616 `kendalltau` keyword argument `initial_lexsort` is deprecated as it
5617 is unused and will be removed in SciPy 1.12.0.
5618 nan_policy : {'propagate', 'raise', 'omit'}, optional
5619 Defines how to handle when input contains nan.
5620 The following options are available (default is 'propagate'):
5622 * 'propagate': returns nan
5623 * 'raise': throws an error
5624 * 'omit': performs the calculations ignoring nan values
5626 method : {'auto', 'asymptotic', 'exact'}, optional
5627 Defines which method is used to calculate the p-value [5]_.
5628 The following options are available (default is 'auto'):
5630 * 'auto': selects the appropriate method based on a trade-off
5631 between speed and accuracy
5632 * 'asymptotic': uses a normal approximation valid for large samples
5633 * 'exact': computes the exact p-value, but can only be used if no ties
5634 are present. As the sample size increases, the 'exact' computation
5635 time may grow and the result may lose some precision.
5636 variant : {'b', 'c'}, optional
5637 Defines which variant of Kendall's tau is returned. Default is 'b'.
5638 alternative : {'two-sided', 'less', 'greater'}, optional
5639 Defines the alternative hypothesis. Default is 'two-sided'.
5640 The following options are available:
5642 * 'two-sided': the rank correlation is nonzero
5643 * 'less': the rank correlation is negative (less than zero)
5644 * 'greater': the rank correlation is positive (greater than zero)
5646 Returns
5647 -------
5648 res : SignificanceResult
5649 An object containing attributes:
5651 statistic : float
5652 The tau statistic.
5653 pvalue : float
5654 The p-value for a hypothesis test whose null hypothesis is
5655 an absence of association, tau = 0.
5657 See Also
5658 --------
5659 spearmanr : Calculates a Spearman rank-order correlation coefficient.
5660 theilslopes : Computes the Theil-Sen estimator for a set of points (x, y).
5661 weightedtau : Computes a weighted version of Kendall's tau.
5663 Notes
5664 -----
5665 The definition of Kendall's tau that is used is [2]_::
5667 tau_b = (P - Q) / sqrt((P + Q + T) * (P + Q + U))
5669 tau_c = 2 (P - Q) / (n**2 * (m - 1) / m)
5671 where P is the number of concordant pairs, Q the number of discordant
5672 pairs, T the number of ties only in `x`, and U the number of ties only in
5673 `y`. If a tie occurs for the same pair in both `x` and `y`, it is not
5674 added to either T or U. n is the total number of samples, and m is the
5675 number of unique values in either `x` or `y`, whichever is smaller.
5677 References
5678 ----------
5679 .. [1] Maurice G. Kendall, "A New Measure of Rank Correlation", Biometrika
5680 Vol. 30, No. 1/2, pp. 81-93, 1938.
5681 .. [2] Maurice G. Kendall, "The treatment of ties in ranking problems",
5682 Biometrika Vol. 33, No. 3, pp. 239-251. 1945.
5683 .. [3] Gottfried E. Noether, "Elements of Nonparametric Statistics", John
5684 Wiley & Sons, 1967.
5685 .. [4] Peter M. Fenwick, "A new data structure for cumulative frequency
5686 tables", Software: Practice and Experience, Vol. 24, No. 3,
5687 pp. 327-336, 1994.
5688 .. [5] Maurice G. Kendall, "Rank Correlation Methods" (4th Edition),
5689 Charles Griffin & Co., 1970.
5690 .. [6] Kershenobich, D., Fierro, F. J., & Rojkind, M. (1970). The
5691 relationship between the free pool of proline and collagen content
5692 in human liver cirrhosis. The Journal of Clinical Investigation,
5693 49(12), 2246-2249.
5694 .. [7] Hollander, M., Wolfe, D. A., & Chicken, E. (2013). Nonparametric
5695 statistical methods. John Wiley & Sons.
5696 .. [8] B. Phipson and G. K. Smyth. "Permutation P-values Should Never Be
5697 Zero: Calculating Exact P-values When Permutations Are Randomly
5698 Drawn." Statistical Applications in Genetics and Molecular Biology
5699 9.1 (2010).
5701 Examples
5702 --------
5703 Consider the following data from [6]_, which studied the relationship
5704 between free proline (an amino acid) and total collagen (a protein often
5705 found in connective tissue) in unhealthy human livers.
5707 The ``x`` and ``y`` arrays below record measurements of the two compounds.
5708 The observations are paired: each free proline measurement was taken from
5709 the same liver as the total collagen measurement at the same index.
5711 >>> import numpy as np
5712 >>> # total collagen (mg/g dry weight of liver)
5713 >>> x = np.array([7.1, 7.1, 7.2, 8.3, 9.4, 10.5, 11.4])
5714 >>> # free proline (μ mole/g dry weight of liver)
5715 >>> y = np.array([2.8, 2.9, 2.8, 2.6, 3.5, 4.6, 5.0])
5717 These data were analyzed in [7]_ using Spearman's correlation coefficient,
5718 a statistic similar to to Kendall's tau in that it is also sensitive to
5719 ordinal correlation between the samples. Let's perform an analagous study
5720 using Kendall's tau.
5722 >>> from scipy import stats
5723 >>> res = stats.kendalltau(x, y)
5724 >>> res.statistic
5725 0.5499999999999999
5727 The value of this statistic tends to be high (close to 1) for samples with
5728 a strongly positive ordinal correlation, low (close to -1) for samples with
5729 a strongly negative ordinal correlation, and small in magnitude (close to
5730 zero) for samples with weak ordinal correlation.
5732 The test is performed by comparing the observed value of the
5733 statistic against the null distribution: the distribution of statistic
5734 values derived under the null hypothesis that total collagen and free
5735 proline measurements are independent.
5737 For this test, the null distribution for large samples without ties is
5738 approximated as the normal distribution with variance
5739 ``(2*(2*n + 5))/(9*n*(n - 1))``, where ``n = len(x)``.
5741 >>> import matplotlib.pyplot as plt
5742 >>> n = len(x) # len(x) == len(y)
5743 >>> var = (2*(2*n + 5))/(9*n*(n - 1))
5744 >>> dist = stats.norm(scale=np.sqrt(var))
5745 >>> z_vals = np.linspace(-1.25, 1.25, 100)
5746 >>> pdf = dist.pdf(z_vals)
5747 >>> fig, ax = plt.subplots(figsize=(8, 5))
5748 >>> def plot(ax): # we'll re-use this
5749 ... ax.plot(z_vals, pdf)
5750 ... ax.set_title("Kendall Tau Test Null Distribution")
5751 ... ax.set_xlabel("statistic")
5752 ... ax.set_ylabel("probability density")
5753 >>> plot(ax)
5754 >>> plt.show()
5756 The comparison is quantified by the p-value: the proportion of values in
5757 the null distribution as extreme or more extreme than the observed
5758 value of the statistic. In a two-sided test in which the statistic is
5759 positive, elements of the null distribution greater than the transformed
5760 statistic and elements of the null distribution less than the negative of
5761 the observed statistic are both considered "more extreme".
5763 >>> fig, ax = plt.subplots(figsize=(8, 5))
5764 >>> plot(ax)
5765 >>> pvalue = dist.cdf(-res.statistic) + dist.sf(res.statistic)
5766 >>> annotation = (f'p-value={pvalue:.4f}\n(shaded area)')
5767 >>> props = dict(facecolor='black', width=1, headwidth=5, headlength=8)
5768 >>> _ = ax.annotate(annotation, (0.65, 0.15), (0.8, 0.3), arrowprops=props)
5769 >>> i = z_vals >= res.statistic
5770 >>> ax.fill_between(z_vals[i], y1=0, y2=pdf[i], color='C0')
5771 >>> i = z_vals <= -res.statistic
5772 >>> ax.fill_between(z_vals[i], y1=0, y2=pdf[i], color='C0')
5773 >>> ax.set_xlim(-1.25, 1.25)
5774 >>> ax.set_ylim(0, 0.5)
5775 >>> plt.show()
5776 >>> res.pvalue
5777 0.09108705741631495 # approximate p-value
5779 Note that there is slight disagreement between the shaded area of the curve
5780 and the p-value returned by `kendalltau`. This is because our data has
5781 ties, and we have neglected a tie correction to the null distribution
5782 variance that `kendalltau` performs. For samples without ties, the shaded
5783 areas of our plot and p-value returned by `kendalltau` would match exactly.
5785 If the p-value is "small" - that is, if there is a low probability of
5786 sampling data from independent distributions that produces such an extreme
5787 value of the statistic - this may be taken as evidence against the null
5788 hypothesis in favor of the alternative: the distribution of total collagen
5789 and free proline are *not* independent. Note that:
5791 - The inverse is not true; that is, the test is not used to provide
5792 evidence for the null hypothesis.
5793 - The threshold for values that will be considered "small" is a choice that
5794 should be made before the data is analyzed [8]_ with consideration of the
5795 risks of both false positives (incorrectly rejecting the null hypothesis)
5796 and false negatives (failure to reject a false null hypothesis).
5797 - Small p-values are not evidence for a *large* effect; rather, they can
5798 only provide evidence for a "significant" effect, meaning that they are
5799 unlikely to have occurred under the null hypothesis.
5801 For samples without ties of moderate size, `kendalltau` can compute the
5802 p-value exactly. However, in the presence of ties, `kendalltau` resorts
5803 to an asymptotic approximation. Nonetheles, we can use a permutation test
5804 to compute the null distribution exactly: Under the null hypothesis that
5805 total collagen and free proline are independent, each of the free proline
5806 measurements were equally likely to have been observed with any of the
5807 total collagen measurements. Therefore, we can form an *exact* null
5808 distribution by calculating the statistic under each possible pairing of
5809 elements between ``x`` and ``y``.
5811 >>> def statistic(x): # explore all possible pairings by permuting `x`
5812 ... return stats.kendalltau(x, y).statistic # ignore pvalue
5813 >>> ref = stats.permutation_test((x,), statistic,
5814 ... permutation_type='pairings')
5815 >>> fig, ax = plt.subplots(figsize=(8, 5))
5816 >>> plot(ax)
5817 >>> bins = np.linspace(-1.25, 1.25, 25)
5818 >>> ax.hist(ref.null_distribution, bins=bins, density=True)
5819 >>> ax.legend(['aymptotic approximation\n(many observations)',
5820 ... 'exact null distribution'])
5821 >>> plot(ax)
5822 >>> plt.show()
5823 >>> ref.pvalue
5824 0.12222222222222222 # exact p-value
5826 Note that there is significant disagreement between the exact p-value
5827 calculated here and the approximation returned by `kendalltau` above. For
5828 small samples with ties, consider performing a permutation test for more
5829 accurate results.
5831 """
5832 if initial_lexsort is not None:
5833 msg = ("'kendalltau' keyword argument 'initial_lexsort' is deprecated"
5834 " as it is unused and will be removed in SciPy 1.12.0.")
5835 warnings.warn(msg, DeprecationWarning, stacklevel=2)
5837 x = np.asarray(x).ravel()
5838 y = np.asarray(y).ravel()
5840 if x.size != y.size:
5841 raise ValueError("All inputs to `kendalltau` must be of the same "
5842 f"size, found x-size {x.size} and y-size {y.size}")
5843 elif not x.size or not y.size:
5844 # Return NaN if arrays are empty
5845 res = SignificanceResult(np.nan, np.nan)
5846 res.correlation = np.nan
5847 return res
5849 # check both x and y
5850 cnx, npx = _contains_nan(x, nan_policy)
5851 cny, npy = _contains_nan(y, nan_policy)
5852 contains_nan = cnx or cny
5853 if npx == 'omit' or npy == 'omit':
5854 nan_policy = 'omit'
5856 if contains_nan and nan_policy == 'propagate':
5857 res = SignificanceResult(np.nan, np.nan)
5858 res.correlation = np.nan
5859 return res
5861 elif contains_nan and nan_policy == 'omit':
5862 x = ma.masked_invalid(x)
5863 y = ma.masked_invalid(y)
5864 if variant == 'b':
5865 return mstats_basic.kendalltau(x, y, method=method, use_ties=True,
5866 alternative=alternative)
5867 else:
5868 message = ("nan_policy='omit' is currently compatible only with "
5869 "variant='b'.")
5870 raise ValueError(message)
5872 def count_rank_tie(ranks):
5873 cnt = np.bincount(ranks).astype('int64', copy=False)
5874 cnt = cnt[cnt > 1]
5875 # Python ints to avoid overflow down the line
5876 return (int((cnt * (cnt - 1) // 2).sum()),
5877 int((cnt * (cnt - 1.) * (cnt - 2)).sum()),
5878 int((cnt * (cnt - 1.) * (2*cnt + 5)).sum()))
5880 size = x.size
5881 perm = np.argsort(y) # sort on y and convert y to dense ranks
5882 x, y = x[perm], y[perm]
5883 y = np.r_[True, y[1:] != y[:-1]].cumsum(dtype=np.intp)
5885 # stable sort on x and convert x to dense ranks
5886 perm = np.argsort(x, kind='mergesort')
5887 x, y = x[perm], y[perm]
5888 x = np.r_[True, x[1:] != x[:-1]].cumsum(dtype=np.intp)
5890 dis = _kendall_dis(x, y) # discordant pairs
5892 obs = np.r_[True, (x[1:] != x[:-1]) | (y[1:] != y[:-1]), True]
5893 cnt = np.diff(np.nonzero(obs)[0]).astype('int64', copy=False)
5895 ntie = int((cnt * (cnt - 1) // 2).sum()) # joint ties
5896 xtie, x0, x1 = count_rank_tie(x) # ties in x, stats
5897 ytie, y0, y1 = count_rank_tie(y) # ties in y, stats
5899 tot = (size * (size - 1)) // 2
5901 if xtie == tot or ytie == tot:
5902 res = SignificanceResult(np.nan, np.nan)
5903 res.correlation = np.nan
5904 return res
5906 # Note that tot = con + dis + (xtie - ntie) + (ytie - ntie) + ntie
5907 # = con + dis + xtie + ytie - ntie
5908 con_minus_dis = tot - xtie - ytie + ntie - 2 * dis
5909 if variant == 'b':
5910 tau = con_minus_dis / np.sqrt(tot - xtie) / np.sqrt(tot - ytie)
5911 elif variant == 'c':
5912 minclasses = min(len(set(x)), len(set(y)))
5913 tau = 2*con_minus_dis / (size**2 * (minclasses-1)/minclasses)
5914 else:
5915 raise ValueError(f"Unknown variant of the method chosen: {variant}. "
5916 "variant must be 'b' or 'c'.")
5918 # Limit range to fix computational errors
5919 tau = min(1., max(-1., tau))
5921 # The p-value calculation is the same for all variants since the p-value
5922 # depends only on con_minus_dis.
5923 if method == 'exact' and (xtie != 0 or ytie != 0):
5924 raise ValueError("Ties found, exact method cannot be used.")
5926 if method == 'auto':
5927 if (xtie == 0 and ytie == 0) and (size <= 33 or
5928 min(dis, tot-dis) <= 1):
5929 method = 'exact'
5930 else:
5931 method = 'asymptotic'
5933 if xtie == 0 and ytie == 0 and method == 'exact':
5934 pvalue = mstats_basic._kendall_p_exact(size, tot-dis, alternative)
5935 elif method == 'asymptotic':
5936 # con_minus_dis is approx normally distributed with this variance [3]_
5937 m = size * (size - 1.)
5938 var = ((m * (2*size + 5) - x1 - y1) / 18 +
5939 (2 * xtie * ytie) / m + x0 * y0 / (9 * m * (size - 2)))
5940 z = con_minus_dis / np.sqrt(var)
5941 _, pvalue = _normtest_finish(z, alternative)
5942 else:
5943 raise ValueError(f"Unknown method {method} specified. Use 'auto', "
5944 "'exact' or 'asymptotic'.")
5946 # create result object with alias for backward compatibility
5947 res = SignificanceResult(tau, pvalue)
5948 res.correlation = tau
5949 return res
5952def weightedtau(x, y, rank=True, weigher=None, additive=True):
5953 r"""Compute a weighted version of Kendall's :math:`\tau`.
5955 The weighted :math:`\tau` is a weighted version of Kendall's
5956 :math:`\tau` in which exchanges of high weight are more influential than
5957 exchanges of low weight. The default parameters compute the additive
5958 hyperbolic version of the index, :math:`\tau_\mathrm h`, which has
5959 been shown to provide the best balance between important and
5960 unimportant elements [1]_.
5962 The weighting is defined by means of a rank array, which assigns a
5963 nonnegative rank to each element (higher importance ranks being
5964 associated with smaller values, e.g., 0 is the highest possible rank),
5965 and a weigher function, which assigns a weight based on the rank to
5966 each element. The weight of an exchange is then the sum or the product
5967 of the weights of the ranks of the exchanged elements. The default
5968 parameters compute :math:`\tau_\mathrm h`: an exchange between
5969 elements with rank :math:`r` and :math:`s` (starting from zero) has
5970 weight :math:`1/(r+1) + 1/(s+1)`.
5972 Specifying a rank array is meaningful only if you have in mind an
5973 external criterion of importance. If, as it usually happens, you do
5974 not have in mind a specific rank, the weighted :math:`\tau` is
5975 defined by averaging the values obtained using the decreasing
5976 lexicographical rank by (`x`, `y`) and by (`y`, `x`). This is the
5977 behavior with default parameters. Note that the convention used
5978 here for ranking (lower values imply higher importance) is opposite
5979 to that used by other SciPy statistical functions.
5981 Parameters
5982 ----------
5983 x, y : array_like
5984 Arrays of scores, of the same shape. If arrays are not 1-D, they will
5985 be flattened to 1-D.
5986 rank : array_like of ints or bool, optional
5987 A nonnegative rank assigned to each element. If it is None, the
5988 decreasing lexicographical rank by (`x`, `y`) will be used: elements of
5989 higher rank will be those with larger `x`-values, using `y`-values to
5990 break ties (in particular, swapping `x` and `y` will give a different
5991 result). If it is False, the element indices will be used
5992 directly as ranks. The default is True, in which case this
5993 function returns the average of the values obtained using the
5994 decreasing lexicographical rank by (`x`, `y`) and by (`y`, `x`).
5995 weigher : callable, optional
5996 The weigher function. Must map nonnegative integers (zero
5997 representing the most important element) to a nonnegative weight.
5998 The default, None, provides hyperbolic weighing, that is,
5999 rank :math:`r` is mapped to weight :math:`1/(r+1)`.
6000 additive : bool, optional
6001 If True, the weight of an exchange is computed by adding the
6002 weights of the ranks of the exchanged elements; otherwise, the weights
6003 are multiplied. The default is True.
6005 Returns
6006 -------
6007 res: SignificanceResult
6008 An object containing attributes:
6010 statistic : float
6011 The weighted :math:`\tau` correlation index.
6012 pvalue : float
6013 Presently ``np.nan``, as the null distribution of the statistic is
6014 unknown (even in the additive hyperbolic case).
6016 See Also
6017 --------
6018 kendalltau : Calculates Kendall's tau.
6019 spearmanr : Calculates a Spearman rank-order correlation coefficient.
6020 theilslopes : Computes the Theil-Sen estimator for a set of points (x, y).
6022 Notes
6023 -----
6024 This function uses an :math:`O(n \log n)`, mergesort-based algorithm
6025 [1]_ that is a weighted extension of Knight's algorithm for Kendall's
6026 :math:`\tau` [2]_. It can compute Shieh's weighted :math:`\tau` [3]_
6027 between rankings without ties (i.e., permutations) by setting
6028 `additive` and `rank` to False, as the definition given in [1]_ is a
6029 generalization of Shieh's.
6031 NaNs are considered the smallest possible score.
6033 .. versionadded:: 0.19.0
6035 References
6036 ----------
6037 .. [1] Sebastiano Vigna, "A weighted correlation index for rankings with
6038 ties", Proceedings of the 24th international conference on World
6039 Wide Web, pp. 1166-1176, ACM, 2015.
6040 .. [2] W.R. Knight, "A Computer Method for Calculating Kendall's Tau with
6041 Ungrouped Data", Journal of the American Statistical Association,
6042 Vol. 61, No. 314, Part 1, pp. 436-439, 1966.
6043 .. [3] Grace S. Shieh. "A weighted Kendall's tau statistic", Statistics &
6044 Probability Letters, Vol. 39, No. 1, pp. 17-24, 1998.
6046 Examples
6047 --------
6048 >>> import numpy as np
6049 >>> from scipy import stats
6050 >>> x = [12, 2, 1, 12, 2]
6051 >>> y = [1, 4, 7, 1, 0]
6052 >>> res = stats.weightedtau(x, y)
6053 >>> res.statistic
6054 -0.56694968153682723
6055 >>> res.pvalue
6056 nan
6057 >>> res = stats.weightedtau(x, y, additive=False)
6058 >>> res.statistic
6059 -0.62205716951801038
6061 NaNs are considered the smallest possible score:
6063 >>> x = [12, 2, 1, 12, 2]
6064 >>> y = [1, 4, 7, 1, np.nan]
6065 >>> res = stats.weightedtau(x, y)
6066 >>> res.statistic
6067 -0.56694968153682723
6069 This is exactly Kendall's tau:
6071 >>> x = [12, 2, 1, 12, 2]
6072 >>> y = [1, 4, 7, 1, 0]
6073 >>> res = stats.weightedtau(x, y, weigher=lambda x: 1)
6074 >>> res.statistic
6075 -0.47140452079103173
6077 >>> x = [12, 2, 1, 12, 2]
6078 >>> y = [1, 4, 7, 1, 0]
6079 >>> stats.weightedtau(x, y, rank=None)
6080 SignificanceResult(statistic=-0.4157652301037516, pvalue=nan)
6081 >>> stats.weightedtau(y, x, rank=None)
6082 SignificanceResult(statistic=-0.7181341329699028, pvalue=nan)
6084 """
6085 x = np.asarray(x).ravel()
6086 y = np.asarray(y).ravel()
6088 if x.size != y.size:
6089 raise ValueError("All inputs to `weightedtau` must be "
6090 "of the same size, "
6091 "found x-size {} and y-size {}".format(x.size, y.size))
6092 if not x.size:
6093 # Return NaN if arrays are empty
6094 res = SignificanceResult(np.nan, np.nan)
6095 res.correlation = np.nan
6096 return res
6098 # If there are NaNs we apply _toint64()
6099 if np.isnan(np.sum(x)):
6100 x = _toint64(x)
6101 if np.isnan(np.sum(y)):
6102 y = _toint64(y)
6104 # Reduce to ranks unsupported types
6105 if x.dtype != y.dtype:
6106 if x.dtype != np.int64:
6107 x = _toint64(x)
6108 if y.dtype != np.int64:
6109 y = _toint64(y)
6110 else:
6111 if x.dtype not in (np.int32, np.int64, np.float32, np.float64):
6112 x = _toint64(x)
6113 y = _toint64(y)
6115 if rank is True:
6116 tau = (
6117 _weightedrankedtau(x, y, None, weigher, additive) +
6118 _weightedrankedtau(y, x, None, weigher, additive)
6119 ) / 2
6120 res = SignificanceResult(tau, np.nan)
6121 res.correlation = tau
6122 return res
6124 if rank is False:
6125 rank = np.arange(x.size, dtype=np.intp)
6126 elif rank is not None:
6127 rank = np.asarray(rank).ravel()
6128 if rank.size != x.size:
6129 raise ValueError(
6130 "All inputs to `weightedtau` must be of the same size, "
6131 "found x-size {} and rank-size {}".format(x.size, rank.size)
6132 )
6134 tau = _weightedrankedtau(x, y, rank, weigher, additive)
6135 res = SignificanceResult(tau, np.nan)
6136 res.correlation = tau
6137 return res
6140# FROM MGCPY: https://github.com/neurodata/mgcpy
6143class _ParallelP:
6144 """Helper function to calculate parallel p-value."""
6146 def __init__(self, x, y, random_states):
6147 self.x = x
6148 self.y = y
6149 self.random_states = random_states
6151 def __call__(self, index):
6152 order = self.random_states[index].permutation(self.y.shape[0])
6153 permy = self.y[order][:, order]
6155 # calculate permuted stats, store in null distribution
6156 perm_stat = _mgc_stat(self.x, permy)[0]
6158 return perm_stat
6161def _perm_test(x, y, stat, reps=1000, workers=-1, random_state=None):
6162 r"""Helper function that calculates the p-value. See below for uses.
6164 Parameters
6165 ----------
6166 x, y : ndarray
6167 `x` and `y` have shapes `(n, p)` and `(n, q)`.
6168 stat : float
6169 The sample test statistic.
6170 reps : int, optional
6171 The number of replications used to estimate the null when using the
6172 permutation test. The default is 1000 replications.
6173 workers : int or map-like callable, optional
6174 If `workers` is an int the population is subdivided into `workers`
6175 sections and evaluated in parallel (uses
6176 `multiprocessing.Pool <multiprocessing>`). Supply `-1` to use all cores
6177 available to the Process. Alternatively supply a map-like callable,
6178 such as `multiprocessing.Pool.map` for evaluating the population in
6179 parallel. This evaluation is carried out as `workers(func, iterable)`.
6180 Requires that `func` be pickleable.
6181 random_state : {None, int, `numpy.random.Generator`,
6182 `numpy.random.RandomState`}, optional
6184 If `seed` is None (or `np.random`), the `numpy.random.RandomState`
6185 singleton is used.
6186 If `seed` is an int, a new ``RandomState`` instance is used,
6187 seeded with `seed`.
6188 If `seed` is already a ``Generator`` or ``RandomState`` instance then
6189 that instance is used.
6191 Returns
6192 -------
6193 pvalue : float
6194 The sample test p-value.
6195 null_dist : list
6196 The approximated null distribution.
6198 """
6199 # generate seeds for each rep (change to new parallel random number
6200 # capabilities in numpy >= 1.17+)
6201 random_state = check_random_state(random_state)
6202 random_states = [np.random.RandomState(rng_integers(random_state, 1 << 32,
6203 size=4, dtype=np.uint32)) for _ in range(reps)]
6205 # parallelizes with specified workers over number of reps and set seeds
6206 parallelp = _ParallelP(x=x, y=y, random_states=random_states)
6207 with MapWrapper(workers) as mapwrapper:
6208 null_dist = np.array(list(mapwrapper(parallelp, range(reps))))
6210 # calculate p-value and significant permutation map through list
6211 pvalue = (1 + (null_dist >= stat).sum()) / (1 + reps)
6213 return pvalue, null_dist
6216def _euclidean_dist(x):
6217 return cdist(x, x)
6220MGCResult = _make_tuple_bunch('MGCResult',
6221 ['statistic', 'pvalue', 'mgc_dict'], [])
6224def multiscale_graphcorr(x, y, compute_distance=_euclidean_dist, reps=1000,
6225 workers=1, is_twosamp=False, random_state=None):
6226 r"""Computes the Multiscale Graph Correlation (MGC) test statistic.
6228 Specifically, for each point, MGC finds the :math:`k`-nearest neighbors for
6229 one property (e.g. cloud density), and the :math:`l`-nearest neighbors for
6230 the other property (e.g. grass wetness) [1]_. This pair :math:`(k, l)` is
6231 called the "scale". A priori, however, it is not know which scales will be
6232 most informative. So, MGC computes all distance pairs, and then efficiently
6233 computes the distance correlations for all scales. The local correlations
6234 illustrate which scales are relatively informative about the relationship.
6235 The key, therefore, to successfully discover and decipher relationships
6236 between disparate data modalities is to adaptively determine which scales
6237 are the most informative, and the geometric implication for the most
6238 informative scales. Doing so not only provides an estimate of whether the
6239 modalities are related, but also provides insight into how the
6240 determination was made. This is especially important in high-dimensional
6241 data, where simple visualizations do not reveal relationships to the
6242 unaided human eye. Characterizations of this implementation in particular
6243 have been derived from and benchmarked within in [2]_.
6245 Parameters
6246 ----------
6247 x, y : ndarray
6248 If ``x`` and ``y`` have shapes ``(n, p)`` and ``(n, q)`` where `n` is
6249 the number of samples and `p` and `q` are the number of dimensions,
6250 then the MGC independence test will be run. Alternatively, ``x`` and
6251 ``y`` can have shapes ``(n, n)`` if they are distance or similarity
6252 matrices, and ``compute_distance`` must be sent to ``None``. If ``x``
6253 and ``y`` have shapes ``(n, p)`` and ``(m, p)``, an unpaired
6254 two-sample MGC test will be run.
6255 compute_distance : callable, optional
6256 A function that computes the distance or similarity among the samples
6257 within each data matrix. Set to ``None`` if ``x`` and ``y`` are
6258 already distance matrices. The default uses the euclidean norm metric.
6259 If you are calling a custom function, either create the distance
6260 matrix before-hand or create a function of the form
6261 ``compute_distance(x)`` where `x` is the data matrix for which
6262 pairwise distances are calculated.
6263 reps : int, optional
6264 The number of replications used to estimate the null when using the
6265 permutation test. The default is ``1000``.
6266 workers : int or map-like callable, optional
6267 If ``workers`` is an int the population is subdivided into ``workers``
6268 sections and evaluated in parallel (uses ``multiprocessing.Pool
6269 <multiprocessing>``). Supply ``-1`` to use all cores available to the
6270 Process. Alternatively supply a map-like callable, such as
6271 ``multiprocessing.Pool.map`` for evaluating the p-value in parallel.
6272 This evaluation is carried out as ``workers(func, iterable)``.
6273 Requires that `func` be pickleable. The default is ``1``.
6274 is_twosamp : bool, optional
6275 If `True`, a two sample test will be run. If ``x`` and ``y`` have
6276 shapes ``(n, p)`` and ``(m, p)``, this optional will be overridden and
6277 set to ``True``. Set to ``True`` if ``x`` and ``y`` both have shapes
6278 ``(n, p)`` and a two sample test is desired. The default is ``False``.
6279 Note that this will not run if inputs are distance matrices.
6280 random_state : {None, int, `numpy.random.Generator`,
6281 `numpy.random.RandomState`}, optional
6283 If `seed` is None (or `np.random`), the `numpy.random.RandomState`
6284 singleton is used.
6285 If `seed` is an int, a new ``RandomState`` instance is used,
6286 seeded with `seed`.
6287 If `seed` is already a ``Generator`` or ``RandomState`` instance then
6288 that instance is used.
6290 Returns
6291 -------
6292 res : MGCResult
6293 An object containing attributes:
6295 statistic : float
6296 The sample MGC test statistic within `[-1, 1]`.
6297 pvalue : float
6298 The p-value obtained via permutation.
6299 mgc_dict : dict
6300 Contains additional useful results:
6302 - mgc_map : ndarray
6303 A 2D representation of the latent geometry of the
6304 relationship.
6305 - opt_scale : (int, int)
6306 The estimated optimal scale as a `(x, y)` pair.
6307 - null_dist : list
6308 The null distribution derived from the permuted matrices.
6310 See Also
6311 --------
6312 pearsonr : Pearson correlation coefficient and p-value for testing
6313 non-correlation.
6314 kendalltau : Calculates Kendall's tau.
6315 spearmanr : Calculates a Spearman rank-order correlation coefficient.
6317 Notes
6318 -----
6319 A description of the process of MGC and applications on neuroscience data
6320 can be found in [1]_. It is performed using the following steps:
6322 #. Two distance matrices :math:`D^X` and :math:`D^Y` are computed and
6323 modified to be mean zero columnwise. This results in two
6324 :math:`n \times n` distance matrices :math:`A` and :math:`B` (the
6325 centering and unbiased modification) [3]_.
6327 #. For all values :math:`k` and :math:`l` from :math:`1, ..., n`,
6329 * The :math:`k`-nearest neighbor and :math:`l`-nearest neighbor graphs
6330 are calculated for each property. Here, :math:`G_k (i, j)` indicates
6331 the :math:`k`-smallest values of the :math:`i`-th row of :math:`A`
6332 and :math:`H_l (i, j)` indicates the :math:`l` smallested values of
6333 the :math:`i`-th row of :math:`B`
6335 * Let :math:`\circ` denotes the entry-wise matrix product, then local
6336 correlations are summed and normalized using the following statistic:
6338 .. math::
6340 c^{kl} = \frac{\sum_{ij} A G_k B H_l}
6341 {\sqrt{\sum_{ij} A^2 G_k \times \sum_{ij} B^2 H_l}}
6343 #. The MGC test statistic is the smoothed optimal local correlation of
6344 :math:`\{ c^{kl} \}`. Denote the smoothing operation as :math:`R(\cdot)`
6345 (which essentially set all isolated large correlations) as 0 and
6346 connected large correlations the same as before, see [3]_.) MGC is,
6348 .. math::
6350 MGC_n (x, y) = \max_{(k, l)} R \left(c^{kl} \left( x_n, y_n \right)
6351 \right)
6353 The test statistic returns a value between :math:`(-1, 1)` since it is
6354 normalized.
6356 The p-value returned is calculated using a permutation test. This process
6357 is completed by first randomly permuting :math:`y` to estimate the null
6358 distribution and then calculating the probability of observing a test
6359 statistic, under the null, at least as extreme as the observed test
6360 statistic.
6362 MGC requires at least 5 samples to run with reliable results. It can also
6363 handle high-dimensional data sets.
6364 In addition, by manipulating the input data matrices, the two-sample
6365 testing problem can be reduced to the independence testing problem [4]_.
6366 Given sample data :math:`U` and :math:`V` of sizes :math:`p \times n`
6367 :math:`p \times m`, data matrix :math:`X` and :math:`Y` can be created as
6368 follows:
6370 .. math::
6372 X = [U | V] \in \mathcal{R}^{p \times (n + m)}
6373 Y = [0_{1 \times n} | 1_{1 \times m}] \in \mathcal{R}^{(n + m)}
6375 Then, the MGC statistic can be calculated as normal. This methodology can
6376 be extended to similar tests such as distance correlation [4]_.
6378 .. versionadded:: 1.4.0
6380 References
6381 ----------
6382 .. [1] Vogelstein, J. T., Bridgeford, E. W., Wang, Q., Priebe, C. E.,
6383 Maggioni, M., & Shen, C. (2019). Discovering and deciphering
6384 relationships across disparate data modalities. ELife.
6385 .. [2] Panda, S., Palaniappan, S., Xiong, J., Swaminathan, A.,
6386 Ramachandran, S., Bridgeford, E. W., ... Vogelstein, J. T. (2019).
6387 mgcpy: A Comprehensive High Dimensional Independence Testing Python
6388 Package. :arXiv:`1907.02088`
6389 .. [3] Shen, C., Priebe, C.E., & Vogelstein, J. T. (2019). From distance
6390 correlation to multiscale graph correlation. Journal of the American
6391 Statistical Association.
6392 .. [4] Shen, C. & Vogelstein, J. T. (2018). The Exact Equivalence of
6393 Distance and Kernel Methods for Hypothesis Testing.
6394 :arXiv:`1806.05514`
6396 Examples
6397 --------
6398 >>> import numpy as np
6399 >>> from scipy.stats import multiscale_graphcorr
6400 >>> x = np.arange(100)
6401 >>> y = x
6402 >>> res = multiscale_graphcorr(x, y)
6403 >>> res.statistic, res.pvalue
6404 (1.0, 0.001)
6406 To run an unpaired two-sample test,
6408 >>> x = np.arange(100)
6409 >>> y = np.arange(79)
6410 >>> res = multiscale_graphcorr(x, y)
6411 >>> res.statistic, res.pvalue # doctest: +SKIP
6412 (0.033258146255703246, 0.023)
6414 or, if shape of the inputs are the same,
6416 >>> x = np.arange(100)
6417 >>> y = x
6418 >>> res = multiscale_graphcorr(x, y, is_twosamp=True)
6419 >>> res.statistic, res.pvalue # doctest: +SKIP
6420 (-0.008021809890200488, 1.0)
6422 """
6423 if not isinstance(x, np.ndarray) or not isinstance(y, np.ndarray):
6424 raise ValueError("x and y must be ndarrays")
6426 # convert arrays of type (n,) to (n, 1)
6427 if x.ndim == 1:
6428 x = x[:, np.newaxis]
6429 elif x.ndim != 2:
6430 raise ValueError("Expected a 2-D array `x`, found shape "
6431 "{}".format(x.shape))
6432 if y.ndim == 1:
6433 y = y[:, np.newaxis]
6434 elif y.ndim != 2:
6435 raise ValueError("Expected a 2-D array `y`, found shape "
6436 "{}".format(y.shape))
6438 nx, px = x.shape
6439 ny, py = y.shape
6441 # check for NaNs
6442 _contains_nan(x, nan_policy='raise')
6443 _contains_nan(y, nan_policy='raise')
6445 # check for positive or negative infinity and raise error
6446 if np.sum(np.isinf(x)) > 0 or np.sum(np.isinf(y)) > 0:
6447 raise ValueError("Inputs contain infinities")
6449 if nx != ny:
6450 if px == py:
6451 # reshape x and y for two sample testing
6452 is_twosamp = True
6453 else:
6454 raise ValueError("Shape mismatch, x and y must have shape [n, p] "
6455 "and [n, q] or have shape [n, p] and [m, p].")
6457 if nx < 5 or ny < 5:
6458 raise ValueError("MGC requires at least 5 samples to give reasonable "
6459 "results.")
6461 # convert x and y to float
6462 x = x.astype(np.float64)
6463 y = y.astype(np.float64)
6465 # check if compute_distance_matrix if a callable()
6466 if not callable(compute_distance) and compute_distance is not None:
6467 raise ValueError("Compute_distance must be a function.")
6469 # check if number of reps exists, integer, or > 0 (if under 1000 raises
6470 # warning)
6471 if not isinstance(reps, int) or reps < 0:
6472 raise ValueError("Number of reps must be an integer greater than 0.")
6473 elif reps < 1000:
6474 msg = ("The number of replications is low (under 1000), and p-value "
6475 "calculations may be unreliable. Use the p-value result, with "
6476 "caution!")
6477 warnings.warn(msg, RuntimeWarning)
6479 if is_twosamp:
6480 if compute_distance is None:
6481 raise ValueError("Cannot run if inputs are distance matrices")
6482 x, y = _two_sample_transform(x, y)
6484 if compute_distance is not None:
6485 # compute distance matrices for x and y
6486 x = compute_distance(x)
6487 y = compute_distance(y)
6489 # calculate MGC stat
6490 stat, stat_dict = _mgc_stat(x, y)
6491 stat_mgc_map = stat_dict["stat_mgc_map"]
6492 opt_scale = stat_dict["opt_scale"]
6494 # calculate permutation MGC p-value
6495 pvalue, null_dist = _perm_test(x, y, stat, reps=reps, workers=workers,
6496 random_state=random_state)
6498 # save all stats (other than stat/p-value) in dictionary
6499 mgc_dict = {"mgc_map": stat_mgc_map,
6500 "opt_scale": opt_scale,
6501 "null_dist": null_dist}
6503 # create result object with alias for backward compatibility
6504 res = MGCResult(stat, pvalue, mgc_dict)
6505 res.stat = stat
6506 return res
6509def _mgc_stat(distx, disty):
6510 r"""Helper function that calculates the MGC stat. See above for use.
6512 Parameters
6513 ----------
6514 distx, disty : ndarray
6515 `distx` and `disty` have shapes `(n, p)` and `(n, q)` or
6516 `(n, n)` and `(n, n)`
6517 if distance matrices.
6519 Returns
6520 -------
6521 stat : float
6522 The sample MGC test statistic within `[-1, 1]`.
6523 stat_dict : dict
6524 Contains additional useful additional returns containing the following
6525 keys:
6527 - stat_mgc_map : ndarray
6528 MGC-map of the statistics.
6529 - opt_scale : (float, float)
6530 The estimated optimal scale as a `(x, y)` pair.
6532 """
6533 # calculate MGC map and optimal scale
6534 stat_mgc_map = _local_correlations(distx, disty, global_corr='mgc')
6536 n, m = stat_mgc_map.shape
6537 if m == 1 or n == 1:
6538 # the global scale at is the statistic calculated at maximial nearest
6539 # neighbors. There is not enough local scale to search over, so
6540 # default to global scale
6541 stat = stat_mgc_map[m - 1][n - 1]
6542 opt_scale = m * n
6543 else:
6544 samp_size = len(distx) - 1
6546 # threshold to find connected region of significant local correlations
6547 sig_connect = _threshold_mgc_map(stat_mgc_map, samp_size)
6549 # maximum within the significant region
6550 stat, opt_scale = _smooth_mgc_map(sig_connect, stat_mgc_map)
6552 stat_dict = {"stat_mgc_map": stat_mgc_map,
6553 "opt_scale": opt_scale}
6555 return stat, stat_dict
6558def _threshold_mgc_map(stat_mgc_map, samp_size):
6559 r"""
6560 Finds a connected region of significance in the MGC-map by thresholding.
6562 Parameters
6563 ----------
6564 stat_mgc_map : ndarray
6565 All local correlations within `[-1,1]`.
6566 samp_size : int
6567 The sample size of original data.
6569 Returns
6570 -------
6571 sig_connect : ndarray
6572 A binary matrix with 1's indicating the significant region.
6574 """
6575 m, n = stat_mgc_map.shape
6577 # 0.02 is simply an empirical threshold, this can be set to 0.01 or 0.05
6578 # with varying levels of performance. Threshold is based on a beta
6579 # approximation.
6580 per_sig = 1 - (0.02 / samp_size) # Percentile to consider as significant
6581 threshold = samp_size * (samp_size - 3)/4 - 1/2 # Beta approximation
6582 threshold = distributions.beta.ppf(per_sig, threshold, threshold) * 2 - 1
6584 # the global scale at is the statistic calculated at maximial nearest
6585 # neighbors. Threshold is the maximum on the global and local scales
6586 threshold = max(threshold, stat_mgc_map[m - 1][n - 1])
6588 # find the largest connected component of significant correlations
6589 sig_connect = stat_mgc_map > threshold
6590 if np.sum(sig_connect) > 0:
6591 sig_connect, _ = _measurements.label(sig_connect)
6592 _, label_counts = np.unique(sig_connect, return_counts=True)
6594 # skip the first element in label_counts, as it is count(zeros)
6595 max_label = np.argmax(label_counts[1:]) + 1
6596 sig_connect = sig_connect == max_label
6597 else:
6598 sig_connect = np.array([[False]])
6600 return sig_connect
6603def _smooth_mgc_map(sig_connect, stat_mgc_map):
6604 """Finds the smoothed maximal within the significant region R.
6606 If area of R is too small it returns the last local correlation. Otherwise,
6607 returns the maximum within significant_connected_region.
6609 Parameters
6610 ----------
6611 sig_connect : ndarray
6612 A binary matrix with 1's indicating the significant region.
6613 stat_mgc_map : ndarray
6614 All local correlations within `[-1, 1]`.
6616 Returns
6617 -------
6618 stat : float
6619 The sample MGC statistic within `[-1, 1]`.
6620 opt_scale: (float, float)
6621 The estimated optimal scale as an `(x, y)` pair.
6623 """
6624 m, n = stat_mgc_map.shape
6626 # the global scale at is the statistic calculated at maximial nearest
6627 # neighbors. By default, statistic and optimal scale are global.
6628 stat = stat_mgc_map[m - 1][n - 1]
6629 opt_scale = [m, n]
6631 if np.linalg.norm(sig_connect) != 0:
6632 # proceed only when the connected region's area is sufficiently large
6633 # 0.02 is simply an empirical threshold, this can be set to 0.01 or 0.05
6634 # with varying levels of performance
6635 if np.sum(sig_connect) >= np.ceil(0.02 * max(m, n)) * min(m, n):
6636 max_corr = max(stat_mgc_map[sig_connect])
6638 # find all scales within significant_connected_region that maximize
6639 # the local correlation
6640 max_corr_index = np.where((stat_mgc_map >= max_corr) & sig_connect)
6642 if max_corr >= stat:
6643 stat = max_corr
6645 k, l = max_corr_index
6646 one_d_indices = k * n + l # 2D to 1D indexing
6647 k = np.max(one_d_indices) // n
6648 l = np.max(one_d_indices) % n
6649 opt_scale = [k+1, l+1] # adding 1s to match R indexing
6651 return stat, opt_scale
6654def _two_sample_transform(u, v):
6655 """Helper function that concatenates x and y for two sample MGC stat.
6657 See above for use.
6659 Parameters
6660 ----------
6661 u, v : ndarray
6662 `u` and `v` have shapes `(n, p)` and `(m, p)`.
6664 Returns
6665 -------
6666 x : ndarray
6667 Concatenate `u` and `v` along the `axis = 0`. `x` thus has shape
6668 `(2n, p)`.
6669 y : ndarray
6670 Label matrix for `x` where 0 refers to samples that comes from `u` and
6671 1 refers to samples that come from `v`. `y` thus has shape `(2n, 1)`.
6673 """
6674 nx = u.shape[0]
6675 ny = v.shape[0]
6676 x = np.concatenate([u, v], axis=0)
6677 y = np.concatenate([np.zeros(nx), np.ones(ny)], axis=0).reshape(-1, 1)
6678 return x, y
6681#####################################
6682# INFERENTIAL STATISTICS #
6683#####################################
6685TtestResultBase = _make_tuple_bunch('TtestResultBase',
6686 ['statistic', 'pvalue'], ['df'])
6689class TtestResult(TtestResultBase):
6690 """
6691 Result of a t-test.
6693 See the documentation of the particular t-test function for more
6694 information about the definition of the statistic and meaning of
6695 the confidence interval.
6697 Attributes
6698 ----------
6699 statistic : float or array
6700 The t-statistic of the sample.
6701 pvalue : float or array
6702 The p-value associated with the given alternative.
6703 df : float or array
6704 The number of degrees of freedom used in calculation of the
6705 t-statistic; this is one less than the size of the sample
6706 (``a.shape[axis]-1`` if there are no masked elements or omitted NaNs).
6708 Methods
6709 -------
6710 confidence_interval
6711 Computes a confidence interval around the population statistic
6712 for the given confidence level.
6713 The confidence interval is returned in a ``namedtuple`` with
6714 fields `low` and `high`.
6716 """
6718 def __init__(self, statistic, pvalue, df, # public
6719 alternative, standard_error, estimate): # private
6720 super().__init__(statistic, pvalue, df=df)
6721 self._alternative = alternative
6722 self._standard_error = standard_error # denominator of t-statistic
6723 self._estimate = estimate # point estimate of sample mean
6725 def confidence_interval(self, confidence_level=0.95):
6726 """
6727 Parameters
6728 ----------
6729 confidence_level : float
6730 The confidence level for the calculation of the population mean
6731 confidence interval. Default is 0.95.
6733 Returns
6734 -------
6735 ci : namedtuple
6736 The confidence interval is returned in a ``namedtuple`` with
6737 fields `low` and `high`.
6739 """
6740 low, high = _t_confidence_interval(self.df, self.statistic,
6741 confidence_level, self._alternative)
6742 low = low * self._standard_error + self._estimate
6743 high = high * self._standard_error + self._estimate
6744 return ConfidenceInterval(low=low, high=high)
6747def pack_TtestResult(statistic, pvalue, df, alternative, standard_error,
6748 estimate):
6749 # this could be any number of dimensions (including 0d), but there is
6750 # at most one unique non-NaN value
6751 alternative = np.atleast_1d(alternative) # can't index 0D object
6752 alternative = alternative[np.isfinite(alternative)]
6753 alternative = alternative[0] if alternative.size else np.nan
6754 return TtestResult(statistic, pvalue, df=df, alternative=alternative,
6755 standard_error=standard_error, estimate=estimate)
6758def unpack_TtestResult(res):
6759 return (res.statistic, res.pvalue, res.df, res._alternative,
6760 res._standard_error, res._estimate)
6763@_axis_nan_policy_factory(pack_TtestResult, default_axis=0, n_samples=2,
6764 result_to_tuple=unpack_TtestResult, n_outputs=6)
6765def ttest_1samp(a, popmean, axis=0, nan_policy='propagate',
6766 alternative="two-sided"):
6767 """Calculate the T-test for the mean of ONE group of scores.
6769 This is a test for the null hypothesis that the expected value
6770 (mean) of a sample of independent observations `a` is equal to the given
6771 population mean, `popmean`.
6773 Parameters
6774 ----------
6775 a : array_like
6776 Sample observation.
6777 popmean : float or array_like
6778 Expected value in null hypothesis. If array_like, then its length along
6779 `axis` must equal 1, and it must otherwise be broadcastable with `a`.
6780 axis : int or None, optional
6781 Axis along which to compute test; default is 0. If None, compute over
6782 the whole array `a`.
6783 nan_policy : {'propagate', 'raise', 'omit'}, optional
6784 Defines how to handle when input contains nan.
6785 The following options are available (default is 'propagate'):
6787 * 'propagate': returns nan
6788 * 'raise': throws an error
6789 * 'omit': performs the calculations ignoring nan values
6791 alternative : {'two-sided', 'less', 'greater'}, optional
6792 Defines the alternative hypothesis.
6793 The following options are available (default is 'two-sided'):
6795 * 'two-sided': the mean of the underlying distribution of the sample
6796 is different than the given population mean (`popmean`)
6797 * 'less': the mean of the underlying distribution of the sample is
6798 less than the given population mean (`popmean`)
6799 * 'greater': the mean of the underlying distribution of the sample is
6800 greater than the given population mean (`popmean`)
6802 Returns
6803 -------
6804 result : `~scipy.stats._result_classes.TtestResult`
6805 An object with the following attributes:
6807 statistic : float or array
6808 The t-statistic.
6809 pvalue : float or array
6810 The p-value associated with the given alternative.
6811 df : float or array
6812 The number of degrees of freedom used in calculation of the
6813 t-statistic; this is one less than the size of the sample
6814 (``a.shape[axis]``).
6816 .. versionadded:: 1.10.0
6818 The object also has the following method:
6820 confidence_interval(confidence_level=0.95)
6821 Computes a confidence interval around the population
6822 mean for the given confidence level.
6823 The confidence interval is returned in a ``namedtuple`` with
6824 fields `low` and `high`.
6826 .. versionadded:: 1.10.0
6828 Notes
6829 -----
6830 The statistic is calculated as ``(np.mean(a) - popmean)/se``, where
6831 ``se`` is the standard error. Therefore, the statistic will be positive
6832 when the sample mean is greater than the population mean and negative when
6833 the sample mean is less than the population mean.
6835 Examples
6836 --------
6837 Suppose we wish to test the null hypothesis that the mean of a population
6838 is equal to 0.5. We choose a confidence level of 99%; that is, we will
6839 reject the null hypothesis in favor of the alternative if the p-value is
6840 less than 0.01.
6842 When testing random variates from the standard uniform distribution, which
6843 has a mean of 0.5, we expect the data to be consistent with the null
6844 hypothesis most of the time.
6846 >>> import numpy as np
6847 >>> from scipy import stats
6848 >>> rng = np.random.default_rng()
6849 >>> rvs = stats.uniform.rvs(size=50, random_state=rng)
6850 >>> stats.ttest_1samp(rvs, popmean=0.5)
6851 TtestResult(statistic=2.456308468440, pvalue=0.017628209047638, df=49)
6853 As expected, the p-value of 0.017 is not below our threshold of 0.01, so
6854 we cannot reject the null hypothesis.
6856 When testing data from the standard *normal* distribution, which has a mean
6857 of 0, we would expect the null hypothesis to be rejected.
6859 >>> rvs = stats.norm.rvs(size=50, random_state=rng)
6860 >>> stats.ttest_1samp(rvs, popmean=0.5)
6861 TtestResult(statistic=-7.433605518875, pvalue=1.416760157221e-09, df=49)
6863 Indeed, the p-value is lower than our threshold of 0.01, so we reject the
6864 null hypothesis in favor of the default "two-sided" alternative: the mean
6865 of the population is *not* equal to 0.5.
6867 However, suppose we were to test the null hypothesis against the
6868 one-sided alternative that the mean of the population is *greater* than
6869 0.5. Since the mean of the standard normal is less than 0.5, we would not
6870 expect the null hypothesis to be rejected.
6872 >>> stats.ttest_1samp(rvs, popmean=0.5, alternative='greater')
6873 TtestResult(statistic=-7.433605518875, pvalue=0.99999999929, df=49)
6875 Unsurprisingly, with a p-value greater than our threshold, we would not
6876 reject the null hypothesis.
6878 Note that when working with a confidence level of 99%, a true null
6879 hypothesis will be rejected approximately 1% of the time.
6881 >>> rvs = stats.uniform.rvs(size=(100, 50), random_state=rng)
6882 >>> res = stats.ttest_1samp(rvs, popmean=0.5, axis=1)
6883 >>> np.sum(res.pvalue < 0.01)
6884 1
6886 Indeed, even though all 100 samples above were drawn from the standard
6887 uniform distribution, which *does* have a population mean of 0.5, we would
6888 mistakenly reject the null hypothesis for one of them.
6890 `ttest_1samp` can also compute a confidence interval around the population
6891 mean.
6893 >>> rvs = stats.norm.rvs(size=50, random_state=rng)
6894 >>> res = stats.ttest_1samp(rvs, popmean=0)
6895 >>> ci = res.confidence_interval(confidence_level=0.95)
6896 >>> ci
6897 ConfidenceInterval(low=-0.3193887540880017, high=0.2898583388980972)
6899 The bounds of the 95% confidence interval are the
6900 minimum and maximum values of the parameter `popmean` for which the
6901 p-value of the test would be 0.05.
6903 >>> res = stats.ttest_1samp(rvs, popmean=ci.low)
6904 >>> np.testing.assert_allclose(res.pvalue, 0.05)
6905 >>> res = stats.ttest_1samp(rvs, popmean=ci.high)
6906 >>> np.testing.assert_allclose(res.pvalue, 0.05)
6908 Under certain assumptions about the population from which a sample
6909 is drawn, the confidence interval with confidence level 95% is expected
6910 to contain the true population mean in 95% of sample replications.
6912 >>> rvs = stats.norm.rvs(size=(50, 1000), loc=1, random_state=rng)
6913 >>> res = stats.ttest_1samp(rvs, popmean=0)
6914 >>> ci = res.confidence_interval()
6915 >>> contains_pop_mean = (ci.low < 1) & (ci.high > 1)
6916 >>> contains_pop_mean.sum()
6917 953
6919 """
6920 a, axis = _chk_asarray(a, axis)
6922 n = a.shape[axis]
6923 df = n - 1
6925 mean = np.mean(a, axis)
6926 try:
6927 popmean = np.squeeze(popmean, axis=axis)
6928 except ValueError as e:
6929 raise ValueError("`popmean.shape[axis]` must equal 1.") from e
6930 d = mean - popmean
6931 v = _var(a, axis, ddof=1)
6932 denom = np.sqrt(v / n)
6934 with np.errstate(divide='ignore', invalid='ignore'):
6935 t = np.divide(d, denom)
6936 t, prob = _ttest_finish(df, t, alternative)
6938 # when nan_policy='omit', `df` can be different for different axis-slices
6939 df = np.broadcast_to(df, t.shape)[()]
6940 # _axis_nan_policy decorator doesn't play well with strings
6941 alternative_num = {"less": -1, "two-sided": 0, "greater": 1}[alternative]
6942 return TtestResult(t, prob, df=df, alternative=alternative_num,
6943 standard_error=denom, estimate=mean)
6946def _t_confidence_interval(df, t, confidence_level, alternative):
6947 # Input validation on `alternative` is already done
6948 # We just need IV on confidence_level
6949 if confidence_level < 0 or confidence_level > 1:
6950 message = "`confidence_level` must be a number between 0 and 1."
6951 raise ValueError(message)
6953 if alternative < 0: # 'less'
6954 p = confidence_level
6955 low, high = np.broadcast_arrays(-np.inf, special.stdtrit(df, p))
6956 elif alternative > 0: # 'greater'
6957 p = 1 - confidence_level
6958 low, high = np.broadcast_arrays(special.stdtrit(df, p), np.inf)
6959 elif alternative == 0: # 'two-sided'
6960 tail_probability = (1 - confidence_level)/2
6961 p = tail_probability, 1-tail_probability
6962 # axis of p must be the zeroth and orthogonal to all the rest
6963 p = np.reshape(p, [2] + [1]*np.asarray(df).ndim)
6964 low, high = special.stdtrit(df, p)
6965 else: # alternative is NaN when input is empty (see _axis_nan_policy)
6966 p, nans = np.broadcast_arrays(t, np.nan)
6967 low, high = nans, nans
6969 return low[()], high[()]
6972def _ttest_finish(df, t, alternative):
6973 """Common code between all 3 t-test functions."""
6974 # We use ``stdtr`` directly here as it handles the case when ``nan``
6975 # values are present in the data and masked arrays are passed
6976 # while ``t.cdf`` emits runtime warnings. This way ``_ttest_finish``
6977 # can be shared between the ``stats`` and ``mstats`` versions.
6979 if alternative == 'less':
6980 pval = special.stdtr(df, t)
6981 elif alternative == 'greater':
6982 pval = special.stdtr(df, -t)
6983 elif alternative == 'two-sided':
6984 pval = special.stdtr(df, -np.abs(t))*2
6985 else:
6986 raise ValueError("alternative must be "
6987 "'less', 'greater' or 'two-sided'")
6989 if t.ndim == 0:
6990 t = t[()]
6991 if pval.ndim == 0:
6992 pval = pval[()]
6994 return t, pval
6997def _ttest_ind_from_stats(mean1, mean2, denom, df, alternative):
6999 d = mean1 - mean2
7000 with np.errstate(divide='ignore', invalid='ignore'):
7001 t = np.divide(d, denom)
7002 t, prob = _ttest_finish(df, t, alternative)
7004 return (t, prob)
7007def _unequal_var_ttest_denom(v1, n1, v2, n2):
7008 vn1 = v1 / n1
7009 vn2 = v2 / n2
7010 with np.errstate(divide='ignore', invalid='ignore'):
7011 df = (vn1 + vn2)**2 / (vn1**2 / (n1 - 1) + vn2**2 / (n2 - 1))
7013 # If df is undefined, variances are zero (assumes n1 > 0 & n2 > 0).
7014 # Hence it doesn't matter what df is as long as it's not NaN.
7015 df = np.where(np.isnan(df), 1, df)
7016 denom = np.sqrt(vn1 + vn2)
7017 return df, denom
7020def _equal_var_ttest_denom(v1, n1, v2, n2):
7021 # If there is a single observation in one sample, this formula for pooled
7022 # variance breaks down because the variance of that sample is undefined.
7023 # The pooled variance is still defined, though, because the (n-1) in the
7024 # numerator should cancel with the (n-1) in the denominator, leaving only
7025 # the sum of squared differences from the mean: zero.
7026 v1 = np.where(n1 == 1, 0, v1)[()]
7027 v2 = np.where(n2 == 1, 0, v2)[()]
7029 df = n1 + n2 - 2.0
7030 svar = ((n1 - 1) * v1 + (n2 - 1) * v2) / df
7031 denom = np.sqrt(svar * (1.0 / n1 + 1.0 / n2))
7032 return df, denom
7035Ttest_indResult = namedtuple('Ttest_indResult', ('statistic', 'pvalue'))
7038def ttest_ind_from_stats(mean1, std1, nobs1, mean2, std2, nobs2,
7039 equal_var=True, alternative="two-sided"):
7040 r"""
7041 T-test for means of two independent samples from descriptive statistics.
7043 This is a test for the null hypothesis that two independent
7044 samples have identical average (expected) values.
7046 Parameters
7047 ----------
7048 mean1 : array_like
7049 The mean(s) of sample 1.
7050 std1 : array_like
7051 The corrected sample standard deviation of sample 1 (i.e. ``ddof=1``).
7052 nobs1 : array_like
7053 The number(s) of observations of sample 1.
7054 mean2 : array_like
7055 The mean(s) of sample 2.
7056 std2 : array_like
7057 The corrected sample standard deviation of sample 2 (i.e. ``ddof=1``).
7058 nobs2 : array_like
7059 The number(s) of observations of sample 2.
7060 equal_var : bool, optional
7061 If True (default), perform a standard independent 2 sample test
7062 that assumes equal population variances [1]_.
7063 If False, perform Welch's t-test, which does not assume equal
7064 population variance [2]_.
7065 alternative : {'two-sided', 'less', 'greater'}, optional
7066 Defines the alternative hypothesis.
7067 The following options are available (default is 'two-sided'):
7069 * 'two-sided': the means of the distributions are unequal.
7070 * 'less': the mean of the first distribution is less than the
7071 mean of the second distribution.
7072 * 'greater': the mean of the first distribution is greater than the
7073 mean of the second distribution.
7075 .. versionadded:: 1.6.0
7077 Returns
7078 -------
7079 statistic : float or array
7080 The calculated t-statistics.
7081 pvalue : float or array
7082 The two-tailed p-value.
7084 See Also
7085 --------
7086 scipy.stats.ttest_ind
7088 Notes
7089 -----
7090 The statistic is calculated as ``(mean1 - mean2)/se``, where ``se`` is the
7091 standard error. Therefore, the statistic will be positive when `mean1` is
7092 greater than `mean2` and negative when `mean1` is less than `mean2`.
7094 This method does not check whether any of the elements of `std1` or `std2`
7095 are negative. If any elements of the `std1` or `std2` parameters are
7096 negative in a call to this method, this method will return the same result
7097 as if it were passed ``numpy.abs(std1)`` and ``numpy.abs(std2)``,
7098 respectively, instead; no exceptions or warnings will be emitted.
7100 References
7101 ----------
7102 .. [1] https://en.wikipedia.org/wiki/T-test#Independent_two-sample_t-test
7104 .. [2] https://en.wikipedia.org/wiki/Welch%27s_t-test
7106 Examples
7107 --------
7108 Suppose we have the summary data for two samples, as follows (with the
7109 Sample Variance being the corrected sample variance)::
7111 Sample Sample
7112 Size Mean Variance
7113 Sample 1 13 15.0 87.5
7114 Sample 2 11 12.0 39.0
7116 Apply the t-test to this data (with the assumption that the population
7117 variances are equal):
7119 >>> import numpy as np
7120 >>> from scipy.stats import ttest_ind_from_stats
7121 >>> ttest_ind_from_stats(mean1=15.0, std1=np.sqrt(87.5), nobs1=13,
7122 ... mean2=12.0, std2=np.sqrt(39.0), nobs2=11)
7123 Ttest_indResult(statistic=0.9051358093310269, pvalue=0.3751996797581487)
7125 For comparison, here is the data from which those summary statistics
7126 were taken. With this data, we can compute the same result using
7127 `scipy.stats.ttest_ind`:
7129 >>> a = np.array([1, 3, 4, 6, 11, 13, 15, 19, 22, 24, 25, 26, 26])
7130 >>> b = np.array([2, 4, 6, 9, 11, 13, 14, 15, 18, 19, 21])
7131 >>> from scipy.stats import ttest_ind
7132 >>> ttest_ind(a, b)
7133 Ttest_indResult(statistic=0.905135809331027, pvalue=0.3751996797581486)
7135 Suppose we instead have binary data and would like to apply a t-test to
7136 compare the proportion of 1s in two independent groups::
7138 Number of Sample Sample
7139 Size ones Mean Variance
7140 Sample 1 150 30 0.2 0.161073
7141 Sample 2 200 45 0.225 0.175251
7143 The sample mean :math:`\hat{p}` is the proportion of ones in the sample
7144 and the variance for a binary observation is estimated by
7145 :math:`\hat{p}(1-\hat{p})`.
7147 >>> ttest_ind_from_stats(mean1=0.2, std1=np.sqrt(0.161073), nobs1=150,
7148 ... mean2=0.225, std2=np.sqrt(0.175251), nobs2=200)
7149 Ttest_indResult(statistic=-0.5627187905196761, pvalue=0.5739887114209541)
7151 For comparison, we could compute the t statistic and p-value using
7152 arrays of 0s and 1s and `scipy.stat.ttest_ind`, as above.
7154 >>> group1 = np.array([1]*30 + [0]*(150-30))
7155 >>> group2 = np.array([1]*45 + [0]*(200-45))
7156 >>> ttest_ind(group1, group2)
7157 Ttest_indResult(statistic=-0.5627179589855622, pvalue=0.573989277115258)
7159 """
7160 mean1 = np.asarray(mean1)
7161 std1 = np.asarray(std1)
7162 mean2 = np.asarray(mean2)
7163 std2 = np.asarray(std2)
7164 if equal_var:
7165 df, denom = _equal_var_ttest_denom(std1**2, nobs1, std2**2, nobs2)
7166 else:
7167 df, denom = _unequal_var_ttest_denom(std1**2, nobs1,
7168 std2**2, nobs2)
7170 res = _ttest_ind_from_stats(mean1, mean2, denom, df, alternative)
7171 return Ttest_indResult(*res)
7174@_axis_nan_policy_factory(pack_TtestResult, default_axis=0, n_samples=2,
7175 result_to_tuple=unpack_TtestResult, n_outputs=6)
7176def ttest_ind(a, b, axis=0, equal_var=True, nan_policy='propagate',
7177 permutations=None, random_state=None, alternative="two-sided",
7178 trim=0):
7179 """
7180 Calculate the T-test for the means of *two independent* samples of scores.
7182 This is a test for the null hypothesis that 2 independent samples
7183 have identical average (expected) values. This test assumes that the
7184 populations have identical variances by default.
7186 Parameters
7187 ----------
7188 a, b : array_like
7189 The arrays must have the same shape, except in the dimension
7190 corresponding to `axis` (the first, by default).
7191 axis : int or None, optional
7192 Axis along which to compute test. If None, compute over the whole
7193 arrays, `a`, and `b`.
7194 equal_var : bool, optional
7195 If True (default), perform a standard independent 2 sample test
7196 that assumes equal population variances [1]_.
7197 If False, perform Welch's t-test, which does not assume equal
7198 population variance [2]_.
7200 .. versionadded:: 0.11.0
7202 nan_policy : {'propagate', 'raise', 'omit'}, optional
7203 Defines how to handle when input contains nan.
7204 The following options are available (default is 'propagate'):
7206 * 'propagate': returns nan
7207 * 'raise': throws an error
7208 * 'omit': performs the calculations ignoring nan values
7210 The 'omit' option is not currently available for permutation tests or
7211 one-sided asympyotic tests.
7213 permutations : non-negative int, np.inf, or None (default), optional
7214 If 0 or None (default), use the t-distribution to calculate p-values.
7215 Otherwise, `permutations` is the number of random permutations that
7216 will be used to estimate p-values using a permutation test. If
7217 `permutations` equals or exceeds the number of distinct partitions of
7218 the pooled data, an exact test is performed instead (i.e. each
7219 distinct partition is used exactly once). See Notes for details.
7221 .. versionadded:: 1.7.0
7223 random_state : {None, int, `numpy.random.Generator`,
7224 `numpy.random.RandomState`}, optional
7226 If `seed` is None (or `np.random`), the `numpy.random.RandomState`
7227 singleton is used.
7228 If `seed` is an int, a new ``RandomState`` instance is used,
7229 seeded with `seed`.
7230 If `seed` is already a ``Generator`` or ``RandomState`` instance then
7231 that instance is used.
7233 Pseudorandom number generator state used to generate permutations
7234 (used only when `permutations` is not None).
7236 .. versionadded:: 1.7.0
7238 alternative : {'two-sided', 'less', 'greater'}, optional
7239 Defines the alternative hypothesis.
7240 The following options are available (default is 'two-sided'):
7242 * 'two-sided': the means of the distributions underlying the samples
7243 are unequal.
7244 * 'less': the mean of the distribution underlying the first sample
7245 is less than the mean of the distribution underlying the second
7246 sample.
7247 * 'greater': the mean of the distribution underlying the first
7248 sample is greater than the mean of the distribution underlying
7249 the second sample.
7251 .. versionadded:: 1.6.0
7253 trim : float, optional
7254 If nonzero, performs a trimmed (Yuen's) t-test.
7255 Defines the fraction of elements to be trimmed from each end of the
7256 input samples. If 0 (default), no elements will be trimmed from either
7257 side. The number of trimmed elements from each tail is the floor of the
7258 trim times the number of elements. Valid range is [0, .5).
7260 .. versionadded:: 1.7
7262 Returns
7263 -------
7264 result : `~scipy.stats._result_classes.TtestResult`
7265 An object with the following attributes:
7267 statistic : float or ndarray
7268 The t-statistic.
7269 pvalue : float or ndarray
7270 The p-value associated with the given alternative.
7271 df : float or ndarray
7272 The number of degrees of freedom used in calculation of the
7273 t-statistic. This is always NaN for a permutation t-test.
7275 .. versionadded:: 1.11.0
7277 The object also has the following method:
7279 confidence_interval(confidence_level=0.95)
7280 Computes a confidence interval around the difference in
7281 population means for the given confidence level.
7282 The confidence interval is returned in a ``namedtuple`` with
7283 fields ``low`` and ``high``.
7284 When a permutation t-test is performed, the confidence interval
7285 is not computed, and fields ``low`` and ``high`` contain NaN.
7287 .. versionadded:: 1.11.0
7289 Notes
7290 -----
7291 Suppose we observe two independent samples, e.g. flower petal lengths, and
7292 we are considering whether the two samples were drawn from the same
7293 population (e.g. the same species of flower or two species with similar
7294 petal characteristics) or two different populations.
7296 The t-test quantifies the difference between the arithmetic means
7297 of the two samples. The p-value quantifies the probability of observing
7298 as or more extreme values assuming the null hypothesis, that the
7299 samples are drawn from populations with the same population means, is true.
7300 A p-value larger than a chosen threshold (e.g. 5% or 1%) indicates that
7301 our observation is not so unlikely to have occurred by chance. Therefore,
7302 we do not reject the null hypothesis of equal population means.
7303 If the p-value is smaller than our threshold, then we have evidence
7304 against the null hypothesis of equal population means.
7306 By default, the p-value is determined by comparing the t-statistic of the
7307 observed data against a theoretical t-distribution.
7308 When ``1 < permutations < binom(n, k)``, where
7310 * ``k`` is the number of observations in `a`,
7311 * ``n`` is the total number of observations in `a` and `b`, and
7312 * ``binom(n, k)`` is the binomial coefficient (``n`` choose ``k``),
7314 the data are pooled (concatenated), randomly assigned to either group `a`
7315 or `b`, and the t-statistic is calculated. This process is performed
7316 repeatedly (`permutation` times), generating a distribution of the
7317 t-statistic under the null hypothesis, and the t-statistic of the observed
7318 data is compared to this distribution to determine the p-value.
7319 Specifically, the p-value reported is the "achieved significance level"
7320 (ASL) as defined in 4.4 of [3]_. Note that there are other ways of
7321 estimating p-values using randomized permutation tests; for other
7322 options, see the more general `permutation_test`.
7324 When ``permutations >= binom(n, k)``, an exact test is performed: the data
7325 are partitioned between the groups in each distinct way exactly once.
7327 The permutation test can be computationally expensive and not necessarily
7328 more accurate than the analytical test, but it does not make strong
7329 assumptions about the shape of the underlying distribution.
7331 Use of trimming is commonly referred to as the trimmed t-test. At times
7332 called Yuen's t-test, this is an extension of Welch's t-test, with the
7333 difference being the use of winsorized means in calculation of the variance
7334 and the trimmed sample size in calculation of the statistic. Trimming is
7335 recommended if the underlying distribution is long-tailed or contaminated
7336 with outliers [4]_.
7338 The statistic is calculated as ``(np.mean(a) - np.mean(b))/se``, where
7339 ``se`` is the standard error. Therefore, the statistic will be positive
7340 when the sample mean of `a` is greater than the sample mean of `b` and
7341 negative when the sample mean of `a` is less than the sample mean of
7342 `b`.
7344 References
7345 ----------
7346 .. [1] https://en.wikipedia.org/wiki/T-test#Independent_two-sample_t-test
7348 .. [2] https://en.wikipedia.org/wiki/Welch%27s_t-test
7350 .. [3] B. Efron and T. Hastie. Computer Age Statistical Inference. (2016).
7352 .. [4] Yuen, Karen K. "The Two-Sample Trimmed t for Unequal Population
7353 Variances." Biometrika, vol. 61, no. 1, 1974, pp. 165-170. JSTOR,
7354 www.jstor.org/stable/2334299. Accessed 30 Mar. 2021.
7356 .. [5] Yuen, Karen K., and W. J. Dixon. "The Approximate Behaviour and
7357 Performance of the Two-Sample Trimmed t." Biometrika, vol. 60,
7358 no. 2, 1973, pp. 369-374. JSTOR, www.jstor.org/stable/2334550.
7359 Accessed 30 Mar. 2021.
7361 Examples
7362 --------
7363 >>> import numpy as np
7364 >>> from scipy import stats
7365 >>> rng = np.random.default_rng()
7367 Test with sample with identical means:
7369 >>> rvs1 = stats.norm.rvs(loc=5, scale=10, size=500, random_state=rng)
7370 >>> rvs2 = stats.norm.rvs(loc=5, scale=10, size=500, random_state=rng)
7371 >>> stats.ttest_ind(rvs1, rvs2)
7372 Ttest_indResult(statistic=-0.4390847099199348, pvalue=0.6606952038870015)
7373 >>> stats.ttest_ind(rvs1, rvs2, equal_var=False)
7374 Ttest_indResult(statistic=-0.4390847099199348, pvalue=0.6606952553131064)
7376 `ttest_ind` underestimates p for unequal variances:
7378 >>> rvs3 = stats.norm.rvs(loc=5, scale=20, size=500, random_state=rng)
7379 >>> stats.ttest_ind(rvs1, rvs3)
7380 Ttest_indResult(statistic=-1.6370984482905417, pvalue=0.1019251574705033)
7381 >>> stats.ttest_ind(rvs1, rvs3, equal_var=False)
7382 Ttest_indResult(statistic=-1.637098448290542, pvalue=0.10202110497954867)
7384 When ``n1 != n2``, the equal variance t-statistic is no longer equal to the
7385 unequal variance t-statistic:
7387 >>> rvs4 = stats.norm.rvs(loc=5, scale=20, size=100, random_state=rng)
7388 >>> stats.ttest_ind(rvs1, rvs4)
7389 Ttest_indResult(statistic=-1.9481646859513422, pvalue=0.05186270935842703)
7390 >>> stats.ttest_ind(rvs1, rvs4, equal_var=False)
7391 Ttest_indResult(statistic=-1.3146566100751664, pvalue=0.1913495266513811)
7393 T-test with different means, variance, and n:
7395 >>> rvs5 = stats.norm.rvs(loc=8, scale=20, size=100, random_state=rng)
7396 >>> stats.ttest_ind(rvs1, rvs5)
7397 Ttest_indResult(statistic=-2.8415950600298774, pvalue=0.0046418707568707885)
7398 >>> stats.ttest_ind(rvs1, rvs5, equal_var=False)
7399 Ttest_indResult(statistic=-1.8686598649188084, pvalue=0.06434714193919686)
7401 When performing a permutation test, more permutations typically yields
7402 more accurate results. Use a ``np.random.Generator`` to ensure
7403 reproducibility:
7405 >>> stats.ttest_ind(rvs1, rvs5, permutations=10000,
7406 ... random_state=rng)
7407 Ttest_indResult(statistic=-2.8415950600298774, pvalue=0.0052994700529947)
7409 Take these two samples, one of which has an extreme tail.
7411 >>> a = (56, 128.6, 12, 123.8, 64.34, 78, 763.3)
7412 >>> b = (1.1, 2.9, 4.2)
7414 Use the `trim` keyword to perform a trimmed (Yuen) t-test. For example,
7415 using 20% trimming, ``trim=.2``, the test will reduce the impact of one
7416 (``np.floor(trim*len(a))``) element from each tail of sample `a`. It will
7417 have no effect on sample `b` because ``np.floor(trim*len(b))`` is 0.
7419 >>> stats.ttest_ind(a, b, trim=.2)
7420 Ttest_indResult(statistic=3.4463884028073513,
7421 pvalue=0.01369338726499547)
7422 """
7423 if not (0 <= trim < .5):
7424 raise ValueError("Trimming percentage should be 0 <= `trim` < .5.")
7426 NaN = _get_nan(a, b)
7428 if a.size == 0 or b.size == 0:
7429 # _axis_nan_policy decorator ensures this only happens with 1d input
7430 return TtestResult(NaN, NaN, df=NaN, alternative=NaN,
7431 standard_error=NaN, estimate=NaN)
7433 if permutations is not None and permutations != 0:
7434 if trim != 0:
7435 raise ValueError("Permutations are currently not supported "
7436 "with trimming.")
7437 if permutations < 0 or (np.isfinite(permutations) and
7438 int(permutations) != permutations):
7439 raise ValueError("Permutations must be a non-negative integer.")
7441 t, prob = _permutation_ttest(a, b, permutations=permutations,
7442 axis=axis, equal_var=equal_var,
7443 nan_policy=nan_policy,
7444 random_state=random_state,
7445 alternative=alternative)
7446 df, denom, estimate = NaN, NaN, NaN
7448 else:
7449 n1 = a.shape[axis]
7450 n2 = b.shape[axis]
7452 if trim == 0:
7453 if equal_var:
7454 old_errstate = np.geterr()
7455 np.seterr(divide='ignore', invalid='ignore')
7456 v1 = _var(a, axis, ddof=1)
7457 v2 = _var(b, axis, ddof=1)
7458 if equal_var:
7459 np.seterr(**old_errstate)
7460 m1 = np.mean(a, axis)
7461 m2 = np.mean(b, axis)
7462 else:
7463 v1, m1, n1 = _ttest_trim_var_mean_len(a, trim, axis)
7464 v2, m2, n2 = _ttest_trim_var_mean_len(b, trim, axis)
7466 if equal_var:
7467 df, denom = _equal_var_ttest_denom(v1, n1, v2, n2)
7468 else:
7469 df, denom = _unequal_var_ttest_denom(v1, n1, v2, n2)
7470 t, prob = _ttest_ind_from_stats(m1, m2, denom, df, alternative)
7472 # when nan_policy='omit', `df` can be different for different axis-slices
7473 df = np.broadcast_to(df, t.shape)[()]
7474 estimate = m1-m2
7476 # _axis_nan_policy decorator doesn't play well with strings
7477 alternative_num = {"less": -1, "two-sided": 0, "greater": 1}[alternative]
7478 return TtestResult(t, prob, df=df, alternative=alternative_num,
7479 standard_error=denom, estimate=estimate)
7482def _ttest_trim_var_mean_len(a, trim, axis):
7483 """Variance, mean, and length of winsorized input along specified axis"""
7484 # for use with `ttest_ind` when trimming.
7485 # further calculations in this test assume that the inputs are sorted.
7486 # From [4] Section 1 "Let x_1, ..., x_n be n ordered observations..."
7487 a = np.sort(a, axis=axis)
7489 # `g` is the number of elements to be replaced on each tail, converted
7490 # from a percentage amount of trimming
7491 n = a.shape[axis]
7492 g = int(n * trim)
7494 # Calculate the Winsorized variance of the input samples according to
7495 # specified `g`
7496 v = _calculate_winsorized_variance(a, g, axis)
7498 # the total number of elements in the trimmed samples
7499 n -= 2 * g
7501 # calculate the g-times trimmed mean, as defined in [4] (1-1)
7502 m = trim_mean(a, trim, axis=axis)
7503 return v, m, n
7506def _calculate_winsorized_variance(a, g, axis):
7507 """Calculates g-times winsorized variance along specified axis"""
7508 # it is expected that the input `a` is sorted along the correct axis
7509 if g == 0:
7510 return _var(a, ddof=1, axis=axis)
7511 # move the intended axis to the end that way it is easier to manipulate
7512 a_win = np.moveaxis(a, axis, -1)
7514 # save where NaNs are for later use.
7515 nans_indices = np.any(np.isnan(a_win), axis=-1)
7517 # Winsorization and variance calculation are done in one step in [4]
7518 # (1-3), but here winsorization is done first; replace the left and
7519 # right sides with the repeating value. This can be see in effect in (
7520 # 1-3) in [4], where the leftmost and rightmost tails are replaced with
7521 # `(g + 1) * x_{g + 1}` on the left and `(g + 1) * x_{n - g}` on the
7522 # right. Zero-indexing turns `g + 1` to `g`, and `n - g` to `- g - 1` in
7523 # array indexing.
7524 a_win[..., :g] = a_win[..., [g]]
7525 a_win[..., -g:] = a_win[..., [-g - 1]]
7527 # Determine the variance. In [4], the degrees of freedom is expressed as
7528 # `h - 1`, where `h = n - 2g` (unnumbered equations in Section 1, end of
7529 # page 369, beginning of page 370). This is converted to NumPy's format,
7530 # `n - ddof` for use with `np.var`. The result is converted to an
7531 # array to accommodate indexing later.
7532 var_win = np.asarray(_var(a_win, ddof=(2 * g + 1), axis=-1))
7534 # with `nan_policy='propagate'`, NaNs may be completely trimmed out
7535 # because they were sorted into the tail of the array. In these cases,
7536 # replace computed variances with `np.nan`.
7537 var_win[nans_indices] = np.nan
7538 return var_win
7541def _permutation_distribution_t(data, permutations, size_a, equal_var,
7542 random_state=None):
7543 """Generation permutation distribution of t statistic"""
7545 random_state = check_random_state(random_state)
7547 # prepare permutation indices
7548 size = data.shape[-1]
7549 # number of distinct combinations
7550 n_max = special.comb(size, size_a)
7552 if permutations < n_max:
7553 perm_generator = (random_state.permutation(size)
7554 for i in range(permutations))
7555 else:
7556 permutations = n_max
7557 perm_generator = (np.concatenate(z)
7558 for z in _all_partitions(size_a, size-size_a))
7560 t_stat = []
7561 for indices in _batch_generator(perm_generator, batch=50):
7562 # get one batch from perm_generator at a time as a list
7563 indices = np.array(indices)
7564 # generate permutations
7565 data_perm = data[..., indices]
7566 # move axis indexing permutations to position 0 to broadcast
7567 # nicely with t_stat_observed, which doesn't have this dimension
7568 data_perm = np.moveaxis(data_perm, -2, 0)
7570 a = data_perm[..., :size_a]
7571 b = data_perm[..., size_a:]
7572 t_stat.append(_calc_t_stat(a, b, equal_var))
7574 t_stat = np.concatenate(t_stat, axis=0)
7576 return t_stat, permutations, n_max
7579def _calc_t_stat(a, b, equal_var, axis=-1):
7580 """Calculate the t statistic along the given dimension."""
7581 na = a.shape[axis]
7582 nb = b.shape[axis]
7583 avg_a = np.mean(a, axis=axis)
7584 avg_b = np.mean(b, axis=axis)
7585 var_a = _var(a, axis=axis, ddof=1)
7586 var_b = _var(b, axis=axis, ddof=1)
7588 if not equal_var:
7589 denom = _unequal_var_ttest_denom(var_a, na, var_b, nb)[1]
7590 else:
7591 denom = _equal_var_ttest_denom(var_a, na, var_b, nb)[1]
7593 return (avg_a-avg_b)/denom
7596def _permutation_ttest(a, b, permutations, axis=0, equal_var=True,
7597 nan_policy='propagate', random_state=None,
7598 alternative="two-sided"):
7599 """
7600 Calculates the T-test for the means of TWO INDEPENDENT samples of scores
7601 using permutation methods.
7603 This test is similar to `stats.ttest_ind`, except it doesn't rely on an
7604 approximate normality assumption since it uses a permutation test.
7605 This function is only called from ttest_ind when permutations is not None.
7607 Parameters
7608 ----------
7609 a, b : array_like
7610 The arrays must be broadcastable, except along the dimension
7611 corresponding to `axis` (the zeroth, by default).
7612 axis : int, optional
7613 The axis over which to operate on a and b.
7614 permutations : int, optional
7615 Number of permutations used to calculate p-value. If greater than or
7616 equal to the number of distinct permutations, perform an exact test.
7617 equal_var : bool, optional
7618 If False, an equal variance (Welch's) t-test is conducted. Otherwise,
7619 an ordinary t-test is conducted.
7620 random_state : {None, int, `numpy.random.Generator`}, optional
7621 If `seed` is None the `numpy.random.Generator` singleton is used.
7622 If `seed` is an int, a new ``Generator`` instance is used,
7623 seeded with `seed`.
7624 If `seed` is already a ``Generator`` instance then that instance is
7625 used.
7626 Pseudorandom number generator state used for generating random
7627 permutations.
7629 Returns
7630 -------
7631 statistic : float or array
7632 The calculated t-statistic.
7633 pvalue : float or array
7634 The p-value.
7636 """
7637 random_state = check_random_state(random_state)
7639 t_stat_observed = _calc_t_stat(a, b, equal_var, axis=axis)
7641 na = a.shape[axis]
7642 mat = _broadcast_concatenate((a, b), axis=axis)
7643 mat = np.moveaxis(mat, axis, -1)
7645 t_stat, permutations, n_max = _permutation_distribution_t(
7646 mat, permutations, size_a=na, equal_var=equal_var,
7647 random_state=random_state)
7649 compare = {"less": np.less_equal,
7650 "greater": np.greater_equal,
7651 "two-sided": lambda x, y: (x <= -np.abs(y)) | (x >= np.abs(y))}
7653 # Calculate the p-values
7654 cmps = compare[alternative](t_stat, t_stat_observed)
7655 # Randomized test p-value calculation should use biased estimate; see e.g.
7656 # https://www.degruyter.com/document/doi/10.2202/1544-6115.1585/
7657 adjustment = 1 if n_max > permutations else 0
7658 pvalues = (cmps.sum(axis=0) + adjustment) / (permutations + adjustment)
7660 # nans propagate naturally in statistic calculation, but need to be
7661 # propagated manually into pvalues
7662 if nan_policy == 'propagate' and np.isnan(t_stat_observed).any():
7663 if np.ndim(pvalues) == 0:
7664 pvalues = np.float64(np.nan)
7665 else:
7666 pvalues[np.isnan(t_stat_observed)] = np.nan
7668 return (t_stat_observed, pvalues)
7671def _get_len(a, axis, msg):
7672 try:
7673 n = a.shape[axis]
7674 except IndexError:
7675 raise np.AxisError(axis, a.ndim, msg) from None
7676 return n
7679@_axis_nan_policy_factory(pack_TtestResult, default_axis=0, n_samples=2,
7680 result_to_tuple=unpack_TtestResult, n_outputs=6,
7681 paired=True)
7682def ttest_rel(a, b, axis=0, nan_policy='propagate', alternative="two-sided"):
7683 """Calculate the t-test on TWO RELATED samples of scores, a and b.
7685 This is a test for the null hypothesis that two related or
7686 repeated samples have identical average (expected) values.
7688 Parameters
7689 ----------
7690 a, b : array_like
7691 The arrays must have the same shape.
7692 axis : int or None, optional
7693 Axis along which to compute test. If None, compute over the whole
7694 arrays, `a`, and `b`.
7695 nan_policy : {'propagate', 'raise', 'omit'}, optional
7696 Defines how to handle when input contains nan.
7697 The following options are available (default is 'propagate'):
7699 * 'propagate': returns nan
7700 * 'raise': throws an error
7701 * 'omit': performs the calculations ignoring nan values
7702 alternative : {'two-sided', 'less', 'greater'}, optional
7703 Defines the alternative hypothesis.
7704 The following options are available (default is 'two-sided'):
7706 * 'two-sided': the means of the distributions underlying the samples
7707 are unequal.
7708 * 'less': the mean of the distribution underlying the first sample
7709 is less than the mean of the distribution underlying the second
7710 sample.
7711 * 'greater': the mean of the distribution underlying the first
7712 sample is greater than the mean of the distribution underlying
7713 the second sample.
7715 .. versionadded:: 1.6.0
7717 Returns
7718 -------
7719 result : `~scipy.stats._result_classes.TtestResult`
7720 An object with the following attributes:
7722 statistic : float or array
7723 The t-statistic.
7724 pvalue : float or array
7725 The p-value associated with the given alternative.
7726 df : float or array
7727 The number of degrees of freedom used in calculation of the
7728 t-statistic; this is one less than the size of the sample
7729 (``a.shape[axis]``).
7731 .. versionadded:: 1.10.0
7733 The object also has the following method:
7735 confidence_interval(confidence_level=0.95)
7736 Computes a confidence interval around the difference in
7737 population means for the given confidence level.
7738 The confidence interval is returned in a ``namedtuple`` with
7739 fields `low` and `high`.
7741 .. versionadded:: 1.10.0
7743 Notes
7744 -----
7745 Examples for use are scores of the same set of student in
7746 different exams, or repeated sampling from the same units. The
7747 test measures whether the average score differs significantly
7748 across samples (e.g. exams). If we observe a large p-value, for
7749 example greater than 0.05 or 0.1 then we cannot reject the null
7750 hypothesis of identical average scores. If the p-value is smaller
7751 than the threshold, e.g. 1%, 5% or 10%, then we reject the null
7752 hypothesis of equal averages. Small p-values are associated with
7753 large t-statistics.
7755 The t-statistic is calculated as ``np.mean(a - b)/se``, where ``se`` is the
7756 standard error. Therefore, the t-statistic will be positive when the sample
7757 mean of ``a - b`` is greater than zero and negative when the sample mean of
7758 ``a - b`` is less than zero.
7760 References
7761 ----------
7762 https://en.wikipedia.org/wiki/T-test#Dependent_t-test_for_paired_samples
7764 Examples
7765 --------
7766 >>> import numpy as np
7767 >>> from scipy import stats
7768 >>> rng = np.random.default_rng()
7770 >>> rvs1 = stats.norm.rvs(loc=5, scale=10, size=500, random_state=rng)
7771 >>> rvs2 = (stats.norm.rvs(loc=5, scale=10, size=500, random_state=rng)
7772 ... + stats.norm.rvs(scale=0.2, size=500, random_state=rng))
7773 >>> stats.ttest_rel(rvs1, rvs2)
7774 TtestResult(statistic=-0.4549717054410304, pvalue=0.6493274702088672, df=499) # noqa
7775 >>> rvs3 = (stats.norm.rvs(loc=8, scale=10, size=500, random_state=rng)
7776 ... + stats.norm.rvs(scale=0.2, size=500, random_state=rng))
7777 >>> stats.ttest_rel(rvs1, rvs3)
7778 TtestResult(statistic=-5.879467544540889, pvalue=7.540777129099917e-09, df=499) # noqa
7780 """
7781 a, b, axis = _chk2_asarray(a, b, axis)
7783 na = _get_len(a, axis, "first argument")
7784 nb = _get_len(b, axis, "second argument")
7785 if na != nb:
7786 raise ValueError('unequal length arrays')
7788 if na == 0 or nb == 0:
7789 # _axis_nan_policy decorator ensures this only happens with 1d input
7790 NaN = _get_nan(a, b)
7791 return TtestResult(NaN, NaN, df=NaN, alternative=NaN,
7792 standard_error=NaN, estimate=NaN)
7794 n = a.shape[axis]
7795 df = n - 1
7797 d = (a - b).astype(np.float64)
7798 v = _var(d, axis, ddof=1)
7799 dm = np.mean(d, axis)
7800 denom = np.sqrt(v / n)
7802 with np.errstate(divide='ignore', invalid='ignore'):
7803 t = np.divide(dm, denom)
7804 t, prob = _ttest_finish(df, t, alternative)
7806 # when nan_policy='omit', `df` can be different for different axis-slices
7807 df = np.broadcast_to(df, t.shape)[()]
7809 # _axis_nan_policy decorator doesn't play well with strings
7810 alternative_num = {"less": -1, "two-sided": 0, "greater": 1}[alternative]
7811 return TtestResult(t, prob, df=df, alternative=alternative_num,
7812 standard_error=denom, estimate=dm)
7815# Map from names to lambda_ values used in power_divergence().
7816_power_div_lambda_names = {
7817 "pearson": 1,
7818 "log-likelihood": 0,
7819 "freeman-tukey": -0.5,
7820 "mod-log-likelihood": -1,
7821 "neyman": -2,
7822 "cressie-read": 2/3,
7823}
7826def _count(a, axis=None):
7827 """Count the number of non-masked elements of an array.
7829 This function behaves like `np.ma.count`, but is much faster
7830 for ndarrays.
7831 """
7832 if hasattr(a, 'count'):
7833 num = a.count(axis=axis)
7834 if isinstance(num, np.ndarray) and num.ndim == 0:
7835 # In some cases, the `count` method returns a scalar array (e.g.
7836 # np.array(3)), but we want a plain integer.
7837 num = int(num)
7838 else:
7839 if axis is None:
7840 num = a.size
7841 else:
7842 num = a.shape[axis]
7843 return num
7846def _m_broadcast_to(a, shape):
7847 if np.ma.isMaskedArray(a):
7848 return np.ma.masked_array(np.broadcast_to(a, shape),
7849 mask=np.broadcast_to(a.mask, shape))
7850 return np.broadcast_to(a, shape, subok=True)
7853Power_divergenceResult = namedtuple('Power_divergenceResult',
7854 ('statistic', 'pvalue'))
7857def power_divergence(f_obs, f_exp=None, ddof=0, axis=0, lambda_=None):
7858 """Cressie-Read power divergence statistic and goodness of fit test.
7860 This function tests the null hypothesis that the categorical data
7861 has the given frequencies, using the Cressie-Read power divergence
7862 statistic.
7864 Parameters
7865 ----------
7866 f_obs : array_like
7867 Observed frequencies in each category.
7868 f_exp : array_like, optional
7869 Expected frequencies in each category. By default the categories are
7870 assumed to be equally likely.
7871 ddof : int, optional
7872 "Delta degrees of freedom": adjustment to the degrees of freedom
7873 for the p-value. The p-value is computed using a chi-squared
7874 distribution with ``k - 1 - ddof`` degrees of freedom, where `k`
7875 is the number of observed frequencies. The default value of `ddof`
7876 is 0.
7877 axis : int or None, optional
7878 The axis of the broadcast result of `f_obs` and `f_exp` along which to
7879 apply the test. If axis is None, all values in `f_obs` are treated
7880 as a single data set. Default is 0.
7881 lambda_ : float or str, optional
7882 The power in the Cressie-Read power divergence statistic. The default
7883 is 1. For convenience, `lambda_` may be assigned one of the following
7884 strings, in which case the corresponding numerical value is used:
7886 * ``"pearson"`` (value 1)
7887 Pearson's chi-squared statistic. In this case, the function is
7888 equivalent to `chisquare`.
7889 * ``"log-likelihood"`` (value 0)
7890 Log-likelihood ratio. Also known as the G-test [3]_.
7891 * ``"freeman-tukey"`` (value -1/2)
7892 Freeman-Tukey statistic.
7893 * ``"mod-log-likelihood"`` (value -1)
7894 Modified log-likelihood ratio.
7895 * ``"neyman"`` (value -2)
7896 Neyman's statistic.
7897 * ``"cressie-read"`` (value 2/3)
7898 The power recommended in [5]_.
7900 Returns
7901 -------
7902 res: Power_divergenceResult
7903 An object containing attributes:
7905 statistic : float or ndarray
7906 The Cressie-Read power divergence test statistic. The value is
7907 a float if `axis` is None or if` `f_obs` and `f_exp` are 1-D.
7908 pvalue : float or ndarray
7909 The p-value of the test. The value is a float if `ddof` and the
7910 return value `stat` are scalars.
7912 See Also
7913 --------
7914 chisquare
7916 Notes
7917 -----
7918 This test is invalid when the observed or expected frequencies in each
7919 category are too small. A typical rule is that all of the observed
7920 and expected frequencies should be at least 5.
7922 Also, the sum of the observed and expected frequencies must be the same
7923 for the test to be valid; `power_divergence` raises an error if the sums
7924 do not agree within a relative tolerance of ``1e-8``.
7926 When `lambda_` is less than zero, the formula for the statistic involves
7927 dividing by `f_obs`, so a warning or error may be generated if any value
7928 in `f_obs` is 0.
7930 Similarly, a warning or error may be generated if any value in `f_exp` is
7931 zero when `lambda_` >= 0.
7933 The default degrees of freedom, k-1, are for the case when no parameters
7934 of the distribution are estimated. If p parameters are estimated by
7935 efficient maximum likelihood then the correct degrees of freedom are
7936 k-1-p. If the parameters are estimated in a different way, then the
7937 dof can be between k-1-p and k-1. However, it is also possible that
7938 the asymptotic distribution is not a chisquare, in which case this
7939 test is not appropriate.
7941 This function handles masked arrays. If an element of `f_obs` or `f_exp`
7942 is masked, then data at that position is ignored, and does not count
7943 towards the size of the data set.
7945 .. versionadded:: 0.13.0
7947 References
7948 ----------
7949 .. [1] Lowry, Richard. "Concepts and Applications of Inferential
7950 Statistics". Chapter 8.
7951 https://web.archive.org/web/20171015035606/http://faculty.vassar.edu/lowry/ch8pt1.html
7952 .. [2] "Chi-squared test", https://en.wikipedia.org/wiki/Chi-squared_test
7953 .. [3] "G-test", https://en.wikipedia.org/wiki/G-test
7954 .. [4] Sokal, R. R. and Rohlf, F. J. "Biometry: the principles and
7955 practice of statistics in biological research", New York: Freeman
7956 (1981)
7957 .. [5] Cressie, N. and Read, T. R. C., "Multinomial Goodness-of-Fit
7958 Tests", J. Royal Stat. Soc. Series B, Vol. 46, No. 3 (1984),
7959 pp. 440-464.
7961 Examples
7962 --------
7963 (See `chisquare` for more examples.)
7965 When just `f_obs` is given, it is assumed that the expected frequencies
7966 are uniform and given by the mean of the observed frequencies. Here we
7967 perform a G-test (i.e. use the log-likelihood ratio statistic):
7969 >>> import numpy as np
7970 >>> from scipy.stats import power_divergence
7971 >>> power_divergence([16, 18, 16, 14, 12, 12], lambda_='log-likelihood')
7972 (2.006573162632538, 0.84823476779463769)
7974 The expected frequencies can be given with the `f_exp` argument:
7976 >>> power_divergence([16, 18, 16, 14, 12, 12],
7977 ... f_exp=[16, 16, 16, 16, 16, 8],
7978 ... lambda_='log-likelihood')
7979 (3.3281031458963746, 0.6495419288047497)
7981 When `f_obs` is 2-D, by default the test is applied to each column.
7983 >>> obs = np.array([[16, 18, 16, 14, 12, 12], [32, 24, 16, 28, 20, 24]]).T
7984 >>> obs.shape
7985 (6, 2)
7986 >>> power_divergence(obs, lambda_="log-likelihood")
7987 (array([ 2.00657316, 6.77634498]), array([ 0.84823477, 0.23781225]))
7989 By setting ``axis=None``, the test is applied to all data in the array,
7990 which is equivalent to applying the test to the flattened array.
7992 >>> power_divergence(obs, axis=None)
7993 (23.31034482758621, 0.015975692534127565)
7994 >>> power_divergence(obs.ravel())
7995 (23.31034482758621, 0.015975692534127565)
7997 `ddof` is the change to make to the default degrees of freedom.
7999 >>> power_divergence([16, 18, 16, 14, 12, 12], ddof=1)
8000 (2.0, 0.73575888234288467)
8002 The calculation of the p-values is done by broadcasting the
8003 test statistic with `ddof`.
8005 >>> power_divergence([16, 18, 16, 14, 12, 12], ddof=[0,1,2])
8006 (2.0, array([ 0.84914504, 0.73575888, 0.5724067 ]))
8008 `f_obs` and `f_exp` are also broadcast. In the following, `f_obs` has
8009 shape (6,) and `f_exp` has shape (2, 6), so the result of broadcasting
8010 `f_obs` and `f_exp` has shape (2, 6). To compute the desired chi-squared
8011 statistics, we must use ``axis=1``:
8013 >>> power_divergence([16, 18, 16, 14, 12, 12],
8014 ... f_exp=[[16, 16, 16, 16, 16, 8],
8015 ... [8, 20, 20, 16, 12, 12]],
8016 ... axis=1)
8017 (array([ 3.5 , 9.25]), array([ 0.62338763, 0.09949846]))
8019 """
8020 # Convert the input argument `lambda_` to a numerical value.
8021 if isinstance(lambda_, str):
8022 if lambda_ not in _power_div_lambda_names:
8023 names = repr(list(_power_div_lambda_names.keys()))[1:-1]
8024 raise ValueError("invalid string for lambda_: {!r}. "
8025 "Valid strings are {}".format(lambda_, names))
8026 lambda_ = _power_div_lambda_names[lambda_]
8027 elif lambda_ is None:
8028 lambda_ = 1
8030 f_obs = np.asanyarray(f_obs)
8031 f_obs_float = f_obs.astype(np.float64)
8033 if f_exp is not None:
8034 f_exp = np.asanyarray(f_exp)
8035 bshape = np.broadcast_shapes(f_obs_float.shape, f_exp.shape)
8036 f_obs_float = _m_broadcast_to(f_obs_float, bshape)
8037 f_exp = _m_broadcast_to(f_exp, bshape)
8038 rtol = 1e-8 # to pass existing tests
8039 with np.errstate(invalid='ignore'):
8040 f_obs_sum = f_obs_float.sum(axis=axis)
8041 f_exp_sum = f_exp.sum(axis=axis)
8042 relative_diff = (np.abs(f_obs_sum - f_exp_sum) /
8043 np.minimum(f_obs_sum, f_exp_sum))
8044 diff_gt_tol = (relative_diff > rtol).any()
8045 if diff_gt_tol:
8046 msg = (f"For each axis slice, the sum of the observed "
8047 f"frequencies must agree with the sum of the "
8048 f"expected frequencies to a relative tolerance "
8049 f"of {rtol}, but the percent differences are:\n"
8050 f"{relative_diff}")
8051 raise ValueError(msg)
8053 else:
8054 # Ignore 'invalid' errors so the edge case of a data set with length 0
8055 # is handled without spurious warnings.
8056 with np.errstate(invalid='ignore'):
8057 f_exp = f_obs.mean(axis=axis, keepdims=True)
8059 # `terms` is the array of terms that are summed along `axis` to create
8060 # the test statistic. We use some specialized code for a few special
8061 # cases of lambda_.
8062 if lambda_ == 1:
8063 # Pearson's chi-squared statistic
8064 terms = (f_obs_float - f_exp)**2 / f_exp
8065 elif lambda_ == 0:
8066 # Log-likelihood ratio (i.e. G-test)
8067 terms = 2.0 * special.xlogy(f_obs, f_obs / f_exp)
8068 elif lambda_ == -1:
8069 # Modified log-likelihood ratio
8070 terms = 2.0 * special.xlogy(f_exp, f_exp / f_obs)
8071 else:
8072 # General Cressie-Read power divergence.
8073 terms = f_obs * ((f_obs / f_exp)**lambda_ - 1)
8074 terms /= 0.5 * lambda_ * (lambda_ + 1)
8076 stat = terms.sum(axis=axis)
8078 num_obs = _count(terms, axis=axis)
8079 ddof = asarray(ddof)
8080 p = distributions.chi2.sf(stat, num_obs - 1 - ddof)
8082 return Power_divergenceResult(stat, p)
8085def chisquare(f_obs, f_exp=None, ddof=0, axis=0):
8086 """Calculate a one-way chi-square test.
8088 The chi-square test tests the null hypothesis that the categorical data
8089 has the given frequencies.
8091 Parameters
8092 ----------
8093 f_obs : array_like
8094 Observed frequencies in each category.
8095 f_exp : array_like, optional
8096 Expected frequencies in each category. By default the categories are
8097 assumed to be equally likely.
8098 ddof : int, optional
8099 "Delta degrees of freedom": adjustment to the degrees of freedom
8100 for the p-value. The p-value is computed using a chi-squared
8101 distribution with ``k - 1 - ddof`` degrees of freedom, where `k`
8102 is the number of observed frequencies. The default value of `ddof`
8103 is 0.
8104 axis : int or None, optional
8105 The axis of the broadcast result of `f_obs` and `f_exp` along which to
8106 apply the test. If axis is None, all values in `f_obs` are treated
8107 as a single data set. Default is 0.
8109 Returns
8110 -------
8111 res: Power_divergenceResult
8112 An object containing attributes:
8114 chisq : float or ndarray
8115 The chi-squared test statistic. The value is a float if `axis` is
8116 None or `f_obs` and `f_exp` are 1-D.
8117 pvalue : float or ndarray
8118 The p-value of the test. The value is a float if `ddof` and the
8119 return value `chisq` are scalars.
8121 See Also
8122 --------
8123 scipy.stats.power_divergence
8124 scipy.stats.fisher_exact : Fisher exact test on a 2x2 contingency table.
8125 scipy.stats.barnard_exact : An unconditional exact test. An alternative
8126 to chi-squared test for small sample sizes.
8128 Notes
8129 -----
8130 This test is invalid when the observed or expected frequencies in each
8131 category are too small. A typical rule is that all of the observed
8132 and expected frequencies should be at least 5. According to [3]_, the
8133 total number of samples is recommended to be greater than 13,
8134 otherwise exact tests (such as Barnard's Exact test) should be used
8135 because they do not overreject.
8137 Also, the sum of the observed and expected frequencies must be the same
8138 for the test to be valid; `chisquare` raises an error if the sums do not
8139 agree within a relative tolerance of ``1e-8``.
8141 The default degrees of freedom, k-1, are for the case when no parameters
8142 of the distribution are estimated. If p parameters are estimated by
8143 efficient maximum likelihood then the correct degrees of freedom are
8144 k-1-p. If the parameters are estimated in a different way, then the
8145 dof can be between k-1-p and k-1. However, it is also possible that
8146 the asymptotic distribution is not chi-square, in which case this test
8147 is not appropriate.
8149 References
8150 ----------
8151 .. [1] Lowry, Richard. "Concepts and Applications of Inferential
8152 Statistics". Chapter 8.
8153 https://web.archive.org/web/20171022032306/http://vassarstats.net:80/textbook/ch8pt1.html
8154 .. [2] "Chi-squared test", https://en.wikipedia.org/wiki/Chi-squared_test
8155 .. [3] Pearson, Karl. "On the criterion that a given system of deviations from the probable
8156 in the case of a correlated system of variables is such that it can be reasonably
8157 supposed to have arisen from random sampling", Philosophical Magazine. Series 5. 50
8158 (1900), pp. 157-175.
8159 .. [4] Mannan, R. William and E. Charles. Meslow. "Bird populations and
8160 vegetation characteristics in managed and old-growth forests,
8161 northeastern Oregon." Journal of Wildlife Management
8162 48, 1219-1238, :doi:`10.2307/3801783`, 1984.
8164 Examples
8165 --------
8166 In [4]_, bird foraging behavior was investigated in an old-growth forest
8167 of Oregon.
8168 In the forest, 44% of the canopy volume was Douglas fir,
8169 24% was ponderosa pine, 29% was grand fir, and 3% was western larch.
8170 The authors observed the behavior of several species of birds, one of
8171 which was the red-breasted nuthatch. They made 189 observations of this
8172 species foraging, recording 43 ("23%") of observations in Douglas fir,
8173 52 ("28%") in ponderosa pine, 54 ("29%") in grand fir, and 40 ("21%") in
8174 western larch.
8176 Using a chi-square test, we can test the null hypothesis that the
8177 proportions of foraging events are equal to the proportions of canopy
8178 volume. The authors of the paper considered a p-value less than 1% to be
8179 significant.
8181 Using the above proportions of canopy volume and observed events, we can
8182 infer expected frequencies.
8184 >>> import numpy as np
8185 >>> f_exp = np.array([44, 24, 29, 3]) / 100 * 189
8187 The observed frequencies of foraging were:
8189 >>> f_obs = np.array([43, 52, 54, 40])
8191 We can now compare the observed frequencies with the expected frequencies.
8193 >>> from scipy.stats import chisquare
8194 >>> chisquare(f_obs=f_obs, f_exp=f_exp)
8195 Power_divergenceResult(statistic=228.23515947653874, pvalue=3.3295585338846486e-49)
8197 The p-value is well below the chosen significance level. Hence, the
8198 authors considered the difference to be significant and concluded
8199 that the relative proportions of foraging events were not the same
8200 as the relative proportions of tree canopy volume.
8202 Following are other generic examples to demonstrate how the other
8203 parameters can be used.
8205 When just `f_obs` is given, it is assumed that the expected frequencies
8206 are uniform and given by the mean of the observed frequencies.
8208 >>> chisquare([16, 18, 16, 14, 12, 12])
8209 Power_divergenceResult(statistic=2.0, pvalue=0.84914503608460956)
8211 With `f_exp` the expected frequencies can be given.
8213 >>> chisquare([16, 18, 16, 14, 12, 12], f_exp=[16, 16, 16, 16, 16, 8])
8214 Power_divergenceResult(statistic=3.5, pvalue=0.62338762774958223)
8216 When `f_obs` is 2-D, by default the test is applied to each column.
8218 >>> obs = np.array([[16, 18, 16, 14, 12, 12], [32, 24, 16, 28, 20, 24]]).T
8219 >>> obs.shape
8220 (6, 2)
8221 >>> chisquare(obs)
8222 Power_divergenceResult(statistic=array([2. , 6.66666667]), pvalue=array([0.84914504, 0.24663415]))
8224 By setting ``axis=None``, the test is applied to all data in the array,
8225 which is equivalent to applying the test to the flattened array.
8227 >>> chisquare(obs, axis=None)
8228 Power_divergenceResult(statistic=23.31034482758621, pvalue=0.015975692534127565)
8229 >>> chisquare(obs.ravel())
8230 Power_divergenceResult(statistic=23.310344827586206, pvalue=0.01597569253412758)
8232 `ddof` is the change to make to the default degrees of freedom.
8234 >>> chisquare([16, 18, 16, 14, 12, 12], ddof=1)
8235 Power_divergenceResult(statistic=2.0, pvalue=0.7357588823428847)
8237 The calculation of the p-values is done by broadcasting the
8238 chi-squared statistic with `ddof`.
8240 >>> chisquare([16, 18, 16, 14, 12, 12], ddof=[0,1,2])
8241 Power_divergenceResult(statistic=2.0, pvalue=array([0.84914504, 0.73575888, 0.5724067 ]))
8243 `f_obs` and `f_exp` are also broadcast. In the following, `f_obs` has
8244 shape (6,) and `f_exp` has shape (2, 6), so the result of broadcasting
8245 `f_obs` and `f_exp` has shape (2, 6). To compute the desired chi-squared
8246 statistics, we use ``axis=1``:
8248 >>> chisquare([16, 18, 16, 14, 12, 12],
8249 ... f_exp=[[16, 16, 16, 16, 16, 8], [8, 20, 20, 16, 12, 12]],
8250 ... axis=1)
8251 Power_divergenceResult(statistic=array([3.5 , 9.25]), pvalue=array([0.62338763, 0.09949846]))
8253 """ # noqa
8254 return power_divergence(f_obs, f_exp=f_exp, ddof=ddof, axis=axis,
8255 lambda_="pearson")
8258KstestResult = _make_tuple_bunch('KstestResult', ['statistic', 'pvalue'],
8259 ['statistic_location', 'statistic_sign'])
8262def _compute_dplus(cdfvals, x):
8263 """Computes D+ as used in the Kolmogorov-Smirnov test.
8265 Parameters
8266 ----------
8267 cdfvals : array_like
8268 Sorted array of CDF values between 0 and 1
8269 x: array_like
8270 Sorted array of the stochastic variable itself
8272 Returns
8273 -------
8274 res: Pair with the following elements:
8275 - The maximum distance of the CDF values below Uniform(0, 1).
8276 - The location at which the maximum is reached.
8278 """
8279 n = len(cdfvals)
8280 dplus = (np.arange(1.0, n + 1) / n - cdfvals)
8281 amax = dplus.argmax()
8282 loc_max = x[amax]
8283 return (dplus[amax], loc_max)
8286def _compute_dminus(cdfvals, x):
8287 """Computes D- as used in the Kolmogorov-Smirnov test.
8289 Parameters
8290 ----------
8291 cdfvals : array_like
8292 Sorted array of CDF values between 0 and 1
8293 x: array_like
8294 Sorted array of the stochastic variable itself
8296 Returns
8297 -------
8298 res: Pair with the following elements:
8299 - Maximum distance of the CDF values above Uniform(0, 1)
8300 - The location at which the maximum is reached.
8301 """
8302 n = len(cdfvals)
8303 dminus = (cdfvals - np.arange(0.0, n)/n)
8304 amax = dminus.argmax()
8305 loc_max = x[amax]
8306 return (dminus[amax], loc_max)
8309@_rename_parameter("mode", "method")
8310def ks_1samp(x, cdf, args=(), alternative='two-sided', method='auto'):
8311 """
8312 Performs the one-sample Kolmogorov-Smirnov test for goodness of fit.
8314 This test compares the underlying distribution F(x) of a sample
8315 against a given continuous distribution G(x). See Notes for a description
8316 of the available null and alternative hypotheses.
8318 Parameters
8319 ----------
8320 x : array_like
8321 a 1-D array of observations of iid random variables.
8322 cdf : callable
8323 callable used to calculate the cdf.
8324 args : tuple, sequence, optional
8325 Distribution parameters, used with `cdf`.
8326 alternative : {'two-sided', 'less', 'greater'}, optional
8327 Defines the null and alternative hypotheses. Default is 'two-sided'.
8328 Please see explanations in the Notes below.
8329 method : {'auto', 'exact', 'approx', 'asymp'}, optional
8330 Defines the distribution used for calculating the p-value.
8331 The following options are available (default is 'auto'):
8333 * 'auto' : selects one of the other options.
8334 * 'exact' : uses the exact distribution of test statistic.
8335 * 'approx' : approximates the two-sided probability with twice
8336 the one-sided probability
8337 * 'asymp': uses asymptotic distribution of test statistic
8339 Returns
8340 -------
8341 res: KstestResult
8342 An object containing attributes:
8344 statistic : float
8345 KS test statistic, either D+, D-, or D (the maximum of the two)
8346 pvalue : float
8347 One-tailed or two-tailed p-value.
8348 statistic_location : float
8349 Value of `x` corresponding with the KS statistic; i.e., the
8350 distance between the empirical distribution function and the
8351 hypothesized cumulative distribution function is measured at this
8352 observation.
8353 statistic_sign : int
8354 +1 if the KS statistic is the maximum positive difference between
8355 the empirical distribution function and the hypothesized cumulative
8356 distribution function (D+); -1 if the KS statistic is the maximum
8357 negative difference (D-).
8360 See Also
8361 --------
8362 ks_2samp, kstest
8364 Notes
8365 -----
8366 There are three options for the null and corresponding alternative
8367 hypothesis that can be selected using the `alternative` parameter.
8369 - `two-sided`: The null hypothesis is that the two distributions are
8370 identical, F(x)=G(x) for all x; the alternative is that they are not
8371 identical.
8373 - `less`: The null hypothesis is that F(x) >= G(x) for all x; the
8374 alternative is that F(x) < G(x) for at least one x.
8376 - `greater`: The null hypothesis is that F(x) <= G(x) for all x; the
8377 alternative is that F(x) > G(x) for at least one x.
8379 Note that the alternative hypotheses describe the *CDFs* of the
8380 underlying distributions, not the observed values. For example,
8381 suppose x1 ~ F and x2 ~ G. If F(x) > G(x) for all x, the values in
8382 x1 tend to be less than those in x2.
8384 Examples
8385 --------
8386 Suppose we wish to test the null hypothesis that a sample is distributed
8387 according to the standard normal.
8388 We choose a confidence level of 95%; that is, we will reject the null
8389 hypothesis in favor of the alternative if the p-value is less than 0.05.
8391 When testing uniformly distributed data, we would expect the
8392 null hypothesis to be rejected.
8394 >>> import numpy as np
8395 >>> from scipy import stats
8396 >>> rng = np.random.default_rng()
8397 >>> stats.ks_1samp(stats.uniform.rvs(size=100, random_state=rng),
8398 ... stats.norm.cdf)
8399 KstestResult(statistic=0.5001899973268688, pvalue=1.1616392184763533e-23)
8401 Indeed, the p-value is lower than our threshold of 0.05, so we reject the
8402 null hypothesis in favor of the default "two-sided" alternative: the data
8403 are *not* distributed according to the standard normal.
8405 When testing random variates from the standard normal distribution, we
8406 expect the data to be consistent with the null hypothesis most of the time.
8408 >>> x = stats.norm.rvs(size=100, random_state=rng)
8409 >>> stats.ks_1samp(x, stats.norm.cdf)
8410 KstestResult(statistic=0.05345882212970396, pvalue=0.9227159037744717)
8412 As expected, the p-value of 0.92 is not below our threshold of 0.05, so
8413 we cannot reject the null hypothesis.
8415 Suppose, however, that the random variates are distributed according to
8416 a normal distribution that is shifted toward greater values. In this case,
8417 the cumulative density function (CDF) of the underlying distribution tends
8418 to be *less* than the CDF of the standard normal. Therefore, we would
8419 expect the null hypothesis to be rejected with ``alternative='less'``:
8421 >>> x = stats.norm.rvs(size=100, loc=0.5, random_state=rng)
8422 >>> stats.ks_1samp(x, stats.norm.cdf, alternative='less')
8423 KstestResult(statistic=0.17482387821055168, pvalue=0.001913921057766743)
8425 and indeed, with p-value smaller than our threshold, we reject the null
8426 hypothesis in favor of the alternative.
8428 """
8429 mode = method
8431 alternative = {'t': 'two-sided', 'g': 'greater', 'l': 'less'}.get(
8432 alternative.lower()[0], alternative)
8433 if alternative not in ['two-sided', 'greater', 'less']:
8434 raise ValueError("Unexpected alternative %s" % alternative)
8435 if np.ma.is_masked(x):
8436 x = x.compressed()
8438 N = len(x)
8439 x = np.sort(x)
8440 cdfvals = cdf(x, *args)
8442 if alternative == 'greater':
8443 Dplus, d_location = _compute_dplus(cdfvals, x)
8444 return KstestResult(Dplus, distributions.ksone.sf(Dplus, N),
8445 statistic_location=d_location,
8446 statistic_sign=1)
8448 if alternative == 'less':
8449 Dminus, d_location = _compute_dminus(cdfvals, x)
8450 return KstestResult(Dminus, distributions.ksone.sf(Dminus, N),
8451 statistic_location=d_location,
8452 statistic_sign=-1)
8454 # alternative == 'two-sided':
8455 Dplus, dplus_location = _compute_dplus(cdfvals, x)
8456 Dminus, dminus_location = _compute_dminus(cdfvals, x)
8457 if Dplus > Dminus:
8458 D = Dplus
8459 d_location = dplus_location
8460 d_sign = 1
8461 else:
8462 D = Dminus
8463 d_location = dminus_location
8464 d_sign = -1
8466 if mode == 'auto': # Always select exact
8467 mode = 'exact'
8468 if mode == 'exact':
8469 prob = distributions.kstwo.sf(D, N)
8470 elif mode == 'asymp':
8471 prob = distributions.kstwobign.sf(D * np.sqrt(N))
8472 else:
8473 # mode == 'approx'
8474 prob = 2 * distributions.ksone.sf(D, N)
8475 prob = np.clip(prob, 0, 1)
8476 return KstestResult(D, prob,
8477 statistic_location=d_location,
8478 statistic_sign=d_sign)
8481Ks_2sampResult = KstestResult
8484def _compute_prob_outside_square(n, h):
8485 """
8486 Compute the proportion of paths that pass outside the two diagonal lines.
8488 Parameters
8489 ----------
8490 n : integer
8491 n > 0
8492 h : integer
8493 0 <= h <= n
8495 Returns
8496 -------
8497 p : float
8498 The proportion of paths that pass outside the lines x-y = +/-h.
8500 """
8501 # Compute Pr(D_{n,n} >= h/n)
8502 # Prob = 2 * ( binom(2n, n-h) - binom(2n, n-2a) + binom(2n, n-3a) - ... )
8503 # / binom(2n, n)
8504 # This formulation exhibits subtractive cancellation.
8505 # Instead divide each term by binom(2n, n), then factor common terms
8506 # and use a Horner-like algorithm
8507 # P = 2 * A0 * (1 - A1*(1 - A2*(1 - A3*(1 - A4*(...)))))
8509 P = 0.0
8510 k = int(np.floor(n / h))
8511 while k >= 0:
8512 p1 = 1.0
8513 # Each of the Ai terms has numerator and denominator with
8514 # h simple terms.
8515 for j in range(h):
8516 p1 = (n - k * h - j) * p1 / (n + k * h + j + 1)
8517 P = p1 * (1.0 - P)
8518 k -= 1
8519 return 2 * P
8522def _count_paths_outside_method(m, n, g, h):
8523 """Count the number of paths that pass outside the specified diagonal.
8525 Parameters
8526 ----------
8527 m : integer
8528 m > 0
8529 n : integer
8530 n > 0
8531 g : integer
8532 g is greatest common divisor of m and n
8533 h : integer
8534 0 <= h <= lcm(m,n)
8536 Returns
8537 -------
8538 p : float
8539 The number of paths that go low.
8540 The calculation may overflow - check for a finite answer.
8542 Notes
8543 -----
8544 Count the integer lattice paths from (0, 0) to (m, n), which at some
8545 point (x, y) along the path, satisfy:
8546 m*y <= n*x - h*g
8547 The paths make steps of size +1 in either positive x or positive y
8548 directions.
8550 We generally follow Hodges' treatment of Drion/Gnedenko/Korolyuk.
8551 Hodges, J.L. Jr.,
8552 "The Significance Probability of the Smirnov Two-Sample Test,"
8553 Arkiv fiur Matematik, 3, No. 43 (1958), 469-86.
8555 """
8556 # Compute #paths which stay lower than x/m-y/n = h/lcm(m,n)
8557 # B(x, y) = #{paths from (0,0) to (x,y) without
8558 # previously crossing the boundary}
8559 # = binom(x, y) - #{paths which already reached the boundary}
8560 # Multiply by the number of path extensions going from (x, y) to (m, n)
8561 # Sum.
8563 # Probability is symmetrical in m, n. Computation below assumes m >= n.
8564 if m < n:
8565 m, n = n, m
8566 mg = m // g
8567 ng = n // g
8569 # Not every x needs to be considered.
8570 # xj holds the list of x values to be checked.
8571 # Wherever n*x/m + ng*h crosses an integer
8572 lxj = n + (mg-h)//mg
8573 xj = [(h + mg * j + ng-1)//ng for j in range(lxj)]
8574 # B is an array just holding a few values of B(x,y), the ones needed.
8575 # B[j] == B(x_j, j)
8576 if lxj == 0:
8577 return special.binom(m + n, n)
8578 B = np.zeros(lxj)
8579 B[0] = 1
8580 # Compute the B(x, y) terms
8581 for j in range(1, lxj):
8582 Bj = special.binom(xj[j] + j, j)
8583 for i in range(j):
8584 bin = special.binom(xj[j] - xj[i] + j - i, j-i)
8585 Bj -= bin * B[i]
8586 B[j] = Bj
8587 # Compute the number of path extensions...
8588 num_paths = 0
8589 for j in range(lxj):
8590 bin = special.binom((m-xj[j]) + (n - j), n-j)
8591 term = B[j] * bin
8592 num_paths += term
8593 return num_paths
8596def _attempt_exact_2kssamp(n1, n2, g, d, alternative):
8597 """Attempts to compute the exact 2sample probability.
8599 n1, n2 are the sample sizes
8600 g is the gcd(n1, n2)
8601 d is the computed max difference in ECDFs
8603 Returns (success, d, probability)
8604 """
8605 lcm = (n1 // g) * n2
8606 h = int(np.round(d * lcm))
8607 d = h * 1.0 / lcm
8608 if h == 0:
8609 return True, d, 1.0
8610 saw_fp_error, prob = False, np.nan
8611 try:
8612 with np.errstate(invalid="raise", over="raise"):
8613 if alternative == 'two-sided':
8614 if n1 == n2:
8615 prob = _compute_prob_outside_square(n1, h)
8616 else:
8617 prob = _compute_outer_prob_inside_method(n1, n2, g, h)
8618 else:
8619 if n1 == n2:
8620 # prob = binom(2n, n-h) / binom(2n, n)
8621 # Evaluating in that form incurs roundoff errors
8622 # from special.binom. Instead calculate directly
8623 jrange = np.arange(h)
8624 prob = np.prod((n1 - jrange) / (n1 + jrange + 1.0))
8625 else:
8626 with np.errstate(over='raise'):
8627 num_paths = _count_paths_outside_method(n1, n2, g, h)
8628 bin = special.binom(n1 + n2, n1)
8629 if num_paths > bin or np.isinf(bin):
8630 saw_fp_error = True
8631 else:
8632 prob = num_paths / bin
8634 except (FloatingPointError, OverflowError):
8635 saw_fp_error = True
8637 if saw_fp_error:
8638 return False, d, np.nan
8639 if not (0 <= prob <= 1):
8640 return False, d, prob
8641 return True, d, prob
8644@_rename_parameter("mode", "method")
8645def ks_2samp(data1, data2, alternative='two-sided', method='auto'):
8646 """
8647 Performs the two-sample Kolmogorov-Smirnov test for goodness of fit.
8649 This test compares the underlying continuous distributions F(x) and G(x)
8650 of two independent samples. See Notes for a description of the available
8651 null and alternative hypotheses.
8653 Parameters
8654 ----------
8655 data1, data2 : array_like, 1-Dimensional
8656 Two arrays of sample observations assumed to be drawn from a continuous
8657 distribution, sample sizes can be different.
8658 alternative : {'two-sided', 'less', 'greater'}, optional
8659 Defines the null and alternative hypotheses. Default is 'two-sided'.
8660 Please see explanations in the Notes below.
8661 method : {'auto', 'exact', 'asymp'}, optional
8662 Defines the method used for calculating the p-value.
8663 The following options are available (default is 'auto'):
8665 * 'auto' : use 'exact' for small size arrays, 'asymp' for large
8666 * 'exact' : use exact distribution of test statistic
8667 * 'asymp' : use asymptotic distribution of test statistic
8669 Returns
8670 -------
8671 res: KstestResult
8672 An object containing attributes:
8674 statistic : float
8675 KS test statistic.
8676 pvalue : float
8677 One-tailed or two-tailed p-value.
8678 statistic_location : float
8679 Value from `data1` or `data2` corresponding with the KS statistic;
8680 i.e., the distance between the empirical distribution functions is
8681 measured at this observation.
8682 statistic_sign : int
8683 +1 if the empirical distribution function of `data1` exceeds
8684 the empirical distribution function of `data2` at
8685 `statistic_location`, otherwise -1.
8687 See Also
8688 --------
8689 kstest, ks_1samp, epps_singleton_2samp, anderson_ksamp
8691 Notes
8692 -----
8693 There are three options for the null and corresponding alternative
8694 hypothesis that can be selected using the `alternative` parameter.
8696 - `less`: The null hypothesis is that F(x) >= G(x) for all x; the
8697 alternative is that F(x) < G(x) for at least one x. The statistic
8698 is the magnitude of the minimum (most negative) difference between the
8699 empirical distribution functions of the samples.
8701 - `greater`: The null hypothesis is that F(x) <= G(x) for all x; the
8702 alternative is that F(x) > G(x) for at least one x. The statistic
8703 is the maximum (most positive) difference between the empirical
8704 distribution functions of the samples.
8706 - `two-sided`: The null hypothesis is that the two distributions are
8707 identical, F(x)=G(x) for all x; the alternative is that they are not
8708 identical. The statistic is the maximum absolute difference between the
8709 empirical distribution functions of the samples.
8711 Note that the alternative hypotheses describe the *CDFs* of the
8712 underlying distributions, not the observed values of the data. For example,
8713 suppose x1 ~ F and x2 ~ G. If F(x) > G(x) for all x, the values in
8714 x1 tend to be less than those in x2.
8716 If the KS statistic is large, then the p-value will be small, and this may
8717 be taken as evidence against the null hypothesis in favor of the
8718 alternative.
8720 If ``method='exact'``, `ks_2samp` attempts to compute an exact p-value,
8721 that is, the probability under the null hypothesis of obtaining a test
8722 statistic value as extreme as the value computed from the data.
8723 If ``method='asymp'``, the asymptotic Kolmogorov-Smirnov distribution is
8724 used to compute an approximate p-value.
8725 If ``method='auto'``, an exact p-value computation is attempted if both
8726 sample sizes are less than 10000; otherwise, the asymptotic method is used.
8727 In any case, if an exact p-value calculation is attempted and fails, a
8728 warning will be emitted, and the asymptotic p-value will be returned.
8730 The 'two-sided' 'exact' computation computes the complementary probability
8731 and then subtracts from 1. As such, the minimum probability it can return
8732 is about 1e-16. While the algorithm itself is exact, numerical
8733 errors may accumulate for large sample sizes. It is most suited to
8734 situations in which one of the sample sizes is only a few thousand.
8736 We generally follow Hodges' treatment of Drion/Gnedenko/Korolyuk [1]_.
8738 References
8739 ----------
8740 .. [1] Hodges, J.L. Jr., "The Significance Probability of the Smirnov
8741 Two-Sample Test," Arkiv fiur Matematik, 3, No. 43 (1958), 469-86.
8743 Examples
8744 --------
8745 Suppose we wish to test the null hypothesis that two samples were drawn
8746 from the same distribution.
8747 We choose a confidence level of 95%; that is, we will reject the null
8748 hypothesis in favor of the alternative if the p-value is less than 0.05.
8750 If the first sample were drawn from a uniform distribution and the second
8751 were drawn from the standard normal, we would expect the null hypothesis
8752 to be rejected.
8754 >>> import numpy as np
8755 >>> from scipy import stats
8756 >>> rng = np.random.default_rng()
8757 >>> sample1 = stats.uniform.rvs(size=100, random_state=rng)
8758 >>> sample2 = stats.norm.rvs(size=110, random_state=rng)
8759 >>> stats.ks_2samp(sample1, sample2)
8760 KstestResult(statistic=0.5454545454545454, pvalue=7.37417839555191e-15)
8762 Indeed, the p-value is lower than our threshold of 0.05, so we reject the
8763 null hypothesis in favor of the default "two-sided" alternative: the data
8764 were *not* drawn from the same distribution.
8766 When both samples are drawn from the same distribution, we expect the data
8767 to be consistent with the null hypothesis most of the time.
8769 >>> sample1 = stats.norm.rvs(size=105, random_state=rng)
8770 >>> sample2 = stats.norm.rvs(size=95, random_state=rng)
8771 >>> stats.ks_2samp(sample1, sample2)
8772 KstestResult(statistic=0.10927318295739348, pvalue=0.5438289009927495)
8774 As expected, the p-value of 0.54 is not below our threshold of 0.05, so
8775 we cannot reject the null hypothesis.
8777 Suppose, however, that the first sample were drawn from
8778 a normal distribution shifted toward greater values. In this case,
8779 the cumulative density function (CDF) of the underlying distribution tends
8780 to be *less* than the CDF underlying the second sample. Therefore, we would
8781 expect the null hypothesis to be rejected with ``alternative='less'``:
8783 >>> sample1 = stats.norm.rvs(size=105, loc=0.5, random_state=rng)
8784 >>> stats.ks_2samp(sample1, sample2, alternative='less')
8785 KstestResult(statistic=0.4055137844611529, pvalue=3.5474563068855554e-08)
8787 and indeed, with p-value smaller than our threshold, we reject the null
8788 hypothesis in favor of the alternative.
8790 """
8791 mode = method
8793 if mode not in ['auto', 'exact', 'asymp']:
8794 raise ValueError(f'Invalid value for mode: {mode}')
8795 alternative = {'t': 'two-sided', 'g': 'greater', 'l': 'less'}.get(
8796 alternative.lower()[0], alternative)
8797 if alternative not in ['two-sided', 'less', 'greater']:
8798 raise ValueError(f'Invalid value for alternative: {alternative}')
8799 MAX_AUTO_N = 10000 # 'auto' will attempt to be exact if n1,n2 <= MAX_AUTO_N
8800 if np.ma.is_masked(data1):
8801 data1 = data1.compressed()
8802 if np.ma.is_masked(data2):
8803 data2 = data2.compressed()
8804 data1 = np.sort(data1)
8805 data2 = np.sort(data2)
8806 n1 = data1.shape[0]
8807 n2 = data2.shape[0]
8808 if min(n1, n2) == 0:
8809 raise ValueError('Data passed to ks_2samp must not be empty')
8811 data_all = np.concatenate([data1, data2])
8812 # using searchsorted solves equal data problem
8813 cdf1 = np.searchsorted(data1, data_all, side='right') / n1
8814 cdf2 = np.searchsorted(data2, data_all, side='right') / n2
8815 cddiffs = cdf1 - cdf2
8817 # Identify the location of the statistic
8818 argminS = np.argmin(cddiffs)
8819 argmaxS = np.argmax(cddiffs)
8820 loc_minS = data_all[argminS]
8821 loc_maxS = data_all[argmaxS]
8823 # Ensure sign of minS is not negative.
8824 minS = np.clip(-cddiffs[argminS], 0, 1)
8825 maxS = cddiffs[argmaxS]
8827 if alternative == 'less' or (alternative == 'two-sided' and minS > maxS):
8828 d = minS
8829 d_location = loc_minS
8830 d_sign = -1
8831 else:
8832 d = maxS
8833 d_location = loc_maxS
8834 d_sign = 1
8835 g = gcd(n1, n2)
8836 n1g = n1 // g
8837 n2g = n2 // g
8838 prob = -np.inf
8839 if mode == 'auto':
8840 mode = 'exact' if max(n1, n2) <= MAX_AUTO_N else 'asymp'
8841 elif mode == 'exact':
8842 # If lcm(n1, n2) is too big, switch from exact to asymp
8843 if n1g >= np.iinfo(np.int32).max / n2g:
8844 mode = 'asymp'
8845 warnings.warn(
8846 f"Exact ks_2samp calculation not possible with samples sizes "
8847 f"{n1} and {n2}. Switching to 'asymp'.", RuntimeWarning,
8848 stacklevel=3)
8850 if mode == 'exact':
8851 success, d, prob = _attempt_exact_2kssamp(n1, n2, g, d, alternative)
8852 if not success:
8853 mode = 'asymp'
8854 warnings.warn(f"ks_2samp: Exact calculation unsuccessful. "
8855 f"Switching to method={mode}.", RuntimeWarning,
8856 stacklevel=3)
8858 if mode == 'asymp':
8859 # The product n1*n2 is large. Use Smirnov's asymptoptic formula.
8860 # Ensure float to avoid overflow in multiplication
8861 # sorted because the one-sided formula is not symmetric in n1, n2
8862 m, n = sorted([float(n1), float(n2)], reverse=True)
8863 en = m * n / (m + n)
8864 if alternative == 'two-sided':
8865 prob = distributions.kstwo.sf(d, np.round(en))
8866 else:
8867 z = np.sqrt(en) * d
8868 # Use Hodges' suggested approximation Eqn 5.3
8869 # Requires m to be the larger of (n1, n2)
8870 expt = -2 * z**2 - 2 * z * (m + 2*n)/np.sqrt(m*n*(m+n))/3.0
8871 prob = np.exp(expt)
8873 prob = np.clip(prob, 0, 1)
8874 return KstestResult(d, prob, statistic_location=d_location,
8875 statistic_sign=d_sign)
8878def _parse_kstest_args(data1, data2, args, N):
8879 # kstest allows many different variations of arguments.
8880 # Pull out the parsing into a separate function
8881 # (xvals, yvals, ) # 2sample
8882 # (xvals, cdf function,..)
8883 # (xvals, name of distribution, ...)
8884 # (name of distribution, name of distribution, ...)
8886 # Returns xvals, yvals, cdf
8887 # where cdf is a cdf function, or None
8888 # and yvals is either an array_like of values, or None
8889 # and xvals is array_like.
8890 rvsfunc, cdf = None, None
8891 if isinstance(data1, str):
8892 rvsfunc = getattr(distributions, data1).rvs
8893 elif callable(data1):
8894 rvsfunc = data1
8896 if isinstance(data2, str):
8897 cdf = getattr(distributions, data2).cdf
8898 data2 = None
8899 elif callable(data2):
8900 cdf = data2
8901 data2 = None
8903 data1 = np.sort(rvsfunc(*args, size=N) if rvsfunc else data1)
8904 return data1, data2, cdf
8907@_rename_parameter("mode", "method")
8908def kstest(rvs, cdf, args=(), N=20, alternative='two-sided', method='auto'):
8909 """
8910 Performs the (one-sample or two-sample) Kolmogorov-Smirnov test for
8911 goodness of fit.
8913 The one-sample test compares the underlying distribution F(x) of a sample
8914 against a given distribution G(x). The two-sample test compares the
8915 underlying distributions of two independent samples. Both tests are valid
8916 only for continuous distributions.
8918 Parameters
8919 ----------
8920 rvs : str, array_like, or callable
8921 If an array, it should be a 1-D array of observations of random
8922 variables.
8923 If a callable, it should be a function to generate random variables;
8924 it is required to have a keyword argument `size`.
8925 If a string, it should be the name of a distribution in `scipy.stats`,
8926 which will be used to generate random variables.
8927 cdf : str, array_like or callable
8928 If array_like, it should be a 1-D array of observations of random
8929 variables, and the two-sample test is performed
8930 (and rvs must be array_like).
8931 If a callable, that callable is used to calculate the cdf.
8932 If a string, it should be the name of a distribution in `scipy.stats`,
8933 which will be used as the cdf function.
8934 args : tuple, sequence, optional
8935 Distribution parameters, used if `rvs` or `cdf` are strings or
8936 callables.
8937 N : int, optional
8938 Sample size if `rvs` is string or callable. Default is 20.
8939 alternative : {'two-sided', 'less', 'greater'}, optional
8940 Defines the null and alternative hypotheses. Default is 'two-sided'.
8941 Please see explanations in the Notes below.
8942 method : {'auto', 'exact', 'approx', 'asymp'}, optional
8943 Defines the distribution used for calculating the p-value.
8944 The following options are available (default is 'auto'):
8946 * 'auto' : selects one of the other options.
8947 * 'exact' : uses the exact distribution of test statistic.
8948 * 'approx' : approximates the two-sided probability with twice the
8949 one-sided probability
8950 * 'asymp': uses asymptotic distribution of test statistic
8952 Returns
8953 -------
8954 res: KstestResult
8955 An object containing attributes:
8957 statistic : float
8958 KS test statistic, either D+, D-, or D (the maximum of the two)
8959 pvalue : float
8960 One-tailed or two-tailed p-value.
8961 statistic_location : float
8962 In a one-sample test, this is the value of `rvs`
8963 corresponding with the KS statistic; i.e., the distance between
8964 the empirical distribution function and the hypothesized cumulative
8965 distribution function is measured at this observation.
8967 In a two-sample test, this is the value from `rvs` or `cdf`
8968 corresponding with the KS statistic; i.e., the distance between
8969 the empirical distribution functions is measured at this
8970 observation.
8971 statistic_sign : int
8972 In a one-sample test, this is +1 if the KS statistic is the
8973 maximum positive difference between the empirical distribution
8974 function and the hypothesized cumulative distribution function
8975 (D+); it is -1 if the KS statistic is the maximum negative
8976 difference (D-).
8978 In a two-sample test, this is +1 if the empirical distribution
8979 function of `rvs` exceeds the empirical distribution
8980 function of `cdf` at `statistic_location`, otherwise -1.
8982 See Also
8983 --------
8984 ks_1samp, ks_2samp
8986 Notes
8987 -----
8988 There are three options for the null and corresponding alternative
8989 hypothesis that can be selected using the `alternative` parameter.
8991 - `two-sided`: The null hypothesis is that the two distributions are
8992 identical, F(x)=G(x) for all x; the alternative is that they are not
8993 identical.
8995 - `less`: The null hypothesis is that F(x) >= G(x) for all x; the
8996 alternative is that F(x) < G(x) for at least one x.
8998 - `greater`: The null hypothesis is that F(x) <= G(x) for all x; the
8999 alternative is that F(x) > G(x) for at least one x.
9001 Note that the alternative hypotheses describe the *CDFs* of the
9002 underlying distributions, not the observed values. For example,
9003 suppose x1 ~ F and x2 ~ G. If F(x) > G(x) for all x, the values in
9004 x1 tend to be less than those in x2.
9007 Examples
9008 --------
9009 Suppose we wish to test the null hypothesis that a sample is distributed
9010 according to the standard normal.
9011 We choose a confidence level of 95%; that is, we will reject the null
9012 hypothesis in favor of the alternative if the p-value is less than 0.05.
9014 When testing uniformly distributed data, we would expect the
9015 null hypothesis to be rejected.
9017 >>> import numpy as np
9018 >>> from scipy import stats
9019 >>> rng = np.random.default_rng()
9020 >>> stats.kstest(stats.uniform.rvs(size=100, random_state=rng),
9021 ... stats.norm.cdf)
9022 KstestResult(statistic=0.5001899973268688, pvalue=1.1616392184763533e-23)
9024 Indeed, the p-value is lower than our threshold of 0.05, so we reject the
9025 null hypothesis in favor of the default "two-sided" alternative: the data
9026 are *not* distributed according to the standard normal.
9028 When testing random variates from the standard normal distribution, we
9029 expect the data to be consistent with the null hypothesis most of the time.
9031 >>> x = stats.norm.rvs(size=100, random_state=rng)
9032 >>> stats.kstest(x, stats.norm.cdf)
9033 KstestResult(statistic=0.05345882212970396, pvalue=0.9227159037744717)
9035 As expected, the p-value of 0.92 is not below our threshold of 0.05, so
9036 we cannot reject the null hypothesis.
9038 Suppose, however, that the random variates are distributed according to
9039 a normal distribution that is shifted toward greater values. In this case,
9040 the cumulative density function (CDF) of the underlying distribution tends
9041 to be *less* than the CDF of the standard normal. Therefore, we would
9042 expect the null hypothesis to be rejected with ``alternative='less'``:
9044 >>> x = stats.norm.rvs(size=100, loc=0.5, random_state=rng)
9045 >>> stats.kstest(x, stats.norm.cdf, alternative='less')
9046 KstestResult(statistic=0.17482387821055168, pvalue=0.001913921057766743)
9048 and indeed, with p-value smaller than our threshold, we reject the null
9049 hypothesis in favor of the alternative.
9051 For convenience, the previous test can be performed using the name of the
9052 distribution as the second argument.
9054 >>> stats.kstest(x, "norm", alternative='less')
9055 KstestResult(statistic=0.17482387821055168, pvalue=0.001913921057766743)
9057 The examples above have all been one-sample tests identical to those
9058 performed by `ks_1samp`. Note that `kstest` can also perform two-sample
9059 tests identical to those performed by `ks_2samp`. For example, when two
9060 samples are drawn from the same distribution, we expect the data to be
9061 consistent with the null hypothesis most of the time.
9063 >>> sample1 = stats.laplace.rvs(size=105, random_state=rng)
9064 >>> sample2 = stats.laplace.rvs(size=95, random_state=rng)
9065 >>> stats.kstest(sample1, sample2)
9066 KstestResult(statistic=0.11779448621553884, pvalue=0.4494256912629795)
9068 As expected, the p-value of 0.45 is not below our threshold of 0.05, so
9069 we cannot reject the null hypothesis.
9071 """
9072 # to not break compatibility with existing code
9073 if alternative == 'two_sided':
9074 alternative = 'two-sided'
9075 if alternative not in ['two-sided', 'greater', 'less']:
9076 raise ValueError("Unexpected alternative %s" % alternative)
9077 xvals, yvals, cdf = _parse_kstest_args(rvs, cdf, args, N)
9078 if cdf:
9079 return ks_1samp(xvals, cdf, args=args, alternative=alternative,
9080 method=method)
9081 return ks_2samp(xvals, yvals, alternative=alternative, method=method)
9084def tiecorrect(rankvals):
9085 """Tie correction factor for Mann-Whitney U and Kruskal-Wallis H tests.
9087 Parameters
9088 ----------
9089 rankvals : array_like
9090 A 1-D sequence of ranks. Typically this will be the array
9091 returned by `~scipy.stats.rankdata`.
9093 Returns
9094 -------
9095 factor : float
9096 Correction factor for U or H.
9098 See Also
9099 --------
9100 rankdata : Assign ranks to the data
9101 mannwhitneyu : Mann-Whitney rank test
9102 kruskal : Kruskal-Wallis H test
9104 References
9105 ----------
9106 .. [1] Siegel, S. (1956) Nonparametric Statistics for the Behavioral
9107 Sciences. New York: McGraw-Hill.
9109 Examples
9110 --------
9111 >>> from scipy.stats import tiecorrect, rankdata
9112 >>> tiecorrect([1, 2.5, 2.5, 4])
9113 0.9
9114 >>> ranks = rankdata([1, 3, 2, 4, 5, 7, 2, 8, 4])
9115 >>> ranks
9116 array([ 1. , 4. , 2.5, 5.5, 7. , 8. , 2.5, 9. , 5.5])
9117 >>> tiecorrect(ranks)
9118 0.9833333333333333
9120 """
9121 arr = np.sort(rankvals)
9122 idx = np.nonzero(np.r_[True, arr[1:] != arr[:-1], True])[0]
9123 cnt = np.diff(idx).astype(np.float64)
9125 size = np.float64(arr.size)
9126 return 1.0 if size < 2 else 1.0 - (cnt**3 - cnt).sum() / (size**3 - size)
9129RanksumsResult = namedtuple('RanksumsResult', ('statistic', 'pvalue'))
9132@_axis_nan_policy_factory(RanksumsResult, n_samples=2)
9133def ranksums(x, y, alternative='two-sided'):
9134 """Compute the Wilcoxon rank-sum statistic for two samples.
9136 The Wilcoxon rank-sum test tests the null hypothesis that two sets
9137 of measurements are drawn from the same distribution. The alternative
9138 hypothesis is that values in one sample are more likely to be
9139 larger than the values in the other sample.
9141 This test should be used to compare two samples from continuous
9142 distributions. It does not handle ties between measurements
9143 in x and y. For tie-handling and an optional continuity correction
9144 see `scipy.stats.mannwhitneyu`.
9146 Parameters
9147 ----------
9148 x,y : array_like
9149 The data from the two samples.
9150 alternative : {'two-sided', 'less', 'greater'}, optional
9151 Defines the alternative hypothesis. Default is 'two-sided'.
9152 The following options are available:
9154 * 'two-sided': one of the distributions (underlying `x` or `y`) is
9155 stochastically greater than the other.
9156 * 'less': the distribution underlying `x` is stochastically less
9157 than the distribution underlying `y`.
9158 * 'greater': the distribution underlying `x` is stochastically greater
9159 than the distribution underlying `y`.
9161 .. versionadded:: 1.7.0
9163 Returns
9164 -------
9165 statistic : float
9166 The test statistic under the large-sample approximation that the
9167 rank sum statistic is normally distributed.
9168 pvalue : float
9169 The p-value of the test.
9171 References
9172 ----------
9173 .. [1] https://en.wikipedia.org/wiki/Wilcoxon_rank-sum_test
9175 Examples
9176 --------
9177 We can test the hypothesis that two independent unequal-sized samples are
9178 drawn from the same distribution with computing the Wilcoxon rank-sum
9179 statistic.
9181 >>> import numpy as np
9182 >>> from scipy.stats import ranksums
9183 >>> rng = np.random.default_rng()
9184 >>> sample1 = rng.uniform(-1, 1, 200)
9185 >>> sample2 = rng.uniform(-0.5, 1.5, 300) # a shifted distribution
9186 >>> ranksums(sample1, sample2)
9187 RanksumsResult(statistic=-7.887059, pvalue=3.09390448e-15) # may vary
9188 >>> ranksums(sample1, sample2, alternative='less')
9189 RanksumsResult(statistic=-7.750585297581713, pvalue=4.573497606342543e-15) # may vary
9190 >>> ranksums(sample1, sample2, alternative='greater')
9191 RanksumsResult(statistic=-7.750585297581713, pvalue=0.9999999999999954) # may vary
9193 The p-value of less than ``0.05`` indicates that this test rejects the
9194 hypothesis at the 5% significance level.
9196 """
9197 x, y = map(np.asarray, (x, y))
9198 n1 = len(x)
9199 n2 = len(y)
9200 alldata = np.concatenate((x, y))
9201 ranked = rankdata(alldata)
9202 x = ranked[:n1]
9203 s = np.sum(x, axis=0)
9204 expected = n1 * (n1+n2+1) / 2.0
9205 z = (s - expected) / np.sqrt(n1*n2*(n1+n2+1)/12.0)
9206 z, prob = _normtest_finish(z, alternative)
9208 return RanksumsResult(z, prob)
9211KruskalResult = namedtuple('KruskalResult', ('statistic', 'pvalue'))
9214@_axis_nan_policy_factory(KruskalResult, n_samples=None)
9215def kruskal(*samples, nan_policy='propagate'):
9216 """Compute the Kruskal-Wallis H-test for independent samples.
9218 The Kruskal-Wallis H-test tests the null hypothesis that the population
9219 median of all of the groups are equal. It is a non-parametric version of
9220 ANOVA. The test works on 2 or more independent samples, which may have
9221 different sizes. Note that rejecting the null hypothesis does not
9222 indicate which of the groups differs. Post hoc comparisons between
9223 groups are required to determine which groups are different.
9225 Parameters
9226 ----------
9227 sample1, sample2, ... : array_like
9228 Two or more arrays with the sample measurements can be given as
9229 arguments. Samples must be one-dimensional.
9230 nan_policy : {'propagate', 'raise', 'omit'}, optional
9231 Defines how to handle when input contains nan.
9232 The following options are available (default is 'propagate'):
9234 * 'propagate': returns nan
9235 * 'raise': throws an error
9236 * 'omit': performs the calculations ignoring nan values
9238 Returns
9239 -------
9240 statistic : float
9241 The Kruskal-Wallis H statistic, corrected for ties.
9242 pvalue : float
9243 The p-value for the test using the assumption that H has a chi
9244 square distribution. The p-value returned is the survival function of
9245 the chi square distribution evaluated at H.
9247 See Also
9248 --------
9249 f_oneway : 1-way ANOVA.
9250 mannwhitneyu : Mann-Whitney rank test on two samples.
9251 friedmanchisquare : Friedman test for repeated measurements.
9253 Notes
9254 -----
9255 Due to the assumption that H has a chi square distribution, the number
9256 of samples in each group must not be too small. A typical rule is
9257 that each sample must have at least 5 measurements.
9259 References
9260 ----------
9261 .. [1] W. H. Kruskal & W. W. Wallis, "Use of Ranks in
9262 One-Criterion Variance Analysis", Journal of the American Statistical
9263 Association, Vol. 47, Issue 260, pp. 583-621, 1952.
9264 .. [2] https://en.wikipedia.org/wiki/Kruskal-Wallis_one-way_analysis_of_variance
9266 Examples
9267 --------
9268 >>> from scipy import stats
9269 >>> x = [1, 3, 5, 7, 9]
9270 >>> y = [2, 4, 6, 8, 10]
9271 >>> stats.kruskal(x, y)
9272 KruskalResult(statistic=0.2727272727272734, pvalue=0.6015081344405895)
9274 >>> x = [1, 1, 1]
9275 >>> y = [2, 2, 2]
9276 >>> z = [2, 2]
9277 >>> stats.kruskal(x, y, z)
9278 KruskalResult(statistic=7.0, pvalue=0.0301973834223185)
9280 """
9281 samples = list(map(np.asarray, samples))
9283 num_groups = len(samples)
9284 if num_groups < 2:
9285 raise ValueError("Need at least two groups in stats.kruskal()")
9287 for sample in samples:
9288 if sample.size == 0:
9289 NaN = _get_nan(*samples)
9290 return KruskalResult(NaN, NaN)
9291 elif sample.ndim != 1:
9292 raise ValueError("Samples must be one-dimensional.")
9294 n = np.asarray(list(map(len, samples)))
9296 if nan_policy not in ('propagate', 'raise', 'omit'):
9297 raise ValueError("nan_policy must be 'propagate', 'raise' or 'omit'")
9299 contains_nan = False
9300 for sample in samples:
9301 cn = _contains_nan(sample, nan_policy)
9302 if cn[0]:
9303 contains_nan = True
9304 break
9306 if contains_nan and nan_policy == 'omit':
9307 for sample in samples:
9308 sample = ma.masked_invalid(sample)
9309 return mstats_basic.kruskal(*samples)
9311 if contains_nan and nan_policy == 'propagate':
9312 return KruskalResult(np.nan, np.nan)
9314 alldata = np.concatenate(samples)
9315 ranked = rankdata(alldata)
9316 ties = tiecorrect(ranked)
9317 if ties == 0:
9318 raise ValueError('All numbers are identical in kruskal')
9320 # Compute sum^2/n for each group and sum
9321 j = np.insert(np.cumsum(n), 0, 0)
9322 ssbn = 0
9323 for i in range(num_groups):
9324 ssbn += _square_of_sums(ranked[j[i]:j[i+1]]) / n[i]
9326 totaln = np.sum(n, dtype=float)
9327 h = 12.0 / (totaln * (totaln + 1)) * ssbn - 3 * (totaln + 1)
9328 df = num_groups - 1
9329 h /= ties
9331 return KruskalResult(h, distributions.chi2.sf(h, df))
9334FriedmanchisquareResult = namedtuple('FriedmanchisquareResult',
9335 ('statistic', 'pvalue'))
9338def friedmanchisquare(*samples):
9339 """Compute the Friedman test for repeated samples.
9341 The Friedman test tests the null hypothesis that repeated samples of
9342 the same individuals have the same distribution. It is often used
9343 to test for consistency among samples obtained in different ways.
9344 For example, if two sampling techniques are used on the same set of
9345 individuals, the Friedman test can be used to determine if the two
9346 sampling techniques are consistent.
9348 Parameters
9349 ----------
9350 sample1, sample2, sample3... : array_like
9351 Arrays of observations. All of the arrays must have the same number
9352 of elements. At least three samples must be given.
9354 Returns
9355 -------
9356 statistic : float
9357 The test statistic, correcting for ties.
9358 pvalue : float
9359 The associated p-value assuming that the test statistic has a chi
9360 squared distribution.
9362 Notes
9363 -----
9364 Due to the assumption that the test statistic has a chi squared
9365 distribution, the p-value is only reliable for n > 10 and more than
9366 6 repeated samples.
9368 References
9369 ----------
9370 .. [1] https://en.wikipedia.org/wiki/Friedman_test
9371 .. [2] P. Sprent and N.C. Smeeton, "Applied Nonparametric Statistical
9372 Methods, Third Edition". Chapter 6, Section 6.3.2.
9374 Examples
9375 --------
9376 In [2]_, the pulse rate (per minute) of a group of seven students was
9377 measured before exercise, immediately after exercise and 5 minutes
9378 after exercise. Is there evidence to suggest that the pulse rates on
9379 these three occasions are similar?
9381 We begin by formulating a null hypothesis :math:`H_0`:
9383 The pulse rates are identical on these three occasions.
9385 Let's assess the plausibility of this hypothesis with a Friedman test.
9387 >>> from scipy.stats import friedmanchisquare
9388 >>> before = [72, 96, 88, 92, 74, 76, 82]
9389 >>> immediately_after = [120, 120, 132, 120, 101, 96, 112]
9390 >>> five_min_after = [76, 95, 104, 96, 84, 72, 76]
9391 >>> res = friedmanchisquare(before, immediately_after, five_min_after)
9392 >>> res.statistic
9393 10.57142857142857
9394 >>> res.pvalue
9395 0.005063414171757498
9397 Using a significance level of 5%, we would reject the null hypothesis in
9398 favor of the alternative hypothesis: "the pulse rates are different on
9399 these three occasions".
9401 """
9402 k = len(samples)
9403 if k < 3:
9404 raise ValueError('At least 3 sets of samples must be given '
9405 'for Friedman test, got {}.'.format(k))
9407 n = len(samples[0])
9408 for i in range(1, k):
9409 if len(samples[i]) != n:
9410 raise ValueError('Unequal N in friedmanchisquare. Aborting.')
9412 # Rank data
9413 data = np.vstack(samples).T
9414 data = data.astype(float)
9415 for i in range(len(data)):
9416 data[i] = rankdata(data[i])
9418 # Handle ties
9419 ties = 0
9420 for d in data:
9421 replist, repnum = find_repeats(array(d))
9422 for t in repnum:
9423 ties += t * (t*t - 1)
9424 c = 1 - ties / (k*(k*k - 1)*n)
9426 ssbn = np.sum(data.sum(axis=0)**2)
9427 chisq = (12.0 / (k*n*(k+1)) * ssbn - 3*n*(k+1)) / c
9429 return FriedmanchisquareResult(chisq, distributions.chi2.sf(chisq, k - 1))
9432BrunnerMunzelResult = namedtuple('BrunnerMunzelResult',
9433 ('statistic', 'pvalue'))
9436def brunnermunzel(x, y, alternative="two-sided", distribution="t",
9437 nan_policy='propagate'):
9438 """Compute the Brunner-Munzel test on samples x and y.
9440 The Brunner-Munzel test is a nonparametric test of the null hypothesis that
9441 when values are taken one by one from each group, the probabilities of
9442 getting large values in both groups are equal.
9443 Unlike the Wilcoxon-Mann-Whitney's U test, this does not require the
9444 assumption of equivariance of two groups. Note that this does not assume
9445 the distributions are same. This test works on two independent samples,
9446 which may have different sizes.
9448 Parameters
9449 ----------
9450 x, y : array_like
9451 Array of samples, should be one-dimensional.
9452 alternative : {'two-sided', 'less', 'greater'}, optional
9453 Defines the alternative hypothesis.
9454 The following options are available (default is 'two-sided'):
9456 * 'two-sided'
9457 * 'less': one-sided
9458 * 'greater': one-sided
9459 distribution : {'t', 'normal'}, optional
9460 Defines how to get the p-value.
9461 The following options are available (default is 't'):
9463 * 't': get the p-value by t-distribution
9464 * 'normal': get the p-value by standard normal distribution.
9465 nan_policy : {'propagate', 'raise', 'omit'}, optional
9466 Defines how to handle when input contains nan.
9467 The following options are available (default is 'propagate'):
9469 * 'propagate': returns nan
9470 * 'raise': throws an error
9471 * 'omit': performs the calculations ignoring nan values
9473 Returns
9474 -------
9475 statistic : float
9476 The Brunner-Munzer W statistic.
9477 pvalue : float
9478 p-value assuming an t distribution. One-sided or
9479 two-sided, depending on the choice of `alternative` and `distribution`.
9481 See Also
9482 --------
9483 mannwhitneyu : Mann-Whitney rank test on two samples.
9485 Notes
9486 -----
9487 Brunner and Munzel recommended to estimate the p-value by t-distribution
9488 when the size of data is 50 or less. If the size is lower than 10, it would
9489 be better to use permuted Brunner Munzel test (see [2]_).
9491 References
9492 ----------
9493 .. [1] Brunner, E. and Munzel, U. "The nonparametric Benhrens-Fisher
9494 problem: Asymptotic theory and a small-sample approximation".
9495 Biometrical Journal. Vol. 42(2000): 17-25.
9496 .. [2] Neubert, K. and Brunner, E. "A studentized permutation test for the
9497 non-parametric Behrens-Fisher problem". Computational Statistics and
9498 Data Analysis. Vol. 51(2007): 5192-5204.
9500 Examples
9501 --------
9502 >>> from scipy import stats
9503 >>> x1 = [1,2,1,1,1,1,1,1,1,1,2,4,1,1]
9504 >>> x2 = [3,3,4,3,1,2,3,1,1,5,4]
9505 >>> w, p_value = stats.brunnermunzel(x1, x2)
9506 >>> w
9507 3.1374674823029505
9508 >>> p_value
9509 0.0057862086661515377
9511 """
9512 x = np.asarray(x)
9513 y = np.asarray(y)
9515 # check both x and y
9516 cnx, npx = _contains_nan(x, nan_policy)
9517 cny, npy = _contains_nan(y, nan_policy)
9518 contains_nan = cnx or cny
9519 if npx == "omit" or npy == "omit":
9520 nan_policy = "omit"
9522 if contains_nan and nan_policy == "propagate":
9523 return BrunnerMunzelResult(np.nan, np.nan)
9524 elif contains_nan and nan_policy == "omit":
9525 x = ma.masked_invalid(x)
9526 y = ma.masked_invalid(y)
9527 return mstats_basic.brunnermunzel(x, y, alternative, distribution)
9529 nx = len(x)
9530 ny = len(y)
9531 if nx == 0 or ny == 0:
9532 return BrunnerMunzelResult(np.nan, np.nan)
9533 rankc = rankdata(np.concatenate((x, y)))
9534 rankcx = rankc[0:nx]
9535 rankcy = rankc[nx:nx+ny]
9536 rankcx_mean = np.mean(rankcx)
9537 rankcy_mean = np.mean(rankcy)
9538 rankx = rankdata(x)
9539 ranky = rankdata(y)
9540 rankx_mean = np.mean(rankx)
9541 ranky_mean = np.mean(ranky)
9543 Sx = np.sum(np.power(rankcx - rankx - rankcx_mean + rankx_mean, 2.0))
9544 Sx /= nx - 1
9545 Sy = np.sum(np.power(rankcy - ranky - rankcy_mean + ranky_mean, 2.0))
9546 Sy /= ny - 1
9548 wbfn = nx * ny * (rankcy_mean - rankcx_mean)
9549 wbfn /= (nx + ny) * np.sqrt(nx * Sx + ny * Sy)
9551 if distribution == "t":
9552 df_numer = np.power(nx * Sx + ny * Sy, 2.0)
9553 df_denom = np.power(nx * Sx, 2.0) / (nx - 1)
9554 df_denom += np.power(ny * Sy, 2.0) / (ny - 1)
9555 df = df_numer / df_denom
9557 if (df_numer == 0) and (df_denom == 0):
9558 message = ("p-value cannot be estimated with `distribution='t' "
9559 "because degrees of freedom parameter is undefined "
9560 "(0/0). Try using `distribution='normal'")
9561 warnings.warn(message, RuntimeWarning)
9563 p = distributions.t.cdf(wbfn, df)
9564 elif distribution == "normal":
9565 p = distributions.norm.cdf(wbfn)
9566 else:
9567 raise ValueError(
9568 "distribution should be 't' or 'normal'")
9570 if alternative == "greater":
9571 pass
9572 elif alternative == "less":
9573 p = 1 - p
9574 elif alternative == "two-sided":
9575 p = 2 * np.min([p, 1-p])
9576 else:
9577 raise ValueError(
9578 "alternative should be 'less', 'greater' or 'two-sided'")
9580 return BrunnerMunzelResult(wbfn, p)
9583def combine_pvalues(pvalues, method='fisher', weights=None):
9584 """
9585 Combine p-values from independent tests that bear upon the same hypothesis.
9587 These methods are intended only for combining p-values from hypothesis
9588 tests based upon continuous distributions.
9590 Each method assumes that under the null hypothesis, the p-values are
9591 sampled independently and uniformly from the interval [0, 1]. A test
9592 statistic (different for each method) is computed and a combined
9593 p-value is calculated based upon the distribution of this test statistic
9594 under the null hypothesis.
9596 Parameters
9597 ----------
9598 pvalues : array_like, 1-D
9599 Array of p-values assumed to come from independent tests based on
9600 continuous distributions.
9601 method : {'fisher', 'pearson', 'tippett', 'stouffer', 'mudholkar_george'}
9603 Name of method to use to combine p-values.
9605 The available methods are (see Notes for details):
9607 * 'fisher': Fisher's method (Fisher's combined probability test)
9608 * 'pearson': Pearson's method
9609 * 'mudholkar_george': Mudholkar's and George's method
9610 * 'tippett': Tippett's method
9611 * 'stouffer': Stouffer's Z-score method
9612 weights : array_like, 1-D, optional
9613 Optional array of weights used only for Stouffer's Z-score method.
9615 Returns
9616 -------
9617 res : SignificanceResult
9618 An object containing attributes:
9620 statistic : float
9621 The statistic calculated by the specified method.
9622 pvalue : float
9623 The combined p-value.
9625 Notes
9626 -----
9627 If this function is applied to tests with a discrete statistics such as
9628 any rank test or contingency-table test, it will yield systematically
9629 wrong results, e.g. Fisher's method will systematically overestimate the
9630 p-value [1]_. This problem becomes less severe for large sample sizes
9631 when the discrete distributions become approximately continuous.
9633 The differences between the methods can be best illustrated by their
9634 statistics and what aspects of a combination of p-values they emphasise
9635 when considering significance [2]_. For example, methods emphasising large
9636 p-values are more sensitive to strong false and true negatives; conversely
9637 methods focussing on small p-values are sensitive to positives.
9639 * The statistics of Fisher's method (also known as Fisher's combined
9640 probability test) [3]_ is :math:`-2\\sum_i \\log(p_i)`, which is
9641 equivalent (as a test statistics) to the product of individual p-values:
9642 :math:`\\prod_i p_i`. Under the null hypothesis, this statistics follows
9643 a :math:`\\chi^2` distribution. This method emphasises small p-values.
9644 * Pearson's method uses :math:`-2\\sum_i\\log(1-p_i)`, which is equivalent
9645 to :math:`\\prod_i \\frac{1}{1-p_i}` [2]_.
9646 It thus emphasises large p-values.
9647 * Mudholkar and George compromise between Fisher's and Pearson's method by
9648 averaging their statistics [4]_. Their method emphasises extreme
9649 p-values, both close to 1 and 0.
9650 * Stouffer's method [5]_ uses Z-scores and the statistic:
9651 :math:`\\sum_i \\Phi^{-1} (p_i)`, where :math:`\\Phi` is the CDF of the
9652 standard normal distribution. The advantage of this method is that it is
9653 straightforward to introduce weights, which can make Stouffer's method
9654 more powerful than Fisher's method when the p-values are from studies
9655 of different size [6]_ [7]_.
9656 * Tippett's method uses the smallest p-value as a statistic.
9657 (Mind that this minimum is not the combined p-value.)
9659 Fisher's method may be extended to combine p-values from dependent tests
9660 [8]_. Extensions such as Brown's method and Kost's method are not currently
9661 implemented.
9663 .. versionadded:: 0.15.0
9665 References
9666 ----------
9667 .. [1] Kincaid, W. M., "The Combination of Tests Based on Discrete
9668 Distributions." Journal of the American Statistical Association 57,
9669 no. 297 (1962), 10-19.
9670 .. [2] Heard, N. and Rubin-Delanchey, P. "Choosing between methods of
9671 combining p-values." Biometrika 105.1 (2018): 239-246.
9672 .. [3] https://en.wikipedia.org/wiki/Fisher%27s_method
9673 .. [4] George, E. O., and G. S. Mudholkar. "On the convolution of logistic
9674 random variables." Metrika 30.1 (1983): 1-13.
9675 .. [5] https://en.wikipedia.org/wiki/Fisher%27s_method#Relation_to_Stouffer.27s_Z-score_method
9676 .. [6] Whitlock, M. C. "Combining probability from independent tests: the
9677 weighted Z-method is superior to Fisher's approach." Journal of
9678 Evolutionary Biology 18, no. 5 (2005): 1368-1373.
9679 .. [7] Zaykin, Dmitri V. "Optimally weighted Z-test is a powerful method
9680 for combining probabilities in meta-analysis." Journal of
9681 Evolutionary Biology 24, no. 8 (2011): 1836-1841.
9682 .. [8] https://en.wikipedia.org/wiki/Extensions_of_Fisher%27s_method
9684 """
9685 pvalues = np.asarray(pvalues)
9686 if pvalues.ndim != 1:
9687 raise ValueError("pvalues is not 1-D")
9689 if method == 'fisher':
9690 statistic = -2 * np.sum(np.log(pvalues))
9691 pval = distributions.chi2.sf(statistic, 2 * len(pvalues))
9692 elif method == 'pearson':
9693 statistic = 2 * np.sum(np.log1p(-pvalues))
9694 pval = distributions.chi2.cdf(-statistic, 2 * len(pvalues))
9695 elif method == 'mudholkar_george':
9696 normalizing_factor = np.sqrt(3/len(pvalues))/np.pi
9697 statistic = -np.sum(np.log(pvalues)) + np.sum(np.log1p(-pvalues))
9698 nu = 5 * len(pvalues) + 4
9699 approx_factor = np.sqrt(nu / (nu - 2))
9700 pval = distributions.t.sf(statistic * normalizing_factor
9701 * approx_factor, nu)
9702 elif method == 'tippett':
9703 statistic = np.min(pvalues)
9704 pval = distributions.beta.cdf(statistic, 1, len(pvalues))
9705 elif method == 'stouffer':
9706 if weights is None:
9707 weights = np.ones_like(pvalues)
9708 elif len(weights) != len(pvalues):
9709 raise ValueError("pvalues and weights must be of the same size.")
9711 weights = np.asarray(weights)
9712 if weights.ndim != 1:
9713 raise ValueError("weights is not 1-D")
9715 Zi = distributions.norm.isf(pvalues)
9716 statistic = np.dot(weights, Zi) / np.linalg.norm(weights)
9717 pval = distributions.norm.sf(statistic)
9719 else:
9720 raise ValueError(
9721 f"Invalid method {method!r}. Valid methods are 'fisher', "
9722 "'pearson', 'mudholkar_george', 'tippett', and 'stouffer'"
9723 )
9725 return SignificanceResult(statistic, pval)
9728#####################################
9729# STATISTICAL DISTANCES #
9730#####################################
9733def wasserstein_distance(u_values, v_values, u_weights=None, v_weights=None):
9734 r"""
9735 Compute the first Wasserstein distance between two 1D distributions.
9737 This distance is also known as the earth mover's distance, since it can be
9738 seen as the minimum amount of "work" required to transform :math:`u` into
9739 :math:`v`, where "work" is measured as the amount of distribution weight
9740 that must be moved, multiplied by the distance it has to be moved.
9742 .. versionadded:: 1.0.0
9744 Parameters
9745 ----------
9746 u_values, v_values : array_like
9747 Values observed in the (empirical) distribution.
9748 u_weights, v_weights : array_like, optional
9749 Weight for each value. If unspecified, each value is assigned the same
9750 weight.
9751 `u_weights` (resp. `v_weights`) must have the same length as
9752 `u_values` (resp. `v_values`). If the weight sum differs from 1, it
9753 must still be positive and finite so that the weights can be normalized
9754 to sum to 1.
9756 Returns
9757 -------
9758 distance : float
9759 The computed distance between the distributions.
9761 Notes
9762 -----
9763 The first Wasserstein distance between the distributions :math:`u` and
9764 :math:`v` is:
9766 .. math::
9768 l_1 (u, v) = \inf_{\pi \in \Gamma (u, v)} \int_{\mathbb{R} \times
9769 \mathbb{R}} |x-y| \mathrm{d} \pi (x, y)
9771 where :math:`\Gamma (u, v)` is the set of (probability) distributions on
9772 :math:`\mathbb{R} \times \mathbb{R}` whose marginals are :math:`u` and
9773 :math:`v` on the first and second factors respectively.
9775 If :math:`U` and :math:`V` are the respective CDFs of :math:`u` and
9776 :math:`v`, this distance also equals to:
9778 .. math::
9780 l_1(u, v) = \int_{-\infty}^{+\infty} |U-V|
9782 See [2]_ for a proof of the equivalence of both definitions.
9784 The input distributions can be empirical, therefore coming from samples
9785 whose values are effectively inputs of the function, or they can be seen as
9786 generalized functions, in which case they are weighted sums of Dirac delta
9787 functions located at the specified values.
9789 References
9790 ----------
9791 .. [1] "Wasserstein metric", https://en.wikipedia.org/wiki/Wasserstein_metric
9792 .. [2] Ramdas, Garcia, Cuturi "On Wasserstein Two Sample Testing and Related
9793 Families of Nonparametric Tests" (2015). :arXiv:`1509.02237`.
9795 Examples
9796 --------
9797 >>> from scipy.stats import wasserstein_distance
9798 >>> wasserstein_distance([0, 1, 3], [5, 6, 8])
9799 5.0
9800 >>> wasserstein_distance([0, 1], [0, 1], [3, 1], [2, 2])
9801 0.25
9802 >>> wasserstein_distance([3.4, 3.9, 7.5, 7.8], [4.5, 1.4],
9803 ... [1.4, 0.9, 3.1, 7.2], [3.2, 3.5])
9804 4.0781331438047861
9806 """
9807 return _cdf_distance(1, u_values, v_values, u_weights, v_weights)
9810def energy_distance(u_values, v_values, u_weights=None, v_weights=None):
9811 r"""Compute the energy distance between two 1D distributions.
9813 .. versionadded:: 1.0.0
9815 Parameters
9816 ----------
9817 u_values, v_values : array_like
9818 Values observed in the (empirical) distribution.
9819 u_weights, v_weights : array_like, optional
9820 Weight for each value. If unspecified, each value is assigned the same
9821 weight.
9822 `u_weights` (resp. `v_weights`) must have the same length as
9823 `u_values` (resp. `v_values`). If the weight sum differs from 1, it
9824 must still be positive and finite so that the weights can be normalized
9825 to sum to 1.
9827 Returns
9828 -------
9829 distance : float
9830 The computed distance between the distributions.
9832 Notes
9833 -----
9834 The energy distance between two distributions :math:`u` and :math:`v`, whose
9835 respective CDFs are :math:`U` and :math:`V`, equals to:
9837 .. math::
9839 D(u, v) = \left( 2\mathbb E|X - Y| - \mathbb E|X - X'| -
9840 \mathbb E|Y - Y'| \right)^{1/2}
9842 where :math:`X` and :math:`X'` (resp. :math:`Y` and :math:`Y'`) are
9843 independent random variables whose probability distribution is :math:`u`
9844 (resp. :math:`v`).
9846 Sometimes the square of this quantity is referred to as the "energy
9847 distance" (e.g. in [2]_, [4]_), but as noted in [1]_ and [3]_, only the
9848 definition above satisfies the axioms of a distance function (metric).
9850 As shown in [2]_, for one-dimensional real-valued variables, the energy
9851 distance is linked to the non-distribution-free version of the Cramér-von
9852 Mises distance:
9854 .. math::
9856 D(u, v) = \sqrt{2} l_2(u, v) = \left( 2 \int_{-\infty}^{+\infty} (U-V)^2
9857 \right)^{1/2}
9859 Note that the common Cramér-von Mises criterion uses the distribution-free
9860 version of the distance. See [2]_ (section 2), for more details about both
9861 versions of the distance.
9863 The input distributions can be empirical, therefore coming from samples
9864 whose values are effectively inputs of the function, or they can be seen as
9865 generalized functions, in which case they are weighted sums of Dirac delta
9866 functions located at the specified values.
9868 References
9869 ----------
9870 .. [1] Rizzo, Szekely "Energy distance." Wiley Interdisciplinary Reviews:
9871 Computational Statistics, 8(1):27-38 (2015).
9872 .. [2] Szekely "E-statistics: The energy of statistical samples." Bowling
9873 Green State University, Department of Mathematics and Statistics,
9874 Technical Report 02-16 (2002).
9875 .. [3] "Energy distance", https://en.wikipedia.org/wiki/Energy_distance
9876 .. [4] Bellemare, Danihelka, Dabney, Mohamed, Lakshminarayanan, Hoyer,
9877 Munos "The Cramer Distance as a Solution to Biased Wasserstein
9878 Gradients" (2017). :arXiv:`1705.10743`.
9880 Examples
9881 --------
9882 >>> from scipy.stats import energy_distance
9883 >>> energy_distance([0], [2])
9884 2.0000000000000004
9885 >>> energy_distance([0, 8], [0, 8], [3, 1], [2, 2])
9886 1.0000000000000002
9887 >>> energy_distance([0.7, 7.4, 2.4, 6.8], [1.4, 8. ],
9888 ... [2.1, 4.2, 7.4, 8. ], [7.6, 8.8])
9889 0.88003340976158217
9891 """
9892 return np.sqrt(2) * _cdf_distance(2, u_values, v_values,
9893 u_weights, v_weights)
9896def _cdf_distance(p, u_values, v_values, u_weights=None, v_weights=None):
9897 r"""
9898 Compute, between two one-dimensional distributions :math:`u` and
9899 :math:`v`, whose respective CDFs are :math:`U` and :math:`V`, the
9900 statistical distance that is defined as:
9902 .. math::
9904 l_p(u, v) = \left( \int_{-\infty}^{+\infty} |U-V|^p \right)^{1/p}
9906 p is a positive parameter; p = 1 gives the Wasserstein distance, p = 2
9907 gives the energy distance.
9909 Parameters
9910 ----------
9911 u_values, v_values : array_like
9912 Values observed in the (empirical) distribution.
9913 u_weights, v_weights : array_like, optional
9914 Weight for each value. If unspecified, each value is assigned the same
9915 weight.
9916 `u_weights` (resp. `v_weights`) must have the same length as
9917 `u_values` (resp. `v_values`). If the weight sum differs from 1, it
9918 must still be positive and finite so that the weights can be normalized
9919 to sum to 1.
9921 Returns
9922 -------
9923 distance : float
9924 The computed distance between the distributions.
9926 Notes
9927 -----
9928 The input distributions can be empirical, therefore coming from samples
9929 whose values are effectively inputs of the function, or they can be seen as
9930 generalized functions, in which case they are weighted sums of Dirac delta
9931 functions located at the specified values.
9933 References
9934 ----------
9935 .. [1] Bellemare, Danihelka, Dabney, Mohamed, Lakshminarayanan, Hoyer,
9936 Munos "The Cramer Distance as a Solution to Biased Wasserstein
9937 Gradients" (2017). :arXiv:`1705.10743`.
9939 """
9940 u_values, u_weights = _validate_distribution(u_values, u_weights)
9941 v_values, v_weights = _validate_distribution(v_values, v_weights)
9943 u_sorter = np.argsort(u_values)
9944 v_sorter = np.argsort(v_values)
9946 all_values = np.concatenate((u_values, v_values))
9947 all_values.sort(kind='mergesort')
9949 # Compute the differences between pairs of successive values of u and v.
9950 deltas = np.diff(all_values)
9952 # Get the respective positions of the values of u and v among the values of
9953 # both distributions.
9954 u_cdf_indices = u_values[u_sorter].searchsorted(all_values[:-1], 'right')
9955 v_cdf_indices = v_values[v_sorter].searchsorted(all_values[:-1], 'right')
9957 # Calculate the CDFs of u and v using their weights, if specified.
9958 if u_weights is None:
9959 u_cdf = u_cdf_indices / u_values.size
9960 else:
9961 u_sorted_cumweights = np.concatenate(([0],
9962 np.cumsum(u_weights[u_sorter])))
9963 u_cdf = u_sorted_cumweights[u_cdf_indices] / u_sorted_cumweights[-1]
9965 if v_weights is None:
9966 v_cdf = v_cdf_indices / v_values.size
9967 else:
9968 v_sorted_cumweights = np.concatenate(([0],
9969 np.cumsum(v_weights[v_sorter])))
9970 v_cdf = v_sorted_cumweights[v_cdf_indices] / v_sorted_cumweights[-1]
9972 # Compute the value of the integral based on the CDFs.
9973 # If p = 1 or p = 2, we avoid using np.power, which introduces an overhead
9974 # of about 15%.
9975 if p == 1:
9976 return np.sum(np.multiply(np.abs(u_cdf - v_cdf), deltas))
9977 if p == 2:
9978 return np.sqrt(np.sum(np.multiply(np.square(u_cdf - v_cdf), deltas)))
9979 return np.power(np.sum(np.multiply(np.power(np.abs(u_cdf - v_cdf), p),
9980 deltas)), 1/p)
9983def _validate_distribution(values, weights):
9984 """
9985 Validate the values and weights from a distribution input of `cdf_distance`
9986 and return them as ndarray objects.
9988 Parameters
9989 ----------
9990 values : array_like
9991 Values observed in the (empirical) distribution.
9992 weights : array_like
9993 Weight for each value.
9995 Returns
9996 -------
9997 values : ndarray
9998 Values as ndarray.
9999 weights : ndarray
10000 Weights as ndarray.
10002 """
10003 # Validate the value array.
10004 values = np.asarray(values, dtype=float)
10005 if len(values) == 0:
10006 raise ValueError("Distribution can't be empty.")
10008 # Validate the weight array, if specified.
10009 if weights is not None:
10010 weights = np.asarray(weights, dtype=float)
10011 if len(weights) != len(values):
10012 raise ValueError('Value and weight array-likes for the same '
10013 'empirical distribution must be of the same size.')
10014 if np.any(weights < 0):
10015 raise ValueError('All weights must be non-negative.')
10016 if not 0 < np.sum(weights) < np.inf:
10017 raise ValueError('Weight array-like sum must be positive and '
10018 'finite. Set as None for an equal distribution of '
10019 'weight.')
10021 return values, weights
10023 return values, None
10026#####################################
10027# SUPPORT FUNCTIONS #
10028#####################################
10030RepeatedResults = namedtuple('RepeatedResults', ('values', 'counts'))
10033def find_repeats(arr):
10034 """Find repeats and repeat counts.
10036 Parameters
10037 ----------
10038 arr : array_like
10039 Input array. This is cast to float64.
10041 Returns
10042 -------
10043 values : ndarray
10044 The unique values from the (flattened) input that are repeated.
10046 counts : ndarray
10047 Number of times the corresponding 'value' is repeated.
10049 Notes
10050 -----
10051 In numpy >= 1.9 `numpy.unique` provides similar functionality. The main
10052 difference is that `find_repeats` only returns repeated values.
10054 Examples
10055 --------
10056 >>> from scipy import stats
10057 >>> stats.find_repeats([2, 1, 2, 3, 2, 2, 5])
10058 RepeatedResults(values=array([2.]), counts=array([4]))
10060 >>> stats.find_repeats([[10, 20, 1, 2], [5, 5, 4, 4]])
10061 RepeatedResults(values=array([4., 5.]), counts=array([2, 2]))
10063 """
10064 # Note: always copies.
10065 return RepeatedResults(*_find_repeats(np.array(arr, dtype=np.float64)))
10068def _sum_of_squares(a, axis=0):
10069 """Square each element of the input array, and return the sum(s) of that.
10071 Parameters
10072 ----------
10073 a : array_like
10074 Input array.
10075 axis : int or None, optional
10076 Axis along which to calculate. Default is 0. If None, compute over
10077 the whole array `a`.
10079 Returns
10080 -------
10081 sum_of_squares : ndarray
10082 The sum along the given axis for (a**2).
10084 See Also
10085 --------
10086 _square_of_sums : The square(s) of the sum(s) (the opposite of
10087 `_sum_of_squares`).
10089 """
10090 a, axis = _chk_asarray(a, axis)
10091 return np.sum(a*a, axis)
10094def _square_of_sums(a, axis=0):
10095 """Sum elements of the input array, and return the square(s) of that sum.
10097 Parameters
10098 ----------
10099 a : array_like
10100 Input array.
10101 axis : int or None, optional
10102 Axis along which to calculate. Default is 0. If None, compute over
10103 the whole array `a`.
10105 Returns
10106 -------
10107 square_of_sums : float or ndarray
10108 The square of the sum over `axis`.
10110 See Also
10111 --------
10112 _sum_of_squares : The sum of squares (the opposite of `square_of_sums`).
10114 """
10115 a, axis = _chk_asarray(a, axis)
10116 s = np.sum(a, axis)
10117 if not np.isscalar(s):
10118 return s.astype(float) * s
10119 else:
10120 return float(s) * s
10123def rankdata(a, method='average', *, axis=None, nan_policy='propagate'):
10124 """Assign ranks to data, dealing with ties appropriately.
10126 By default (``axis=None``), the data array is first flattened, and a flat
10127 array of ranks is returned. Separately reshape the rank array to the
10128 shape of the data array if desired (see Examples).
10130 Ranks begin at 1. The `method` argument controls how ranks are assigned
10131 to equal values. See [1]_ for further discussion of ranking methods.
10133 Parameters
10134 ----------
10135 a : array_like
10136 The array of values to be ranked.
10137 method : {'average', 'min', 'max', 'dense', 'ordinal'}, optional
10138 The method used to assign ranks to tied elements.
10139 The following methods are available (default is 'average'):
10141 * 'average': The average of the ranks that would have been assigned to
10142 all the tied values is assigned to each value.
10143 * 'min': The minimum of the ranks that would have been assigned to all
10144 the tied values is assigned to each value. (This is also
10145 referred to as "competition" ranking.)
10146 * 'max': The maximum of the ranks that would have been assigned to all
10147 the tied values is assigned to each value.
10148 * 'dense': Like 'min', but the rank of the next highest element is
10149 assigned the rank immediately after those assigned to the tied
10150 elements.
10151 * 'ordinal': All values are given a distinct rank, corresponding to
10152 the order that the values occur in `a`.
10153 axis : {None, int}, optional
10154 Axis along which to perform the ranking. If ``None``, the data array
10155 is first flattened.
10156 nan_policy : {'propagate', 'omit', 'raise'}, optional
10157 Defines how to handle when input contains nan.
10158 The following options are available (default is 'propagate'):
10160 * 'propagate': propagates nans through the rank calculation
10161 * 'omit': performs the calculations ignoring nan values
10162 * 'raise': raises an error
10164 .. note::
10166 When `nan_policy` is 'propagate', the output is an array of *all*
10167 nans because ranks relative to nans in the input are undefined.
10168 When `nan_policy` is 'omit', nans in `a` are ignored when ranking
10169 the other values, and the corresponding locations of the output
10170 are nan.
10172 .. versionadded:: 1.10
10174 Returns
10175 -------
10176 ranks : ndarray
10177 An array of size equal to the size of `a`, containing rank
10178 scores.
10180 References
10181 ----------
10182 .. [1] "Ranking", https://en.wikipedia.org/wiki/Ranking
10184 Examples
10185 --------
10186 >>> import numpy as np
10187 >>> from scipy.stats import rankdata
10188 >>> rankdata([0, 2, 3, 2])
10189 array([ 1. , 2.5, 4. , 2.5])
10190 >>> rankdata([0, 2, 3, 2], method='min')
10191 array([ 1, 2, 4, 2])
10192 >>> rankdata([0, 2, 3, 2], method='max')
10193 array([ 1, 3, 4, 3])
10194 >>> rankdata([0, 2, 3, 2], method='dense')
10195 array([ 1, 2, 3, 2])
10196 >>> rankdata([0, 2, 3, 2], method='ordinal')
10197 array([ 1, 2, 4, 3])
10198 >>> rankdata([[0, 2], [3, 2]]).reshape(2,2)
10199 array([[1. , 2.5],
10200 [4. , 2.5]])
10201 >>> rankdata([[0, 2, 2], [3, 2, 5]], axis=1)
10202 array([[1. , 2.5, 2.5],
10203 [2. , 1. , 3. ]])
10204 >>> rankdata([0, 2, 3, np.nan, -2, np.nan], nan_policy="propagate")
10205 array([nan, nan, nan, nan, nan, nan])
10206 >>> rankdata([0, 2, 3, np.nan, -2, np.nan], nan_policy="omit")
10207 array([ 2., 3., 4., nan, 1., nan])
10209 """
10210 if method not in ('average', 'min', 'max', 'dense', 'ordinal'):
10211 raise ValueError(f'unknown method "{method}"')
10213 a = np.asarray(a)
10215 if axis is not None:
10216 if a.size == 0:
10217 # The return values of `normalize_axis_index` are ignored. The
10218 # call validates `axis`, even though we won't use it.
10219 # use scipy._lib._util._normalize_axis_index when available
10220 np.core.multiarray.normalize_axis_index(axis, a.ndim)
10221 dt = np.float64 if method == 'average' else np.int_
10222 return np.empty(a.shape, dtype=dt)
10223 return np.apply_along_axis(rankdata, axis, a, method,
10224 nan_policy=nan_policy)
10226 arr = np.ravel(a)
10227 contains_nan, nan_policy = _contains_nan(arr, nan_policy)
10228 nan_indexes = None
10229 if contains_nan:
10230 if nan_policy == 'omit':
10231 nan_indexes = np.isnan(arr)
10232 if nan_policy == 'propagate':
10233 return np.full_like(arr, np.nan)
10235 algo = 'mergesort' if method == 'ordinal' else 'quicksort'
10236 sorter = np.argsort(arr, kind=algo)
10238 inv = np.empty(sorter.size, dtype=np.intp)
10239 inv[sorter] = np.arange(sorter.size, dtype=np.intp)
10241 if method == 'ordinal':
10242 result = inv + 1
10243 else:
10244 arr = arr[sorter]
10245 obs = np.r_[True, arr[1:] != arr[:-1]]
10246 dense = obs.cumsum()[inv]
10248 if method == 'dense':
10249 result = dense
10250 else:
10251 # cumulative counts of each unique value
10252 count = np.r_[np.nonzero(obs)[0], len(obs)]
10254 if method == 'max':
10255 result = count[dense]
10257 if method == 'min':
10258 result = count[dense - 1] + 1
10260 if method == 'average':
10261 result = .5 * (count[dense] + count[dense - 1] + 1)
10263 if nan_indexes is not None:
10264 result = result.astype('float64')
10265 result[nan_indexes] = np.nan
10267 return result
10270def expectile(a, alpha=0.5, *, weights=None):
10271 r"""Compute the expectile at the specified level.
10273 Expectiles are a generalization of the expectation in the same way as
10274 quantiles are a generalization of the median. The expectile at level
10275 `alpha = 0.5` is the mean (average). See Notes for more details.
10277 Parameters
10278 ----------
10279 a : array_like
10280 Array containing numbers whose expectile is desired.
10281 alpha : float, default: 0.5
10282 The level of the expectile; e.g., `alpha=0.5` gives the mean.
10283 weights : array_like, optional
10284 An array of weights associated with the values in `a`.
10285 The `weights` must be broadcastable to the same shape as `a`.
10286 Default is None, which gives each value a weight of 1.0.
10287 An integer valued weight element acts like repeating the corresponding
10288 observation in `a` that many times. See Notes for more details.
10290 Returns
10291 -------
10292 expectile : ndarray
10293 The empirical expectile at level `alpha`.
10295 See Also
10296 --------
10297 numpy.mean : Arithmetic average
10298 numpy.quantile : Quantile
10300 Notes
10301 -----
10302 In general, the expectile at level :math:`\alpha` of a random variable
10303 :math:`X` with cumulative distribution function (CDF) :math:`F` is given
10304 by the unique solution :math:`t` of:
10306 .. math::
10308 \alpha E((X - t)_+) = (1 - \alpha) E((t - X)_+) \,.
10310 Here, :math:`(x)_+ = \max(0, x)` is the positive part of :math:`x`.
10311 This equation can be equivalently written as:
10313 .. math::
10315 \alpha \int_t^\infty (x - t)\mathrm{d}F(x)
10316 = (1 - \alpha) \int_{-\infty}^t (t - x)\mathrm{d}F(x) \,.
10318 The empirical expectile at level :math:`\alpha` (`alpha`) of a sample
10319 :math:`a_i` (the array `a`) is defined by plugging in the empirical CDF of
10320 `a`. Given sample or case weights :math:`w` (the array `weights`), it
10321 reads :math:`F_a(x) = \frac{1}{\sum_i w_i} \sum_i w_i 1_{a_i \leq x}`
10322 with indicator function :math:`1_{A}`. This leads to the definition of the
10323 empirical expectile at level `alpha` as the unique solution :math:`t` of:
10325 .. math::
10327 \alpha \sum_{i=1}^n w_i (a_i - t)_+ =
10328 (1 - \alpha) \sum_{i=1}^n w_i (t - a_i)_+ \,.
10330 For :math:`\alpha=0.5`, this simplifies to the weighted average.
10331 Furthermore, the larger :math:`\alpha`, the larger the value of the
10332 expectile.
10334 As a final remark, the expectile at level :math:`\alpha` can also be
10335 written as a minimization problem. One often used choice is
10337 .. math::
10339 \operatorname{argmin}_t
10340 E(\lvert 1_{t\geq X} - \alpha\rvert(t - X)^2) \,.
10342 References
10343 ----------
10344 .. [1] W. K. Newey and J. L. Powell (1987), "Asymmetric Least Squares
10345 Estimation and Testing," Econometrica, 55, 819-847.
10346 .. [2] T. Gneiting (2009). "Making and Evaluating Point Forecasts,"
10347 Journal of the American Statistical Association, 106, 746 - 762.
10348 :doi:`10.48550/arXiv.0912.0902`
10350 Examples
10351 --------
10352 >>> import numpy as np
10353 >>> from scipy.stats import expectile
10354 >>> a = [1, 4, 2, -1]
10355 >>> expectile(a, alpha=0.5) == np.mean(a)
10356 True
10357 >>> expectile(a, alpha=0.2)
10358 0.42857142857142855
10359 >>> expectile(a, alpha=0.8)
10360 2.5714285714285716
10361 >>> weights = [1, 3, 1, 1]
10363 """
10364 if alpha < 0 or alpha > 1:
10365 raise ValueError(
10366 "The expectile level alpha must be in the range [0, 1]."
10367 )
10368 a = np.asarray(a)
10370 if weights is not None:
10371 weights = np.broadcast_to(weights, a.shape)
10373 # This is the empirical equivalent of Eq. (13) with identification
10374 # function from Table 9 (omitting a factor of 2) in [2] (their y is our
10375 # data a, their x is our t)
10376 def first_order(t):
10377 return np.average(np.abs((a <= t) - alpha) * (t - a), weights=weights)
10379 if alpha >= 0.5:
10380 x0 = np.average(a, weights=weights)
10381 x1 = np.amax(a)
10382 else:
10383 x1 = np.average(a, weights=weights)
10384 x0 = np.amin(a)
10386 if x0 == x1:
10387 # a has a single unique element
10388 return x0
10390 # Note that the expectile is the unique solution, so no worries about
10391 # finding a wrong root.
10392 res = root_scalar(first_order, x0=x0, x1=x1)
10393 return res.root