避免在函数中创建重复的变量名
avoid creating duplicate variable names in function
我有一个 dataframe
和一个 function
创建一个新变量,将其添加到数据框,然后 assign
将数据框返回到全局环境。问题是,如果我重新运行函数,它会创建变量的副本。
library(tidyverse)
library(rms)
set.seed(10)
ds <- data.frame(
ftime = rexp(200),
fstatus = sample(0:1,200, replace = TRUE),
x1 = runif(200),
x2 = runif(200),
x3 = factor(sample(LETTERS[1:3], size = 200, replace = TRUE)))
ds
#model
s <- Surv(ds$ftime, ds$fstatus == 1)
fit <- cph(s ~ x1 + x2 + x3, data = ds, surv = TRUE, x = TRUE, y = TRUE)
#function to add prediction to dataset
pred_fun <- function(time_to_sur, model) {
pred_data <- ds[, c("x1", "x2", "x3")] %>%
mutate(ftime = time_to_sur,
fstatus = 1) %>%
as.data.frame()
ds$pred_var_tmp <-
rms::survest(model, times = time_to_sur,
newdata = pred_data,
se.fit = FALSE, what = "survival")$surv
#rename variable
pred_var <- paste0("pred_prob_", as.character(time_to_sur), "_rms")
names(ds)[names(ds) == "pred_var_tmp"] <- pred_var
#assign dataset back to global environment
assign("ds", ds, env = .GlobalEnv)
}
函数正常运行:
pred_fun(time_to_sur = 0.2, fit)
names(ds)
# [1] "ftime" "fstatus" "x1"
# [4] "x2" "x3" "pred_prob_0.2_rms"
但是如果我再次运行它,它会创建变量的副本
pred_fun(time_to_sur = 0.2, fit)
names(ds)
# [1] "ftime" "fstatus" "x1"
# [4] "x2" "x3" "pred_prob_0.2_rms"
# [7] "pred_prob_0.2_rms"
这是意料之中的,因为该函数首先创建一个具有不同名称的新变量,然后分配名称。我认为以下内容可能会在该函数中起作用,但它不起作用:
ds$eval(substitute(paste0("pred_prob_", as.character(tt), "_rms"))) <-
rms::survest(model, times = time_to_sur,
newdata = pred_data,
se.fit = FALSE, what = "survival")$surv
我该如何解决这个问题以及在这种情况下的最佳做法是什么?
谢谢
1) Base R 这将覆盖现有的列(如果它已经存在)。这将用 11:16.
覆盖最初为 c(1, 2, 3, 4, 5, 7) 的时间
newName <- "Time" # duplicated column name
values <- 11:16
replace(BOD, newName, values)
## Time demand
## 1 11 8.3
## 2 12 10.3
## ...
如果新列名不存在,则会创建一个新列。
newName <- "Time2" # new column name, not duplicate
values <- 11:16
replace(BOD, newName, values)
## Time demand Time2
## 1 1 8.3 11
## 2 2 10.3 12
## ...
2) dplyr 如果你想为此使用 dplyr,那么:
library(dplyr)
newName <- "Time" # duplicated column name
values <- 11:16
mutate(BOD, {{newName}} := values)
## Time demand
## 1 11 8.3
## 2 12 10.3
## ...
newName <- "Time2" # new column name, not duplicate
values <- 11:16
mutate(BOD, {{newName}} := values)
## Time demand Time2
## 1 1 8.3 11
## 2 2 10.3 12
## ...
其他
R 的功能性质。 R 是一种函数式语言,通常编写函数以通过参数传递输入并将输出作为 return 值传递。这里 x 是输入,y 是输出。
# ok
f <- function(x) x + 1
y <- f(3)
y
## [1] 4
最好不要这样做:
# not good
f <- function() assign("y", x + 1, .GlobalEnv)
x <- 3
f()
y
## [1] 4
替换函数。尽管不经常使用,但 R 确实支持这样的替换函数和语法(参见 https://cran.r-project.org/doc/manuals/r-release/R-lang.html#Subset-assignment)。例如,names<-
是一个常用的替换函数。我们首先创建 BOD 的副本,这样我们就不会覆盖它。这会保留输入,使其更易于调试。
`f<-` <- function(x, value) replace(x, "Time", value)
BOD2 <- BOD
f(BOD2) <- 11:16
BOD2
## Time demand
## 1 11 8.3
## 2 12 10.3
## ...
感谢@G。 Grothendieck 和@Limey,尽管我确实收到了 warning
消息,但以下简化仍然有效(pred_fun_final
)。
#original function in OP
pred_fun_original <- function(time_to_sur, model) {
pred_data <- ds[, c("x1", "x2", "x3")] %>%
mutate(ftime = time_to_sur,
fstatus = 1) %>%
as.data.frame()
ds$pred_var_tmp <-
rms::survest(model, times = time_to_sur,
newdata = pred_data,
se.fit = FALSE, what = "survival")$surv
#rename variable
pred_var <- paste0("pred_prob_", as.character(time_to_sur), "_rms")
names(ds)[names(ds) == "pred_var_tmp"] <- pred_var
assign("ds", ds, env = .GlobalEnv)
}
pred_fun_original(time_to_sur = 0.2, fit)
#save created variable
test1 <- ds$pred_prob_0.2_rms
#remove pred_prob_0.2_rms
ds <- ds %>%
select(-pred_prob_0.2_rms)
warning
的新功能:
#fixed function
pred_fun_final <- function(data, time_to_sur, model) {
newName <- paste0("pred_prob_", as.character(time_to_sur), "_rms")
pred_data <- data[, c("x1", "x2", "x3")] %>%
mutate(ftime = time_to_sur,
fstatus = 1) %>%
as.data.frame()
data <- data %>%
mutate({{newName}} := rms::survest(model, times = time_to_sur,
newdata = pred_data,
se.fit = FALSE, what = "survival")$surv)
data
}
ds <- pred_fun_final(ds, time_to_sur = 0.2, fit)
# Warning message:
# Problem with `mutate()` column `pred_prob_0.2_rms`.
# i `pred_prob_0.2_rms = ...$NULL`.
#save variable
test2 <- ds$pred_prob_0.2_rms
这两个变量并不相同,但这是因为一个已命名而另一个未命名(as.numeric()
将解决此问题)。不过,它并没有解释 warning
消息。
identical(test1, test2)
#FALSE
str(test1)
# num [1:200] 0.906 0.9 0.884 0.884 0.886 ...
str(test2)
# Named num [1:200] 0.906 0.9 0.884 0.884 0.886 ...
# - attr(*, "names")= chr [1:200] "1" "2" "3" "4" ...
我有一个 dataframe
和一个 function
创建一个新变量,将其添加到数据框,然后 assign
将数据框返回到全局环境。问题是,如果我重新运行函数,它会创建变量的副本。
library(tidyverse)
library(rms)
set.seed(10)
ds <- data.frame(
ftime = rexp(200),
fstatus = sample(0:1,200, replace = TRUE),
x1 = runif(200),
x2 = runif(200),
x3 = factor(sample(LETTERS[1:3], size = 200, replace = TRUE)))
ds
#model
s <- Surv(ds$ftime, ds$fstatus == 1)
fit <- cph(s ~ x1 + x2 + x3, data = ds, surv = TRUE, x = TRUE, y = TRUE)
#function to add prediction to dataset
pred_fun <- function(time_to_sur, model) {
pred_data <- ds[, c("x1", "x2", "x3")] %>%
mutate(ftime = time_to_sur,
fstatus = 1) %>%
as.data.frame()
ds$pred_var_tmp <-
rms::survest(model, times = time_to_sur,
newdata = pred_data,
se.fit = FALSE, what = "survival")$surv
#rename variable
pred_var <- paste0("pred_prob_", as.character(time_to_sur), "_rms")
names(ds)[names(ds) == "pred_var_tmp"] <- pred_var
#assign dataset back to global environment
assign("ds", ds, env = .GlobalEnv)
}
函数正常运行:
pred_fun(time_to_sur = 0.2, fit)
names(ds)
# [1] "ftime" "fstatus" "x1"
# [4] "x2" "x3" "pred_prob_0.2_rms"
但是如果我再次运行它,它会创建变量的副本
pred_fun(time_to_sur = 0.2, fit)
names(ds)
# [1] "ftime" "fstatus" "x1"
# [4] "x2" "x3" "pred_prob_0.2_rms"
# [7] "pred_prob_0.2_rms"
这是意料之中的,因为该函数首先创建一个具有不同名称的新变量,然后分配名称。我认为以下内容可能会在该函数中起作用,但它不起作用:
ds$eval(substitute(paste0("pred_prob_", as.character(tt), "_rms"))) <-
rms::survest(model, times = time_to_sur,
newdata = pred_data,
se.fit = FALSE, what = "survival")$surv
我该如何解决这个问题以及在这种情况下的最佳做法是什么?
谢谢
1) Base R 这将覆盖现有的列(如果它已经存在)。这将用 11:16.
覆盖最初为 c(1, 2, 3, 4, 5, 7) 的时间newName <- "Time" # duplicated column name
values <- 11:16
replace(BOD, newName, values)
## Time demand
## 1 11 8.3
## 2 12 10.3
## ...
如果新列名不存在,则会创建一个新列。
newName <- "Time2" # new column name, not duplicate
values <- 11:16
replace(BOD, newName, values)
## Time demand Time2
## 1 1 8.3 11
## 2 2 10.3 12
## ...
2) dplyr 如果你想为此使用 dplyr,那么:
library(dplyr)
newName <- "Time" # duplicated column name
values <- 11:16
mutate(BOD, {{newName}} := values)
## Time demand
## 1 11 8.3
## 2 12 10.3
## ...
newName <- "Time2" # new column name, not duplicate
values <- 11:16
mutate(BOD, {{newName}} := values)
## Time demand Time2
## 1 1 8.3 11
## 2 2 10.3 12
## ...
其他
R 的功能性质。 R 是一种函数式语言,通常编写函数以通过参数传递输入并将输出作为 return 值传递。这里 x 是输入,y 是输出。
# ok
f <- function(x) x + 1
y <- f(3)
y
## [1] 4
最好不要这样做:
# not good
f <- function() assign("y", x + 1, .GlobalEnv)
x <- 3
f()
y
## [1] 4
替换函数。尽管不经常使用,但 R 确实支持这样的替换函数和语法(参见 https://cran.r-project.org/doc/manuals/r-release/R-lang.html#Subset-assignment)。例如,names<-
是一个常用的替换函数。我们首先创建 BOD 的副本,这样我们就不会覆盖它。这会保留输入,使其更易于调试。
`f<-` <- function(x, value) replace(x, "Time", value)
BOD2 <- BOD
f(BOD2) <- 11:16
BOD2
## Time demand
## 1 11 8.3
## 2 12 10.3
## ...
感谢@G。 Grothendieck 和@Limey,尽管我确实收到了 warning
消息,但以下简化仍然有效(pred_fun_final
)。
#original function in OP
pred_fun_original <- function(time_to_sur, model) {
pred_data <- ds[, c("x1", "x2", "x3")] %>%
mutate(ftime = time_to_sur,
fstatus = 1) %>%
as.data.frame()
ds$pred_var_tmp <-
rms::survest(model, times = time_to_sur,
newdata = pred_data,
se.fit = FALSE, what = "survival")$surv
#rename variable
pred_var <- paste0("pred_prob_", as.character(time_to_sur), "_rms")
names(ds)[names(ds) == "pred_var_tmp"] <- pred_var
assign("ds", ds, env = .GlobalEnv)
}
pred_fun_original(time_to_sur = 0.2, fit)
#save created variable
test1 <- ds$pred_prob_0.2_rms
#remove pred_prob_0.2_rms
ds <- ds %>%
select(-pred_prob_0.2_rms)
warning
的新功能:
#fixed function
pred_fun_final <- function(data, time_to_sur, model) {
newName <- paste0("pred_prob_", as.character(time_to_sur), "_rms")
pred_data <- data[, c("x1", "x2", "x3")] %>%
mutate(ftime = time_to_sur,
fstatus = 1) %>%
as.data.frame()
data <- data %>%
mutate({{newName}} := rms::survest(model, times = time_to_sur,
newdata = pred_data,
se.fit = FALSE, what = "survival")$surv)
data
}
ds <- pred_fun_final(ds, time_to_sur = 0.2, fit)
# Warning message:
# Problem with `mutate()` column `pred_prob_0.2_rms`.
# i `pred_prob_0.2_rms = ...$NULL`.
#save variable
test2 <- ds$pred_prob_0.2_rms
这两个变量并不相同,但这是因为一个已命名而另一个未命名(as.numeric()
将解决此问题)。不过,它并没有解释 warning
消息。
identical(test1, test2)
#FALSE
str(test1)
# num [1:200] 0.906 0.9 0.884 0.884 0.886 ...
str(test2)
# Named num [1:200] 0.906 0.9 0.884 0.884 0.886 ...
# - attr(*, "names")= chr [1:200] "1" "2" "3" "4" ...