如何测量数据量不均的两个CDF间水平距离并匹配KS统计量
问题描述
我有两个数据集,一个含261个数据点,另一个含373个数据点,数据生成代码如下:
dataset_1 = data.frame(dataset_name = rep("dataset_1", 261), value = seq(40, 10000, length.out = 261)) dataset_2 = data.frame(dataset_name = rep("dataset_2", 373), value = seq(50, 5000, length.out = 373)) dataset <- rbind(dataset_1, dataset_2)
我已经执行了KS检验:
ks.test(dataset$value[dataset$dataset_name=="dataset_1"], dataset$value[dataset$dataset_name=="dataset_2"], alternative = c("less")) -> test_result
并绘制了ECDF曲线:
library(ggplot2) dataset %>% ggplot(aes(x= value, group = dataset_name, color = dataset_name)) + stat_ecdf(size =2)

现在我需要测量各概率点处的水平距离,比如在概率0.25时,dataset_1对应值为2500,dataset_2对应值为1250,距离为1250。但两个数据集数据点数量不同,请问如何生成展示这些距离的DataFrame?
我尝试过用线性插值把dataset_1扩展为373个数据点来验证:
interpolated_dataset_1 <- approx(dataset_1$value, n = 373) # 创建数据框 interpolated_dataset_1_dataframe <- data.frame(dataset_name = "modified_dataset_1", value = interpolated_dataset_1$y) # 合并数据 modified_dataset <- rbind(dataset,interpolated_dataset_1_dataframe) # KS检验 ks.test(modified_dataset$value[modified_dataset$dataset_name== "modified_dataset_1"], modified_dataset$value[modified_dataset$dataset_name=="dataset_2"], alternative = c("less")) -> modified_test_result # 绘制ECDF library(ggplot2) modified_dataset %>% ggplot(aes(x= value, group = dataset_name, color = dataset_name)) + stat_ecdf(size =2)
但此时得到的D统计量和原检验接近但不完全一致,尽管结果显著。请问是否存在使用阶梯函数的更佳方法,能得到和原KS检验完全一致的统计量?
解决方案
要准确计算ECDF阶梯函数上各概率点的水平距离,不需要插值扩展数据集,直接利用ECDF的逆函数(分位数函数)即可,这样能完全匹配KS检验的阶梯逻辑,得到一致的统计量。
步骤1:定义ECDF与逆ECDF函数
ECDF函数返回给定x值对应的累积概率,逆ECDF(分位数函数)返回给定概率对应的x值,完全贴合阶梯型ECDF的特性:
# 提取两个数据集的数值向量 vals1 <- dataset_1$value vals2 <- dataset_2$value # 创建ECDF函数 ecdf1 <- ecdf(vals1) ecdf2 <- ecdf(vals2) # 创建逆ECDF(分位数)函数,type=1对应左连续阶梯特性,与KS检验逻辑完全一致 quantile1 <- function(p) quantile(vals1, p, type = 1) quantile2 <- function(p) quantile(vals2, p, type = 1)
步骤2:生成覆盖所有阶梯的概率点
选择两个ECDF所有独特的阶梯概率点,确保覆盖所有可能的差异位置:
# 获取两个数据集的阶梯概率点 probs1 <- unique(ecdf1(vals1)) probs2 <- unique(ecdf2(vals2)) all_probs <- sort(unique(c(probs1, probs2))) # 合并去重并排序
步骤3:计算概率点对应的距离
用逆ECDF函数计算每个概率的x值,再求水平距离:
# 构建结果DataFrame distance_df <- data.frame( probability = all_probs, dataset_1_value = sapply(all_probs, quantile1), dataset_2_value = sapply(all_probs, quantile2), horizontal_distance = sapply(all_probs, quantile1) - sapply(all_probs, quantile2) )
验证结果一致性
这个方法完全遵循KS检验的阶梯逻辑,计算出的最大水平距离会和原检验的D统计量完全一致:
# 检查最大距离是否等于KS检验的D统计量 max(distance_df$horizontal_distance) == test_result$statistic
可选:自定义概率点计算
如果不需要覆盖所有阶梯点,也可以指定自定义概率(如0.25、0.5等):
custom_probs <- seq(0, 1, by = 0.05) # 0到1,步长0.05 custom_distance_df <- data.frame( probability = custom_probs, dataset_1_value = sapply(custom_probs, quantile1), dataset_2_value = sapply(custom_probs, quantile2), horizontal_distance = sapply(custom_probs, quantile1) - sapply(custom_probs, quantile2) )
插值方法偏差的原因
线性插值是对数据点做连续插值,改变了原数据的阶梯分布特性,因此KS检验统计量会出现偏差。而直接使用type=1的分位数函数,完全匹配KS检验的左连续阶梯逻辑,能保证结果一致。
内容的提问来源于stack exchange,提问作者mra343
相关产品推荐
相关产品推荐

