【问题标题】:data.table update join by groupdata.table 更新按组加入
【发布时间】:2020-05-16 00:42:24
【问题描述】:

我有一个特定的data.table 问题:有没有办法进行更新加入但按组?举个例子吧:

df1 <- data.table(ID = rep(letters[1:3],each = 3),x = c(runif(3,0,1),runif(3,1,2),runif(3,2,3)))
df2 <- data.table(ID = c(letters[1],letters[1:5]))

> df2
   ID
1:  a
2:  a
3:  b
4:  c
5:  d
6:  e

> df1
   ID         x
1:  a 0.9719153
2:  a 0.8897171
3:  a 0.7067390
4:  b 1.2122764
5:  b 1.7441528
6:  b 1.3389710
7:  c 2.8898255
8:  c 2.0388562
9:  c 2.3025064

我想做类似的事情

df2[df1,plouf := sample(i.x),on ="ID"]

但是对于每个ID 组,这意味着plouf 将是每个对应IDx 值的样本。上面这行代码不能这样工作,它对整个x 向量进行采样:

> df2

   ID     plouf
1:  a 1.3099715
2:  a 0.8540039
3:  b 2.0767138
4:  c 0.6530148
5:  d        NA
6:  e        NA

您看到plouf 的值不是df1ID 组对应的x。我希望plouf 的值在 a 的 0 和 1 之间,b 的值介于 1 和 2 之间,c 的值介于 2 和 3 之间。我想不更换样品。 我试过了:

df2[df1,plouf := as.numeric(sample(i.x,.N)),on ="ID",by = .EACHI]

这不起作用:

Error in sample.int(length(x), size, replace, prob) : 
  cannot take a sample larger than the population when 'replace = FALSE'

这另一种尝试似乎奏效了:

df2$plouf <- df2[df1,on ="ID"][,sample(x,df2[ID == ID2,.N]),by = .(ID2 = ID)]$V1

但我发现很难阅读或理解,对于多个分组变量可能会有问题,而且我不确定它是否非常有效。我确信有一个很好的简单方法来编写它,但我没有它。有什么想法吗?

【问题讨论】:

  • 另一个有趣的选择:df2[, plouf := sample(df1[.(.BY[[1L]]), x, on = .(ID)], .N), by = ID]
  • [[1L]] 位是什么意思?
  • @DavidArenburg 哇,不确定是否能正确理解。如果你有一点时间,我会使用更详细的解释。不过谢谢你的回答
  • 这几乎是您的工作想法,但使用了正确的 data.table 语法。对于df2 中的每个ID,我根据dt2ID 的行数从df1 中对相同的ID 进行采样。我相信这几乎是你想要做的。无论哪种方式,这只是为了提高您的尝试的乐趣。我认为 chinsoons 解决方案应该是更快、更惯用的方式。 .BY[[1L]] 部分,如果你徘徊,只是某个组中的 ID 值。例如,第一组是c(a, a),第二组是b,等等。我使用它是为了加入df1
  • @DavidArenburg 谢谢你的解释。我不知道.BY[[1L]],它可以帮助很多。乐趣很重要,我学到了一些有用的东西,非常完美。

标签: r join data.table


【解决方案1】:

另一种选择:

df1[df2[, .N, ID], on=.(ID), sample(x, N), by=.EACHI]

输出:

   ID        V1
1:  a 0.2655087
2:  a 0.3721239
3:  b 1.2016819
4:  c 2.6607978
5:  d        NA
6:  e        NA

数据:

library(data.table)
set.seed(0L)
df1 <- data.table(ID = rep(letters[1:3],each = 3),x = c(runif(3,0,1),runif(3,1,2),runif(3,2,3)))
df2 <- data.table(ID = c(letters[1],letters[1:5]))

解决评论:

library(data.table)
set.seed(0L)
df1 <- data.table(ID = rep(letters[1:3],each = 3),
    NAME = rep(LETTERS[1:3],each = 3),
    x = c(runif(3,0,1),runif(3,1,2),runif(3,2,3)))
df2 <- data.table(ID = c(letters[1],letters[1:5]),
    NAME = c(LETTERS[1],LETTERS[1:5]))

df2[, ri := rowid(ID, NAME)][
    df1[df2[, .N, .(ID, NAME)], on=.(ID, NAME), .(ri=1L:N, VAL=sample(x, N)), by=.EACHI],
    on=.(ri, ID, NAME), VAL := VAL]
df2

如果输入ID, NAME过于重复,可以使用

cols <- c("ID", "NAME")
df2[, ri := rowidv(.SD, cols)][
    df1[df2[, .N, cols], on=cols, .(ri=1L:N, VAL=sample(x, N)), by=.EACHI],
    on=c("ri", cols), VAL := VAL]
df2

【讨论】:

  • 此解决方案是否适用于多个分组变量?
  • 我实际上对这个解决方案有一个问题:我想让 V1 直接分配给 df2 中的一个变量,就像在更新连接期间一样。在这里,我需要以某种方式将输出分配/合并回 df2
  • 为了不让我误解你,你能举个例子吗?
【解决方案2】:

替换样本

你可以这样做:

df2[, plouf := df1[df2, on = .(ID),
                        sample(x, size = 1),
                        by=.EACHI]$V1]

您可以加入ID 变量,但您必须指定by=.EACHI,因为您要返回多个值。 $V1 告诉它返回结果的第一列。

结果:

   ID      sample
1:  a 0.042188292
2:  a 0.002502247
3:  b 1.145714600
4:  c 2.541768627
5:  d          NA
6:  e          NA

无需更换的样品

它不漂亮,但它有效:

df2$plouf = as.numeric(NA)

# create temporary table of number of sample required for each group
temp = df2[, .N, by = ID]

for(i in temp$ID){
  # create a temporary sample
  temp_sample = sample(df1[i==ID]$x, size = temp[ID==i]$n, replace = FALSE)

  # assign sample
  for(j in seq(1, length(temp_sample))){
    df2[ID==i][j]$plouf = temp_sample[j] 
  }
}

感谢@David Arenburg 的帮助

【讨论】:

  • 是但不,我想使用.N,因为组的大小可能会有所不同。我将编辑我的问题
  • 每组只能返回 1 个采样值。是否要从每个组的前N 行中抽样?
  • 可以通过dim(df2[ID==i.ID])引用N的大小,例如:df2[, sample := df1[df2, on = .(ID), sample(x, size = dim(df2[ID==i.ID])), by=.EACHI]$V1],但是每组不能返回超过1个值
  • 我想对该组的所有行进行采样。我想对df1的组a的3行进行采样以提取2个值并将其分配给df2的组a
  • 准确。我要不换。您的解决方案是更换
猜你喜欢
  • 2021-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-06
  • 2016-07-31
  • 1970-01-01
  • 2022-01-04
  • 1970-01-01
相关资源
最近更新 更多