【问题标题】:How to randomly sample dataframe rows with unique column values如何随机采样具有唯一列值的数据框行
【发布时间】:2017-09-03 04:04:16
【问题描述】:

最终目标是比较来自avg_score 的简单统计量(分子/分母/true_count)的方差和标准偏差,对来自数据集的每个单词的增量随机样本进行 10 次试验,类似于:

library (data.table)
set.seed(1)
df <- data.frame(
  word_ID = c(rep(1,4),rep(2,3),rep(3,2),rep(4,5),rep(5,5),rep(6,3),rep(7,4),rep(8,4),rep(9,6),rep(10,4)),
  word = c(rep("cat",4), rep("house", 3), rep("sung",2), rep("door",5), rep("pretty", 5), rep("towel",3), rep("car",4), rep("island",4), rep("ran",6), rep("pizza", 4)), 
  true_count = c(rep(234,4),rep(39,3),rep(876,2),rep(4,5),rep(67,5),rep(81,3),rep(90,4),rep(43,4),rep(54,6),rep(53,4)),
  occurrences = c(rep(234,4),rep(34,3),rep(876,2),rep(4,5),rep(65,5),rep(81,3),rep(90,4),rep(43,4),rep(54,6),rep(51,4)),
  item_score = runif(40),
  avg_score = rnorm(40),
  line = c(71,234,71,34,25,32,573,3,673,899,904,2,4,55,55,1003,100,432,100,29,87,326,413,32,54,523,87,988,988,12,24,754,987,12,4276,987,93,65,45,49),
  validity = sample(c("T", "F"), 40, replace = T)

)
dt <- data.table(df)
dt[ , denominator := 1:.N, by=word_ID]
dt[ , numerator := 1:.N, by=c("word_ID", "validity")]
dt$numerator[df$validity=="F"] <- 0
df <- dt

<df
    word_ID  word  true_count occurrences item_score   avg_score line validity denominator numerator
 1:       1    cat        234         234 0.25497614  0.15268651   71        F           1         0
 2:       1    cat        234         234 0.18662407  1.77376261  234        F           2         0
 3:       1    cat        234         234 0.74554352 -0.64807093   71        T           3         1
 4:       1    cat        234         234 0.93296878 -0.19981748   34        T           4         2
 5:       2  house         39          34 0.49471189  0.68924373   25        F           1         0
 6:       2  house         39          34 0.64499368  0.03614551   32        T           2         1
 7:       2  house         39          34 0.17580259  1.94353631  573        F           3         0
 8:       3   sung        876         876 0.60299465  0.73721373    3        T           1         1
 9:       3   sung        876         876 0.88775767  2.32133393  673        F           2         0
10:       4   door          4           4 0.49020940  0.34890935  899        T           1         1
11:       4   door          4           4 0.01838357 -1.13391666  904        T           2         2

数据代表文档中每个单词的检测,因此一个单词可能会多次出现在同一行。任务是让样本大小表示唯一的列值 (line),但要返回行号相同的所有实例——这意味着返回的实际行数可能超过指定的样本大小。因此,对于“cat”的一个两词样本大小试验,所需结果的形式是:

    word_ID  word  true_count occurrences item_score   avg_score line validity denominator numerator
 1:       1    cat        234         234 0.25497614  0.15268651   71        F           1         0
 2:       1    cat        234         234 0.18662407  1.77376261  234        F           2         0
 3:       1    cat        234         234 0.74554352 -0.64807093   71        T           3         1

我的基本迭代(在这个网站上找到)目前看起来像:

for (i in 1:10) {

  a2[[i]] <- lapply(split(df, df$word_ID), function(x) x[sample(nrow(x), 2, replace = T), ])

  b3[[i]] <- lapply(split(df, df$word_ID), function(x) x[sample(nrow(x), 3, replace = T), ])}

}

所以,我可以做标准的随机样本大小,但我不确定(并且找不到类似的东西或没有找到正确的方法)如何实现上述目标。有没有直接的方法来解决这个问题?

谢谢,

【问题讨论】:

    标签: r random dataframe


    【解决方案1】:

    这是一个 data.table 解决方案,它在采样的 data.table 上使用连接。

    set.seed(1234)
    df[df[, .(line=sample(unique(line), 2)), by=word], on=.(word, line)]
    

    内部 data.table 由两列组成,word 和 line,每个 word 有两行,每行都有一个唯一的 line 值。 line 的值由sample 返回,它被输入 line 的唯一值,并为每个单词单独执行(使用by=word)。您可以通过将 2 更改为所需值来改变唯一行值的数量。此 data.table 连接到主 data.table 以选择所需的行。

    在这种情况下,你得到

        word_ID   word true_count occurrences item_score   avg_score line validity
     1:       1    cat        234         234 0.26550866  0.91897737   71        F
     2:       1    cat        234         234 0.57285336  0.07456498   71        T
     3:       1    cat        234         234 0.37212390  0.78213630  234        T
     4:       2  house         39          34 0.89838968 -0.05612874   32        T
     5:       2  house         39          34 0.94467527 -0.15579551  573        F
     6:       3   sung        876         876 0.62911404 -0.47815006  673        T
     7:       3   sung        876         876 0.66079779 -1.47075238    3        T
     8:       4   door          4           4 0.06178627  0.41794156  899        F
     9:       4   door          4           4 0.38410372 -0.05380504   55        F
    10:       5 pretty         67          65 0.71761851 -0.39428995  100        F
    11:       5 pretty         67          65 0.38003518  1.10002537  100        F
    12:       5 pretty         67          65 0.49769924 -0.41499456 1003        F
    13:       6  towel         81          81 0.21214252 -0.25336168  326        F
    14:       6  towel         81          81 0.93470523 -0.16452360   87        F
    15:       7    car         90          90 0.12555510  0.55666320   32        T
    16:       7    car         90          90 0.26722067 -0.68875569   54        F
    17:       8 island         43          43 0.01339033  0.36458196   87        T
    18:       8 island         43          43 0.38238796  0.76853292  988        F
    19:       8 island         43          43 0.86969085 -0.11234621  988        T
    20:       9    ran         54          54 0.59956583 -0.61202639  754        F
    21:       9    ran         54          54 0.82737332  1.43302370 4276        F
    22:      10  pizza         53          51 0.79423986 -0.36722148   93        F
    23:      10  pizza         53          51 0.41127443 -0.13505460   49        T
        word_ID   word true_count occurrences item_score   avg_score line validity
    

    【讨论】:

    • 47 秒 ...但由于我的是基础而你的是 data.table,所以我将保留我的答案作为补充。
    • 这些看起来非常不同的方法,基础答案越多越好。
    • 我一直想学习data.table。当我看到基础(我认识的)与类似的非基础解决方案配对时,我学得很好。所以对我来说,看到两者是完全相关的。谢谢:-)
    • 如果可以的话,我会接受两者,因为它们对我来说都非常有启发性(有很好的解释),尤其是串联起来。是否有可能使该方法保持这种紧凑性,但只为一个单词返回比样本大小更多的行如果有重复的line 值[不是全部提取]?意思是,采样直到有 n 行具有唯一 line 值的单词,同时保持已经提取的样本。这样做需要一个循环,对吗? [你们都回答了我最初的问题,这只是在看到解决方法后的后续]。谢谢
    【解决方案2】:

    如果您从去重的 data.frame 中进行采样,然后对原始数据进行左连接,则可以确保您需要什么。

    我不精通data.table,所以我将使用基本函数。 (dplyr 在这里也可以很好地工作,但由于您使用的是data.table,所以我暂时避免使用它。)(当我即将点击提交时,@lmo 提供了一个dt 特定的答案...)

    “去重”是指:

    subdf <- df[,c("word_ID", "line")]
    subdf <- subdf[!duplicated(subdf),]
    dim(subdf)
    # [1] 36  2
    head(subdf)
    #   word_ID line
    # 1       1   71
    # 2       1  234
    # 4       1   34
    # 5       2   25
    # 6       2   32
    # 7       2  573
    

    注意subdf只有三行1,而原始数据有4:

    df[1:4,]
    #   word_ID word true_count occurrences item_score   avg_score line validity
    # 1       1  cat        234         234  0.2655087  0.91897737   71        F
    # 2       1  cat        234         234  0.3721239  0.78213630  234        T
    # 3       1  cat        234         234  0.5728534  0.07456498   71        T
    # 4       1  cat        234         234  0.9082078 -1.98935170   34        T
    

    我这里用的是by而不是lapply/split,但是结果应该是一样的:

    out <- by(subdf, subdf$word_ID, function(x) merge(x[sample(nrow(x), 2, replace=TRUE),], df, by=c("word_ID", "line")))
    out[1]
    # $`1`
    #   word_ID line word true_count occurrences item_score   avg_score validity
    # 1       1   34  cat        234         234  0.9082078 -1.98935170        T
    # 2       1   71  cat        234         234  0.5728534  0.07456498        T
    # 3       1   71  cat        234         234  0.2655087  0.91897737        F
    

    【讨论】:

      猜你喜欢
      • 2012-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-06
      • 1970-01-01
      • 2018-03-10
      • 2021-08-07
      相关资源
      最近更新 更多