【问题标题】:How to get same grouping result using data.table comparing to the sqldf?与 sqldf 相比,如何使用 data.table 获得相同的分组结果?
【发布时间】:2021-07-19 17:09:01
【问题描述】:

我尝试使用 sqldf 和 data.table 实现 SQL 查询。
我需要使用这两个不同的库分别执行此操作。
不幸的是,我无法使用 data.table 产生相同的结果。

library(sqldf)
library(data.table)

Id       <- c(1,2,3,4)
HasPet   <- c(0,0,1,1)
Age      <- c(20,1,14,10)

Posts <- data.table(Id, HasPet, Age)

# sqldf way
ref <- sqldf("
      SELECT Id, HasPet, MAX(Age) AS MaxAge
      FROM Posts
      GROUP BY HasPet
  ")

# data.table way
res <- Posts[,
      list(Id, HasPet, MaxAge=max(Age)),
      by=list(HasPet)]

head(ref)
head(res)

sqldf 的输出是:

> head(ref)
  Id HasPet MaxAge
1  1      0     20
2  3      1     14

虽然 data.table 的输出不同:

> head(res)
   HasPet Id HasPet MaxAge
1:      0  1      0     20
2:      0  2      0     20
3:      1  3      1     14
4:      1  4      1     14

请注意,不能修改 SQL 查询。

【问题讨论】:

  • GROUP BY 查询无效,预计会引发异常。
  • 一个小标题?这不是data.table 的输出。
  • @jarlh 这个 SQL 查询是在一些 R 课程中给出的。在我看来,这是正确的。无论如何,它适用于 sqldf。
  • @StéphaneLaurent 现在 class(res) 打印 [1] "data.table" "data.frame"。

标签: sql r data.table sqldf


【解决方案1】:

data.table 经常出现这种情况。如果您想要按组的最大值或最小值,最好的方法是自加入。它很快,而且有点神秘。

您可以逐步构建它: 在data.table中,你可以在i中选择,在j中做,然后分组。所以第一步是在组的每个级别中找到我们想要的东西

Posts[, Age == max(Age), by = HasPet]
#    HasPet    V1
# 1:      0  TRUE
# 2:      0 FALSE
# 3:      1  TRUE
# 4:      1 FALSE

我们可以使用.I 来检索每行的整数向量,然后是之前每个组中的 V1 逻辑向量 TRUE 和 FALSE 索引,因此我们只有包含每组最大值的行。

Posts[, .I[Age == max(Age)], by=HasPet]

# From the data.table special symbols help:
# .I is an integer vector equal to seq_len(nrow(x)). While grouping,
# it holds for each item in the group, its row location in x. This is useful
# to subset in j; e.g. DT[, .I[which.max(somecol)], by=grp].

#    HasPet V1
# 1:      0  1
# 2:      1  3

然后我们使用刚刚创建的列 V1 来调用 data.table 中的特定行(1 和 3)。就是这样!

Posts[Posts[, .I[Age == max(Age)], by=HasPet]$V1]

【讨论】:

  • 您能改写成更明确的形式吗?恕我直言,这对社区成员来说更有价值。
  • 顺便说一句,谢谢你的回答!怎么样:GroupedByHasPet
  • 注意事项:您可以在 data.table 中将list 替换为.。当您使用 &lt;- 分配时,您不会创建深层副本,如果您在 GroupedByHasPet 中更改内容并且它们在 Posts 中发生意外更改,这可能会导致问题。一般来说,我发现如果我可以避免创建新的 data.tables,我遇到的意外惊喜就会更少。它对于弄清楚你在开始时正在做什么将其拆分很有用,但是带有 data.table 的高性能代码通常希望避免这些步骤。如果没关系,当然没关系!那么只有参考部分的修改才重要。
【解决方案2】:

您可以使用.SD 获取HasPet 的每个值的行子集。

library(data.table)
Posts[, .SD[Age==max(Age)], HasPet]

#   HasPet Id Age
#1:      0  1  20
#2:      1  3  14

【讨论】:

  • 感谢您提供这个优雅的解决方案。但是,它看起来与 Danielle McCool 给出的解决方案非常相似。只是为了满足我的好奇心,我对这两个类似的解决方案进行了微基准测试。我以为你的版本会更快。反而发现慢了一点。
  • 至少目前,.SD 更清晰,但效率低了很多。 github.com/Rdatatable/data.table/issues/4886
猜你喜欢
  • 2021-07-17
  • 2018-10-24
  • 1970-01-01
  • 1970-01-01
  • 2020-08-15
  • 1970-01-01
  • 1970-01-01
  • 2013-12-24
  • 1970-01-01
相关资源
最近更新 更多