在 data.table 中按组分配间隔/箱

Assign intervals / bins by group in data.table

我想按组应用一个函数,该函数根据该组中的值将观察所属的区间分配给一个新变量。我觉得下面的代码应该可以工作,但它似乎使用了整个数据集,而不是该组的最大值和最小值。我错过了什么?

#require(data.table)
#fake data
set.seed(12345)
df1 <- data.frame(id_f=c(rep("a a",100),rep("b b",100),rep("c c",100)), 
                   L=c(abs(rnorm(100,50,20)),abs(rnorm(100,90,20)),abs(rnorm(100,220,20))), 
                   w=abs(rnorm(300,6,3))) 
dt2 = as.data.table(df1)

#the offending data.table function
dt2[,"bins":= findInterval(L, c((max(L)-min(L))/10*c(1:9)),left.open=T)+1, by=id_f]

编辑:

在 "a a" 列中,在 "a a" 的范围内将有 10 个等距的 bin,并且将为每个原始观察值分配一个 bin 编号,因为真实数据有 6,000 个观察值每个容器中有多个成员。所以输出会是这样的:(为了简洁起见,这是一个三区间的例子)

id_f  L    w     bins
a a   1    1.0   1
a a   2    1.1   2
a a   3    5.0   3
b b   3    2.0   1
b b   6    3.5   2
b b   9    7.0   3
c c   10   1.0   1
c c   15   1.5   2
c c   20   6.0   3

我原以为我调用 findInterval 会完成此操作,但显然它是从全球数据集中获取 minmax,而不仅仅是来自组。我如何让它从组中获取 minmax,然后使用它来计算用于该组的间隔?

您将需要 运行 表格函数来演示该问题。 by-操作似乎是"working"

    > dt2[ , list(mn=min(L), mx=max(L) ), by=id_f]
   id_f         mn       mx
1:  a a   5.462025 104.2456
2:  b b  43.824476 138.4843
3:  c c 168.075002 276.5598
> dt2[ , table(id_f, bins)]
     bins
id_f    1   2   3   4   5   6   7   8   9  10
  a a   3   5  10  10  19  13  21  10   4   5
  b b   0   0   0   0   1   3  10   8  19  59
  c c   0   0   0   0   0   0   0   0   0 100

显然你的结果会有所不同,因为你没有使用 set.seed()

png(); par(mfrow=c(3,1)); tapply(df1$L, df1$id_f, hist); dev.off()

我认为通过指定我们想要的 breaks 的数量,在这里使用 cut 会容易得多

library(data.table)
setDT(dt2)[,"bins":= cut(L, breaks = 10, labels = 1:10), by=id_f]

dt2
#     id_f     L     w bins
#  1:  a a  71.5  2.96    8
#  2:  a a  49.5  3.63    5
#  3:  a a  49.3  6.90    5
#  4:  a a  19.7 10.92    2
#  5:  a a  65.8  9.25    7
# ---                      
#296:  c c 206.0  6.50    4
#297:  c c 224.8  4.04    6
#298:  c c 213.0 10.36    5
#299:  c c 227.4  3.58    6
#300:  c c 224.9  7.12    6

我们也可以在 dplyr 或基础 R 中做到这一点

library(dplyr)

dt2 %>%
  group_by(id_f) %>%
  mutate(bins = cut(L, breaks = 10, labels = 1:10))

with(dt2, ave(L, id_f, FUN = function(x) cut(x, breaks = 10, labels = 1:10)))