I'd like to rename objects in environment r. For example,
y1 <- vector('list', 3)
x1 <- matrix(0, 3, 3)
x2 <- matrix(1, 3, 3)
x3 <- matrix(2, 3, 3)
y1[[1]] <- x1
y1[[2]] <- x2
y1[[3]] <- x3
y2 <- vector('list', 3)
y2[[1]] <- x1
y2[[2]] <- x2
y2[[3]] <- x3
y <- new.env()
y$y1 <- y1
y$y2 <- y2
names(y)
names(y) <- c('a', 'b')
I expected that the name of lists inside y was a and b, that is, names(y) equals c('a', 'b'),
Obs.: I can't rename manually the variables y1 and y2, I need to change them inside the environment.
If you can’t assign them directly with the correct name, then the easiest is to replace the environment by a new one. If you absolutely need to preserve the environment (because it’s referenced elsewhere), you can replace its contents using the same trick:
objs = mget(ls(env), env)
rm(list = ls(env), envir = env)
list2env(setNames(objs, new_names), env)
The relevant part here is the last parameter to list2env: if you leave it off, this just creates a new environment. If you specify an existing environment, the names are added to that instead.
This code will leave hidden names (i.e. names starting with .) untouched — to change this, provide the all.names argument to ls, or use names.
R doesn't really have a built in operation to rename variables in any environment. YOu could write a simple helper function to do that.
env_rename <- function(e, new_names, old_names = names(e)) {
stopifnot(length(new_names)==length(old_names))
orig_val <- mget(old_names, envir=e)
rm(list=old_names, envir=e)
for(i in seq_along(old_names)) {
assign(new_names[i], orig_val[[i]], envir=e)
}
}
and call that with
env_rename(y, c("a","b"))
Do you really need an environment, or a list could do the job?
If so, you could rename the list items easily:
...
...
y=list()
y$y1 <- y1
y$y2 <- y2
names(y)=c('a','b')
names(y)
[1] "a" "b"
I have the opposite problem: getSymbols put the result in an environment and I changed it to a list to rename them:
acao
[1] "PETR4.SA" "VALE3.SA" "ITUB4.SA"
require(quantmod)
e1=new.env()
x=getSymbols(acao,env=e1)
e1=as.list(e1)
names(e1)
[1] "ITUB4.SA" "VALE3.SA" "PETR4.SA"
names(e1)=sub('.SA$','',names(e1))
names(e1)
[1] "ITUB4" "VALE3" "PETR4"
Related
A post on here a day back has me wondering how to assign values to multiple objects in the global environment from within a function. This is my attempt using lapply (assign may be safer than <<- but I have never actually used it and am not familiar with it).
#fake data set
df <- data.frame(
x.2=rnorm(25),
y.2=rnorm(25),
g=rep(factor(LETTERS[1:5]), 5)
)
#split it into a list of data frames
LIST <- split(df, df$g)
#pre-allot 5 objects in R with class data.frame()
V <- W <- X <- Y <- Z <- data.frame()
#attempt to assign the data frames in the LIST to the objects just created
lapply(seq_along(LIST), function(x) c(V, W, X, Y, Z)[x] <<- LIST[[x]])
Please feel free to shorten any/all parts of my code to make this work (or work better/faster).
Update of 2018-10-10:
The most succinct way to carry out this specific task is to use list2env() like so:
## Create an example list of five data.frames
df <- data.frame(x = rnorm(25),
g = rep(factor(LETTERS[1:5]), 5))
LIST <- split(df, df$g)
## Assign them to the global environment
list2env(LIST, envir = .GlobalEnv)
## Check that it worked
ls()
## [1] "A" "B" "C" "D" "df" "E" "LIST"
Original answer, demonstrating use of assign()
You're right that assign() is the right tool for the job. Its envir argument gives you precise control over where assignment takes place -- control that is not available with either <- or <<-.
So, for example, to assign the value of X to an object named NAME in the the global environment, you would do:
assign("NAME", X, envir = .GlobalEnv)
In your case:
df <- data.frame(x = rnorm(25),
g = rep(factor(LETTERS[1:5]), 5))
LIST <- split(df, df$g)
NAMES <- c("V", "W", "X", "Y", "Z")
lapply(seq_along(LIST),
function(x) {
assign(NAMES[x], LIST[[x]], envir=.GlobalEnv)
}
)
ls()
[1] "df" "LIST" "NAMES" "V" "W" "X" "Y" "Z"
I think this question can have a nice crossover with this one: Can lists be created that name themselves based on input object names?
Say you want to do the same modification to a set of objects on the fly. But list2env() requires a named list, and you don't want to copy and paste them again. Borrowing the namedList function, and combining it with
Josh O'Brien anwser:
> namedList <- function(...) {
+ L <- list(...)
+ snm <- sapply(substitute(list(...)), deparse)[-1]
+ if (is.null(nm <- names(L))) nm <- snm
+ if (any(nonames <- nm=="")) nm[nonames] <- snm[nonames]
+ setNames(L ,nm)
+ }
>
> df_1 <- data.frame(x = 1)
> df_2 <- data.frame(x = 2)
> df_3 <- data.frame(x = 3)
>
> list2env(lapply(namedList(df_1, df_2, df_3), function(x) {
+ x <- cbind.data.frame(x, y = "B")
+ }), envir = .GlobalEnv)
<environment: R_GlobalEnv>
>
> df_1
x y
1 1 B
> df_2
x y
1 2 B
> df_3
x y
1 3 B
If you have a list of object names and file paths you can also use mapply:
object_names <- c("df_1", "df_2", "df_3")
file_paths <- list.files({path}, pattern = ".csv", full.names = T)
mapply(function(df_name, file)
assign(df_name, read.csv(file), envir=.GlobalEnv),
object_names,
file_paths)
I used list.files() to construct a vector of all the .csv files in a
specific directory. But file_paths could be written or constructed in any way.
If the files you want to read in are in the current working
directory, then file_paths could be replaced with a character vector of
file names.
In the code above, you need to replace {path} with a
string of the desired directory's path.
This demonstrates how to split out a nested dataframe into objects in the global environment with tidyverse functions:
library(tidyverse)
library(palmerpenguins)
penguins %>%
group_nest(species) %>%
deframe() %>%
list2env(.GlobalEnv)
I know that if i have the name of a variable stored like a = "var.name", i can call this var.name by doing eval(as.symbol(a)) or get(a), but i wanted to not only call a variable, but also make changes to it. Example:
names = c("X1","X2")
for(i in names){
assign(i, cbind(replicate(2,rnorm(3))) #Just creating a 3x2 matrix with dummy data
###
At ### i'd like to make a change to the variables, specifically change its column names to "a" and "b".
I tried colnames(get(i)) = c("a","b"), or colnames(eval(as.symbol(i))) = c("a","b"), but they return errors like could not find function "eval<-"
One option could be to create the matrix in the first step, and name and assign to a new name in the second step.
names = c("X1","X2")
for(i in names){
x <- cbind(replicate(2,rnorm(3)))
assign(i, provideDimnames(x))
}
#--------------
> X1
A B
A -0.59174062 1.8527780
B -0.53088643 -3.2713544
C -0.09330006 -0.5977568
Another option would be to assign the dimnames at the time of creation of the matrix.
for (i in names) {
x <- matrix(replicate(2, rnorm(3)),
ncol = 2,
dimnames = list(a = c(LETTERS[1:3]), b = c(LETTERS[1:2])))
assign(i, x)
}
#-------------------
> X1
b
a A B
A -0.2313692 -0.93161762
B -0.9666849 0.06164904
C 1.5614446 -0.09391062
I am looking to make a function that takes a vector as input, does some simple arithmetic with the vector and call the new vector something which consists of a set string (say, "log.") plus the original vector name.
d = c(1 2, 3)
my.function <- function { x
x2 <- log(x)
...
I would like the function to return a vector called log.d (that is, not log.x or something set, but something dependent on the name of the vector input as x).
You can try next:
d = c(1, 2, 3)
my.function <- function(x){
x2 <- log(x)
arg_name <- deparse(substitute(x)) # Get argument name
var_name <- paste("log", arg_name, sep="_") # Construct the name
assign(var_name, x2, env=.GlobalEnv) # Assign values to variable
# variable will be created in .GlobalEnv
}
One way to do this would be to store separately names of all your input vector names and then pass them to assign function. Like assign takes text string for output object name, get looks up object from string.
I will assume your vectors all follow common pattern and start with "d", to make it all as dynamic as possible.
d1 <- c(1,2,3)
d2 <- c(2,3,4)
vec_names <- ls(pattern = "^d")
log_vec <- function(x){
log(x)
}
sapply(vec_names, function(x) assign(paste0("log.", x), log_vec(get(x)), envir = globalenv()))
This should create two new objects "log.d1" and "log.d2".
A post on here a day back has me wondering how to assign values to multiple objects in the global environment from within a function. This is my attempt using lapply (assign may be safer than <<- but I have never actually used it and am not familiar with it).
#fake data set
df <- data.frame(
x.2=rnorm(25),
y.2=rnorm(25),
g=rep(factor(LETTERS[1:5]), 5)
)
#split it into a list of data frames
LIST <- split(df, df$g)
#pre-allot 5 objects in R with class data.frame()
V <- W <- X <- Y <- Z <- data.frame()
#attempt to assign the data frames in the LIST to the objects just created
lapply(seq_along(LIST), function(x) c(V, W, X, Y, Z)[x] <<- LIST[[x]])
Please feel free to shorten any/all parts of my code to make this work (or work better/faster).
Update of 2018-10-10:
The most succinct way to carry out this specific task is to use list2env() like so:
## Create an example list of five data.frames
df <- data.frame(x = rnorm(25),
g = rep(factor(LETTERS[1:5]), 5))
LIST <- split(df, df$g)
## Assign them to the global environment
list2env(LIST, envir = .GlobalEnv)
## Check that it worked
ls()
## [1] "A" "B" "C" "D" "df" "E" "LIST"
Original answer, demonstrating use of assign()
You're right that assign() is the right tool for the job. Its envir argument gives you precise control over where assignment takes place -- control that is not available with either <- or <<-.
So, for example, to assign the value of X to an object named NAME in the the global environment, you would do:
assign("NAME", X, envir = .GlobalEnv)
In your case:
df <- data.frame(x = rnorm(25),
g = rep(factor(LETTERS[1:5]), 5))
LIST <- split(df, df$g)
NAMES <- c("V", "W", "X", "Y", "Z")
lapply(seq_along(LIST),
function(x) {
assign(NAMES[x], LIST[[x]], envir=.GlobalEnv)
}
)
ls()
[1] "df" "LIST" "NAMES" "V" "W" "X" "Y" "Z"
I think this question can have a nice crossover with this one: Can lists be created that name themselves based on input object names?
Say you want to do the same modification to a set of objects on the fly. But list2env() requires a named list, and you don't want to copy and paste them again. Borrowing the namedList function, and combining it with
Josh O'Brien anwser:
> namedList <- function(...) {
+ L <- list(...)
+ snm <- sapply(substitute(list(...)), deparse)[-1]
+ if (is.null(nm <- names(L))) nm <- snm
+ if (any(nonames <- nm=="")) nm[nonames] <- snm[nonames]
+ setNames(L ,nm)
+ }
>
> df_1 <- data.frame(x = 1)
> df_2 <- data.frame(x = 2)
> df_3 <- data.frame(x = 3)
>
> list2env(lapply(namedList(df_1, df_2, df_3), function(x) {
+ x <- cbind.data.frame(x, y = "B")
+ }), envir = .GlobalEnv)
<environment: R_GlobalEnv>
>
> df_1
x y
1 1 B
> df_2
x y
1 2 B
> df_3
x y
1 3 B
If you have a list of object names and file paths you can also use mapply:
object_names <- c("df_1", "df_2", "df_3")
file_paths <- list.files({path}, pattern = ".csv", full.names = T)
mapply(function(df_name, file)
assign(df_name, read.csv(file), envir=.GlobalEnv),
object_names,
file_paths)
I used list.files() to construct a vector of all the .csv files in a
specific directory. But file_paths could be written or constructed in any way.
If the files you want to read in are in the current working
directory, then file_paths could be replaced with a character vector of
file names.
In the code above, you need to replace {path} with a
string of the desired directory's path.
This demonstrates how to split out a nested dataframe into objects in the global environment with tidyverse functions:
library(tidyverse)
library(palmerpenguins)
penguins %>%
group_nest(species) %>%
deframe() %>%
list2env(.GlobalEnv)
A post on here a day back has me wondering how to assign values to multiple objects in the global environment from within a function. This is my attempt using lapply (assign may be safer than <<- but I have never actually used it and am not familiar with it).
#fake data set
df <- data.frame(
x.2=rnorm(25),
y.2=rnorm(25),
g=rep(factor(LETTERS[1:5]), 5)
)
#split it into a list of data frames
LIST <- split(df, df$g)
#pre-allot 5 objects in R with class data.frame()
V <- W <- X <- Y <- Z <- data.frame()
#attempt to assign the data frames in the LIST to the objects just created
lapply(seq_along(LIST), function(x) c(V, W, X, Y, Z)[x] <<- LIST[[x]])
Please feel free to shorten any/all parts of my code to make this work (or work better/faster).
Update of 2018-10-10:
The most succinct way to carry out this specific task is to use list2env() like so:
## Create an example list of five data.frames
df <- data.frame(x = rnorm(25),
g = rep(factor(LETTERS[1:5]), 5))
LIST <- split(df, df$g)
## Assign them to the global environment
list2env(LIST, envir = .GlobalEnv)
## Check that it worked
ls()
## [1] "A" "B" "C" "D" "df" "E" "LIST"
Original answer, demonstrating use of assign()
You're right that assign() is the right tool for the job. Its envir argument gives you precise control over where assignment takes place -- control that is not available with either <- or <<-.
So, for example, to assign the value of X to an object named NAME in the the global environment, you would do:
assign("NAME", X, envir = .GlobalEnv)
In your case:
df <- data.frame(x = rnorm(25),
g = rep(factor(LETTERS[1:5]), 5))
LIST <- split(df, df$g)
NAMES <- c("V", "W", "X", "Y", "Z")
lapply(seq_along(LIST),
function(x) {
assign(NAMES[x], LIST[[x]], envir=.GlobalEnv)
}
)
ls()
[1] "df" "LIST" "NAMES" "V" "W" "X" "Y" "Z"
I think this question can have a nice crossover with this one: Can lists be created that name themselves based on input object names?
Say you want to do the same modification to a set of objects on the fly. But list2env() requires a named list, and you don't want to copy and paste them again. Borrowing the namedList function, and combining it with
Josh O'Brien anwser:
> namedList <- function(...) {
+ L <- list(...)
+ snm <- sapply(substitute(list(...)), deparse)[-1]
+ if (is.null(nm <- names(L))) nm <- snm
+ if (any(nonames <- nm=="")) nm[nonames] <- snm[nonames]
+ setNames(L ,nm)
+ }
>
> df_1 <- data.frame(x = 1)
> df_2 <- data.frame(x = 2)
> df_3 <- data.frame(x = 3)
>
> list2env(lapply(namedList(df_1, df_2, df_3), function(x) {
+ x <- cbind.data.frame(x, y = "B")
+ }), envir = .GlobalEnv)
<environment: R_GlobalEnv>
>
> df_1
x y
1 1 B
> df_2
x y
1 2 B
> df_3
x y
1 3 B
If you have a list of object names and file paths you can also use mapply:
object_names <- c("df_1", "df_2", "df_3")
file_paths <- list.files({path}, pattern = ".csv", full.names = T)
mapply(function(df_name, file)
assign(df_name, read.csv(file), envir=.GlobalEnv),
object_names,
file_paths)
I used list.files() to construct a vector of all the .csv files in a
specific directory. But file_paths could be written or constructed in any way.
If the files you want to read in are in the current working
directory, then file_paths could be replaced with a character vector of
file names.
In the code above, you need to replace {path} with a
string of the desired directory's path.
This demonstrates how to split out a nested dataframe into objects in the global environment with tidyverse functions:
library(tidyverse)
library(palmerpenguins)
penguins %>%
group_nest(species) %>%
deframe() %>%
list2env(.GlobalEnv)