跳至内容

Kruskal-Wallis 检验:在不满足正态性的情况下比较多个组

Kruskal-Wallis 检验实用指南——它是什么、如何工作、何时优于 ANOVA,以及如何在 Python 和 R 中运行与解读。
更新 2026年5月4日  · 9分钟

当您的数据服从正态分布时,比较多个组并不难。问题在于,大多数真实世界的数据并非如此。

如果您默认使用 ANOVA,它会将您带向错误的结论,因为它假设数据服从正态分布。当数据不满足时——比如偏态分布或样本量很小——您需要换一种方法。

Kruskal-Wallis 检验正是这种不同的方法。它是 ANOVA 的非参数替代方案,基于秩而非原始值进行分析,因此不要求正态分布。

本文将介绍其概念、背后的数学原理、如何在 Python 和 R 中运行,以及如何解读结果。

什么是 Kruskal-Wallis 检验?

Kruskal-Wallis 检验是一种用于比较三个或以上独立组的非参数方法。它会将所有观测值转换为秩,并在组间比较这些秩,而不是直接处理原始数值。

您可以把它看作是Mann-Whitney U 检验的扩展,我也写过相关内容。

Mann-Whitney U 同样进行基于秩的比较,但只适用于两个组。Kruskal-Wallis 检验将其扩展到三个或以上,因此当您有多个组且无法使用 ANOVA 时,应当使用它。

因为它基于秩而非原始值进行工作,所以不假设数据服从任何特定分布。这也是它在真实世界数据中有用的原因,因为真实数据往往很难完美地服从某一种分布。

何时使用 Kruskal-Wallis 检验

当您遇到以下情况时,Kruskal-Wallis 检验非常合适:

  • 三个或以上独立组 需要进行比较
  • 有序或连续型数据,如李克特量表评分或测量数据
  • 非正态分布,例如偏态、离群值、样本量小,或任何 ANOVA 无法处理的情况
  • 小样本量,难以验证正态性

下面是一个简单的例子。

假设您想比较三个不同班级的考试成绩。分数呈偏态且样本较小,因此 ANOVA 并不合适。Kruskal-Wallis 检验不需要正态性,因此可在此使用。它将告诉您是否至少有一个班级与其他班级的成绩不同,而无需做出您的数据无法支撑的假设。

Kruskal-Wallis 与 ANOVA 的比较

两种检验都用于比较组别,但方法不同。

ANOVA 比较的是组别的均值,并假设数据近似正态且方差相等。当这些假设成立时,它是更好的选择——统计功效更高,结果也更易解读。

Kruskal-Wallis 检验基于秩比较组别的分布。它不关心正态性或方差齐性。这让它更灵活,但代价是一定程度上的统计功效损失。

下面是一张快速对比表:

ANOVA 与 Kruskal-Wallis 检验的比较

ANOVA 与 Kruskal-Wallis 检验的比较

如果您的数据服从正态分布,请使用 ANOVA。如果不服从——或无法验证其服从——就使用 Kruskal-Wallis。

Kruskal-Wallis 检验公式

Kruskal-Wallis 检验可归结为一个检验统计量 H。公式如下:

Kruskal-Wallis 公式

Kruskal-Wallis 公式

各组成部分解释如下:

  • N —— 所有组的观测值总数

  • k —— 组的数量

  • n_i —— 第 i 个组的观测值个数

  • R_i —— 第 i 个组的秩和

该公式衡量各组的秩和相对于“若所有组完全相同”这一期望的偏离程度。H 大表示组间存在差异,H 小表示差异不大。

得到 H 后,将其与自由度为 k - 1 的卡方分布比较以获得 p 值。

Kruskal-Wallis 检验的工作流程

执行 Kruskal-Wallis 检验需要四个步骤:

  1. 合并所有组: 将各组的所有观测值合并为一个数据集
  2. 为所有观测值排序并赋秩: 将合并后的数据从小到大排序并赋予秩。最小值赋秩 1,下一项赋秩 2,依此类推。若有并列值,则取它们本应占据秩位的平均值作为共享秩。
  3. 计算秩和: 将秩按原始组别拆分,对每个组的秩求和,即公式中的 R_i
  4. 计算检验统计量: 将秩和代入 H 公式中。如果各组相似,则它们的秩和会接近,H 会较小;如果某一组的秩系统性更高或更低,H 会变大

就是这样!

您可以看到,该检验并不关注具体数值,而是关注它们相对于整体的位置。

在 Python 中进行 Kruskal-Wallis 检验

Python 的 scipy 库提供了 Kruskal-Wallis 检验的内置函数,您无需手动实现公式。让我们看一个示例。

假设您在比较三个班级的考试成绩。以下是运行检验的方法:

from scipy import stats

# Exam scores
class_a = [78, 85, 90, 72, 88]
class_b = [65, 70, 68, 74, 60]
class_c = [88, 92, 95, 85, 91]

# Run the test
statistic, p_value = stats.kruskal(class_a, class_b, class_c)

print(f"H statistic: {statistic:.4f}")
print(f"P-value: {p_value:.4f}")

Python 输出

Python 输出

p 值低于 0.05,这意味着至少有一个班级的成绩与其他班级不同。请注意,该检验不会告诉您具体是哪一个——您需要进行事后检验,下一节会介绍。

在 R 中进行 Kruskal-Wallis 检验

与 Python 类似,R 也有该检验的内置函数。我们继续使用同样的考试成绩情境。

# Exam scores
class_a <- c(78, 85, 90, 72, 88)
class_b <- c(65, 70, 68, 74, 60)
class_c <- c(88, 92, 95, 85, 91)

# Combine
scores <- c(class_a, class_b, class_c)
groups <- factor(rep(c("A", "B", "C"), each = 5))

# Run the test
kruskal.test(scores ~ groups)

R 输出

R 输出

输出与我在 Python 中得到的相同——同样的 H 统计量和同样的 p 值。由于 p < 0.05,您将拒绝原假设,并得出至少有一组存在差异的结论。

如何解读 Kruskal-Wallis 结果

Kruskal-Wallis 检验的原假设是所有组具有相同的分布。p 值告诉您是否拒绝该假设。解读如下:

  • p < 0.05: 至少有一组与其他组不同,因此拒绝原假设
  • p >= 0.05: 没有充分证据表明组间存在差异,因此不拒绝原假设

0.05 是一种惯例。根据您的领域或分析的重要性,您可能会使用更严格的阈值(如 0.01)或更宽松的阈值(如 0.10)。

请记住,此检验不会告诉您具体哪个组不同。显著结果仅意味着各组并非完全相同。您知道存在差异,但不知道差异在哪里。要找出哪些成对组驱动了差异,您需要进行事后检验。

Kruskal-Wallis 之后的事后检验

该检验告诉您至少有一组不同,但不会指出究竟哪个组不同。若您有三个组且 p < 0.05,可能是 A 对 B、A 对 C、B 对 C,或它们的某种组合。您需要执行事后检验以获得这些两两比较。

Dunn 检验是最常见的选择。它对所有组进行两两比较,并调整 p 值以控制多重比较带来的影响——如果不做调整,误报的概率会被抬高。比较越多,单纯由偶然性导致“显著”结果的风险越高。

在 Python 中进行 Dunn 检验

您需要使用 scikit_posthocs 库。如果尚未安装,请运行 pip install scikit-posthocs 进行安装。

之后,计算很简单:

import scikit_posthocs as sp
import pandas as pd

# Same exam scores as before
class_a = [78, 85, 90, 72, 88]
class_b = [65, 70, 68, 74, 60]
class_c = [88, 92, 95, 85, 91]

# Combine
scores = class_a + class_b + class_c
groups = ["A"] * 5 + ["B"] * 5 + ["C"] * 5

df = pd.DataFrame({"score": scores, "group": groups})

# Run the test
result = sp.posthoc_dunn(df, val_col="score", group_col="group", p_adjust="bonferroni")
print(result)

Python 中的 Dunn 检验

Python 中的 Dunn 检验

每个单元格显示该对组别的调整后 p 值。此处,只有 B 与 C(p = 0.004)低于 0.05 阈值,因此这两个组存在差异。A 与 B(p = 0.167)以及 A 与 C(p = 0.607)不显著,意味着班级 A 与另两个班级在统计上均无显著差异。

在 R 中进行 Dunn 检验

首先,如有需要,请通过 install.packages("dunn.test") 命令安装该库:

library(dunn.test)

# Same exam scores as before
class_a <- c(78, 85, 90, 72, 88)
class_b <- c(65, 70, 68, 74, 60)
class_c <- c(88, 92, 95, 85, 91)

scores <- c(class_a, class_b, class_c)
groups <- factor(rep(c("A", "B", "C"), each = 5))

# Run the test
dunn.test(scores, groups, method = "bonferroni")

R 中的 Dunn 检验

R 中的 Dunn 检验

结果与 Python 相符,正如所预期。只有 B 与 C 显著,而 A 与 B、A 与 C 不显著。由 Kruskal-Wallis 检验检测到的差异,主要来自班级 B 与班级 C。

Kruskal-Wallis 检验的假设

Kruskal-Wallis 检验比 ANOVA 更灵活,但在运行前仍需检查三个假设:

  • 独立样本: 一个组中的观测不应影响另一个组的观测。如果数据是配对或重复测量,该检验并不适用
  • 有序或连续型数据: 该检验需要可排序的数据。名义型类别(如颜色或标签)无法排序,因此不适用
  • 分布形状相似: 若您希望将结果解读为中位数的比较(而不仅是分布比较),各组需要大致相同的分布形状。若形状差异很大,仍可比较分布,但无法做中位数层面的解释

若违反前两个假设,检验结果将不可靠。第三个假设相对宽松,它影响的是如何解读结果,而非是否可以运行检验。

何时不应使用 Kruskal-Wallis 检验

以下三种情况更适合使用其他检验:

  • 数据为配对或重复测量: 若同一受试者出现在多个组中,请使用 Friedman 检验。它是针对相关样本设计的非参数等价方法。将 Kruskal-Wallis 用于配对数据会忽略观测之间的关系,导致错误结论
  • 数据满足 ANOVA 的假设: 若数据近似正态且方差大致相等,ANOVA 更合适。它的统计功效更高,更有利于在真实存在差异时将其检测出来
  • 样本量很大: 当样本量很大时,即使数据不完全正态,参数方法通常也表现良好。中心极限定理会起作用,ANOVA 相较基于秩的方法会提供更可靠的结果。若每组有数百甚至上千观测,Kruskal-Wallis 并非您的首选

总结

当数据不满足 ANOVA 等检验所要求的正态分布时,Kruskal-Wallis 检验可用于比较三个或以上独立组。这得益于其基于秩而非原始值进行分析。

尽管如此,它并不是ANOVA的替代品。如果数据服从正态分布,ANOVA 更好,因为它具有更高的统计效力。另一方面,如果数据是配对的,请使用 Friedman 检验。归根结底,合适的检验取决于您的数据。

在条件合适时,Kruskal-Wallis 检验是可靠且直接的选择。您需要运行它、查看 p 值,并在需要定位差异来源时结合 Dunn 检验进行后续分析。

统计学知识有些生疏?参加我们的统计学入门课程,只需一个下午即可重回正轨。

Kruskal-Wallis 检验常见问答

Kruskal-Wallis 检验用于什么?

当您无法假设数据服从正态分布时,Kruskal-Wallis 检验用于比较三个或以上的独立组。它是 ANOVA 的非参数替代方案,基于秩而非原始值进行分析。在分布偏态或数据为有序型时,它尤其有用。

Kruskal-Wallis 检验结果显著意味着什么?

显著结果(通常 p < 0.05)意味着至少有一组与其他组不同。它不会告诉您哪些组不同,只能说明它们并非完全相同。要找出哪些成对组导致差异,您需要进行如 Dunn 检验等事后分析。

Kruskal-Wallis 检验有哪些假设?

该检验要求独立样本,即一个组中的观测不应影响另一个组的观测。您的数据需要是有序或连续型——也就是可以进行排序。如果您想把结果解读为中位数比较,各组还应具有相似的分布形状。

Kruskal-Wallis 检验与 Mann-Whitney U 检验有何区别?

Mann-Whitney U 检验用于比较两个独立组,Kruskal-Wallis 检验则将这一方法扩展到三个或以上组。两者都基于秩且不假设正态性。若您仅有两个组,选择 Mann-Whitney U;Kruskal-Wallis 是其多组等价方法。

Kruskal-Wallis 之后何时应使用 Dunn 检验?

当 Kruskal-Wallis 的结果显著且您需要知道具体哪些组对存在差异时,运行 Dunn 检验。它在所有组之间进行两两比较,并调整 p 值以降低误报概率。在 Python 中,可使用 scikit_posthocs.posthoc_dunn();在 R 中,可使用 dunn.test 包实现相同功能。

主题

与 DataCamp 一起学习

Courses

Introduction to Statistics in R

4小时
128.6K
Grow your statistical skills and learn how to collect, analyze, and draw accurate conclusions from data.
查看详情Right Arrow
开始课程
查看更多Right Arrow