The Kolmogorov–Smirnov Test
Free University Berlin
Warning
These slides are intended to provide extra background to understand the GSEA algorithm presented in lecture #2 and do not intend to provide a full account of the KS test.
Suppose that we have an i.i.d. sample \(X_1,\ldots , X_n\) with some unknown distribution \(\mathbb{P}\) and we would like to test the hypothesis that \(\mathbb{P}\) is equal to a particular distribution \(\mathbb{P}_0\), i.e. decide between the following hypotheses:
\[ \begin{aligned} H_0 : \mathbb{P} &= \mathbb{P}_0 \\ H_1 : \mathbb{P} & \neq \mathbb{P}_0 \\ \end{aligned} \]
The KS test is a non-parametric1 method for comparing probability distributions and checking if two samples differ significantly.
The ECDF is the fraction of \(n\) ordered observations at or below \(x\):
\[F_n(x) = \frac{1}{n} \sum_{i=1}^{n} \mathbf{1}\{x_i \le x\}\]
It is a step function that jumps by \(1/n\) at each data point and rises from 0 to 1.
\[D_n = \sup_x \left| F_n(x) - F(x) \right|\]
\(D_n\) is the largest vertical gap between the ECDF and the hypothesized CDF.
The ECDF is a step function and \(F\) is continuous, so the largest gap occurs at a data point, either just before or just after a jump. We must check both:
\[D_n = \max(D_n^+,\, D_n^-)\]
Simulate 10,000 samples of size \(n = 30\) from \(N(0,1)\) and compute \(D_n\) for each:
The p-value is the fraction of this null distribution at least as large as the observed \(D_n\).
Under \(H_0\), \(D_n\) shrinks like \(1/\sqrt{n}\).1 The scaled quantity \(\sqrt{n}\, D_n\) has (approximately) the same distribution for every \(n\). This is the Kolmogorov distribution.
n= 30 95th pct of D_n = 0.241 95th pct of sqrt(n)*D_n = 1.322
n= 120 95th pct of D_n = 0.122 95th pct of sqrt(n)*D_n = 1.338
At \(\alpha = 0.05\), the asymptotic critical value is
\[D_{n,\,0.05} \approx \frac{1.36}{\sqrt{n}}\]
scipy.stats.kstest or R’s ks.test).Sample from \(N(0.5, 1)\) and test against \(N(0,1)\):
KstestResult(statistic=np.float64(0.4050996543021373), pvalue=np.float64(5.826477046905052e-05), statistic_location=np.float64(0.35427336756472355), statistic_sign=np.int8(-1))
Compare two samples directly with no hypothesized \(F\):
\[D_{n,m} = \sup_x \left| F_{1,n}(x) - F_{2,m}(x) \right|\]
KstestResult(statistic=np.float64(0.145), pvalue=np.float64(0.6860480075289528), statistic_location=np.float64(-0.06437262729863982), statistic_sign=np.int8(1))
The rejection threshold uses the effective sample size \(nm/(n+m)\), in place of \(n\).
scipy.stats.kstest or ks_2samp (R: ks.test).References: * NIST Engineering Statistics Handbook §1.3.5.16 MIT OCW 18.443, Lecture 14.