0

我有两个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 并将 b1 的第一个值分配给 a2,使得 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 中的第五个值,因为它太大了,所以没有分配。

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

4

2 回答 2

1

您的代码将运行更改:

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

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

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

于 2013-03-18T12:00:59.750 回答
1

一旦你创建了你的 data.table,就几乎不需要常规的赋值操作符<-,而是你想使用:=,它位于该位置的括号内j(避免的原因<-<-创建对象的副本,而:=没有,因此效率)

因此,对您的代码的第一次修改是:

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

现在,data.table's (许多) 最好的特性之一是它的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

s上的两个旁注key

  • 您可以在创建 data.table 的同时设置密钥,节省两行代码
  • data.table 按其键排序。从您使用行位置来确定分配的事实来看,我猜您不会想按自己的方式设置键。在上面的代码中,我将B.DT' 键更改为 `b2.
于 2013-03-21T03:05:20.473 回答