You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何按多变量比例将样本分为两组?以mtcars数据集为例

问题:如何按多变量比例代表性拆分样本?

我希望将一个样本分成两组,确保两组中2个或更多变量的比例与原样本的比例具有代表性。以mtcars数据集为例,以下是该数据框中最后3个变量的比例:

> data(mtcars)
> round(prop.table(table(mtcars$carb)),2)

   1    2    3    4    6    8 
0.22 0.31 0.09 0.31 0.03 0.03 
> round(prop.table(table(mtcars$gear)),2)

   3    4    5 
0.47 0.38 0.16 
> round(prop.table(table(mtcars$am)),2)

   0    1 
0.59 0.41 

在这个例子中,我希望将样本分成两组,使得am变量接近60/40的拆分比例,同时另外两个变量的拆分比例与原数据集的比例相近。

我目前了解的最接近的方法是匹配抽样,比如处理研究中的方法,但该方法是基于某个处理变量预先定义两组,仅需匹配控制单元与处理单元,使1个或多个协变量的比例在两组间相似。而当前需求有所不同,我认为应该有类似方法,但无法理清思路。是否存在高效的实现方法?或者我应该换一种思路来解决这个问题?


解决方案

1. 分层抽样(首选方案)

核心逻辑是基于多个变量的交叉组合创建分层单元,再在每个分层内按目标比例拆分样本,能最大程度保证各组的变量分布与原样本一致。

用R的dplyr包实现:

library(dplyr)

set.seed(123) # 固定随机种子,保证结果可重复
split_result <- mtcars %>%
  # 按am、gear、carb的交叉组合分层
  group_by(am, gear, carb) %>%
  # 每层内按60/40比例分配到两组
  mutate(group = sample(c("组1", "组2"), 
                        size = n(), 
                        replace = TRUE, 
                        prob = c(0.6, 0.4))) %>%
  ungroup()

# 验证分组比例
# 查看am的分组比例
split_result %>% count(am, group) %>% group_by(group) %>% mutate(占比 = n/sum(n))
# 查看gear的分组比例
split_result %>% count(gear, group) %>% group_by(group) %>% mutate(占比 = n/sum(n))
# 查看carb的分组比例
split_result %>% count(carb, group) %>% group_by(group) %>% mutate(占比 = n/sum(n))

如果遇到部分分层单元样本量极小(比如carb=6或8仅1个样本),可以合并相似小分层,或者直接将小样本强制分配到某一组,后续再整体校验比例偏差。

2. 平衡抽样(适配复杂变量场景)

如果变量类别多、交叉组合过多导致分层单元碎片化,平衡抽样可以在不严格分层的前提下,让多个变量的边际分布在两组中尽可能接近原样本。

用R的balancedSampling包实现:

library(balancedSampling)

set.seed(123)
# 将分类变量转换为哑变量矩阵,用于定义平衡目标
X <- model.matrix(~ factor(am) + factor(gear) + factor(carb) - 1, data = mtcars)
# 计算组1的目标样本量(总样本的60%)
n_group1 <- round(nrow(mtcars) * 0.6)
# 执行平衡抽样
sample_indices <- balancedcube(X, n_group1)

# 拆分两组
组1 <- mtcars[sample_indices, ]
组2 <- mtcars[-sample_indices, ]

# 验证比例
round(prop.table(table(组1$am)), 2)
round(prop.table(table(组2$am)), 2)

3. 遗传算法优化抽样(高精度需求场景)

如果对比例一致性要求极高,可以用遗传算法寻找最优拆分方案,最小化各组变量分布与原样本的差异。

用R的GA包实现:

library(GA)

set.seed(123)
# 定义适应度函数:计算分组后与原样本的比例差异之和,差异越小适应度越高
fitness_func <- function(x) {
  # x是0/1向量,1代表分到组1,0代表组2
  group1 <- mtcars[x == 1, ]
  # 计算am的比例差异
  diff_am <- abs(prop.table(table(group1$am))[1] - 0.59) + abs(prop.table(table(group1$am))[2] - 0.41)
  # 计算gear的比例差异
  diff_gear <- sum(abs(prop.table(table(group1$gear)) - c(0.47, 0.38, 0.16)))
  # 计算carb的比例差异
  diff_carb <- sum(abs(prop.table(table(group1$carb)) - c(0.22, 0.31, 0.09, 0.31, 0.03, 0.03)))
  # 返回负的差异值,因为GA默认最大化适应度
  return(-(diff_am + diff_gear + diff_carb))
}

# 运行遗传算法,约束组1样本量为总样本的60%
ga_result <- ga(type = "binary", 
                fitness = fitness_func, 
                nBits = nrow(mtcars), 
                popSize = 50, 
                maxiter = 100,
                constraints = list(ineqFun = function(x) sum(x) - n_group1, 
                                   ineqType = "eq"))

# 获取最优分组
best_split <- ga_result@solution[1, ]
组1 <- mtcars[best_split == 1, ]
组2 <- mtcars[best_split == 0, ]

这种方法精度最高,但计算成本也更高,适合样本量不大的场景。


内容的提问来源于stack exchange,提问作者Jon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 13:54:56