【问题标题】:How to get same grouping results using dplyr to get result consistent with sqldf result?如何使用 dplyr 获得相同的分组结果以获得与 sqldf 结果一致的结果?
【发布时间】:2021-07-17 12:00:04
【问题描述】:

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

library(sqldf)
library(dplyr)

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

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

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

# dplyr way
res <- Posts %>%
  group_by(HasPet) %>%
  summarize(
    Id,
    HasPet,
    MaxAge = max(Age)
    ) %>%
  select(Id, HasPet, MaxAge)

head(ref)
head(res)

sqldf 的输出是:

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

虽然 sqldf 的输出不同:

> head(res)
# A tibble: 4 x 3
# Groups:   HasPet [2]
     Id HasPet MaxAge
  <dbl>  <dbl>  <dbl>
1     1      0     20
2     2      0     20
3     3      1     14
4     4      1     14

UPD。 SQL 查询无法修改。

【问题讨论】:

  • 您希望dplyr 的确切输出是什么?为什么?
  • @Tim Biegeleisen 这已在某些 R 课程中提出要求。我需要得到与 sqldf 产生的结果相同但使用 dplyr 的结果。这是通过测试的先决条件。

标签: r dplyr grouping sqldf


【解决方案1】:

您的问题的答案是 SQL 查询不与您的 R 代码版本执行相同的操作。这是等效的 SQL 查询:

SELECT Id, HasPet, MAX(Age) OVER (PARTITION BY HasPet) AS MaxAge
FROM Posts

实际上,您当前的查询在技术上是无效的,因为它按HasPet 聚合,但选择了Id。不清楚您要选择Id 的哪个 值。这是您原始查询的有效版本:

SELECT HasPet, MAX(Age) AS MaxAge
FROM Posts
GROUP BY HasPet

【讨论】:

  • 感谢您的回答!实际上,我的问题是关于如何获得与 sqldf 相同的行为,但使用 dplyr。相反,您调整了 sqldf 的查询请求。我不知道原始 SQL 查询是否无效。我认为不是,因为它执行时没有警告或错误。
  • 您尝试匹配的 SQL 输出是由无效查询生成的。请解释如何选择 Id 值背后的逻辑。
  • 您确定此请求无效吗?与此特定查询相关的 SQL 文档是否有任何限制?据我了解,来自 sqldf 的最终结果会产生带有 HasPet 列中列出的过滤重复值的结果。我还发现使用“聚合”可以实现与 sqldf 相同的行为(它不是 dplyr 函数)。见stackoverflow.com/questions/6289538/…
  • 我之前的评论有错误的 URL。现在已经修好了。对不起。
  • 我同意你的观点,原始 SQL 请求看起来很奇怪。无论如何,它有效。我不确定 sql 查询在技术上是否无效。
【解决方案2】:

这个问题可以通过以下方式解决:

slice(which.min(Id))

在“group_by”和“summarize”函数调用之后。

例如:

# dplyr way
res <- Posts %>%
  group_by(HasPet) %>%
  summarize(
    Id,
    HasPet,
    MaxAge = max(Age)
    ) %>%
  select(Id, HasPet, MaxAge) %>%
  slice(which.min(Id))

在这种情况下,输出与使用 dplyr 相同:

> res
# A tibble: 2 x 3
# Groups:   HasPet [2]
     Id HasPet MaxAge
  <dbl>  <dbl>  <dbl>
1     1      0     20
2     3      1     14

附:我认为有更简单的方法,但到目前为止我还没有找到它们。

【讨论】:

  • 试试:Posts %&gt;% group_by(HasPet) %&gt;% slice_min(Id) %&gt;% ungroup
  • @G。 Grothendieck It LGTM,但您的变体具有不同的列名。它返回“Age”列名而不是必需的“AgeMax”。
【解决方案3】:

代码没有错,但你试图实现的逻辑就是它。让我解释一下:

您的分组预期输出包含Id=1,3。但是 R 怎么知道那些而不是 Id=2,4?。更具体地说,当您按HasPet=0 分组时,R 会选择Id 的哪个值? 1 还是 2?如果您没有给出具体的使用标准,R 将如何知道它?也就是说,这给出了您的预期输出:

res <- Posts %>%
  group_by(HasPet) %>%
  summarize(Id = min(Id),
            MaxAge = max(Age))

【讨论】:

    猜你喜欢
    • 2021-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多