简体   繁体   中英

Overwriting an object in the global environment after using deparse(substitute()) in a function call

This is my debut post here. So please bear with me if it doesn't live up to the high standards of clarity of more seasoned members.

I have 4 objects (representing 4 years) in my global environment that are lists consisting of 12 data-frames (one for each month in the year). They have a consistent structure, and the column names of the data-frames are all the same. I'm trying to change these column names of the data-frames in all 4 lists in one fell swoop using a function, and then overwrite all 4 objects in my global environment with new objects that have the data-frames with the new column names.

This is my function:

change.name <- function(data){
  for (i in 1:length(data)){
    names(data[[i]]) <- c("a", "b", "c", "d", "e")
  }
  assign(deparse(substitute(data)), value = data, envir = globalenv())
}

I use my function:

change.name(my_object1)

It works, except that I get this warning message:

Warning message: In assign(deparse(substitute(data)), value = data, envir = globalenv()) : only the first element is used as variable name

And the object in my global environment is not overwritten. I get a new object with a name like this:

"list(Jan = structure(list(a = c(11, 34, 36, 49, 55, 68, "

I understand that this has to do with the way the function variable is stored in the new environment R creates when running a function (or something along those lines).

My question is simple: How do I remedy this?

Right you are, the problem lies in the way function s behave. Take a look at the following code, it might help

testFun1 <- function (val) {
  a <<- val
  assign("b",a)
}
testFun2 <- function (val) {
  a <<- val
  assign("b",a, pos = 1)
}

# environment pretty much empty apart from our functions
ls()
[1] "testFun1" "testFun2"
# run
set.seed(123)
testFun1(runif(1))
# less empty
ls()
[1] "a"        "testFun1" "testFun2"
# still not quite it though
testFun2(runif(1))
# now that's better
ls()
[1] "a"        "b"        "testFun1" "testFun2"

For more information, take a look at the documentation ( ?assign ), especially the pos argument.

You can fix this by using deparse(substitute(data)) before you do anything to data :

# Let's change your function just a bit
change.name <- function(data){
    # call deparse(substutite()) *before* you do anything to data
    object_name <- deparse(substitute(data))
    for (i in 1:length(data)){
        names(data[[i]]) <- c("a", "b", "c", "d", "e")
    }
    assign(object_name, value = data, envir = globalenv())
}

# Create sample data
my_object1 <- lapply(1:12, function(x) {
    data.frame(u = 1, v = 2, x = 3, y = 4, z = 5)
})
names(my_object1) <- month.name

change.name(my_object1)
ls()
#> [1] "change.name" "my_object1"
head(my_object1, 2)
#> $January
#>   a b c d e
#> 1 1 2 3 4 5
#> 
#> $February
#>   a b c d e
#> 1 1 2 3 4 5

Created on 2018-12-20 by the reprex package (v0.2.1)

A more idiomatic (and probably safer) way to approach this task might be to simply use lapply and setNames :

my_object1 <- lapply(1:12, function(x) {
    data.frame(u = 1, v = 2, x = 3, y = 4, z = 5)
})
names(my_object1) <- month.name

change.name <- function(obj){
    lapply(obj,function(x) setNames(x,letters[1:5]))
}

my_object1 <- change.name(my_object1)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM