具有 case_when 或 if_else 的 Dbplyr 计数唯一 (n_distinct)

Dbplyr count unique (n_distinct) with case_when or if_else

我想使用 dplyr::tbl df 对象使用 dplyr::case_when 函数创建一个新列来计算唯一日期 (dplyr::n_distinct)。

我尝试了以下代码:

df %>%
group_by(id) %>%
mutate(
    last_date = max(date),
    distinct_date_2020 = case_when(
            date >= "2020-01-01" & date <= "2020-12-31" ~ n_distinct(date[!type == "Online"]),
            TRUE ~ as.integer(0))
) %>%
ungroup() 

distinct_date_2020mutate不起作用。我收到以下错误消息:

[Microsoft][ODBC SQL Server Driver][SQL Server]Use of DISTINCT is not allowed with the OVER clause.

如果我 运行 没有 distinct_date_2020,它会起作用:

df %>%
group_by(id) %>%
mutate(
    last_date = max(date)
) %>%
ungroup() 

我也尝试了这些变体,但没有成功:

distinct_date_2020 = n_distinct(date[date >= "2020-01-01" & date <= "2020-12-31" & !type == "Online"])

distinct_date_2020 = ifelse(date >= "2020-01-01" & date <= "2020-12-31", n_distinct(date[!type == "Online"]), NA)

如果我在 collect() 之后 运行 这些,它就可以工作。但我想将其作为 SQL 命令发送到服务器。

DBI::dbGetInfo:

$dbms.name [1] "Microsoft SQL Server"

$db.version [1] "11.00.6523"

有人知道怎么解决吗?谢谢!

问题不在于 case_when 也不在于 n_distinct,而是在于 date.

的方括号

在 R 中,date[!type == "Online"] 选择列中所有行的子集。 SQL 查询(大部分)逐行工作,因此要求 n_distinct 的输入是根据类型列过滤日期列将不会产生有效的 SQL.

一种测试方法是使用 show_query 命令:

df %>%
group_by(id) %>%
mutate(
    last_date = max(date),
    distinct_date_2020 = case_when(
            date >= "2020-01-01" & date <= "2020-12-31" ~ n_distinct(date[!type == "Online"]),
            TRUE ~ as.integer(0))
) %>%
ungroup() %>%
show_query()

除非这生成有效的 SQL 代码,否则您的查询肯定会出错。

解决方案是单独进行过滤:

dist_dates = df %>%
    group_by(id) %>%
    filter(type != "Online",
           date >= "2020-01-01",
           date <= "2020-12-31") %>%
    mutate(distinct_date_2020 = n_distinct(date)) %>%
    ungroup()

max_dates = df %>%
    group_by(id) %>%
    mutate(last_date = max(date))
    ungroup()

output = max_dates %>%
    left_join(dist_dates, by = "id") %>%
    select(id, last_date, distinct_date_2020)

我把两个总结分开做了,所以筛选不适用于last_date

我已经删除了 case_when,因为在您的示例中它仅用作过滤器。但是如果你想这样做,试试:

dist_dates = df %>%
    group_by(id) %>%
    filter(type != "Online") %>%
    mutate(
        distinct_date_2020 = case_when(
            date >= "2020-01-01" & date <= "2020-12-31" ~ n_distinct(date),
            TRUE ~ as.integer(0))
    ) %>%
    ungroup()

我终于让它工作了。我首先按 id 分组,提取最后日期,然后按年份分组(使用 DATEADDDATEDIFF,避免使用 lubridate 包,因为它与 dbplyr包)。总结条件 [type != "Online"] 并将其放在括号之间也有效。我用下面的代码得到了想要的结果:

df %>%
    group_by(id) %>%
        mutate(
            last_date_temp = max(date)
        ) %>%
    group_by(year = DATEADD(sql("year"), DATEDIFF(sql("year"), 0, date), 0), id) %>%
    summarize(
        last_date = max(last_date_temp, na.rm = TRUE),
        distinct_date = n_distinct(date[type != "Online"]),
    ) %>%
    ungroup() %>%
    select(id, year, everything()) %>%
    arrange(id, year) %>%
    collect()