在 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
会完成此操作,但显然它是从全球数据集中获取 min
和 max
,而不仅仅是来自组。我如何让它从组中获取 min
和 max
,然后使用它来计算用于该组的间隔?
您将需要 运行 表格函数来演示该问题。 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)))
我想按组应用一个函数,该函数根据该组中的值将观察所属的区间分配给一个新变量。我觉得下面的代码应该可以工作,但它似乎使用了整个数据集,而不是该组的最大值和最小值。我错过了什么?
#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
会完成此操作,但显然它是从全球数据集中获取 min
和 max
,而不仅仅是来自组。我如何让它从组中获取 min
和 max
,然后使用它来计算用于该组的间隔?
您将需要 运行 表格函数来演示该问题。 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)))