【问题标题】:Sampling by group without repetition using data.table使用 data.table 按组抽样而不重复
【发布时间】:2018-08-07 05:31:56
【问题描述】:

我将使用一个假设场景来说明这个问题。这是一张放着音乐家和他们演奏的乐器的桌子,还有一张放着乐队作曲的桌子:

musicians <- data.table(
  instrument = rep(c('bass','drums','guitar'), each = 4),
  musician = c('Chas','John','Paul','Stuart','Andy','Paul','Peter','Ringo','George','John','Paul','Ringo')
)

band.comp <- data.table(
  instrument = c('bass','drums','guitar'),
  n = c(2,1,2)
)

为了避免争论谁最擅长哪种乐器,乐队将通过抽签方式组建。这是我的做法:

musicians[band.comp, on = 'instrument'][, sample(musician, n), by = instrument]

   instrument     V1
1:       bass   Paul
2:       bass   Chas
3:      drums   Andy
4:     guitar   Paul
5:     guitar George

问题是:由于有演奏不止一种乐器的音乐家,可能会发生一个人被画不止一次的情况。

可以构建一个 for 循环,为每个后续的乐器子集绘制音乐家,然后从表格的其余部分中消除这些音乐家。但我想要关于如何使用 data.table 来做到这一点的建议。主要是因为我在现实生活中需要用这种逻辑解决的问题涉及具有数十万行的数据库。也因为我试图更好地理解 data.table 语法。

作为参考,我尝试了一些tips from Andrew Brooks blog,但无法提出解决方案。

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    这可以是一个解决方案,首先您按音乐家选择乐器,然后选择样本的音乐家。但是,当为每个音乐家选择乐器时,您的样本量可能大于总体,那么您可能会遇到错误(但在您的真实数据中,这可能不是问题)。

    musicians[, .(instrument = sample(instrument, 1)), by = musician][band.comp, on = 'instrument'][, sample(musician, n), by = instrument]
    

    【讨论】:

    • 对反对票感到困惑。这似乎是一个很好的解决方案。
    • 这很聪明,我没有想到要遵循这条路线。事实上,对于大数据集,样本量应该不是问题,除非有很多类别和大量重复。例如,当 Netflix 不断在不同类别中推荐同一部电影时。
    • 对不起。当时投票太快了,因为它在一段时间内不起作用。回想起来,这是一个很好的解决方案。一直想反转,但没有编辑就被锁定了。已用空格进行了编辑,并改为反转了反对票和赞成票。诚挚的歉意
    【解决方案2】:

    您可以将波段组合扩展到 sum(band.comp$n) 不同的位置并继续采样,直到找到可行的组合:

    roles = musicians[, 
      CJ(posn = 1:band.comp[.BY, on=.(instrument), x.n], musician = musician)
    , by=instrument]
    
    set.seed(1)
    while (TRUE){
      roles[sample(1:.N), keep := !duplicated(.SD, by="musician") & !duplicated(.SD, by=c("instrument", "posn"))][]
      if (sum(roles$keep) == sum(band.comp$n)) break
    }
    
    setorder(roles[keep == TRUE, !"keep"])[]
    
       instrument posn musician
    1:       bass    1   Stuart
    2:       bass    2     John
    3:      drums    1     Andy
    4:     guitar    1   George
    5:     guitar    2     Paul
    

    您可能可以使用线性规划或二分图来回答是否存在可行组合的问题,但就可行组合的分布而言,“抽样”甚至意味着什么还不清楚。

    【讨论】:

    • 这很好。它避免了从整个数据集中消除个体的方法中的偏见问题,并且比带有子集的循环快得多。但这个想法是完全避免循环并只使用 data.table 语法,因为我正在处理从 SQL 数据库中获取的大量集合。​​
    • @Carlos 只要roles(R 中的表,因为 data.table 不支持查询与数据库连接)可以放入内存,我想这不是问题。在循环内部,它只是暂时混淆表的行(在 R 中),而不是重新创建它。但是,如果 band.comp$n 中的值足够大,则此方法在构建 roles 时可能会失败。
    • @Frank 你说得对,我的推理思路是错误的。我想避免将数据相乘,所以我正在考虑一些涉及排除行的解决方案。 roles 表在某些情况下确实可能对内存来说太大了,但我只需要读取 id 和 category 列,绘制,然后使用生成的 id 向量来获取完整的数据。感谢您的见解。
    【解决方案3】:

    遇到了一个相关的帖子:Randomly draw rows from dataframe based on unique values and column values,eddi 的回答非常适合这个 OP:

    #keep number of musicians per instrument in 1 data.table
    musicians[band.comp, n:=n, on=.(instrument)]
    
    #for storing the musician that has been sampled so far
    m <- c()
    
    musicians[, {
        #exclude sampled musician before sampling
        res <- .SD[!musician %chin% m][sample(.N, n[1L])]
        m <- c(m, res$musician)
        res
    }, by=.(instrument)]
    

    样本输出:

       instrument musician n
    1:       bass   Stuart 2
    2:       bass     Chas 2
    3:      drums     Paul 1
    4:     guitar     John 2
    5:     guitar    Ringo 2
    

    或者更简洁的错误处理:

    m <- c()
    musicians[
        band.comp, 
        on=.(instrument), 
        j={
            s <- setdiff(musician, m)
            if (length(s) < n) stop(paste("Not enough musicians playing", .BY))
            res <- sample(s, n)    
            m <- c(m, res)
            res
        }, 
        by=.EACHI]
    

    【讨论】:

    • 老实说,eddi 值得称赞。请在另一个 OP 而不是这个上投票他的回答
    猜你喜欢
    • 2023-02-22
    • 1970-01-01
    • 1970-01-01
    • 2021-12-08
    • 1970-01-01
    • 1970-01-01
    • 2020-02-12
    • 2019-12-28
    相关资源
    最近更新 更多