r-将数据框架拆分为验证性和探索性样本



我有一个非常大的数据帧(N=107251),我希望将其分成相对相等的两半(~53625)。然而,我希望进行拆分,使三个变量在两组中保持相等的比例(涉及性别、6级年龄类别和5级地区)。

我可以独立地(例如,通过prop.table(xtabs(~dat$Gender)))或组合地(例如通过prop.table(xtabs(~dat$Gender + dat$Region + dat$Age))生成变量的比例,但我不确定如何利用这些信息来实际进行采样。

样本数据集:

set.seed(42)
Gender <- sample(c("M", "F"), 1000, replace = TRUE)
Region <- sample(c("1","2","3","4","5"), 1000, replace = TRUE)
Age <- sample(c("1","2","3","4","5","6"), 1000, replace = TRUE)
X1 <- rnorm(1000)
dat <- data.frame(Gender, Region, Age, X1)

概率:

round(prop.table(xtabs(~dat$Gender)), 3)  # 48.5% Female; 51.5% Male
round(prop.table(xtabs(~dat$Age)), 3)     # 16.8, 18.2, ..., 16.0%
round(prop.table(xtabs(~dat$Region)), 3)  # 21.5%, 17.7, ..., 21.9%
# Multidimensional probabilities:
round(prop.table(xtabs(~dat$Gender + dat$Age + dat$Region)), 3)

这个虚拟例子的最终目标是两个数据帧,每个数据帧有大约500个观察结果(完全独立,两个数据框中都没有参与者),并且在性别/地区/年龄划分方面大致相等。在实际分析中,年龄和地区权重之间存在更大的差异,因此进行单一的随机对半分割是不合适的。在现实世界的应用程序中,我不确定是否需要使用每一个观察结果,或者更均匀地进行拆分是否更好。

我一直在阅读package:sampling的文档,但我不确定它的设计是否符合我的要求。

您可以查看我的stratified函数,您应该能够像这样使用它:

set.seed(1) ## just so you can reproduce this
## Take your first group
sample1 <- stratified(dat, c("Gender", "Region", "Age"), .5)
## Then select the remainder
sample2 <- dat[!rownames(dat) %in% rownames(sample1), ]
summary(sample1)
#  Gender  Region  Age          X1          
#  F:235   1:112   1:84   Min.   :-2.82847  
#  M:259   2: 90   2:78   1st Qu.:-0.69711  
#          3: 94   3:82   Median :-0.03200  
#          4: 97   4:80   Mean   :-0.01401  
#          5:101   5:90   3rd Qu.: 0.63844  
#                  6:80   Max.   : 2.90422
summary(sample2)
#  Gender  Region  Age          X1          
#  F:238   1:114   1:85   Min.   :-2.76808  
#  M:268   2: 92   2:81   1st Qu.:-0.55173  
#          3: 97   3:83   Median : 0.02559  
#          4: 99   4:83   Mean   : 0.05789  
#          5:104   5:91   3rd Qu.: 0.74102  
#                  6:83   Max.   : 3.58466 

比较以下内容,看看它们是否在你的预期范围内。

x1 <- round(prop.table(
  xtabs(~dat$Gender + dat$Age + dat$Region)), 3)
x2 <- round(prop.table(
  xtabs(~sample1$Gender + sample1$Age + sample1$Region)), 3)
x3 <- round(prop.table(
  xtabs(~sample2$Gender + sample2$Age + sample2$Region)), 3)

它应该能够很好地处理您所描述的大小的数据,但"data.table"版本正在开发中,有望提高效率。


更新:

stratified现在有了一个新的逻辑参数"bothSets",它允许您将两组样本都保留为list

set.seed(1)
Samples <- stratified(dat, c("Gender", "Region", "Age"), .5, bothSets = TRUE)
lapply(Samples, summary)
# $SET1
#  Gender  Region  Age          X1          
#  F:235   1:112   1:84   Min.   :-2.82847  
#  M:259   2: 90   2:78   1st Qu.:-0.69711  
#          3: 94   3:82   Median :-0.03200  
#          4: 97   4:80   Mean   :-0.01401  
#          5:101   5:90   3rd Qu.: 0.63844  
#                  6:80   Max.   : 2.90422  
#
# $SET2
#  Gender  Region  Age          X1          
#  F:238   1:114   1:85   Min.   :-2.76808  
#  M:268   2: 92   2:81   1st Qu.:-0.55173  
#          3: 97   3:83   Median : 0.02559  
#          4: 99   4:83   Mean   : 0.05789  
#          5:104   5:91   3rd Qu.: 0.74102  
#                  6:83   Max.   : 3.58466

下面的代码基本上基于组成员身份创建一个键,然后在每个组中循环,对一个集采样一半,对另一个集(大致)采样一半。如果你比较得到的概率,它们之间的差距在0.001以内。这样做的缺点是,由于奇数组成员编号的舍入处理方式,它偏向于为第二组制作更大的样本量。在这种情况下,第一个样本是488个观测值,第二个样本是512个。你可能会加入一些逻辑来解释这一点,甚至会更好。

编辑:添加了这个逻辑,它将其平均分配。

set.seed(42)
Gender <- sample(c("M", "F"), 1000, replace = TRUE)
Region <- sample(c("1","2","3","4","5"), 1000, replace = TRUE)
Age <- sample(c("1","2","3","4","5","6"), 1000, replace = TRUE)
X1 <- rnorm(1000)
dat <- data.frame(Gender, Region, Age, X1)
dat$group <- with(dat, paste(Gender, Region, Age))
groups <- unique(dat$group)
setA <- dat[NULL,]
setB <- dat[NULL,]
for (i in 1:length(groups)){
  temp <- dat[dat$group==groups[i],]
  if (nrow(setA) > nrow(setB)){
    tempA <- temp[1:floor(nrow(temp)/2),]
    tempB <- temp[(1+floor(nrow(temp)/2)):nrow(temp),]
  } else {
    tempA <- temp[1:ceiling(nrow(temp)/2),]
    tempB <- temp[(1+ceiling(nrow(temp)/2)):nrow(temp),] 
  }
  setA <- rbind(setA, tempA)
  setB <- rbind(setB, tempB)
}

最新更新