R data.table列名在函数中不起作用。

问题描述 投票:9回答:3

我试图在一个函数中使用data.table,我想知道为什么我的代码会失败。我有一个data.table,内容如下。

DT <- data.table(my_name=c("A","B","C","D","E","F"),my_id=c(2,2,3,3,4,4))
> DT
   my_name my_id
1:       A     2
2:       B     2
3:       C     3
4:       D     3
5:       E     4
6:       F     4

我试图用不同的 "my_id "值来创建所有的 "my_name "对,对DT来说是:

Var1 Var2    
A    C
A    D
A    E
A    F
B    C
B    D
B    E
B    F
C    E
C    F
D    E
D    F

我有一个函数来返回所有的 "my_name "对给定的 "my_id "值的对子,它的工作原理和预期一样。

get_pairs <- function(id1,id2,tdt) {
    return(expand.grid(tdt[my_id==id1,my_name],tdt[my_id==id2,my_name]))
}
> get_pairs(2,3,DT)
Var1 Var2
1    A    C
2    B    C
3    A    D
4    B    D

现在,我想对所有的id对执行这个函数,我尝试通过找到所有的id对,然后使用maply与get_pairs函数来实现。

> combn(unique(DT$my_id),2)
     [,1] [,2] [,3]
[1,]    2    2    3
[2,]    3    4    4
tid1 <- combn(unique(DT$my_id),2)[1,]
tid2 <- combn(unique(DT$my_id),2)[2,]
mapply(get_pairs, tid1, tid2, DT)
Error in expand.grid(tdt[my_id == id1, my_name], tdt[my_id == id2, my_name]) : 
  object 'my_id' not found

同样,如果我试着做同样的事情而不使用 mapply,它也能工作。

get_pairs3(tid1[1],tid2[1],DT)
Var1 Var2
1    A    C
2    B    C
3    A    D
4    B    D

为什么这个函数只有在mapply中使用时才会失败?我认为这与data.table名称的范围有关,但我不确定。

另外,有没有其他更有效的方法来完成这个任务?我有一个大的data.table,其中有第三个id "sample",我需要得到每个样本的所有这些对(例如,在DT[sample=="sample_id",]上操作)。我对 data.table 包很陌生,可能没有以最有效的方式使用它。

r data.table mapply
3个回答
3
投票

为什么这个函数只有在apply中使用时才会失败?我认为这与data.table名称的范围有关,但我不确定。

在这种情况下,函数失败的原因与范围无关。mapply 矢量化函数,它把每个参数的每个元素都传递给函数。所以,在你的例子中, data.table 元素是它的列,所以 mapply 正在通过列 my_name 而不是完整的 data.table.

如果你想通过完整的 data.tablemapply,你应该使用 MoreArgs 参数。那么你的函数就可以工作了。

res <- mapply(get_pairs, tid1, tid2, MoreArgs = list(tdt=DT), SIMPLIFY = FALSE)
do.call("rbind", res)
  Var1 Var2
1     A    C
2     B    C
3     A    D
4     B    D
5     A    E
6     B    E
7     A    F
8     B    F
9     C    E
10    D    E
11    C    F
12    D    F

4
投票

这个函数 debugonce() 在这些情况下是非常有用的。

debugonce(mapply)
mapply(get_pairs, tid1, tid2, DT)

# Hit enter twice
# from within BROWSER
debugonce(FUN)
# Hit enter twice
# you'll be inside your function, and then type DT
DT
# [1] "A" "B" "C" "D" "E" "F"
Q # (to quit debugging mode)

这是错的。基本上。mapply() 取每个输入参数的第一个元素并将其传递给你的函数。在本例中,你提供了一个 数据表,这也是 列表. 所以,它不是传递整个data.table,而是传递列表(列)的每个元素。

所以,你可以通过这样做来解决这个问题。

mapply(get_pairs, tid1, tid2, list(DT))

但是... mapply() 默认情况下简化了结果,因此你会得到一个 matrix 回。你得用 SIMPLIFY = FALSE.

mapply(get_pairs, tid1, tid2, list(DT), SIMPLIFY = FALSE)

或者干脆使用 Map:

Map(get_pairs, tid1, tid2, list(DT))

使用 rbindlist() 来绑定结果。

HTH


4
投票

枚举所有可能的配对

u_name    <- unique(DT$my_name)
all_pairs <- CJ(u_name,u_name)[V1 < V2]

枚举观察到的数据对

obs_pairs <- unique(
  DT[,{un <- unique(my_name); CJ(un,un)[V1 < V2]}, by=my_id][, !"my_id"]
)

取其差额

all_pairs[!J(obs_pairs)]

CJ 就像 expand.grid 除了它创建了一个以所有列作为键的data.table。一个data.table X 必须通过键控才能加入 X[J(Y)] 或不加入 X[!J(Y)] (比如最后一行)来工作。的 J 是可选的,但可以使我们在做加入时更加明显。


简化。 @CathG 指出,有一种更简单的方式来构造 obs_pairs 如果你总是对每个 "id "有两个排序的 "名字"(如在示例数据中):使用 as.list(un) 代替 CJ(un,un)[V1 < V2].

© www.soinside.com 2019 - 2024. All rights reserved.