创建一个跨多个变量平衡的子集

use*_*247 5 r subset

为了说明我的问题,一个虚拟示例:我有一个包含 16 行(这些代表试验)和 3 列(试验难度、标签 X 和标签 Y)的数据集。标签 X 是一个有 4 个水平(1-4)的因子,标签 Y 是一个有 2 个水平(“女性”、“男性”)的因子。例如:

        difficulty    X    Y
trial1   3.0           1    male
trial2   1.4           1    male
trial3   2.1           1    female
trial4   1.5           1    female
trial5   0.3           2    male
trial6   1.2           2    male
trial7   3.0           2    female
trial8   1.6           2    female
trial9   0.8           3    male
trial10  1.4           3    male
trial11  2.8           3    female
trial12  1.5           3    female
trial13  0.3           4    male
trial14  1.2           4    male
trial15  3.0           4    female
trial16  1.6           4    female
Run Code Online (Sandbox Code Playgroud)

我想从 16 次试验中创建 8 次试验的子集;应符合以下标准的子集:

  1. 在标签 X 的四个级别内有相同数量的试验
  2. 在标签 Y 的两个级别内有相同数量的试验(并且标签 X 的四个级别内标签 Y 的每个级别也应该有相同数量的试验)
  3. 试验难度变量(数字,范围从 0 到 3)应尽可能接近 1.5

对于我的示例,这个虚拟示例中的理想集合是:

        difficulty    X    Y
trial2   1.4           1    male
trial4   1.5           1    female
trial6   1.2           2    male
trial8   1.6           2    female
trial10  1.4           3    male
trial12  1.5           3    female
trial14  1.2           4    male
trial16  1.6           4    female
Run Code Online (Sandbox Code Playgroud)

该子集的每个 X 级别有 2 次试验,每个 X 级别的女性和男性数量相等,而所有试验的难度值都尽可能接近 1.5。

我的尝试是使用许多嵌套while和if循环,但我不确定如何同时检查两个变量(目前我循环直到 X 被满足,然后循环直到 Y 被满足,然后循环直到 X 是再次实现等)。这是正确的方法,还是有更明智的方法?

eip*_*i10 3

以下代码假设您的数据框名为dat. difficulty.scaled该代码添加一个等于与 1.5 的偏差的新变量difficulty,然后按 X 和 Y 的值对数据进行分组,然后选择每组中绝对值最接近difficulty.scaled0(即difficulty最接近 1.5)的观测值。

您可以调整函数probs的参数quantile来选择所需的每个子组的百分比。在本例中,我选择了每个子组中 50% 的行(即代表 和 的每个组合的 50% 行X)Y。

library(dplyr)  # Install the dplyr package if you don't already have it
dat2 = dat %.%
         mutate(difficulty.scaled=difficulty - 1.5) %.%
         group_by(X, Y) %.%
         filter(abs(difficulty.scaled) < quantile(abs(difficulty.scaled), .5))
Run Code Online (Sandbox Code Playgroud)

对于您在上面粘贴的数据(我已将试验编号转换为变量),输出如下:

     tnum difficulty X      Y difficulty.scaled
1  trial2        1.4 1   male              -0.1
2  trial4        1.5 1 female               0.0
3  trial6        1.2 2   male              -0.3
4  trial8        1.6 2 female               0.1
5 trial10        1.4 3   male              -0.1
6 trial12        1.5 3 female               0.0
7 trial14        1.2 4   male              -0.3
8 trial16        1.6 4 female               0.1
Run Code Online (Sandbox Code Playgroud)

您提供的数据对于X和的每个组合都有相同数量的观察值Y。如果您的实际数据在这些变量上不平衡,那么您可以选择特定的行数,而不是选择每个子组中行的百分比。下面的代码选择每个子组中n具有最小绝对值的行。difficulty.scaled这样,即使您的完整数据集不是平衡的,您的子集也将是平衡的(只要您的和n的每个组合至少有数据行)。XY

n=1
dat2 = dat %.%
         mutate(difficulty.scaled=difficulty - 1.5) %.%
         group_by(X, Y) %.%
         filter(rank(abs(difficulty.scaled), ties.method="first") <= n)
Run Code Online (Sandbox Code Playgroud)

ties.method="first"确保准确n返回行,即使有多个行具有相同的绝对值difficulty.scaled。

更新:如何将子集数据划分为训练集和测试集。

假设dat2是您的平衡子集,您可以将其分为训练子集和测试子集,如下所示:

# Note that you need to use %>% instead of %.%
train = dat2 %>%
  do(sample_n(., 10)) 
Run Code Online (Sandbox Code Playgroud)

这将为每个子组返回 10 个随机采样的行。只需将此值设置为您希望训练样本中每个子组的行数即可。请注意,您不需要按 X 和 Y 进行分组来创建训练样本。这是因为当您创建时dat2,dplyr添加了分组属性以dat2继续dplyr识别。去做str(dat2)看看这个。

do是一个通用函数,允许您从 内部对数据框执行任意操作dplyr。句点.是一种代表数据帧的“代词”(dat2在本例中)。这仅适用%>%于而不是%.%. (dplyr正在积极开发中,并且正在从 过渡%.%到%>%链接操作,因此最好%>%从现在开始使用。)

# The test set then includes all rows that are not part of train. 
# Since tnum has a unique value for each row, use tnum to select all rows that 
# are not part of train.
test = dat2[!(dat2$tnum %in% train$tnum), ]
Run Code Online (Sandbox Code Playgroud)