为了说明我的问题,一个虚拟示例:我有一个包含 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 次试验的子集;应符合以下标准的子集:
对于我的示例,这个虚拟示例中的理想集合是:
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 是再次实现等)。这是正确的方法,还是有更明智的方法?
以下代码假设您的数据框名为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)