代码之家  ›  专栏  ›  技术社区  ›  Chris Ruehlemann

从R中不同大小的数据帧子层中绘制相同大小的样本[重复]

  •  0
  • Chris Ruehlemann  · 技术社区  · 7 年前

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

    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 回复  |  直到 7 年前
        1
  •  2
  •   Parfait    7 年前

    by 按帧分割数据 有必要的抽样。然后 rbind 所有dfs一起在循环外使用 do.call() .

    df_list <- by(df, df$position, function(sub) sub[sample(1:nrow(sub), 3),])
    
    final_df <- do.call(rbind, df_list)
    

    目前,您在每个迭代中索引整个(非子集)数据帧。此外,您正在使用 for 循环是内存密集型的,不建议使用。

    • 通过 tapply 本质上,将数据帧按因子拆分为子集,并将每个子集传递到定义的函数中。在这里
    • do.call 基本上是跨多个元素运行扩展调用的压缩版本,其中 rbind(df1, df2, df3) 相当于 do.call(rbind, list(df1, df2, df3)) 不是在循环中调用(避免了复杂对象(如迭代中的数据帧)的危险),而是 一旦 在循环之外。
        2
  •  0
  •   xsabatox    7 年前

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

    df_samples <- data.frame()
    df_samples <- rbind(df_samples, df[sample(1:nrow(df[df$position==i,]), 3),])
    
        3
  •  0
  •   akrun    7 年前

    我们可以使用 data.table 与一组 sample 行索引的 .I 并使用它来子集数据集。这将是非常有效的

    i1 <- setDT(df)[, sample(.I, 3), position]$V1
    df[i1]
    

    sample_n 从…起 tidyverse

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

    或者作为一个函数

    f1 <- function(data) {
         data as.data.table(data)
         i1 <- data[, sample(.I, 3), by = position]$V1
         data[i1]
        }