Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Welch’s two-sample t-test

The goal of Welch’s two-sample tt-test to check if the means two unknown population X∼N(μX,σX)X \sim \mathcal{N}(\mu_X, \sigma_X) and Y∼N(μY,σY)Y \sim \mathcal{N}(\mu_Y, \sigma_Y) are the same or different.

For statistical design considerations related to two-sample tests, see the notebook statistical_design.ipynb.

\def\stderr#1{\mathbf{se}_{#1}} \def\stderrhat#1{\hat{\mathbf{se}}_{#1}} \def\Mean{\textbf{mean}} \def\Var{\textbf{var}} \def\Std{\textbf{std}} \def\Freq{\textbf{freq}} \def\RelFreq{\textbf{relfreq}} \def\CI#1{\textbf{CI}_{#1}} \def\CIL#1{\textbf{L}_{#1}} \def\CIU#1{\textbf{U}_{#1}} \def\ci#1{\textbf{ci}_{#1}} \def\cil#1{\textbf{l}_{#1}} \def\ciu#1{\textbf{u}_{#1}} \def\DMeans{\textbf{dmeans}} \def\Prop{\textbf{prop}} \def\DProps{\textbf{dprops}} \def\Err{ {\Large \varepsilon}} \def\calN{\mathcal{N}} \def\Tdist{\mathcal{T}} \def\Expon{\mathrm{Expon}}

(this cell contains the macro definitions like sex‾\stderr{\overline{\mathbf{x}}}, se^\stderrhat{}, mean\Mean, ...)

Data

Two samples of numerical observations x=[x1,x2,…,xn]\mathbf{x}=[x_1, x_2, \ldots, x_n] and y=[y1,y2,…,ym]\mathbf{y}=[y_1, y_2,\ldots, y_m] from independent populations.

Assumptions

We assume the unknown populations are normally distributed (NORM)\textbf{(NORM)}, or the sample is large enough (LARGEn)\textbf{(LARGEn)}, so that the sampling distributions of the means in the two populations will be approximately normally distributed:

X‾∼N(μX,σXn)andY‾∼N(μY,σYn),\overline{\mathbf{X}} \sim \mathcal{N}\left(\mu_X, \frac{\sigma_X}{n}\right) \qquad \text{and} \qquad \overline{\mathbf{Y}} \sim \mathcal{N}\left(\mu_Y, \frac{\sigma_Y}{n}\right),

where σX\sigma_X and σY\sigma_Y are the standard deviations of the unknown populations.

Hypotheses

H0:μX=μYH_0: \mu_X = \mu_Y and HA:μX≠μYH_A: \mu_X \neq \mu_Y.

Estimates

Compute the sample means x‾=mean(x)=1n∑i=1nxi\overline{\mathbf{x}} = \Mean(\mathbf{x}) = \frac{1}{n}\sum_{i=1}^n x_i, y‾=mean(y)=1m∑i=1myi\overline{\mathbf{y}} = \Mean(\mathbf{y}) = \frac{1}{m}\sum_{i=1}^m y_i, and the observed difference between means d^=dmeans(x,y)=x‾−y‾\hat{d} = \DMeans(\mathbf{x}, \mathbf{y}) = \overline{\mathbf{x}} - \overline{\mathbf{y}}.

We need to also compute the standard deviations sx=std(x)=1n−1∑i=1n(xi−x‾)2s_{\mathbf{x}} = \Std(\mathbf{x}) = \sqrt{ \frac{1}{n-1}\sum_{i=1}^n (x_i-\overline{\mathbf{x}})^2 } and sy=std(y)=1m−1∑i=1m(yi−y‾)2s_{\mathbf{y}} = \Std(\mathbf{y}) = \sqrt{ \frac{1}{m-1}\sum_{i=1}^m (y_i-\overline{\mathbf{y}})^2 }, which are estimates for the unknown population standard deviations σX\sigma_X and σY\sigma_Y.

Formulas

The estimated standard error of the difference-between-means estimator is

se^d^  =  se^x‾2+se^y‾2=sx2n+sy2m,\stderrhat{\hat{d}} \; = \; \sqrt{ \stderrhat{\overline{\mathbf{x}}}^2 + \stderrhat{\overline{\mathbf{y}}}^2 } = \sqrt{ \tfrac{s_{\mathbf{x}}^2}{n} + \tfrac{s_{\mathbf{y}}^2}{m} },

where sxs_{\mathbf{x}} and sys_{\mathbf{y}} are the sample standard deviations.

The Welch--Satterthwaite formula for the degrees of freedom parameter νd\nu_d is

νd    =    (se^x‾2+se^y‾2)2se^x‾4n−1+se^y‾4m−1.\nu_d \;\; = \;\; \frac{ \big(\stderrhat{\overline{\mathbf{x}}}^2 + \stderrhat{\overline{\mathbf{y}}}^2\big)^2 } { \tfrac{\stderrhat{\overline{\mathbf{x}}}^4}{n-1} + \tfrac{\stderrhat{\overline{\mathbf{y}}}^4}{m-1} }.

In Python, we can calculate νd\nu_d by calling the helper function calcdf: νd=calcdf(sx,n,sy,m)\nu_d = \tt{calcdf}(s_{\mathbf{x}}, n, s_{\mathbf{y}}, m).

Test statistic

Compute the tt-statistic t=d^−0se^d^t = \frac{\hat{d} - 0}{ \stderrhat{\hat{d}} }.

Sampling distribution

Student’s tt-distribution with νd\nu_d degrees of freedom, where the degrees of freedom parameter is computed using the helper function calcdf: νd=calcdf(sx,n,sy,m)\nu_d = \tt{calcdf}(s_{\mathbf{x}}, n, s_{\mathbf{y}}, m), which implements the Welch--Satterthwaite formula.

P-value calculation

To obtain the pp-value, we first compute the observed tt-statistic, then calculate the tail probabilities in the two tails of the standard normal distribution T0∼T(νd)T_0 \sim \mathcal{T}(\nu_d).

Let’s look at the source code of the function ttest_dmeans, which performs the steps of the two-sample t-test.

import numpy as np
from scipy.stats import t as tdist
from ministats import mean, std, calcdf, tailprobs

def ttest_dmeans(xsample, ysample, alt="two-sided"):
    """
    T-test to detect difference between two populations means
    based on the difference between sample means.
    """
    # Calculate the observed difference between means
    obsdhat = mean(xsample) - mean(ysample)

    # Calculate the sample sizes and the stds
    n, m = len(xsample), len(ysample)
    sx, sy = std(xsample), std(ysample)

    # Calculate the standard error, the degrees of
    # freedom, the null model, and the t-statistic
    seD = np.sqrt(sx**2/n + sy**2/m)
    obst = (obsdhat - 0) / seD
    dfD = calcdf(sx, n, sy, m)
    rvT0 = tdist(df=dfD)

    # Calculate the p-value from the t-distribution
    pvalue = tailprobs(rvT0, obst, alt=alt)
    return pvalue

See Section 3.5 in the No Bullshit Guide to Statistics for the detailed explanation of these steps. You can also check out the section Analytical approximation methods in the notebook notebooks/35_comparing_two_populations.ipynb.

To perform the two-sample tt-test on the samples xs and ys, we call ttest_dmeans(xs, ys) or ttest_ind(xs, ys, equal_var=False), as we’ll show in the Examples section below.

Effect size estimates

import numpy as np
from scipy.stats import t as tdist
from ministats import calcdf

def ci_dmeans(xsample, ysample, alpha=0.1):
    """
    Compute confidence interval for the difference between population means.
    """
    stdX, n = np.std(xsample, ddof=1), len(xsample)
    stdY, m = np.std(ysample, ddof=1), len(ysample)
    dhat = np.mean(xsample) - np.mean(ysample)
    seD = np.sqrt(stdX**2/n + stdY**2/m)
    dfD = calcdf(stdX, n, stdY, m)
    t_l = tdist(df=dfD).ppf(alpha/2)
    t_u = tdist(df=dfD).ppf(1-alpha/2)
    return [dhat + t_l*seD, dhat + t_u*seD]

TODO: narrate code

To use the function ...

  1. The two-sample t-test is an improved version of the two-sample z-test

  2. The Welch’s two-sample t-test always a better option than the two-sample t-test with pooled variance

  3. An alternative approach for comparing two groups is to use the permutation test (TODO link to nb)

  4. Another alternative is to use the linear model (TODO: deep link to the everything is a LM)

Examples

Let’s start by generating some samples from the following three populations for use in the examples we present below.

A∼N(μA=104,σA=3),B∼N(μB=100,σB=5),andC∼N(μC=100,σC=5).A \sim \mathcal{N}(\mu_A= 104, \sigma_A=3), \qquad B \sim \mathcal{N}(\mu_B=100,\sigma_B=5), \quad \text{and} \quad C \sim \mathcal{N}(\mu_C=100,\sigma_C=5).

Note populations BB and CC are identical.

We’ll now generate a samples of size n=20n=20 from each of these populaitons and store them in a Pandas data frame called data.

Example 1: populations are different

We’ll now apply the Welch’s two-sample t-test to compare the samples from population A and B, which we know are different.

The sample means seem to reflect the difference between population means too...

(103.48610431567451, 98.67012442302976)

... but we must consider the difference between means relative to the standard deviations of the two samples.

(2.880085264317079, 4.840193546088457)

Here is a picture that shows the data points, the sample means, and the sample standard deviations for the two samples.

<Figure size 700x150 with 1 Axes>

Let’s now use the helper function ttest_dmeans to perform the two-sample t-test calculation and obtain the pp-value.

0.0005953205918276521

The pp-value we obtain is 0.000595, which is below the cutoff value α=0.05\alpha=0.05, so our conclusion is we reject the null hypothesis: the difference between the means of the two unknown populations is statistically significant.

Effect size

The confidence interval for the effect size Δ=μA−μB\Delta = \mu_A - \mu_B is

[2.6805293784320785, 6.951430406857417]

Example 2: two samples from the same population

Let’s now analyze the samples from populations B and C that come from the same distribution, which is the situation described by the null hypothesis.

(98.67012442302976, 99.86654813812206)
<Figure size 700x150 with 1 Axes>
0.4045691564236631

The pp-value we obtain is 0.40, which is above the cutoff value α=0.05\alpha=0.05 so our conclusion is that we’ve failed to reject the null hypothesis: the means of two samples are not significantly different.

Effect size

The confidence interval for the effect size Δ=μB−μC\Delta = \mu_B - \mu_C is

[-3.590421559991184, 1.1975741298065947]
(6349.143530270271, 4554.801126)
<Figure size 800x150 with 1 Axes>
TtestResult(statistic=2.674145513783345, pvalue=0.007892977714517335, df=307.1324931115885)

Effect size

0.27271540735846456

Example 4: analysis of a dataset that contains outliers

The dataset outliers2.csv consists of measurements of “soil score” variable using a specialized equipment for calculating soil quality.

The measurement apparatus has an upper limit beyound which it doesn’t function properly, so all observations that exceed this limit are recorded as the value 2500.

Loading...
<Figure size 700x200 with 1 Axes>

The effect of the outliers in group B is to pull the mean up, to a much higher value than where the other data points are.

0.028387263383781086

The outliers pull the mean in group B to a high value, which leads us to conclude there is a statistically significant difference between the two groups. This is because we’re using the tt-test when the normality assumption is not valid.

See the example notebook Mann-Whitney_U-test.ipynb for an analysis of this data using the nonparametric test for comparing two groups, which is more appropriate for data with outliers.

Discussion

  1. For more details about the Welch’s two-sample t-test, see the notebook notebooks/35_comparing_two_populations.ipynb, which contains a more detailed derivation.

  2. See the Section 3.5 exercises notebook: exercises/sec35_comparing_two_groups.ipynb for practice problems.