如何消除数据表上的循环?

时间:2013-03-18 11:42:14

标签: r data.table

我有两个data.table,如下所示:

N = 10
A.DT <- data.table(a1 = c(rnorm(N,0,1)), a2 = NA))
B.DT <- data.table(b1 = c(rnorm(N,0,1)), b2 = 1:N)
setkey(A.DT,a1)    
setkey(B.DT,b1)

我尝试通过更改for循环将我之前的data.frame实现更改为data.table实现,如下所示:

for (i in 1:nrow(B.DT)) {
  for (j in nrow(A.DT):1) {
    if (B.DT[i,b2] <= N/2 
        && B.DT[i,b1] < A.DT[j,a1]) {
      A.DT[j,]$a2 <- B.DT[i,]$b1
      break
    }
  }
} 

我收到以下错误消息:

Error in `[<-.data.table`(`*tmp*`, j, a2, value = -0.391987468746123) : 
  object "a2" not found

我认为我访问data.table的方式并不完全正确。我是新手。我想有一种更快捷的方法,就是在两个数据表中上下循环。

我想知道上面显示的循环是否可以简化/矢量化。

编辑复制/粘贴的data.table数据:

# A.DT
    a1  a2
1   -1.4917779  NA
2   -1.0731161  NA
3   -0.7533091  NA
4   -0.3673273  NA
5   -0.159569   NA
6   -0.1551948  NA
7   -0.0430574  NA
8   0.1783496   NA
9   0.4276034   NA
10  1.0697412   NA

# B.DT
    b1  b2
1   0.64229018  1
2   1.00527902  2
3   0.24746294  3
4   -0.50288835 4
5   0.34447791  5
6   -0.22205129 6
7   0.60099079  7
8   -0.70242284 8
9   0.6298599   9
10  0.08917988  10

我期望的输出:

# OUTPUT
    a1  a2
1   -1.4917779  NA
2   -1.0731161  NA
3   -0.7533091  NA
4   -0.3673273  NA
5   -0.159569   NA
6   -0.1551948  NA
7   -0.0430574  NA
8   0.1783496   -0.50288835
9   0.4276034   0.24746294
10  1.0697412   0.64229018

算法向下移动一个表,并且对于每一行向上移动另一个表,检查一些条件并相应地修改值。更具体地说,它下降B.DT,并且B.DT中的每一行上升A.DT并且将a1分配给b1的第一个值,使得b1小于a1。在分配之前检查附加条件(在该示例中b2等于或小于5)。

0.64229018是B.DT中的第一个值,它被分配到A.DT的最后一个单位。 1.00527902是B.DT中的第二个值,但它未被分配,因为它大于A.DT中的所有其他值。 0.24746294是B.DT中的第三个值,它被分配给A.DT中的第二个最后一个单位。 -0.50288835是B.DT中的第四个值,它被分配给A.DT中的单元#8 0.34447791是B.DT中的第五个值,由于它太大而未被分配。

这当然是一个简化的问题(因此可能没有多大意义)。感谢您的时间和意见。

2 个答案:

答案 0 :(得分:1)

您的代码将会更改:

A.DT[j,]$a2 <- B.DT[i,]$b1

A.DT$a2[j,] <- B.DT[i,]$b1

至于更有效地使用data.table,我会把它留给那些比我更专业的人......

答案 1 :(得分:1)

创建data.table后,几乎不需要常规分配运算符<-,而是要使用:=,而 j位置的括号。 (避免<-的原因是<-创建了对象的副本,而:=则没有,因此效率很高)

因此,对您的代码进行首次修改将是:

 # FROM: A.DT[j,]$a2 <- B.DT[i,]$b1
 # TO: 
 A.DT[j, a2 := B.DT[i, b1] ]

现在,data.table(许多)最佳功能之一就是它的by参数,这有助于消除大量for循环和*ply调用。 在这种特定情况下,您可以按如下方式清理双循环:

set.seed(201)
A.DT <- data.table(a1 = rnorm(N,0,1), key="a1")  # no need to create a2 if it will be NA. If you do, make sure it is as.numeric(NA)
B.DT <- data.table(b1 = rnorm(N,0,1), b2 = 1:N, key="b2")

# Assign to a2 in A.DT
A.DT[            
      , a2 := B.DT[ b2 <= N/2 & b1 < a1] [1, b1]
      , by=a1
     ]


> A.DT
             a1         a2
 1: -2.30403431         NA
 2: -1.69658097         NA
 3: -1.28548252         NA
 4: -0.34454603 -0.6478531
 5: -0.07503189 -0.6478531
 6:  0.05593404 -0.6478531
 7:  0.18900414 -0.6478531
 8:  0.26693735  0.2238094
 9:  0.28606069  0.2238094
10:  0.32576373  0.2238094

key s。

上的两个旁注
  • 您可以在创建data.table的同时设置密钥,为您节省两行代码
  • data.table按其键排序。从您使用行位置确定分配的事实来判断,我猜你不会想要设置密钥。在上面的代码中,我将B.DT的键更改为`b2。