带有 dplyr 管道的循环:正确使用动态和日期变量的问题
For loop with dplyr pipeline: problem using dynamic and date variables correctly
我有以下代码和示例数据。我有两个问题:
使用 mutate 创建的新变量的名称在相应的数据框中显示为“New_var”,而不是我的字符串(例如 df1_timediff)在 for 循环中分配给它。
基于对类似问题的回答,我尝试在定义 New_var 变量时和在管道内使用 eval、as.name 和 as.character,但没有成功。当我检查 New_var 的 class 时,R 告诉我它们是“字符”。
我希望 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
-结构,通常有更优雅的方法。
接下来您可以使用 purrr
的 map
函数将您的工作流程应用于这些数据框。在 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 您转换后的 df1
、my_result_list[[2]]
returns df2
等
我有以下代码和示例数据。我有两个问题:
使用 mutate 创建的新变量的名称在相应的数据框中显示为“New_var”,而不是我的字符串(例如 df1_timediff)在 for 循环中分配给它。
基于对类似问题的回答,我尝试在定义 New_var 变量时和在管道内使用 eval、as.name 和 as.character,但没有成功。当我检查 New_var 的 class 时,R 告诉我它们是“字符”。我希望 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
-结构,通常有更优雅的方法。
接下来您可以使用 purrr
的 map
函数将您的工作流程应用于这些数据框。在 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 您转换后的 df1
、my_result_list[[2]]
returns df2
等