【问题标题】:Mutate while accessing value in list column in a pipe with map and pluck使用 map 和 pluck 访问管道中列表列中的值时发生变异
【发布时间】:2019-09-29 14:54:32
【问题描述】:

我想实现以下目标:

  • 根据数据框 orders 中的多列过滤数据框 catalogs,针对数据框 orders 中的每一行,并将结果存储在数据框订单。 (成功)
  • 计算数据框 orders 中的日期与新列表列中的另一个日期之间的差异。

s_orders 包含不同人的订单数据(帐户密钥)。表s_catalogs 包含发送到每个帐户密钥的所有目录

对于每个订单,我都想知道:

  • 是否以及从前一个订单(或开始)到焦点订单前一天发送了哪些目录。更具体地说,消费者在s_catalogs$CATDATE 收到了一份(纸质)目录。我想知道每个订单在上一个订单 (s_orders$PREVORDER) 和最新订单之间收到了哪些目录。因为一些消费者没有之前的订单,所以我将之前的订单日期 startdate 设置为 date("1999-12-31"),这是我的数据集的开始。
  • 然后我想对目录数据做一些计算。 (在本例中:计算目录日期和订单日期之间的差异)

为此,我编写了一个函数getCatalogs,它将帐户密钥和两个日期作为输入,并输出一个包含另一个表结果的数据框。 如果有人有更好、更有效的解决方案,将不胜感激?也许有某种加入?

我认为我的主要问题是如何交替使用 mutate、pmap、管道、pluck 来在多个表上构建复杂的查询。

期望结果问题部分概述了我的实际问题。

# packages needed
library("dplyr")
library("lubridate")
library("purrr")
#library("tidyverse")

示例数据

(我从我的数据中抽取了一些用户。s_ 代表“样本”)

# orders
s_orders <- structure(list(ACCNTKEY = c(2806, 2806, 2806, 3729, 3729, 3729, 
3729, 4607, 4607, 4607, 4607, 4742, 11040, 11040, 11040, 11040, 
11040, 17384), ORDDATE = structure(c(11325, 11703, 11709, 11330, 
11375, 11384, 12153, 11332, 11445, 11589, 11713, 11333, 11353, 
11429, 11662, 11868, 11960, 11382), class = "Date")), class = c("tbl_df", 
"tbl", "data.frame"), row.names = c(NA, -18L))

# # A tibble: 18 x 2
#    ACCNTKEY ORDDATE   
#       <dbl> <date>    
#  1     2806 2001-01-03
#  2     2806 2002-01-16
#  3     2806 2002-01-22
#  4     3729 2001-01-08
#  5     3729 2001-02-22
#  6     3729 2001-03-03
#  7     3729 2003-04-11
#  8     4607 2001-01-10
#  9     4607 2001-05-03
# 10     4607 2001-09-24
# 11     4607 2002-01-26
# 12     4742 2001-01-11
# 13    11040 2001-01-31
# 14    11040 2001-04-17
# 15    11040 2001-12-06
# 16    11040 2002-06-30
# 17    11040 2002-09-30
# 18    17384 2001-03-01

# catalogs
s_catalogs <- structure(list(ACCNTKEY = c("2806", "2806", "4607", "2806", "4607", 
"4607", "4607"), CATDATE = structure(c(11480, 11494, 11522, 11858, 
11886, 12264, 12250), class = "Date"), CODE = c("2806/07/2001", 
"2806/21/2001", "4607/19/2001", "2806/20/2002", "4607/18/2002", 
"4607/31/2003", "4607/17/2003")), row.names = c(NA, -7L), class = c("tbl_df", 
"tbl", "data.frame"))

# # A tibble: 7 x 3
#   ACCNTKEY CATDATE    CODE        
#   <chr>    <date>     <chr>       
# 1 2806     2001-06-07 2806/07/2001
# 2 2806     2001-06-21 2806/21/2001
# 3 4607     2001-07-19 4607/19/2001
# 4 2806     2002-06-20 2806/20/2002
# 5 4607     2002-07-18 4607/18/2002
# 6 4607     2003-07-31 4607/31/2003
# 7 4607     2003-07-17 4607/17/2003

计算滞后订单日期

# calculate previous order date for each order in s_orders
s_orders<-s_orders %>%
  group_by(ACCNTKEY) %>%
  arrange(ORDDATE) %>%
  mutate(PREVORDER=as_date(lag(ORDDATE)))

所以现在我们知道了之前的顺序(如果有的话)

函数 getCatalogs(感谢改进)

因此,以下函数 getCatalogs 返回一个数据帧,其中包含该帐户密钥在订单之前(或实际上在 startdate 之间收到的最后一个订单/目录之间收到的目录结束日期)。

# in case _startdate_ is missing then I set it to some starting value

getCatalogs<-function(key,startdate,enddate){

  if(is.na(startdate)){
    startdate<-as_date(date("1999-12-31")) 
  }
  tmp <- s_catalogs[s_catalogs$ACCNTKEY==key &
                    s_catalogs$CATDATE<enddate &
                    s_catalogs$CATDATE>=startdate,]

  if (NROW(tmp)>0){
    return(tmp)
  }else{return(NA)}
}

使用函数

让我们在 listcolumn

中为每个订单获取所有目录
# For each row in s_orders search in dataframe s_catalogs all catalogs that were received for that account key before the order date but after the previous order. 

s_orders <- s_orders %>% as_tibble() %>% 
  mutate(catalogs = 
pmap(c(list(ACCNTKEY),list(PREVORDER),list(ORDDATE)),.f= function(x,y,z){getCatalogs(x,y,z)}))

此行例如获取最新目录的日期,这是我需要的

s_orders %>% pluck("catalogs") %>% pluck(13) %>% pluck("CATDATE") %>% max()

# [1] "2001-06-21"

想要的结果:

现在我想检索上述日期与订单日期 (ORDDATE) 之间的天数。 以下代码完全正确,但仅在第 13 行是正确的。

# get amount of days since last catalog
s_orders3 <- s_orders %>% 
mutate(diff = ORDDATE - s_orders %>% 
              pluck("catalogs") %>% pluck(13) %>% pluck("CATDATE") %>% max())

# # A tibble: 18 x 5
#    ACCNTKEY ORDDATE    PREVORDER  catalogs         diff     
#       <dbl> <date>     <date>     <list>           <time>   
#  1     2806 2001-01-03 NA         <lgl [1]>        -169 days
#  2     3729 2001-01-08 NA         <lgl [1]>        -164 days
#  3     4607 2001-01-10 NA         <lgl [1]>        -162 days
#  4     4742 2001-01-11 NA         <lgl [1]>        -161 days
#  5    11040 2001-01-31 NA         <lgl [1]>        -141 days
#  6     3729 2001-02-22 2001-01-08 <lgl [1]>        -119 days
#  7    17384 2001-03-01 NA         <lgl [1]>        -112 days
#  8     3729 2001-03-03 2001-02-22 <lgl [1]>        -110 days
#  9    11040 2001-04-17 2001-01-31 <lgl [1]>         -65 days
# 10     4607 2001-05-03 2001-01-10 <lgl [1]>         -49 days
# 11     4607 2001-09-24 2001-05-03 <tibble [1 × 3]>   95 days
# 12    11040 2001-12-06 2001-04-17 <lgl [1]>         168 days
# 13     2806 2002-01-16 2001-01-03 <tibble [2 × 3]>  209 days
# 14     2806 2002-01-22 2002-01-16 <lgl [1]>         215 days
# 15     4607 2002-01-26 2001-09-24 <lgl [1]>         219 days
# 16    11040 2002-06-30 2001-12-06 <lgl [1]>         374 days
# 17    11040 2002-09-30 2002-06-30 <lgl [1]>         466 days
# 18     3729 2003-04-11 2001-03-03 <lgl [1]>         659 days

手动检查:

date("2002-01-16")-date("2001-06-21")
# Time difference of 209 days

问题

但是,代码会从每一行的订单日期中减去相同的日期。我希望它使用属于每个特定行的日期。

所以问题是如何用一些命令替换%&gt;% pluck(13) %&gt;%,该命令将此技巧应用于每一行并将其放在差异列中。

我真的在寻找一种使用 purrr 或 dplyr 或其他同样高效和清晰的软件包的解决方案。

【问题讨论】:

  • 感谢您为解决问题所做的大量工作,但如果您可以隔离您遇到的问题,我们可能更容易提供帮助(也可能让您进行调试) ,可能分成单独的问题,或者将其缩减为问题的基本部分。请参阅minimal reproducible exampleminimal 部分
  • @camille tnx 为您提供反馈!最小的问题在“期望的结果”和“问题”部分。您将需要以上的一切。当然,我可以删除“我试过”部分中的所有内容。然而,这些失败的尝试可能会为答案提供指导。
  • 您能否详细说明一下?:“是否以及从上一个订单(或开始)到焦点订单前一天发送了哪些目录”
  • 亲爱的@VitaliAvagyan,我刚刚更新了这篇文章,对此进行了一些详细说明。感谢您指出这一点!
  • 感谢您的编辑,@Dr.T. ,我会试一试...

标签: r dictionary dplyr pipe purrr


【解决方案1】:

希望我已经清楚地理解了这个问题,这是我试图解决这个问题的尝试。我将 getCatalogs 函数更改为仅返回 max CATDATE 以防万一。

library(dplyr)
library(purrr)

getCatalogs<-function(key,startdate,enddate){
    if(is.na(startdate))  startdate<- as.Date("1999-12-31")
    tmp <- s_catalogs$CATDATE[s_catalogs$ACCNTKEY==key &
                              s_catalogs$CATDATE<enddate &
                              s_catalogs$CATDATE>=startdate]

    if (length(tmp) > 0) max(tmp) else NA
}


s1_orders<- s_orders %>%
               group_by(ACCNTKEY) %>%
               arrange(ORDDATE) %>%
               mutate(PREVORDER=lag(ORDDATE))

然后使用pmap 喜欢:

s1_orders %>% 
  mutate(catalogs = pmap_dbl(list(ACCNTKEY,PREVORDER,ORDDATE), getCatalogs), 
         catalogs = as.Date(catalogs, origin = "1970-01-01"), 
         diff = ORDDATE - catalogs)

#   ACCNTKEY ORDDATE    PREVORDER  catalogs   diff    
#      <dbl> <date>     <date>     <date>     <drtn>  
# 1     2806 2001-01-03 NA         NA          NA days
# 2     3729 2001-01-08 NA         NA          NA days
# 3     4607 2001-01-10 NA         NA          NA days
# 4     4742 2001-01-11 NA         NA          NA days
# 5    11040 2001-01-31 NA         NA          NA days
# 6     3729 2001-02-22 2001-01-08 NA          NA days
# 7    17384 2001-03-01 NA         NA          NA days
# 8     3729 2001-03-03 2001-02-22 NA          NA days
# 9    11040 2001-04-17 2001-01-31 NA          NA days
#10     4607 2001-05-03 2001-01-10 NA          NA days
#11     4607 2001-09-24 2001-05-03 2001-07-19  67 days
#12    11040 2001-12-06 2001-04-17 NA          NA days
#13     2806 2002-01-16 2001-01-03 2001-06-21 209 days
#14     2806 2002-01-22 2002-01-16 NA          NA days
#15     4607 2002-01-26 2001-09-24 NA          NA days
#16    11040 2002-06-30 2001-12-06 NA          NA days
#17    11040 2002-09-30 2002-06-30 NA          NA days
#18     3729 2003-04-11 2001-03-03 NA          NA days

更新

在不改变当前getCatalogs函数的情况下,我们可以测试catalogslength

s1_orders %>% 
  mutate(catalogs = pmap(list(ACCNTKEY,PREVORDER,ORDDATE), getCatalogs),
         temp =  map_dbl(catalogs, ~if (length(.x) > 1)
                   .x %>% pluck("CATDATE") %>% max else NA), 
         temp = as.Date(temp, origin = "1970-01-01"),
         diff = ORDDATE - temp)

【讨论】:

  • 亲爱的@Ronak Shah,感谢您的努力!然而,这只是部分地解决了问题。我使用列表列有一个特定的原因。事实上,我已经发送了 >45k 的订单和 >800k 的目录。当我将每个订单的目录存储在列表列中时,我只需执行一次该操作,之后我就可以保存数据集。因此,我专门寻找一种从列表列中提取数据的解决方案,这样我就可以对同一选择的目录进行多次计算,而无需再次运行 getCatalogs 查询。
  • @Dr.T.我已经更新了从列表列中提取数据的答案。请参阅答案的更新部分。
  • 完美!感谢您的帮助!您的查询更有意义! (首先计算中间列,然后计算简单的差异。我正在以相反的方式工作)
  • 抱歉,我只检查了向上箭头。这是我的第一个问题,所以你可以从我必须熟悉 Stackoverflow 的 cmets 中推断出来。
  • 是的..我看到这是您的第一个问题。 :)
猜你喜欢
  • 1970-01-01
  • 2020-11-07
  • 2019-05-12
  • 1970-01-01
  • 1970-01-01
  • 2019-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多