# Author: Raphael Vallat <raphaelvallat9@gmail.com>
# Date: April 2018
import warnings
import numpy as np
from scipy.stats import pearsonr
from pingouin.utils import _check_eftype, remove_na
# from pingouin.distribution import homoscedasticity
__all__ = [
"compute_esci",
"compute_bootci",
"convert_effsize",
"compute_effsize",
"compute_effsize_from_t",
]
[docs]
def compute_esci(
stat=None,
nx=None,
ny=None,
paired=False,
eftype="cohen",
confidence=0.95,
decimals=2,
alternative="two-sided",
):
"""Parametric confidence intervals around a Cohen d or a correlation coefficient.
Parameters
----------
stat : float
Original effect size. Must be either a correlation coefficient or a Cohen-type effect size
(Cohen d or Hedges g).
nx, ny : int
Length of vector x and y.
paired : bool
Indicates if the effect size was estimated from a paired sample. This is only relevant for
cohen or hedges effect size.
eftype : string
Effect size type. Must be "r" (correlation) or "cohen" (Cohen d or Hedges g).
confidence : float
Confidence level (0.95 = 95%)
decimals : int
Number of rounded decimals.
alternative : string
Defines the alternative hypothesis, or tail for the correlation coefficient. Must be one of
"two-sided" (default), "greater" or "less". This parameter only has an effect if ``eftype``
is "r".
Returns
-------
ci : array
Desired converted effect size
Notes
-----
To compute the parametric confidence interval around a **Pearson r correlation** coefficient,
one must first apply a Fisher's r-to-z transformation:
.. math:: z = 0.5 \\cdot \\ln \\frac{1 + r}{1 - r} = \\text{arctanh}(r)
and compute the standard error:
.. math:: \\text{SE} = \\frac{1}{\\sqrt{n - 3}}
where :math:`n` is the sample size.
The lower and upper confidence intervals - *in z-space* - are then given by:
.. math:: \\text{ci}_z = z \\pm \\text{crit} \\cdot \\text{SE}
where :math:`\\text{crit}` is the critical value of the normal distribution corresponding to
the desired confidence level (e.g. 1.96 in case of a 95% confidence interval).
These confidence intervals can then be easily converted back to *r-space*:
.. math::
\\text{ci}_r = \\frac{\\exp(2 \\cdot \\text{ci}_z) - 1}
{\\exp(2 \\cdot \\text{ci}_z) + 1} = \\text{tanh}(\\text{ci}_z)
A formula for calculating the confidence interval for a **Cohen d effect size** is given by
Hedges and Olkin (1985, p86). If the effect size estimate from the sample is :math:`d`, then
it follows a T distribution with standard error:
.. math::
\\text{SE} = \\sqrt{\\frac{n_x + n_y}{n_x \\cdot n_y} +
\\frac{d^2}{2 (n_x + n_y)}}
where :math:`n_x` and :math:`n_y` are the sample sizes of the two groups.
In one-sample test or paired test, this becomes:
.. math::
\\text{SE} = \\sqrt{\\frac{1}{n_x} + \\frac{d^2}{2 n_x}}
The lower and upper confidence intervals are then given by:
.. math:: \\text{ci}_d = d \\pm \\text{crit} \\cdot \\text{SE}
where :math:`\\text{crit}` is the critical value of the T distribution corresponding to the
desired confidence level.
References
----------
* https://en.wikipedia.org/wiki/Fisher_transformation
* Hedges, L., and Ingram Olkin. "Statistical models for meta-analysis." (1985).
* http://www.leeds.ac.uk/educol/documents/00002182.htm
* https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5133225/
Examples
--------
1. Confidence interval of a Pearson correlation coefficient
>>> import pingouin as pg
>>> x = [3, 4, 6, 7, 5, 6, 7, 3, 5, 4, 2]
>>> y = [4, 6, 6, 7, 6, 5, 5, 2, 3, 4, 1]
>>> nx, ny = len(x), len(y)
>>> stat = pg.compute_effsize(x, y, eftype='r')
>>> ci = pg.compute_esci(stat=stat, nx=nx, ny=ny, eftype='r')
>>> print(round(stat, 4), ci)
0.7468 [0.27 0.93]
2. Confidence interval of a Cohen d
>>> stat = pg.compute_effsize(x, y, eftype='cohen')
>>> ci = pg.compute_esci(stat, nx=nx, ny=ny, eftype='cohen', decimals=3)
>>> print(round(stat, 4), ci)
0.1538 [-0.737 1.045]
"""
from scipy.stats import norm, t
assert eftype.lower() in ["r", "pearson", "spearman", "cohen", "d", "g", "hedges"]
assert alternative in [
"two-sided",
"greater",
"less",
], "Alternative must be one of 'two-sided' (default), 'greater' or 'less'."
assert stat is not None and nx is not None
assert isinstance(confidence, float)
assert 0 < confidence < 1, "confidence must be between 0 and 1."
if eftype.lower() in ["r", "pearson", "spearman"]:
z = np.arctanh(stat) # R-to-z transform
se = 1 / np.sqrt(nx - 3)
# See https://github.com/SurajGupta/r-source/blob/master/src/library/stats/R/cor.test.R
if alternative == "two-sided":
crit = np.abs(norm.ppf((1 - confidence) / 2))
ci_z = np.array([z - crit * se, z + crit * se])
elif alternative == "greater":
crit = norm.ppf(confidence)
ci_z = np.array([z - crit * se, np.inf])
else: # alternative = "less"
crit = norm.ppf(confidence)
ci_z = np.array([-np.inf, z + crit * se])
ci = np.tanh(ci_z) # Transform back to r
else:
# Cohen d. Results are different than JASP which uses a non-central T
# distribution. See github.com/jasp-stats/jasp-issues/issues/525
if ny == 1 or paired:
# One-sample or paired. Results vary slightly from the cohen.d R
# function which uses instead:
# >>> sqrt((n / (n / 2)^2) + .5*(dd^2 / n)) -- one-sample
# >>> sqrt( (1/n1 + dd^2/(2*n1))*(2-2*r)); -- paired
# where r is the correlation between samples
# https://github.com/mtorchiano/effsize/blob/master/R/CohenD.R
# However, Pingouin uses the formulas on www.real-statistics.com
se = np.sqrt(1 / nx + stat**2 / (2 * nx))
dof = nx - 1
else:
# Independent two-samples: give same results as R:
# >>> cohen.d(..., paired = FALSE, noncentral=FALSE)
se = np.sqrt(((nx + ny) / (nx * ny)) + (stat**2) / (2 * (nx + ny)))
dof = nx + ny - 2
crit = np.abs(t.ppf((1 - confidence) / 2, dof))
ci = np.array([stat - crit * se, stat + crit * se])
return np.round(ci, decimals)
[docs]
def compute_bootci(
x,
y=None,
func=None,
method="cper",
paired=False,
confidence=0.95,
n_boot=2000,
decimals=2,
seed=None,
return_dist=False,
):
"""Bootstrapped confidence intervals of univariate and bivariate functions.
Parameters
----------
x : 1D-array or list
First sample. Required for both bivariate and univariate functions.
y : 1D-array, list, or None
Second sample. Required only for bivariate functions.
func : str or custom function
Function to compute the bootstrapped statistic. Accepted string values are:
* ``'pearson'``: Pearson correlation (bivariate, paired x and y)
* ``'spearman'``: Spearman correlation (bivariate, paired x and y)
* ``'cohen'``: Cohen d effect size (bivariate, paired or unpaired x and y)
* ``'hedges'``: Hedges g effect size (bivariate, paired or unpaired x and y)
* ``'mean'``: Mean (univariate = only x)
* ``'std'``: Standard deviation (univariate)
* ``'var'``: Variance (univariate)
method : str
Method to compute the confidence intervals (see Notes):
* ``'cper'``: Bias-corrected percentile method (default)
* ``'norm'``: Normal approximation with bootstrapped bias and standard error
* ``'per'``: Simple percentile
paired : boolean
Indicates whether x and y are paired or not. For example, for correlation functions or
paired T-test, x and y are assumed to be paired. Pingouin will resample the pairs
(x_i, y_i) when paired=True, and resample x and y separately when paired=False.
If paired=True, x and y must have the same number of elements.
confidence : float
Confidence level (0.95 = 95%)
n_boot : int
Number of bootstrap iterations. The higher, the better, the slower.
decimals : int
Number of rounded decimals.
seed : int or None
Random seed for generating bootstrap samples.
return_dist : boolean
If True, return the confidence intervals and the bootstrapped distribution (e.g. for
plotting purposes).
Returns
-------
ci : array
Bootstrapped confidence intervals.
Notes
-----
Results have been tested against the
`bootci <https://www.mathworks.com/help/stats/bootci.html>`_ Matlab function.
Since version 1.7, SciPy also includes a built-in bootstrap function
:py:func:`scipy.stats.bootstrap`. The SciPy implementation has two advantages over Pingouin: it
is faster when using ``vectorized=True``, and it supports the bias-corrected and accelerated
(BCa) confidence intervals for univariate functions. However, unlike Pingouin, it does not
return the bootstrap distribution.
The percentile bootstrap method (``per``) is defined as the
:math:`100 \\times \\frac{\\alpha}{2}` and :math:`100 \\times \\frac{1 - \\alpha}{2}`
percentiles of the distribution of :math:`\\theta` estimates obtained from resampling, where
:math:`\\alpha` is the level of significance (1 - confidence, default = 0.05 for 95% CIs).
The bias-corrected percentile method (``cper``) corrects for bias of the bootstrap
distribution. This method is different from the BCa method — the default in Matlab and SciPy —
which corrects for both bias and skewness of the bootstrap distribution using jackknife
resampling.
The normal approximation method (``norm``) calculates the confidence intervals with the
standard normal distribution using bootstrapped bias and standard error.
References
----------
* DiCiccio, T. J., & Efron, B. (1996). Bootstrap confidence intervals. Statistical science,
189-212.
* Davison, A. C., & Hinkley, D. V. (1997). Bootstrap methods and their application (Vol. 1).
Cambridge university press.
* Jung, Lee, Gupta, & Cho (2019). Comparison of bootstrap confidence interval methods for
GSCA using a Monte Carlo simulation. Frontiers in psychology, 10, 2215.
Examples
--------
1. Bootstrapped 95% confidence interval of a Pearson correlation
>>> import pingouin as pg
>>> import numpy as np
>>> rng = np.random.default_rng(42)
>>> x = rng.normal(loc=4, scale=2, size=100)
>>> y = rng.normal(loc=3, scale=1, size=100)
>>> stat = np.corrcoef(x, y)[0][1]
>>> ci = pg.compute_bootci(x, y, func='pearson', paired=True, seed=42, decimals=4)
>>> print(round(stat, 4), ci)
0.0945 [-0.098 0.2738]
Let's compare to SciPy's built-in bootstrap function
>>> from scipy.stats import bootstrap
>>> bt_scipy = bootstrap(
... data=(x, y), statistic=lambda x, y: np.corrcoef(x, y)[0][1],
... method="basic", vectorized=False, n_resamples=2000, paired=True, random_state=42)
>>> np.round(bt_scipy.confidence_interval, 4)
array([-0.0952, 0.2883])
2. Bootstrapped 95% confidence interval of a Cohen d
>>> stat = pg.compute_effsize(x, y, eftype='cohen')
>>> ci = pg.compute_bootci(x, y, func='cohen', seed=42, decimals=3)
>>> print(round(stat, 4), ci)
0.7009 [0.403 1.009]
3. Bootstrapped confidence interval of a standard deviation (univariate)
>>> import numpy as np
>>> stat = np.std(x, ddof=1)
>>> ci = pg.compute_bootci(x, func='std', seed=123)
>>> print(round(stat, 4), ci)
1.5534 [1.38 1.8 ]
Compare to SciPy's built-in bootstrap function, which returns the bias-corrected and
accelerated CIs (see Notes).
>>> def std(x, axis):
... return np.std(x, ddof=1, axis=axis)
>>> bt_scipy = bootstrap(data=(x, ), statistic=std, n_resamples=2000, random_state=123)
>>> np.round(bt_scipy.confidence_interval, 2)
array([1.39, 1.81])
Changing the confidence intervals type in Pingouin
>>> pg.compute_bootci(x, func='std', seed=123, method="norm")
array([1.37, 1.76])
>>> pg.compute_bootci(x, func='std', seed=123, method="percentile")
array([1.35, 1.75])
4. Bootstrapped confidence interval using a custom univariate function
>>> from scipy.stats import skew
>>> round(skew(x), 4), pg.compute_bootci(x, func=skew, n_boot=10000, seed=123)
(-0.137, array([-0.55, 0.32]))
5. Bootstrapped confidence interval using a custom bivariate function. Here, x and y are not
paired and can therefore have different sizes.
>>> def mean_diff(x, y):
... return np.mean(x) - np.mean(y)
>>> y2 = rng.normal(loc=3, scale=1, size=200) # y2 has 200 samples, x has 100
>>> ci = pg.compute_bootci(x, y2, func=mean_diff, n_boot=10000, seed=123)
>>> print(round(mean_diff(x, y2), 2), ci)
0.88 [0.54 1.21]
We can also get the bootstrapped distribution
>>> ci, bt = pg.compute_bootci(x, y2, func=mean_diff, n_boot=10000, return_dist=True, seed=9)
>>> print(f"The bootstrap distribution has {bt.size} samples. The mean and standard "
... f"{bt.mean():.4f} ± {bt.std():.4f}")
The bootstrap distribution has 10000 samples. The mean and standard 0.8807 ± 0.1704
"""
from inspect import isfunction, isroutine
from scipy.stats import norm
# Check other arguments
assert isinstance(confidence, float)
assert 0 < confidence < 1, "confidence must be between 0 and 1."
assert method in ["norm", "normal", "percentile", "per", "cpercentile", "cper"]
assert isfunction(func) or isinstance(func, str) or isroutine(func), (
"func must be a function (e.g. np.mean, custom function) or a string (e.g. 'pearson'). "
"See documentation for more details."
)
vectorizable = False
# Check x
x = np.asarray(x)
nx = x.size
assert x.ndim == 1, "x must be one-dimensional."
assert nx > 1, "x must have more than one element."
# Check y
if y is not None:
y = np.asarray(y)
ny = y.size
assert y.ndim == 1, "y must be one-dimensional."
assert ny > 1, "y must have more than one element."
if paired:
assert nx == ny, "x and y must have the same number of elements when paired=True."
# Check string functions
if isinstance(func, str):
func_str = "%s" % func
if func == "pearson":
assert paired, "Paired should be True if using correlation functions."
def func(x, y):
return pearsonr(x, y)[0] # Faster than np.corrcoef
elif func == "spearman":
from scipy.stats import spearmanr
assert paired, "Paired should be True if using correlation functions."
def func(x, y):
return spearmanr(x, y)[0]
elif func in ["cohen", "hedges"]:
from pingouin.effsize import compute_effsize
def func(x, y):
return compute_effsize(x, y, paired=paired, eftype=func_str)
elif func == "mean":
vectorizable = True
def func(x):
return np.mean(x, axis=0)
elif func == "std":
vectorizable = True
def func(x):
return np.std(x, ddof=1, axis=0)
elif func == "var":
vectorizable = True
def func(x):
return np.var(x, ddof=1, axis=0)
else:
raise ValueError("Function string not recognized.")
# Bootstrap
bootstat = np.empty(n_boot)
rng = np.random.default_rng(seed) # Random seed
boot_x = rng.choice(np.arange(nx), size=(nx, n_boot), replace=True)
if y is not None:
reference = func(x, y)
if paired:
for i in range(n_boot):
# Note that here we use a bootstrapping procedure with replacement
# of all the pairs (Xi, Yi). This is NOT suited for
# hypothesis testing such as p-value estimation). Instead, for the
# latter, one must only shuffle the Y values while keeping the X
# values constant, i.e.:
# >>> boot_x = rng.random_sample((n_boot, n)).argsort(axis=1)
# >>> for i in range(n_boot):
# >>> bootstat[i] = func(x, y[boot_x[i, :]])
bootstat[i] = func(x[boot_x[:, i]], y[boot_x[:, i]])
else:
boot_y = rng.choice(np.arange(ny), size=(ny, n_boot), replace=True)
for i in range(n_boot):
bootstat[i] = func(x[boot_x[:, i]], y[boot_y[:, i]])
else:
reference = func(x)
if vectorizable:
bootstat = func(x[boot_x])
else:
for i in range(n_boot):
bootstat[i] = func(x[boot_x[:, i]])
# CONFIDENCE INTERVALS
# See Matlab bootci function
alpha = (1 - confidence) / 2
if method in ["norm", "normal"]:
# Normal approximation
za = norm.ppf(alpha) # = 1.96
se = np.std(bootstat, ddof=1)
bias = np.mean(bootstat - reference)
ci = np.array([reference - bias + se * za, reference - bias - se * za])
elif method in ["percentile", "per"]:
# Simple percentile
interval = 100 * np.array([alpha, 1 - alpha])
ci = np.percentile(bootstat, interval)
pass
else:
# Bias-corrected percentile bootstrap
from pingouin.regression import _bias_corrected_ci
ci = _bias_corrected_ci(bootstat, reference, alpha=(1 - confidence))
ci = np.round(ci, decimals)
if return_dist:
return ci, bootstat
else:
return ci
[docs]
def convert_effsize(ef, input_type, output_type, nx=None, ny=None):
"""Conversion between effect sizes.
Parameters
----------
ef : float
Original effect size.
input_type : string
Effect size type of ef. Must be ``'cohen'`` or ``'pointbiserialr'``.
output_type : string
Desired effect size type. Available methods are:
* ``'cohen'``: Unbiased Cohen d
* ``'hedges'``: Hedges g
* ``'pointbiserialr'``: Point-biserial correlation
* ``'eta-square'``: Eta-square
* ``'odds-ratio'``: Odds ratio
* ``'AUC'``: Area Under the Curve
* ``'none'``: pass-through (return ``ef``)
nx, ny : int, optional
Length of vector x and y. Required to convert to Hedges g.
Returns
-------
ef : float
Desired converted effect size
See Also
--------
compute_effsize : Calculate effect size between two set of observations.
compute_effsize_from_t : Convert a T-statistic to an effect size.
Notes
-----
The formula to convert from a`point-biserial correlation
<https://en.wikipedia.org/wiki/Point-biserial_correlation_coefficient>`_ **r** to **d** is
given in [1]_:
.. math:: d = \\frac{2r_{pb}}{\\sqrt{1 - r_{pb}^2}}
The formula to convert **d** to a point-biserial correlation **r** is given in [2]_:
.. math::
r_{pb} = \\frac{d}{\\sqrt{d^2 + \\frac{(n_x + n_y)^2 - 2(n_x + n_y)}
{n_xn_y}}}
The formula to convert **d** to :math:`\\eta^2` is given in [3]_:
.. math:: \\eta^2 = \\frac{(0.5 d)^2}{1 + (0.5 d)^2}
The formula to convert **d** to an odds-ratio is given in [4]_:
.. math:: \\text{OR} = \\exp (\\frac{d \\pi}{\\sqrt{3}})
The formula to convert **d** to area under the curve is given in [5]_:
.. math:: \\text{AUC} = \\mathcal{N}_{cdf}(\\frac{d}{\\sqrt{2}})
References
----------
.. [1] Rosenthal, Robert. "Parametric measures of effect size."
The handbook of research synthesis 621 (1994): 231-244.
.. [2] McGrath, Robert E., and Gregory J. Meyer. "When effect sizes
disagree: the case of r and d." Psychological methods 11.4 (2006): 386.
.. [3] Cohen, Jacob. "Statistical power analysis for the behavioral
sciences. 2nd." (1988).
.. [4] Borenstein, Michael, et al. "Effect sizes for continuous data."
The handbook of research synthesis and meta-analysis 2 (2009): 221-235.
.. [5] Ruscio, John. "A probability-based measure of effect size:
Robustness to base rates and other factors." Psychological methods 1
3.1 (2008): 19.
Examples
--------
1. Convert from Cohen d to eta-square
>>> import pingouin as pg
>>> d = .45
>>> eta = pg.convert_effsize(d, 'cohen', 'eta-square')
>>> print(eta)
0.048185603807257595
2. Convert from Cohen d to Hegdes g (requires the sample sizes of each
group)
>>> pg.convert_effsize(.45, 'cohen', 'hedges', nx=10, ny=10)
0.4309859154929578
3. Convert a point-biserial correlation to Cohen d
>>> rpb = 0.40
>>> d = pg.convert_effsize(rpb, 'pointbiserialr', 'cohen')
>>> print(d)
0.8728715609439696
4. Reverse operation: convert Cohen d to a point-biserial correlation
>>> pg.convert_effsize(d, 'cohen', 'pointbiserialr')
0.4000000000000001
"""
it = input_type.lower()
ot = output_type.lower()
# Check input and output type
for inp in [it, ot]:
if not _check_eftype(inp):
err = f"Could not interpret input '{inp}'"
raise ValueError(err)
if it not in ["pointbiserialr", "cohen"]:
raise ValueError("Input type must be 'cohen' or 'pointbiserialr'")
# Pass-through option
if it == ot or ot == "none":
return ef
# Convert point-biserial r to Cohen d (Rosenthal 1994)
d = (2 * ef) / np.sqrt(1 - ef**2) if it == "pointbiserialr" else ef
# Then convert to the desired output type
if ot == "cohen":
return d
elif ot == "hedges":
if all(v is not None for v in [nx, ny]):
return d * (1 - (3 / (4 * (nx + ny) - 9)))
else:
# If shapes of x and y are not known, return cohen's d
warnings.warn(
"You need to pass nx and ny arguments to compute "
"Hedges g. Returning Cohen's d instead"
)
return d
elif ot == "pointbiserialr":
# McGrath and Meyer 2006
if all(v is not None for v in [nx, ny]):
a = ((nx + ny) ** 2 - 2 * (nx + ny)) / (nx * ny)
else:
a = 4
return d / np.sqrt(d**2 + a)
elif ot == "eta-square":
# Cohen 1988
return (d / 2) ** 2 / (1 + (d / 2) ** 2)
elif ot == "odds-ratio":
# Borenstein et al. 2009
return np.exp(d * np.pi / np.sqrt(3))
elif ot == "r":
# https://github.com/raphaelvallat/pingouin/issues/302
raise ValueError(
"Using effect size 'r' in `pingouin.convert_effsize` has been deprecated. "
"Please use 'pointbiserialr' instead."
)
else: # ['auc']
# Ruscio 2008
from scipy.stats import norm
return norm.cdf(d / np.sqrt(2))
[docs]
def compute_effsize(x, y, paired=False, eftype="cohen"):
"""Calculate effect size between two set of observations.
Parameters
----------
x : np.array or list
First set of observations.
y : np.array or list
Second set of observations.
paired : boolean
If True, uses Cohen d-avg formula to correct for repeated measurements
(see Notes).
eftype : string
Desired output effect size.
Available methods are:
* ``'none'``: no effect size
* ``'cohen'``: Unbiased Cohen d
* ``'hedges'``: Hedges g
* ``'r'``: Pearson correlation coefficient
* ``'pointbiserialr'``: Point-biserial correlation
* ``'eta-square'``: Eta-square
* ``'odds-ratio'``: Odds ratio
* ``'AUC'``: Area Under the Curve
* ``'CLES'``: Common Language Effect Size
Returns
-------
ef : float
Effect size
See Also
--------
convert_effsize : Conversion between effect sizes.
compute_effsize_from_t : Convert a T-statistic to an effect size.
Notes
-----
Missing values are automatically removed from the data. If ``x`` and ``y`` are paired, the
entire row is removed.
If ``x`` and ``y`` are independent, the Cohen :math:`d` is:
.. math::
d = \\frac{\\overline{X} - \\overline{Y}}
{\\sqrt{\\frac{(n_{1} - 1)\\sigma_{1}^{2} + (n_{2} - 1)
\\sigma_{2}^{2}}{n1 + n2 - 2}}}
If ``x`` and ``y`` are paired, the Cohen :math:`d_{avg}` is computed:
.. math::
d_{avg} = \\frac{\\overline{X} - \\overline{Y}}
{\\sqrt{\\frac{(\\sigma_1^2 + \\sigma_2^2)}{2}}}
The Cohen's d is a biased estimate of the population effect size, especially for small samples
(n < 20). It is often preferable to use the corrected Hedges :math:`g` instead:
.. math:: g = d \\times (1 - \\frac{3}{4(n_1 + n_2) - 9})
The common language effect size is the proportion of pairs where ``x`` is higher than ``y``
(calculated with a brute-force approach where each observation of ``x`` is paired to each
observation of ``y``, see :py:func:`pingouin.wilcoxon` for more details):
.. math:: \\text{CL} = P(X > Y) + .5 \\times P(X = Y)
For other effect sizes, Pingouin will first calculate a Cohen :math:`d` and then use the
:py:func:`pingouin.convert_effsize` to convert to the desired effect size.
References
----------
* Lakens, D., 2013. Calculating and reporting effect sizes to
facilitate cumulative science: a practical primer for t-tests and
ANOVAs. Front. Psychol. 4, 863. https://doi.org/10.3389/fpsyg.2013.00863
* Cumming, Geoff. Understanding the new statistics: Effect sizes,
confidence intervals, and meta-analysis. Routledge, 2013.
* https://osf.io/vbdah/
Examples
--------
1. Cohen d from two independent samples.
>>> import numpy as np
>>> import pingouin as pg
>>> x = [1, 2, 3, 4]
>>> y = [3, 4, 5, 6, 7]
>>> pg.compute_effsize(x, y, paired=False, eftype='cohen')
-1.707825127659933
The sign of the Cohen d will be opposite if we reverse the order of
``x`` and ``y``:
>>> pg.compute_effsize(y, x, paired=False, eftype='cohen')
1.707825127659933
2. Hedges g from two paired samples.
>>> x = [1, 2, 3, 4, 5, 6, 7]
>>> y = [1, 3, 5, 7, 9, 11, 13]
>>> pg.compute_effsize(x, y, paired=True, eftype='hedges')
-0.8222477210374874
3. Common Language Effect Size.
>>> pg.compute_effsize(x, y, eftype='cles')
0.2857142857142857
In other words, there are ~29% of pairs where ``x`` is higher than ``y``,
which means that there are ~71% of pairs where ``x`` is *lower* than ``y``.
This can be easily verified by changing the order of ``x`` and ``y``:
>>> pg.compute_effsize(y, x, eftype='cles')
0.7142857142857143
"""
# Check arguments
if not _check_eftype(eftype):
err = f"Could not interpret input '{eftype}'"
raise ValueError(err)
x = np.asarray(x)
y = np.asarray(y)
if x.size != y.size and paired:
warnings.warn("x and y have unequal sizes. Switching to " "paired == False.")
paired = False
# Remove rows with missing values
x, y = remove_na(x, y, paired=paired)
nx, ny = x.size, y.size
if ny == 1:
# Case 1: One-sample Test
d = (x.mean() - y) / x.std(ddof=1)
return d
if eftype.lower() == "r":
# Return correlation coefficient (useful for CI bootstrapping)
r, _ = pearsonr(x, y)
return r
elif eftype.lower() == "cles":
# Compute exact CLES (see pingouin.wilcoxon)
diff = x[:, None] - y
return np.where(diff == 0, 0.5, diff > 0).mean()
else:
# Compute unbiased Cohen's d effect size
if not paired:
# https://en.wikipedia.org/wiki/Effect_size
dof = nx + ny - 2
poolsd = np.sqrt(((nx - 1) * x.var(ddof=1) + (ny - 1) * y.var(ddof=1)) / dof)
d = (x.mean() - y.mean()) / poolsd
else:
# Report Cohen d-avg (Cumming 2012; Lakens 2013)
# Careful, the formula in Lakens 2013 is wrong. Updated in Pingouin
# v0.3.4 to use the formula provided by Cummings 2012.
# Before that the denominator was just (SD1 + SD2) / 2
d = (x.mean() - y.mean()) / np.sqrt((x.var(ddof=1) + y.var(ddof=1)) / 2)
return convert_effsize(d, "cohen", eftype, nx=nx, ny=ny)
[docs]
def compute_effsize_from_t(tval, nx=None, ny=None, N=None, eftype="cohen"):
"""Compute effect size from a T-value.
Parameters
----------
tval : float
T-value
nx, ny : int, optional
Group sample sizes.
N : int, optional
Total sample size (will not be used if nx and ny are specified)
eftype : string, optional
Desired output effect size.
Returns
-------
ef : float
Effect size
See Also
--------
compute_effsize : Calculate effect size between two set of observations.
convert_effsize : Conversion between effect sizes.
Notes
-----
If both nx and ny are specified, the formula to convert from *t* to *d* is:
.. math:: d = t * \\sqrt{\\frac{1}{n_x} + \\frac{1}{n_y}}
If only N (total sample size) is specified, the formula is:
.. math:: d = \\frac{2t}{\\sqrt{N}}
Examples
--------
1. Compute effect size from a T-value when both sample sizes are known.
>>> from pingouin import compute_effsize_from_t
>>> tval, nx, ny = 2.90, 35, 25
>>> d = compute_effsize_from_t(tval, nx=nx, ny=ny, eftype='cohen')
>>> print(d)
0.7593982580212534
2. Compute effect size when only total sample size is known (nx+ny)
>>> tval, N = 2.90, 60
>>> d = compute_effsize_from_t(tval, N=N, eftype='cohen')
>>> print(d)
0.7487767802667672
"""
if not _check_eftype(eftype):
err = f"Could not interpret input '{eftype}'"
raise ValueError(err)
if not isinstance(tval, float):
err = "T-value must be float"
raise ValueError(err)
# Compute Cohen d (Lakens, 2013)
if nx is not None and ny is not None:
d = tval * np.sqrt(1 / nx + 1 / ny)
elif N is not None:
d = 2 * tval / np.sqrt(N)
else:
raise ValueError("You must specify either nx + ny, or just N")
return convert_effsize(d, "cohen", eftype, nx=nx, ny=ny)