繁体   English   中英

R:在j子句中引用cut函数中的data.table字段

[英]R: Referencing data.table fields in cut function in j clause

基本上,我有以下2个data.table s:

dt - 包含值字段( y )和分组字段( x
bk - 包含4个“ 中断 ”字段( bn ),用于描述在dt找到的每个组x的区间[1,inf]的桶结构。 每个bn代表桶的最小值(包括)并延伸到下一个桶(例如: x = 1的4个桶是[1,3],[3,5],[5,10],[10, INF))。 请注意 ,桶结构不一定是唯一的。

> #4 groups (x), each with a bucket structure defined breaks (bn).
> bk<- data.table(x=c(1:4), b1=c(1,1,1,1), b2=c(3,3,4,4), b3=c(5,5,7,8), b4=c(10,10,10,10), key="x")
> bk
   x b1 b2 b3 b4
1: 1  1  3  5 10
2: 2  1  3  5 10
3: 3  1  4  7 10
4: 4  1  4  8 10
> dt<- data.table(x=rep(c(1:4),5), y=rep(c(1:10),2), key="x")
> dt
    x  y
 1: 1  1
 2: 1  5
 3: 1  9
 4: 1  3
 5: 1  7
 6: 2  2
 7: 2  6
 8: 2 10
 9: 2  4
10: 2  8
11: 3  3
12: 3  7
13: 3  1
14: 3  5
15: 3  9
16: 4  4
17: 4  8
18: 4  2
19: 4  6
20: 4 10

我的目标是在dt中添加一个字段b ,根据与组x对应的桶结构,指示记录所属的桶(1,2,3或4)。 请参阅下面的所需输出:

    x  y  b
 1: 1  1  1  #Buckets for x=1
 2: 1  5  3
 3: 1  9  3
 4: 1  3  2
 5: 1  7  3
 6: 2  2  1  #Buckets for x=2 (same as 1)
 7: 2  6  3
 8: 2 10  4
 9: 2  4  2
10: 2  8  3
11: 3  3  1  #Buckets for x=3
12: 3  7  3
13: 3  1  1
14: 3  5  2
15: 3  9  3
16: 4  4  2  #Buckets for x=4
17: 4  8  3
18: 4  2  1
19: 4  6  2
20: 4 10  4

我最初的想法是加入两个data.table并使用cut函数返回每个记录的桶号,但是我遇到了break参数的问题。 首次尝试如下:

> bkt[dt, .(x, y, b=cut(y, breaks=c(b1, b2, b3, b4, "inf"), include.lowest=TRUE, labels=c(1:4)))]
Error in cut.default(y, breaks = c(b1, b2, b3, b4, "inf"), include.lowest = TRUE,  : 
  'breaks' are not unique

如果我创建一个变量a来保存存储桶结构(例如,对于x = 1),以下工作正如我预期的那样:

> a<- c(1, 3, 5, 10, "inf")
> bkt[dt, .(x, y, b=cut(y, breaks=a, include.lowest=TRUE, labels=c(1:4)))]
    x  y b
 1: 1  1 1
 2: 1  5 2
 3: 1  9 3
 4: 1  3 1
 5: 1  7 3
 6: 2  2 1
 7: 2  6 3
 8: 2 10 3
 9: 2  4 2
10: 2  8 3
11: 3  3 1
12: 3  7 3
13: 3  1 1
14: 3  5 2
15: 3  9 3
16: 4  4 2
17: 4  8 3
18: 4  2 1
19: 4  6 3
20: 4 10 3

这仍然不是我的应用程序的实用解决方案,但我希望有人可以帮助我理解如何正确地将桶结构信息传递给breaks参数以获得类似的结果。 我已经尝试了clistunlistas.numeric函数的各种组合来传递正确的break参数,但没有运气。 任何帮助/见解将不胜感激。 谢谢!

完全披露,我是R的新手,这是我的第一篇文章,请温柔。

稍微更改连接语法:

dt[bk, v := 
  cut(y, breaks = c(b1, b2, b3, b4, Inf), include.lowest = TRUE, labels = 1:4)
, by=.EACHI]

    x  y v
 1: 1  1 1
 2: 1  5 2
 3: 1  9 3
 4: 1  3 1
 5: 1  7 3
 6: 2  2 1
 7: 2  6 3
 8: 2 10 3
 9: 2  4 2
10: 2  8 3
11: 3  3 1
12: 3  7 2
13: 3  1 1
14: 3  5 2
15: 3  9 3
16: 4  4 1
17: 4  8 2
18: 4  2 1
19: 4  6 2
20: 4 10 3

这些结果与OP中的期望输出不同,但我认为错误在于对cut参数的解释(我觉得令人困惑)。

这种方法非常不优雅,因为必须编写每个b1,...,b4; 并且随着添加更多断点,它将无法很好地扩展。 我同意@ NathanWerth关于重塑bk表的建议。 他的方法也给出了OP期望的结果,而没有摆弄cut论点。


旁注:正确的符号是Inf不是"inf"

您可以使用melt.data.tablebk数据集重构为更简单的形式:

bk_long <- melt.data.table(
  bk,
  id.vars = 'x',
  measure.vars = paste0('b', 1:4),
  value.name = 'y'
)
setkey(bk_long, x)
bk_long[, variable := NULL]
bk_long[, b := seq_len(.N), by = x]
bk_long
#     x  y b
#  1: 1  1 1
#  2: 1  3 2
#  3: 1  5 3
#  4: 1 10 4
#  5: 2  1 1
#  6: 2  3 2
#  7: 2  5 3
#  8: 2 10 4
#  9: 3  1 1
# 10: 3  4 2
# 11: 3  7 3
# 12: 3 10 4
# 13: 4  1 1
# 14: 4  4 2
# 15: 4  8 3
# 16: 4 10 4

然后做一个滚动加入,正如Frank建议的那样:

bk_long[dt, on = c('x', 'y'), roll = TRUE]
#     x  y b
#  1: 1  1 1
#  2: 1  5 3
#  3: 1  9 3
#  4: 1  3 2
#  5: 1  7 3
#  6: 2  2 1
#  7: 2  6 3
#  8: 2 10 4
#  9: 2  4 2
# 10: 2  8 3
# 11: 3  3 1
# 12: 3  7 3
# 13: 3  1 1
# 14: 3  5 2
# 15: 3  9 3
# 16: 4  4 2
# 17: 4  8 3
# 18: 4  2 1
# 19: 4  6 2
# 20: 4 10 4

经过多次尝试,我终于让findInterval工作了。

该方法类似于frank's,除了每组使用by而不是单个连接。 使用内置值.BY ,您可以迭代输入到findInterval的第二个参数(vec)的bk行。

dt[, b := findInterval(y, c(unlist(bk[.BY, b1:b4]), Inf), rightmost.closed=FALSE), by=x]

返回

dt
    x  y b
 1: 1  1 1
 2: 1  5 3
 3: 1  9 3
 4: 1  3 2
 5: 1  7 3
 6: 2  2 1
 7: 2  6 3
 8: 2 10 4
 9: 2  4 2
10: 2  8 3
11: 3  3 1
12: 3  7 3
13: 3  1 1
14: 3  5 2
15: 3  9 3
16: 4  4 2
17: 4  8 3
18: 4  2 1
19: 4  6 2
20: 4 10 4

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM