【问题标题】:preserve order based on another variable when calling collect_list using sparklyr使用 sparklyr 调用 collect_list 时保留基于另一个变量的顺序
【发布时间】:2019-09-28 16:42:27
【问题描述】:

这个问题本质上是 this question 的重复,除了我在 R 中工作。pyspark 解决方案看起来很可靠,但我无法弄清楚如何将 collect_list 应用于相同的窗口函数在 sparklyr 中的方式。

我有一个具有以下结构的 Spark DataFrame:

------------------------------
userid |     date     | city
------------------------------
   1   |  2018-08-02  |   A
   1   |  2018-08-03  |   B
   1   |  2018-08-04  |   C
   2   |  2018-08-17  |   G
   2   |  2018-08-20  |   E
   2   |  2018-08-23  |   F

我正在尝试按userid 对DataFrame 进行分组,按date 对每个组进行排序,并将city 列折叠成其值的串联。期望的输出:

------------------
userid | cities
------------------
   1   |  A, B, C
   2   |  G, E, F

问题在于,我尝试使用的每种方法都导致一些用户(在对 5000 名用户的测试中约为 3%)的“城市”列的顺序不正确。


尝试 1:使用 dplyrcollect_list

my_sdf %>%
  dplyr::group_by(userid) %>%
  dplyr::arrange(date) %>%
  dplyr::summarise(cities = paste(collect_list(city), sep = ", ")))

尝试2:使用replyr::gapply,因为该操作符合“Grouped-Order-Apply”的描述。

get_cities <- . %>%
   summarise(cities = paste(collect_list(city), sep = ", "))

my_sdf %>%
  replyr::gapply(gcolumn = "userid",
                 f = get_cities,
                 ocolumn = "date",
                 partitionMethod = "group_by")

尝试 3:编写为 SQL 窗口函数。

my_sdf %>% 
  spark_session(sc) %>%
  sparklyr::invoke("sql", 
                   "SELECT userid, CONCAT_WS(', ', collect_list(city)) AS cities
                   OVER (PARTITION BY userid
                         ORDER BY date)
                   FROM my_sdf") %>%
  sparklyr::sdf_register() %>%
  sparklyr::sdf_copy_to(sc, ., "my_sdf", overwrite = T)

^ 抛出以下错误:

Error: org.apache.spark.sql.catalyst.parser.ParseException: 
mismatched input 'OVER' expecting <EOF>(line 2, pos 19)

== SQL ==
SELECT userid, conversion_location, CONCAT_WS(' > ', collect_list(channel)) AS path
                   OVER (PARTITION BY userid, conversion_location
-------------------^^^
                         ORDER BY occurred_at)
                   FROM paths_model

【问题讨论】:

    标签: r sparklyr


    【解决方案1】:

    解决了!我误解了 collect_list() 和 Spark SQL 如何协同工作。我没有意识到可以返回一个列表,我认为连接必须在查询中进行。以下产生了预期的结果:

    spark_output <- spark_session(sc) %>%
      sparklyr::invoke("sql", 
                       "SELECT userid, collect_list(city)
                       OVER (PARTITION BY userid
                             ORDER BY date
                             ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
                       AS cities
                       FROM my_sdf") %>%
      sdf_register() %>%
      group_by(userid) %>%
      filter(row_number(userid) == 1) %>%
      ungroup() %>%
      mutate(cities = paste(cities, sep = " > ")) %>%
      sdf_register()
    

    【讨论】:

    【解决方案2】:

    好的:所以我承认以下解决方案根本没有效率(它使用 for 循环,实际上是很多代码,看起来可能是一项简单的任务),但我相信这应该可行:

    #install.packages("tidyverse") # if needed
    library(tidyverse)
    
    df <- tribble(
      ~userid, ~date, ~city,
      1   ,  "2018-08-02"  ,   "A",
      1   ,  "2018-08-03"  ,   "B",
      1   ,  "2018-08-04"  ,   "C",
      2   ,  "2018-08-17"  ,   "G",
      2   ,  "2018-08-20"  ,   "E",
      2   ,  "2018-08-23"  ,   "F"
    )
    
    cityPerId <- df %>% 
      spread(key = date, value = city) 
    
    toMutate <- NA
    for (i in 1:nrow(cityPerId)) {
      cities <- cityPerId[i,][2:ncol(cityPerId)] %>% t() %>%
        as.vector() %>% 
        na.omit()
      collapsedCities <- paste(cities, collapse = ",")
      toMutate <- c(toMutate, collapsedCities)
    }
    toMutate <- toMutate[2:length(toMutate)]
    
    final <- cityPerId %>% 
      mutate(cities = toMutate) %>% 
      select(userid, cities)
    
    

    【讨论】:

    • 感谢您的想法 - 这适用于本地存储的数据帧,但 spread 和括号子集在 Spark DataFrames 上不起作用。
    • 知道了;你试过这些类似的功能吗? reshape::cast()spreadsheets::pviot()databases::unfold() 用于传播数据?
    • 看起来reshape 与 Spark DataFrames 不兼容,我找不到其他两个函数/包。
    猜你喜欢
    • 2018-03-16
    • 1970-01-01
    • 1970-01-01
    • 2018-08-29
    • 2014-04-01
    • 2019-01-07
    • 1970-01-01
    • 2018-01-20
    相关资源
    最近更新 更多