首页 > 解决方案 > 如何根据r中的多个变量拆分数据?

问题描述

我想将我的数据分成几乎均匀大小的训练和测试集,以实现两个样本中变量“年龄”、“性别”和“扫描仪”的几乎相等(尽可能相等)的分布。变量“站点”的组应分开(例如训练集中的 S01、S03、S04、S10、.. 和测试集中的 S02、S05、S06、...)。我的示例数据如下所示:

set.seed(2)
data <- data.frame(sex = sample(c("f","m"), 6500, replace=TRUE, prob = c(0.52, 0.48)),
               scanner = sample(c("x", "Y"), 6500, replace = TRUE, prob = c(0.25, 0.75)),
               site = sample(c("S01", "S02", "S03", "S04", "S05", "S06", "S07", "S08", "S09", "S10", "S11", "S12", "S13", "S14",
                               "S15", "S16", "S17","S18", "S19"), 6500, replace = TRUE, prob = c(.04, .07, .05, .04, .07, .04, .03,
                                                                                                 .05, .07, .04, .07, .07, .04, .12,
                                                                                                 .04, .08, .04, .02, .02)),
               age = rnorm(6500, mean = 117.5, sd = 8.5))

有谁知道如何做到这一点?很感谢任何形式的帮助!

标签: rdataframesplit

解决方案


minDiff包中的create_groups函数可能是您正在寻找的。

描述

用于将一组项目分配给 N 个组。就特定标准而言,组之间的差异被最小化(例如:最小化学校班级之间平均考试成绩的差异)。

对于这个问题,site变量是固定的——值必须在训练数据集和测试数据集之间分开。因此,聚合其他标准site,然后在函数中使用此聚合数据集,其中用于最小化差异的标准是平均年龄和 sd 年龄,以及男性(或女性)和扫描仪类型“Y”(或“ X”)。

install.packages("remotes")
remotes::install_github("m-Py/minDiff")

library(minDiff)
library(dplyr)

Sites <- data %>%
  group_by(site) %>%
  summarise(n=n(),
            sex_m=sum(sex=="m")/n(),
            scanner_Y=sum(scanner=="Y")/n(),
            age_mu=mean(age),
            age_sd=sd(age)) %>% 
            as.data.frame()   # `create_groups` only accepts pure data frames, tbls aren't allowed.

现在基于此聚合数据框创建 2 个组。

Sites <- create_groups(Sites, 
                       criteria_scale=names(Sites)[-1],
                       sets_n=2, 
                       equalize=list(mean, sd), 
                       exact=TRUE,  # gives "optimum" result.
                                    # FALSE is quicker but tries only 100 samples at random
                       talk=TRUE)   # show the progress as it takes a while...

结果(大约 2 分钟后)是一个数据框,其中包含一个名为 的新变量newSet

head(Sites)
#   site   n     sex_m scanner_Y   age_mu   age_sd newSet
#1   S01 244 0.4959016 0.7377049 118.3024 8.292912      2
#2   S02 466 0.5107296 0.7575107 117.1656 8.491649      2
#3   S03 354 0.4548023 0.7175141 117.6626 8.095703      1
#4   S04 263 0.5247148 0.7870722 118.2087 8.475679      1
#5   S05 438 0.5319635 0.7488584 117.6617 8.826479      1
#6   S06 249 0.5180723 0.7510040 117.0743 8.090794      2

我们可以检查两组之间的变量分布:

aggregate(cbind(n,sex_m, scanner_Y, age_mu, age_sd)~newSet, FUN=sum, data=Sites)
#  newSet    n    sex_m scanner_Y   age_mu   age_sd
#1      1 3450 4.826725  7.599581 1174.639 84.71687
#2      2 3050 4.267651  6.828945 1057.254 76.60529

还不错。现在将此数据与原始数据合并。

dataSet <- merge(data, subset(Sites, select=c("site","newSet")))

我们现在可以检查整个数据中变量的分布。

dataSet %>%
  group_by(newSet) %>%
  summarise(n=n(),
            sex_m=sum(sex=="m")/n(),
            scanner_Y=sum(scanner=="Y")/n(),
            age_mu=mean(age),
            age_sd=sd(age))
# A tibble: 2 x 6
  newSet     n sex_m scanner_Y age_mu age_sd
   <int> <int> <dbl>     <dbl>  <dbl>  <dbl>
1      1  3450 0.486     0.757   118.   8.42
2      2  3050 0.482     0.759   118.   8.49

似乎有道理。样本量差别不大,但男性和“Y”型扫描仪类型的比例非常接近,年龄分布也是如此。

然后,您可以创建训练和测试数据集。

train <- filter(dataSet, newSet==1)
test <- filter(dataSet, newSet==2)

推荐阅读