从R中数据帧大小不同的子层中提取大小相等的样本

时间:2018-11-16 18:48:56

标签: r for-loop sample

我有一个数据框,其中有多列,其中尤其包含单词及其在句子中的位置。对于某些职位,行比其他职位多。这是一个模拟示例:

df <- data.frame(
  word = sample(LETTERS, 100, replace = T),
  position = sample(1:5, 100, replace = T)
)
head(df)
  word position
1    K        1
2    R        5
3    J        2
4    Y        5
5    Z        5
6    U        4

很明显,“位置”的各个部分的大小不同:

table(df$position)
 1  2  3  4  5 
15 15 17 28 25

为使不同批次更易于比较,我想在一个数据帧内的变量“位置”上绘制大小相等的样本。从理论上讲,这可以按如下步骤完成:

df_pos1 <- df[df$position==1,]
df_pos1_sample <- df_pos1[sample(1:nrow(df_pos1), 3),]

df_pos2 <- df[df$position==2,]
df_pos2_sample <- df_pos2[sample(1:nrow(df_pos2), 3),]

df_pos3 <- df[df$position==3,]
df_pos3_sample <- df_pos3[sample(1:nrow(df_pos3), 3),]

df_pos4 <- df[df$position==4,]
df_pos4_sample <- df_pos4[sample(1:nrow(df_pos4), 3),]

df_pos5 <- df[df$position==5,]
df_pos5_sample <- df_pos5[sample(1:nrow(df_pos5), 3),]

依此类推,最终将单个样本合并到一个数据框中:

df_samples <- rbind(df_pos1_sample, df_pos2_sample, df_pos3_sample, df_pos4_sample, df_pos5_sample)

但是此过程麻烦且容易出错。一种更经济的解决方案可能是for循环。到目前为止,我已经尝试过此代码,但是返回的不是每个位置值的单个样本的组合,而是从“位置”的所有值中提取的单个样本:

df_samples <-c()
for(i in unique(df$position)){
   df_samples <- rbind(df[sample(1:nrow(df[df$position==i,]), 3),])
}
df_samples
   word position
13    D        2
2     R        5
12    G        3
4     Y        5
16    Z        3
11    S        3
6     U        4
14    J        3
9     O        5
1     K        1

此代码有什么问题,如何进行改进?

3 个答案:

答案 0 :(得分:2)

考虑by,并根据需要通过采样位置来分割数据帧。然后,rbind将所有df与do.call()一起放在循环之外。

df_list <- by(df, df$position, function(sub) sub[sample(1:nrow(sub), 3),])

final_df <- do.call(rbind, df_list)

当前,您在每次迭代中为整个(非子集)数据帧编制索引。另外,您正在rbind循环内使用for,这会占用大量内存,因此不建议使用。

具体地说,

  • bytapply的面向对象的包装器,实际上是按因子将数据帧拆分为子集,并将每个子集传递到定义的函数中。在这里, sub 只是子集变量的名称(可以命名为任何名称)。这里的结果是一个数据帧列表。
  • do.call本质上跨多个元素运行扩展调用的压缩版本,其中rbind(df1, df2, df3)等效于do.call(rbind, list(df1, df2, df3))。这里要注意的关键是rbind不是在循环内调用(避免在迭代内增长像数据框这样的复杂对象的危险),而是在循环外一次

答案 1 :(得分:0)

我们可以将data.table与行索引sample的{​​{1}}组成的组一起使用,并用它来对数据集进行子集化。这将非常有效

.I

或使用i1 <- setDT(df)[, sample(.I, 3), position]$V1 df[i1] 中的sample_n

tidyverse

或作为功能

library(tidyverse)
df %>% 
   group_by(position) %>% 
   sample_n(3)

答案 2 :(得分:0)

每次运行循环时,都会覆盖最后一个条目。试试:

uap5