我有一个非常大的数据帧(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)
}