如何使用 dplyr 语法选择带有 bigrquery 的嵌套字段?

Kha*_*hir 5 google-analytics r unnest dbplyr bigrquery

如果可能,我想bigrquery使用dplyr语法(而不是 SQL)探索 Google Analytics 360 数据。要点是我想了解用户旅程——我有兴趣在用户级别(甚至跨会话)找到最常见的页面序列。

我以为我可以这样做:

sample_query <- ga_sample %>%
  select(fullVisitorId, date, visitStartTime, totals, channelGrouping,
  hits.page.pagePath) %>% 
  collect()
Run Code Online (Sandbox Code Playgroud)

但我收到一个hits.page.pagePath未找到的错误。然后我尝试:

sample_query <- ga_sample %>%
  select(fullVisitorId, date, visitStartTime, totals, channelGrouping, hits) %>% 
  collect() %>% 
  unnest_wider(hits)
Run Code Online (Sandbox Code Playgroud)

但结果是Error: Requested Resource Too Large to Return [responseTooLarge],这是完全有道理的。

从我收集到的信息来看,使用 SQL 语法,解决方法是unnest远程访问,并且select只有hits.page.pagePath字段(而不是整个hits顶级字段)。

例如,这样的事情(这是一个不同的查询,但传达了这一点):

SELECT
  hits.page.pagePath
FROM
  'bigquery-public-data.google_analytics_sample.ga_sessions_20160801' AS GA,
  UNNEST(GA.hits) AS hits
GROUP BY
  hits.page.pagePath
Run Code Online (Sandbox Code Playgroud)

是否可以用dplyr语法做类似的事情?如果不可能,那么使用 SQL 的最佳方法是什么?

谢谢!

更新:实际查询/代码

SELECT DISTINCT
fullVisitorId, visitId, date, visitStartTime, hits.page.pagePath, hits.time, geoNetwork.networkDomain
FROM 'bigquery-public-data.google_analytics_sample.ga_sessions_*' AS GA, UNNEST(GA.hits) AS hits
WHERE _TABLE_SUFFIX BETWEEN "20191101" AND "20191102"
AND geoNetwork.networkDomain NOT LIKE "%google%"
Run Code Online (Sandbox Code Playgroud)

Sim*_*.A. 4

从 R 转换为 BigQuery(或您使用的任何数据库语言)时可以创建的查询类型dbplyr取决于 R 和 BigQuery 之间定义的翻译。我找不到任何表明UNNEST在现有dbplyr包中定义翻译的示例。参考文献 1 ,参考文献 2

一种解决方法是定义一个自定义函数,不在 中进行翻译dbplyr,而是作为 旁边的翻译器dbplyr。以前,当我需要PIVOTSQL 但找不到tidyr::spread.

该方法有效,因为远程表由dbplyr两件事定义:(1) 到远程数据库的连接,(2) 返回表的当前视图的代码/查询。因此,一旦dbplyr将 R 转换为 BigQuery 或 SQL,它就会更新定义的后半部分。

我们可以使用自定义函数来做到这一点:

unnest <- function(input_tbl, select_columns, array_column, unnested_columns){

  # extract connection
  db_connection <- input_tbl$src$con

  select_columns = paste0(select_columns, collapse = ", ")
  unnested_columns = paste0(paste0("un.", unnested_columns), collapse = ", ")

  # build SQL unnest query
  sql_query <- dbplyr::build_sql(
    con = db_connection
    ,"SELECT ", select_columns, ", ", position, ", ", unnested_columns, "\n"
    ,"FROM (\n"
    ,dbplyr::sql_render(input_tbl)
    ,"\n) AS src\n"
    ,"CROSS JOIN UNNEST(", array_column, ") AS un WITH OFFSET position"
  )

  return(dplyr::tbl(db_connection, dbplyr::sql(sql_query)))
}
Run Code Online (Sandbox Code Playgroud)

请注意,我是dbplyr用户,但不是 BigQuery 用户,所以上面我的语法可能不太完美。我已经关注了这个问题这个问题的语法。

使用示例:

remote_table = tbl(bigquery_connection, from = "table_name")
unnested_table = unnest(remote_table, "ID", "array_col", "list")

# check syntax of dbplyr query
unnested_table %>% show_query()
# if this is not a valid bigquery query then next command will error

# view top 10 rows
unnested_table %>% head(10)
Run Code Online (Sandbox Code Playgroud)

如果remote_table看起来像:

ID ARRAY_COL
01 list = [a,b,c]
02 list = [d,e]
03 list = [q]
Run Code Online (Sandbox Code Playgroud)

那么unnested_table应该看起来像:

ID POSITION un.list
01    0        a
01    1        b
01    2        c
02    0        d
02    1        e
03    0        q
Run Code Online (Sandbox Code Playgroud)

并且unnested_table %>% show_query()应该看起来像这样:

<SQL>
SELECT *, position, un.list
FROM (
    SELECT *
    FROM table_name
) AS src
CROSS JOIN UNNEST(ARRAY_COL) AS un WITH OFFSET position
Run Code Online (Sandbox Code Playgroud)

更新以匹配目标查询

我知道没有任何dbplyr功能可以_TABLE_SUFFIX BETWEEN "20191101" AND "20191102"轻松翻译,因此您必须以另一种方式处理这个问题 - 也许循环遍历 R 中的日期列表。

第一步是在dbplyr取消嵌套之前渲染查询。大概是这样的:

for(date in c("20191101", "20191102")){
    table_name = paste0("bigquery-public-data.google_analytics_sample.ga_sessions_",date)

    remote_table = tbl(bigquery_connection, from = table_name)

    remote_table = remote_table %>%
        filter(! (geoNetwork.networkDomain %like% "%google%")) %>%
        select(fullVisitorId, visitId, date, visitStartTime, hits, geoNetwork.networkDomain) %>%
        distinct()
}
Run Code Online (Sandbox Code Playgroud)

然后调用show_query(remote_table)应该产生与以下内容等效的内容。但它不会完全相同,因为dbplyr编写代码的方式与人类不同。

unnest <- function(input_tbl, select_columns, array_column, unnested_columns){

  # extract connection
  db_connection <- input_tbl$src$con

  select_columns = paste0(select_columns, collapse = ", ")
  unnested_columns = paste0(paste0("un.", unnested_columns), collapse = ", ")

  # build SQL unnest query
  sql_query <- dbplyr::build_sql(
    con = db_connection
    ,"SELECT ", select_columns, ", ", position, ", ", unnested_columns, "\n"
    ,"FROM (\n"
    ,dbplyr::sql_render(input_tbl)
    ,"\n) AS src\n"
    ,"CROSS JOIN UNNEST(", array_column, ") AS un WITH OFFSET position"
  )

  return(dplyr::tbl(db_connection, dbplyr::sql(sql_query)))
}
Run Code Online (Sandbox Code Playgroud)

第二步是调用自定义的取消嵌套函数”

remote_table = unnest(remote_table,
                      select_columns = c("fullVisitorId", "visitId", "date", "visitStartTime", "geoNetwork.networkDomain"),
                      array_column = "hits",
                      unnested_columns = c("page.pagePath", "time")
               )
Run Code Online (Sandbox Code Playgroud)

然后调用show_query(remote_table)应该产生以下结果:

remote_table = tbl(bigquery_connection, from = "table_name")
unnested_table = unnest(remote_table, "ID", "array_col", "list")

# check syntax of dbplyr query
unnested_table %>% show_query()
# if this is not a valid bigquery query then next command will error

# view top 10 rows
unnested_table %>% head(10)
Run Code Online (Sandbox Code Playgroud)

这可能是我所能提供的帮助,因为我自己没有一个 bigquery 环境来测试这一点。您可能需要调整自定义unnest函数以使其完全匹配您的上下文。希望以上内容足以帮助您入门。