我正在学习data.table。我很难转换dplyr连接语法。您能否为以下测试用例推荐data.table等价?
library(data.table)
library(dplyr)
dtProduct <- data.table(
ProductID = c(6, 33, 17, 88, 44, 51),
ProductName= c("Shirt", "Helmet", "Gloves", "Towel", "Chair", "Detergent"),
Price= c(25, 60, 10, 7.5, 135, 16),
key = 'ProductID'
)
set.seed(20141216)
dtOrder <- data.table(
OrderID = sample(1001:9999, 12),
CustomerID = sample(271:279, 12, replace=TRUE),
# NOTE: some non-existent ProductID intentionally introduced
ProductID = sample(c(dtProduct[, ProductID], 155, 439), 12, replace=TRUE),
Qty = sample(1:3, 12, replace=TRUE),
key = 'OrderID'
)
> tables()
NAME NROW NCOL MB COLS KEY
[1,] dtOrder 12 4 1 OrderID,CustomerID,ProductID,Qty OrderID
[2,] dtProduct 6 3 1 ProductID,ProductName,Price ProductID
> dtProduct
ProductID ProductName Price
1: 6 Shirt 25.0
2: 17 Gloves 10.0
3: 33 Helmet 60.0
4: 44 Chair 135.0
5: 51 Detergent 16.0
6: 88 Towel 7.5
> dtOrder
OrderID CustomerID ProductID Qty
1: 1651 275 6 3
2: 2726 272 88 2
3: 3079 275 88 2
4: 3168 274 17 1
5: 4816 277 88 1
6: 4931 278 51 1
7: 5134 274 439 2
8: 5265 272 33 3
9: 7702 275 33 2
10: 7727 279 155 2
11: 8412 273 88 2
12: 9130 271 17 3
案例1:显示订单明细,不匹配产品ID隐藏
dtOrder %>%
inner_join(dtProduct, by="ProductID") %>%
transmute(OrderID, ProductID, ProductName, Qty, Price, ExtPrice=Qty*Price)
OrderID ProductID ProductName Qty Price ExtPrice
1 1651 6 Shirt 3 25.0 75.0
2 3168 17 Gloves 1 10.0 10.0
3 9130 17 Gloves 3 10.0 30.0
4 5265 33 Helmet 3 60.0 180.0
5 7702 33 Helmet 2 60.0 120.0
6 4931 51 Detergent 1 16.0 16.0
7 2726 88 Towel 2 7.5 15.0
8 3079 88 Towel 2 7.5 15.0
9 4816 88 Towel 1 7.5 7.5
10 8412 88 Towel 2 7.5 15.0
案例2:显示订单明细,包含不匹配的产品ID
dtOrder %>%
left_join(dtProduct, by="ProductID") %>%
transmute(OrderID, ProductID, ProductName, Qty, Price, ExtPrice=Qty*Price)
OrderID ProductID ProductName Qty Price ExtPrice
1 1651 6 Shirt 3 25.0 75.0
2 3168 17 Gloves 1 10.0 10.0
3 9130 17 Gloves 3 10.0 30.0
4 5265 33 Helmet 3 60.0 180.0
5 7702 33 Helmet 2 60.0 120.0
6 4931 51 Detergent 1 16.0 16.0
7 2726 88 Towel 2 7.5 15.0
8 3079 88 Towel 2 7.5 15.0
9 4816 88 Towel 1 7.5 7.5
10 8412 88 Towel 2 7.5 15.0
11 7727 155 NA 2 NA NA
12 5134 439 NA 2 NA NA
案例3:显示订单错误(仅限不匹配的产品ID)
dtOrder %>%
left_join(dtProduct, by="ProductID") %>%
filter(is.na(ProductName)) %>%
select(OrderID, ProductID, ProductName, Qty)
OrderID ProductID ProductName Qty
1 7727 155 NA 2
2 5134 439 NA 2
案例4:按产品ID划分的各种聚合,按TotalSales降序排序
dtOrder %>%
inner_join(dtProduct, by="ProductID") %>%
group_by(ProductID) %>%
summarize(OrderCount=n(), TotalQty=sum(Qty), TotalSales=sum(Qty*Price)) %>%
arrange(desc(TotalSales))
ProductID OrderCount TotalQty TotalSales
1 33 2 5 300.0
2 6 1 3 75.0
3 88 4 7 52.5
4 17 2 4 40.0
5 51 1 1 16.0
案例5:按ProductID划分的各种聚合,按TotalSales降序排序
注意2:按降序TotalSales排序不再有效(BUG?)
dtOrder %>%
inner_join(dtProduct, by="ProductID") %>%
group_by(ProductID, ProductName) %>%
summarize(OrderCount=n(), TotalQty=sum(Qty), TotalSales=sum(Qty*Price)) %>%
arrange(desc(TotalSales))
ProductID ProductName OrderCount TotalQty TotalSales
1 6 Shirt 1 3 75.0
2 17 Gloves 2 4 40.0
3 33 Helmet 2 5 300.0
4 51 Detergent 1 1 16.0
5 88 Towel 4 7 52.5
非常感谢您提前寻求帮助。
答案 0 :(得分:9)
setkey(dtOrder, ProductID)
<强>(1-2)强>
# this will be literally what you wrote
dtProduct[dtOrder,
list(OrderID, ProductID, ProductName, Qty, Price, ExtPrice=Qty*Price),
nomatch = 0 # or omit this to get (2)
]
# but I think you'd be better off with this
dtProduct[dtOrder][, ExtPrice := Qty*Price][]
<强>(3)强>
# you can again take the literal direction:
dtProduct[dtOrder][!is.na(ProductName)][,
list(OrderID, ProductID, ProductName, Qty)]
# but again I think you'd be better off with
dtOrder[!dtProduct]
<强>(4-5)强>
dtProduct[dtOrder, nomatch = 0][,
list(OrderCount=.N, TotalQty=sum(Qty), TotalSales=sum(Qty*Price)),
by = list(ProductID, ProductName)][
order(-TotalSales)]
答案 1 :(得分:8)
您应该查看?data.table
并查看其中的示例。这是一种非常好的学习方式。我们正在编写更多detailed vignettes FR #944,计划为1.9.8。但在那之前:
data.table的语法格式如下:
x[i, j, by, ...] # i = where, j = select|modify|update, by = group by
当i
是integer
或logical expression
时,我们将其称为子集操作。例如:
x[a > 1]
这是做什么的?检查data.table a
中的列x
是否存在条件> 1
,这会产生逻辑向量= length(a)
。并且标识条件评估为TRUE
的那些行,并返回与这些行对应的所有列。
在data.table中, join 可以看作是子集的自然扩展。也就是说,我们可以将 join 视为子集操作,但使用另一个data.table 。这就是我们所说的一致语法 - 表格x[i, j, by]
完整无缺。
加入data.tables的第一步是设置密钥。这可以使用目的是双重的setkey()
函数来完成:
按提供的列按递增顺序(升序)重新排序data.table的行。这是通过引用完成的,以提高内存效率。
标记以键列提供的列,可以在其上执行连接(如果和执行连接时)。
请注意,目前,对于
x[i]
形式的连接,x
需要绝对设置关键列。i
可能有也可能没有它的密钥集。
如果
i
也设置了关键字列,那么通过将i
的第一个关键字列与x
的第一个关键字列进行匹配来执行连接,第二次,等等。如果
i
未设置关键列,那么i
的第一列将与{{1}的第一个关键列匹配},x
的第二列,第二列为i
,依此类推......是的,我们知道,当
x
没有关键列但是我们还没有时间到达它时,按列名匹配会很不错
第二步也是最后一步是执行连接: - )。
但是 join 操作如何成为子集的扩展?当i
是data.table时,对于i
中的每一行,它会通过匹配i
的关键列找到x
中的匹配行索引我们已经设定好了。这会为x
中的每一行返回x
的一组行索引(如果未找到匹配,则返回i
。)
现在我们有匹配的行索引。我们要返回的只是列。但由于NA
也是一个data.table,它也可能有其他列。因此,对于匹配行索引的那些,我们返回i
和x
的列。
这是一个小例子,可以帮助您在继续之前将这个概念内化。考虑两个data.tables i
和X
,如下所示:
Y
请注意,我们已使用
X = data.table(a=c(1,1,1,2,2,5,6), b=1:7, key="a") # a b # 1: 1 1 # 2: 1 2 # 3: 1 3 # 4: 2 4 # 5: 2 5 # 6: 5 6 # 7: 6 7 key(X) # [1] "a" Y = data.table(a=c(6,2), c=letters[1:2]) # a c # 1: 6 a # 2: 2 b key(Y) # NULL # join X[Y] # a b c # 1: 6 7 a # 2: 2 4 b # 3: 2 5 b
函数中的key=
参数直接设置键列。或者,我们可以在没有密钥的情况下创建data.table()
,然后X
。函数
setkey(X, a)
返回键列(如果有)。如果未设置任何键,则返回NULL。
key()
没有关键列,Y
只有一个关键列。因此,使用X
的第一列a
和Y
的第一个关键列a
完成加入。 X
中的a=6
与Y
的第7行和第4行和第5行的X
匹配。
您可以使用参数a=2
:
which = TRUE
这也是一种方便(快速)的方法来对data.table进行子集化,但使用data.table 快速二进制搜索的子集。由于此操作非常有用,data.table提供了一种简单的方法,而不必每次都写X[as.data.table(6), which=TRUE] # [1] 7
X[as.data.table(2), which=TRUE] # [1] 4 5
。
as.data.table()
我认为这应该有助于理解子集是连接扩展的含义。
现在,让我们暂时忘掉所有这些&#34;左&#34;,&#34;右&#34;,&#34;内部&#34;,&#34;外部&# 34;等等。看看你想要执行的实际操作。您有两个data.tables - # faster way of doing X[a == 6] on data.table with 'a' as key column
X[J(6)] # J for Join
X[J(2)]
# (or)
X[.(6)] # . is an alias for J
X[.(2)]
和dtP
(为方便起见缩写)。
对于dtO
中的ProductID
列中的每一行,您希望在dtO
中找到匹配的行,但您不想返回dtP
。您还想要选择要输出的列以及一些计算。
即NA
和i = dtO
。 x = dtP
的键列设置正确。但dtP
的关键列是dtO
。如果我们这样加入,它就会orderID
从orderID
加入dtO
来自productID
,这是错误的。
我们必须将dtP
的密钥设置为dtO
或将productID
的密钥设置为NULL并将列dtO
作为第一列移动(直到按名称匹配)实施)。我们在这里设置密钥productID
:
productID
现在这应该是很明显的。仅匹配行索引时,提取所有这些列(包括表达式)。
我们为什么要先加入并选择/聚合?
与案例1相同,但您甚至需要不匹配的行。已从案例1中正确设置密钥。
# set key
setkey(dtO, ProductID)
# join
dtP[dtO, .(OrderID, ProductID, ProductName, Qty, Price, ExtPrice=Qty*Price), nomatch=0L]
返回# join
dtP[dtO, .(OrderID, ProductID, ProductName, Qty, Price, ExtPrice=Qty*Price)]
的所有行,即使没有匹配,也指定了所有列(包括表达式)。
您希望orderID
中与dtO
不匹配的所有行。
dtP
查找dtP&#39键组与dtO匹配的所有行。从dtO返回所有其他行。如有必要,您也可以在not-join or anti-join
dtO[!dtP]
中指定所有必要的列。
从this post了解j
。
您将按by=.EACHI
加入,然后按同一列进行汇总。 但为什么我们需要那个中间结果呢?这完全是不必要的,浪费了内存和计算时间!相反,我们可以使用productID
来评估by=.EACHI
中每行匹配行的j表达式。
i
为了测试您的理解,请尝试找出我们未在此处dtO[dtP, .(.N, sQty = sum(Qty), sSales = sum(Qty*Price)), by=.EACHI, nomatch=0L][order(-sSales)]
进行的原因..
与@ eddi相同。
我个人觉得从我想要执行的实际任务来考虑更自然,而不是找出与我想要执行的任务相关联的连接函数的类型(我永远不会记得哪些data.table是&# 34;左边&#34;哪一个是&#34;右边&#34; ...和顺便说一下,它是什么&#34;内部&#34;,&#34;外部&#34;和&#34;完全外部&#34;加入反正?)。
HTH