data.table join和j-expression意外行为

时间:2013-04-18 21:19:39

标签: r data.table

R 2.15.0data.table 1.8.9

d = data.table(a = 1:5, value = 2:6, key = "a")

d[J(3), value]
#   a value
#   3     4

d[J(3)][, value]
#   4

我希望两者产生相同的输出(第二个),我相信它们应该

为了清除这不是J语法问题,同样的期望适用于以下(与上述相同)表达式:

t = data.table(a = 3, key = "a")
d[t, value]
d[t][, value]

我希望以上两种方法都能返回完全相同的输出。

让我重新解释一下这个问题 - 为什么(data.table设计的那样)列会自动打印在d[t, value]?< / p>

更新(基于下面的答案和评论):感谢@Arun等人,我理解设计 - 为什么现在。以上打印的原因是每次通过data.table语法进行X[Y]合并时,存在隐藏的 by ,并且关键是by。它以这种方式设计的原因似乎如下 - 因为在合并时必须执行by操作,如果你要做的话,也可以利用它而不做另一个by通过合并的关键。

现在说,我认为这是一个语法设计缺陷。我阅读data.table语法d[i, j, by = b]的方式是

  

选择d,应用i操作(是子集化或合并或诸如此类),然后通过“b

执行j表达式”

by-without-by打破了这个阅读,并介绍了一个人必须具体思考的案例(我合并iby只是合并的关键,等等)。我认为这应该是data.table的工作 - 在合并的一个特定情况下使data.table更快的值得赞扬的努力,当by等于密钥时,应该完成以另一种方式(例如,通过内部检查by表达式是否实际上是合并的关键字)。

4 个答案:

答案 0 :(得分:11)

编辑号码无限:常见问题解答1.12 完全回答您的问题:(也有用/相关的是FAQ 1.13,此处未粘贴)。

  

1.12 X [Y]和合并(X,Y)有什么区别?
  X [Y]是一个连接,使用Y(或Y的密钥,如果有的话)作为索引查找X的行。 Y [X]是一个连接,使用X(或X的键,如果有的话)作为索引查找Y的行。 merge(X,Y)1同时执行两种方式。 X [Y]和Y [X]的行数通常为dier;而merge(X,Y)和merge(Y,X)返回的行数相同。但是错过了重点。大多数任务需要在连接或合并后对数据执行某些操作。 为什么要合并所有数据列,之后只使用其中的一小部分?
  您可以建议merge(X[,ColsNeeded1],Y[,ColsNeeded2]),但需要复制数据子集,并且需要程序员确定需要哪些列。 data.table中的 X [Y,j]为您完成所有这一切。当您编写X[Y,sum(foo*bar)],data.table时   自动检查j表达式以查看它使用的列。它只会仅对这些列进行子集化;其他人被忽略了。仅为j使用的列创建内存,Y列在每个组的上下文中享有标准R回收规则。假设foo在X中,bar在Y中(在Y中还有20个其他列)。 X[Y,sum(foo*bar)] 编程速度更快,运行速度更快,而不是合并后跟子集?


没有没有回答O​​P的问题的老答案(来自OP的评论),保留在这里,因为我相信它确实如此)。

当您在j中为d[, 4] d[, value]data.table提供值时,j将被评估为expression。从data.table FAQ 1.1访问DT[, 5](第一个常见问题解答):

  

因为默认情况下,与data.frame不同,第二个参数是在DT范围内计算的表达式。 5评估为5。

因此,首先要理解的是:

d[, value] # produces a "vector"
# [1] 2 3 4 5 6

i的查询是基本索引,

d[3, value] # produces a vector of length 1
# [1] 4

但是,当i本身是data.table时,这是不同的。来自data.table简介(第6页):

d[J(3)] # is equivalent to d[data.table(a = 3)]

在这里,您正在执行join。如果您只是执行d[J(3)],那么您将获得与该联接相对应的所有列。如果你这样做,

d[J(3), value] # which is equivalent to d[J(3), list(value)]

既然你说这个答案没有没有来回答你的问题,我会指出你的“改写”问题的答案在哪里,我相信:---&gt; 然后你就会获得该列,但是由于你正在执行连接,因此也会输出键列(因为它是基于键列的两个表之间的连接)。 < / p>


编辑:在第二次修改后,如果你的问题为什么会这样??那么我不情愿(或者说是无知)回答,Matthew Dowle设计的是为了区分在data.table join-based-subsetindex-based-subset ting操作之间。

您的第二种语法相当于:

d[J(3)][, value] # is equivalent to:

dd <- d[J(3)]
dd[, value]

再次,在dd[, value]中,j被评估为表达式,因此您获得了一个向量。


要回答您的第3个修改过的问题:第3次,这是因为是基于键列的两个data.tables 之间的JOIN。如果我加入两个data.table,我希望data.table

data.table介绍,再次:

  

将data.table传递给data.table子集类似于基R中的A [B]语法,其中A是矩阵,B是2列矩阵。事实上,基础R中的A [B]语法启发了data.table包。

答案 1 :(得分:6)

data.table 1.9.3开始,默认行为已更改,下面的示例产生相同的结果。要获得 by-without-by 结果,现在必须指定一个明确的by=.EACHI

d = data.table(a = 1:5, value = 2:6, key = "a")

d[J(3), value]
#[1] 4

d[J(3), value, by = .EACHI]
#   a value
#1: 3     4

这是一个稍微复杂的例子,说明了不同之处:

d = data.table(a = 1:2, b = 1:6, key = 'a')
#   a b
#1: 1 1
#2: 1 3
#3: 1 5
#4: 2 2
#5: 2 4
#6: 2 6

# normal join
d[J(c(1,2)), sum(b)]
#[1] 21

# join with a by-without-by, or by-each-i
d[J(c(1,2)), sum(b), by = .EACHI]
#   a V1
#1: 1  9
#2: 2 12

# and a more complicated example:
d[J(c(1,2,1)), sum(b), by = .EACHI]
#   a V1
#1: 1  9
#2: 2 12
#3: 1  9

答案 2 :(得分:4)

这是意外行为,它是记录在案的行为。 Arun在常见问题解答中做了很好的解释和演示,其中明确记录。

有一个功能请求FR 1757建议在这种情况下使用drop参数

实施后,您想要的行为可能会被编码

d = data.table(a = 1:5, value = 2:6, key = "a")

d[J(3), value, drop = TRUE]

答案 3 :(得分:3)

我同意阿伦的回答。这是另一个措辞:在您进行连接之后,您通常会使用连接列作为参考或作为进一步转换的输入。所以你保留它,你可以选择使用(更多环形)双[语法来丢弃它。从设计的角度来看,保存频繁相关的信息比在需要时更容易丢弃,而不是提前丢弃,并且有可能丢失难以重建的数据。

您希望保留连接列的另一个原因是您可以在执行连接的同时执行聚合操作( by without )。例如,通过包含连接列,此处的结果更加清晰:

d <- data.table(a=rep.int(1:3,2),value=2:7,other=100:105,key="a")
d[J(1:3),mean(value)]
#   a  V1
#1: 1 3.5
#2: 2 4.5
#3: 3 5.5