带有 dplyr 管道的循环:正确使用动态和日期变量的问题

For loop with dplyr pipeline: problem using dynamic and date variables correctly

我有以下代码和示例数据。我有两个问题:

  1. 使用 mutate 创建的新变量的名称在相应的数据框中显示为“New_var”,而不是我的字符串(例如 df1_timediff)在 for 循环中分配给它。
    基于对类似问题的回答,我尝试在定义 New_var 变量时和在管道内使用 eval、as.name 和 as.character,但没有成功。当我检查 New_var 的 class 时,R 告诉我它们是“字符”。

  2. 我希望 New_var 变量是当前条目和相应参与者的第一个条目之间的时间差变量。我以前使用过类似的代码,但是 New_var 变量似乎与预期的不一样。也就是说,返回的时间差不是条目之间的月份。 Submitted_i 变量的 class 是日期格式,所以我很困惑为什么会这样。

代码

names.dfs <- c("df1", "df2", "df3")

for (i in names.dfs){

  Submitted_i <- as.name(paste0('Submitted_', i))
  New_var <- as.name(paste0(i,'_timediff'))
  
  df_i <-  get(i)
  
  df_i <- df_i %>%
        arrange(eval(Submitted_i)) %>% # Order by date
        group_by(ResultsID) %>% 
        mutate(New_var = (time_length(difftime(eval(Submitted_i), eval(Submitted_i)[1],"months")))) 
               
  assign(paste0(i),df_i)

  }

示例数据


df1 <- structure(list(ResultsID = c(1, 2, 3, 4, 2, 4, 1, 5, 3, 3), RepeatNo = c(0L, 
0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L), Submitted_df1 = structure(c(17509, 
17509, 17514, 17484, 17929, 17484, 17502, 17528, 17497, 17488
), class = "Date")), row.names = c(NA, 10L), class = "data.frame")
  
df2 <- structure(list(ResultsID = c(1, 5, 1, 3, 2, 4, 5), RepeatNo = c(0L, 
0L, 0L, 0L, 0L, 0L, 0L), Submitted_df2 = structure(c(16856, 16858, 
16869, 16861, 16875, 16888, 16891), class = "Date")), row.names = c(NA, 
7L), class = "data.frame")
  
df3 <- structure(list(ResultsID = c(1, 2, 3, 1, 2, 4, 4, 5, 3), RepeatNo = c(0L, 
0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L), Submitted_df3 = structure(c(17913, 
17930, 17919, 17931, 17921, 17912, 17916, 17931, 17915), class = "Date")), row.names = c(NA, 
-9L), groups = structure(list(.rows = structure(list(1L, 2L, 
    3L, 4L, 5L, 6L, 7L, 8L, 9L), ptype = integer(0), class = c("vctrs_list_of", 
"vctrs_vctr", "list"))), row.names = c(NA, -9L), class = c("tbl_df", 
"tbl", "data.frame")), class = c("rowwise_df", "tbl_df", "tbl", 
"data.frame"))

你的第二个问题是括号的问题。在您的代码中,“months”是 difftime 函数的第三个参数,而不是 time_length 函数的单位参数。当您添加来自 Martin Gal 的评论时,它工作正常:

library(lubridate)
library(dplyr)

names.dfs <- c("df1", "df2", "df3")

for (i in names.dfs){

  Submitted_i <- as.name(paste0('Submitted_', i))
  New_var <-  as.name(paste0(i,'_timediff'))

  df_i <-  get(i)

  df_i <- df_i %>%
    arrange(eval(Submitted_i)) %>% # Order by date
    group_by(ResultsID) %>% 
    mutate({{New_var}} := time_length(
                               difftime(
                                   eval(Submitted_i),
                                   eval(Submitted_i)[1]
                               ),
                               "months"
                           ) 
     )

  assign(paste0(i),df_i)

}

我认为您应该考虑将 data.frames 存储在 data.frames 的列表中。如果您需要使用 get-assign-结构,通常有更优雅的方法。

接下来您可以使用 purrrmap 函数将您的工作流程应用于这些数据框。在 map 函数中,我建议重命名列以避免卷曲和 as.name 结构:

library(dplyr)
library(lubridate)
library(purrr)

# create a named list of data.frames
my_list <- list(df1, df2, df3)
names(my_list) <- c("df1", "df2", "df3")

# apply your workflow
my_result_list <- my_list %>% 
  imap(~ .x %>% 
         tibble() %>% 
         # ungroup() %>% 
         `names<-`(., sub("_df.*", "", names(.))) %>% 
         arrange(Submitted) %>%
         group_by(ResultsID) %>% 
# replace / months(1) by %/% months(1) if you want full months, or use a rounding function
         mutate(difftime = interval(first(Submitted), Submitted) / months(1)) %>% 
         rename_with(function(x) paste0("Submitted_", .y), starts_with("Submitted")) %>% 
         rename_with(function(x) paste0(.y, "_difftime"), ends_with("difftime")) %>% 
         ungroup()
  )

这个 returns 列表 data.frames 像这样:

$df1
# A tibble: 10 x 4
   ResultsID RepeatNo Submitted_df1 df1_difftime
       <dbl>    <int> <date>               <dbl>
 1         4        0 2017-11-14           0    
 2         4        0 2017-11-14           0    
 3         3        0 2017-11-18           0    
 4         3        0 2017-11-27           0.3  
 5         1        0 2017-12-02           0    
 6         1        0 2017-12-09           0.226
 7         2        0 2017-12-09           0    
 8         3        0 2017-12-14           0.867
 9         5        0 2017-12-28           0    
10         2        0 2019-02-02          13.8  

$df2
# A tibble: 7 x 4
  ResultsID RepeatNo Submitted_df2 df2_difftime
      <dbl>    <int> <date>               <dbl>
1         1        0 2016-02-25           0    
2         5        0 2016-02-27           0    
3         3        0 2016-03-01           0    
4         1        0 2016-03-09           0.448
5         2        0 2016-03-15           0    
6         4        0 2016-03-28           0    
7         5        0 2016-03-31           1.13 

$df3
# A tibble: 9 x 4
  ResultsID RepeatNo Submitted_df3 df3_difftime
      <dbl>    <int> <date>               <dbl>
1         4        0 2019-01-16           0    
2         1        0 2019-01-17           0    
3         3        0 2019-01-19           0    
4         4        0 2019-01-20           0.129
5         3        0 2019-01-23           0.129
6         2        0 2019-01-25           0    
7         2        0 2019-02-03           0.290
8         1        0 2019-02-04           0.581
9         5        0 2019-02-04           0 

现在您可以像这样使用您的 data.frames:my_result_list[[1]] returns 您转换后的 df1my_result_list[[2]] returns df2