Skip to content

Commit

Permalink
Improvements docs TOST function
Browse files Browse the repository at this point in the history
  • Loading branch information
raphaelvallat committed Jul 21, 2019
1 parent 8473ad6 commit 564667f
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 28 deletions.
30 changes: 15 additions & 15 deletions pingouin/equivalence.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,39 @@
# Date: July 2019
import numpy as np
import pandas as pd
from pingouin.parametric import ttest
from .parametric import ttest


__all__ = ["tost"]


def tost(x, y, bound=1, paired=False, correction=False):
"""Two one-sided test (TOST) for equivalence.
"""Two One-Sided Test (TOST) for equivalence.
Parameters
----------
x, y : array_like
First and second set of observations. ``x`` and ``y`` should have the
same units. One sample tests are not yet supported.
same units. If ``y`` is a single value (e.g. 0), a one-sample test is
performed.
bound : float
Magnitude of region of similarity (epsilon). Note that this should be
expressed in the same unit as ``x`` and ``y``.
Magnitude of region of similarity (a.k.a epsilon). Note that this
should be expressed in the same unit as ``x`` and ``y``.
paired : boolean
Specify whether the two observations are related (i.e. repeated
measures) or independent.
correction : auto or boolean
Specify whether or not to correct for unequal variances using Welch
separate variances T-test. This only applies if ``parametric`` is True.
separate variances T-test. This only applies if ``paired`` is False.
Returns
-------
stats : pandas DataFrame
TOST summary ::
'bound' : bound (= epsilon, or equivalence margin)
'upper' : upper interval p-value
'lower' : lower interval p-value
'p-val' : TOST p-value
'dof' : degrees of freedom
'pval' : TOST p-value
See also
--------
Expand All @@ -50,25 +50,25 @@ def tost(x, y, bound=1, paired=False, correction=False):
Examples
--------
1. TOST with a region of similarity of 1 (default)
1. Independent two-sample TOST with a region of similarity of 1 (default)
>>> import pingouin as pg
>>> a = [4, 7, 8, 6, 3, 2]
>>> b = [6, 8, 7, 10, 11, 9]
>>> pg.tost(a, b)
bound dof p-val
bound dof pval
TOST 1 10 0.965097
2. Paired TOST with a different equivalent region
2. Paired TOST with a different region of similarity
>>> pg.tost(a, b, bound=0.5, paired=True)
bound dof p-val
bound dof pval
TOST 0.5 5 0.954854
3. One sample TOST
>>> pg.tost(a, y=0, bound=4)
bound dof p-val
bound dof pval
TOST 4 5 0.825967
"""
x = np.asarray(x)
Expand All @@ -83,5 +83,5 @@ def tost(x, y, bound=1, paired=False, correction=False):
pval = max(df_a.at['T-test', 'p-val'], df_b.at['T-test', 'p-val'])

# Create output dataframe
stats = {'bound': bound, 'dof': df_a.at['T-test', 'dof'], 'p-val': pval}
stats = {'bound': bound, 'dof': df_a.at['T-test', 'dof'], 'pval': pval}
return pd.DataFrame.from_records(stats, index=['TOST'])
27 changes: 14 additions & 13 deletions pingouin/tests/test_equivalence.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ def test_tost(self):
Compare to R package equivalence (function `tost`).
"""
np.random.seed(1234)
a = np.random.normal(scale=1., size=600)
b = a + 25
a = np.random.normal(scale=1., size=600) # a has a mean of 0
b = a + 25 # b has a mean of ~25

# Simple safety check
assert tost(a, a).at['TOST', 'p-val'] < 0.05
assert tost(a, a, paired=True).at['TOST', 'p-val'] < 0.05
assert tost(a, b).at['TOST', 'p-val'] > 0.5
assert tost(a, b, paired=True).at['TOST', 'p-val'] > 0.5
assert tost(a, a).at['TOST', 'pval'] < 0.05
assert tost(a, a, paired=True).at['TOST', 'pval'] < 0.05
assert tost(a, b).at['TOST', 'pval'] > 0.5
assert tost(a, b, paired=True).at['TOST', 'pval'] > 0.5

# Check all arguments with good data
a = np.array([4, 7, 8, 6, 3, 2])
Expand All @@ -31,23 +32,23 @@ def test_tost(self):
# Compare with R
# R: tost(a, b, epsilon = 1, var.equal = TRUE)
assert tost(a, b).at['TOST', 'dof'] == 10
assert np.isclose(tost(a, b).at['TOST', 'p-val'], 0.9650974)
assert np.isclose(tost(a, b).at['TOST', 'pval'], 0.9650974)

# R: tost(a, b)
assert tost(a, b, correction=True).at['TOST', 'dof'] == 9.49
assert np.isclose(tost(a, b, bound=1,
correction=True).at['TOST', 'p-val'], 0.9643479)
assert np.isclose(tost(a, b, bound=10).at['TOST', 'p-val'], 0.00017933)
correction=True).at['TOST', 'pval'], 0.9643479)
assert np.isclose(tost(a, b, bound=10).at['TOST', 'pval'], 0.00017933)

# Paired
assert tost(a, b, paired=True).at['TOST', 'dof'] == 5
assert np.isclose(tost(a, b, paired=True).at['TOST', 'p-val'],
assert np.isclose(tost(a, b, paired=True).at['TOST', 'pval'],
0.9293826)
assert np.isclose(tost(a, b, bound=2, paired=True).at['TOST', 'p-val'],
assert np.isclose(tost(a, b, bound=2, paired=True).at['TOST', 'pval'],
0.8286101)

# One-sample test yield slightly different results than the equivalence
# package. Not sure to understand why, but I think it has to do with
# the way that they estimate the p-value of the one-sample test.
assert tost(a, 0).at['TOST', 'p-val'] > tost(a, 5).at['TOST', 'p-val']
assert tost(a, 5, bound=3).at['TOST', 'p-val'] < 0.5
assert tost(a, 0).at['TOST', 'pval'] > tost(a, 5).at['TOST', 'pval']
assert tost(a, 5, bound=3).at['TOST', 'pval'] < 0.5

0 comments on commit 564667f

Please sign in to comment.