I want to be able to change the cell of a dataframe by referring to the object's name, rather than to the object itself, but when I attempt do do so it results in the warning could not find function "eval<-".
I can change a cell of a standard dataframe using the code below:
my_object = tibble(x = c("Hello", "Goodbye"),
y = c(1,2))
object[2,1] <- "Bye"
But I am having trouble doing the same when using the object's name. I can evaluate the object using its name and extract the relevant cell:
object_name = "my_object"
eval(sym(object_name))[2, 1]
But I can't assign a new variable to the object (error: could not find function "eval<-"):
eval(sym(object_name))[2, 1] <- "Bye"
You can use get() instead of eval(sym())to obtain an object by name. You can also use the [<- function to write a value to it without requiring an intermediate copy:
my_object = dplyr::tibble(x = c("Hello", "Goodbye"),
y = c(1,2))
object_name = "my_object"
`[<-`(get(object_name), 2, 1, value ="Bye")
#> # A tibble: 2 x 2
#> x y
#> <chr> <dbl>
#> 1 Hello 1
#> 2 Bye 2
Created on 2022-06-02 by the reprex package (v2.0.1)
1) environments
1a) Subscript the current environment with object_name.
e <- environment()
e[[object_name]][2, 1] <- "Bye"
1b) or as one line:
with(list(e = environment()), e[[object_name]][2, 1] <- "Bye")
1c) If my_object is in the global environment, as in the question, it could optionally be written as:
.GlobalEnv[[object_name]][2, 1] <- "Bye"
2) assign We could use assign like this:
assign(object_name, within(get(object_name), x[2] <- "Bye"))
3) without clobbering
3a) If what you really want is to create a new data frame without clobbering the input:
library(dplyr)
mutate(get(object_name), across(1, ~ replace(., 2, "Bye")))
3b) or if we knew that the column name was x then:
library(dplyr)
mutate(get(object_name), x = replace(x, 2, "Bye"))
3c) or without dplyr
within(get(object_name), x[2] <- "Bye")
If you want to define your command as a string, parse it as an expression, and then use eval:
eval(parse(text=paste0(object_name,"[2,1]<-'Bye'")))
> object
x y
1 Hello 1
2 Bye 2
Related
Let's say I create a list using the assign function:
name <- "test_list"
assign(name, list(a = c(1,2), b = c(3,4)))
Now, let's say I want to assign a new value to test_list without typing it out directly (like in a situation where I want objects with specific names to be generated automatically).
Both of the following attempts didn't work:
1.)
as.name(name)$a[[1]] <- 5
2.)
eval(expr = as.name(name))$a[[1]] <- 5
Any ideas?
We can use
assign(name, `[<-`(get(name), get(name)$a[1], 5))
Or make this more explicit
assign(name, {dat <- get(name); dat$a[1] <- 5; dat})
Or extract the object from the globalenv and assign
.GlobalEnv[[name]]$a[1] <- 5
test_list
#$a
#[1] 5 2
#$b
#[1] 3 4
One approach is with eval(parse(text=expression)), which can often be pressed into service in an emergency. But I would try to avoid it as much as possible.
name <- "test_list"
assign(name, list(a = c(1,2), b = c(3,4)))
eval(parse(text=paste0(name,"$a[[1]] <- 5")))
test_list
$a
[1] 5 2
$b
[1] 3 4
I use a dataset with string characters that contains some wrong translations. One column shows the words in the original language ("name.french"). In the next column their translations are listed ("name.english"). Now I want to use the following command to replace the wrong translations with the correct ones:
if(name.french == "framboise"){name.english = str_replace(name.english, "rasperry", "rasberry");}
However, I always get the following error message: Argument cannot be interpreted as logical value. Is there another way of replacing some wrong translations?
If your data are stored in two separate vectors, you can use ifelse:
name.french <- c("framboise", "not framboise")
name.english <- c("rasperry", "rasperry")
name.english2 <-
ifelse(
name.french == "framboise",
str_replace(name.english, "rasperry", "rasberry"),
name.english
)
This also works if your data are stored in a tibble or data.frame and you want to use tidyverse verbs:
library(tidyverse)
d <- tibble(name.french = c("framboise", "not framboise"),
name.english = c("rasperry", "rasperry"))
d2 <- d %>%
mutate(name.english = ifelse(
name.french == "framboise",
str_replace(name.english, "rasperry", "rasberry"),
name.english
))
d
#> # A tibble: 2 x 2
#> name.french name.english
#> <chr> <chr>
#> 1 framboise rasperry
#> 2 not framboise rasperry
d2
#> # A tibble: 2 x 2
#> name.french name.english
#> <chr> <chr>
#> 1 framboise rasberry
#> 2 not framboise rasperry
Created on 2020-03-03 by the reprex package (v0.3.0)
Use ifelse. Assuming your data.frame is called df and you want to make changes to the name.english column:
df$name.english = ifelse(name.french == 'framboise', str_replace(name.english, "rasperry", "rasberry"), df$name.english)
How about good old gsub(...) ie a regexbased solution for your hypothetical data.frame df.
df[df[["name.french"]] == "framboise", "name.english"] <- gsub( pattern = "rasperry", replacement = "rasberry", x = df[df[[name.french == "framboise"]], "name.english"] )
Of cource u can easily build that into a simple function where the column name here: "name.french", the row filter criteria here: "framboise" and the pattern and replacement strings can be passed to as parameters. In case you just want to globally replace "rasperry" with "rasberry" in all of name.english than you can drop the row filter in the df ie the df[["name.french"]] == "framboise", ... part of the code.
I'm trying to call a vector "a" from a data frame "df" using a function. I know I could do this just fine with the following:
> df$a
[1] 1 2 3
But I'd like to use a function where both the data frame and vector names are input separately as arguments. This is the best that I've come up with:
show_vector <- function(data.set, column) {
data.set$column
}
But here's how it goes when I try it out:
> show_vector(df, a)
NULL
How could I change this function in order to successfully reference vector df$a where the names of both are input to a function as arguments?
It's actually possible to do this without passing the column name as a string (in other words, you can pass in the unquoted column name:
show_vector <- function(data.set, column) {
eval(substitute(column), envir = data.set)
}
Usage example:
df <- data.frame(a = 1:3, b = 4:6)
show_vector(df, b)
# 4 5 6
I've wondered about this kind of thing a lot in the past and haven't found an easy fix. The best I've come up with is this:
df <- data.frame(c(1, 2, 3), c(4, 5, 6))
colnames(df) <- c("A", "B")
test <- function(dataframe, columnName) {
return(dataframe[, match(columnName, colnames(dataframe))])
}
test(df, "A")
Your code would work if you only put the column name in quotes i.e. show_vector(df, "a")
Other multiple ways to do this:
Using base functionality
func <- function(df, cname){
return(df[, grep(cname, colnames(df))])
}
Or even
func <- function(df, cname){
return(df[, cname])
}
You can use substitute to capture the input vector name as it is then use `as.character to make it as a character.
show_vector <- function(data.set, column) {
data.set[,as.character(substitute(column))]
}
Now lets take a look:
(dat=data.frame(a=1:3,b=4:6,c=10:12))
a b c
1 1 4 10
2 2 5 11
3 3 6 12
show_vector(dat,a)
[1] 1 2 3
show_vector(dat,"a")
[1] 1 2 3
It works.
we can also write a simple one where we just input a character string:
show_vector1 <- function(data.set, column) {
data.set[,column]
}
show_vector1(dat,"a")
[1] 1 2 3
Although this will not work if the column name is not a character:
show_vector1(dat,a)
**Show Traceback
Rerun with Debug
Error in `[.data.frame`(data.set, , column) : undefined columns selected**
I'm trying to use mutate in dplyr to process strings and I'm not getting the output that I want (see below) where instead of operating line by line, mutate is taking the first element and populating it downward. I was wondering if someone could help me understand what I'm doing wrong and how to tweak this code to work properly.
short.idfun = function(longid)
{
x = strsplit(longid,"_")
y = x[[1]]
study = substr(y[1],8,nchar(y[1]))
subj = y[length(y)]
subj = substr(subj,regexpr("[^0]",subj),nchar(subj)) #remove leading zeros
shortid= paste(study,subj,sep="-")
return(shortid)
}
data = data.frame(test=c("1234567Andy_003_003003","1234567Beth_004_003004","1234567Char_003_003005"),stringsAsFactors=FALSE)
data= mutate(data,shortid=short.idfun(test))
print(data)
#### Below is my output
# test shortid
#1 1234567Andy_003_003003 Andy-3003
#2 1234567Beth_004_003004 Andy-3003
#3 1234567Char_003_003005 Andy-3003
#### This is the behavior I was hoping for
# test shortid
#1 1234567Andy_003_003003 Andy-3003
#2 1234567Beth_004_003004 Beth-3004
#3 1234567Char_003_003005 Char-3005
Another alternative is the use of rowwise():
data %>%
rowwise() %>%
mutate(shortid = short.idfun(test))
Which gives:
#Source: local data frame [3 x 2]
#Groups: <by row>
#
# test shortid
# (chr) (chr)
#1 1234567Andy_003_003003 Andy-3003
#2 1234567Beth_004_003004 Beth-3004
#3 1234567Char_003_003005 Char-3005
The issue is that your function needs a little help vectorizing. You can run it through vapply to get what you're after.
data = data.frame(test=c("1234567Andy_003_003003","1234567Beth_004_003004","1234567Char_003_003005"),stringsAsFactors=FALSE)
data= mutate(data,
shortid=vapply(test, short.idfun, character(1)))
print(data)
To see why you got the result you did, we can look at little at the first few lines of your function.
longid = data$test
(x <- strsplit(longid, "_"))
[[1]]
[1] "1234567Andy" "003" "003003"
[[2]]
[1] "1234567Beth" "004" "003004"
[[3]]
[1] "1234567Char" "003" "003005"
Everything looks good so far, but now you define y.
(y = x[[1]])
[1] "1234567Andy" "003" "003003"
By calling x[[1]], you pulled out only the first element of x, not the first vector in x, not the first element of each vector in x. You could also revise your function by defining y <= vapply(x, function(v) v[1], character(1)) and skip the vapply in mutate. Either way should work.
I have a data frame where the row names are words and I can call the first column of that row of data drame using something like
>df['rowB',1]
i know I can use paste to combine a variable and a string using paste to do something like
>paste("the value is ", df['rowB',1], "."]
and that will get me an output of the string with the value of the variable. what if rowname is a variable that equals 'rowB? I tried to do a first paste to put in the paste above, but the result of the first paste doesn't evaulate to the value, but rather is just a string that says
>rowname<-'rowB'
>type<-paste("relatype[\'", rowname, "\',1]", sep="")
'df['rowB',1]'
long story short, I want to input a value called 'rowname' as a parameter of a function and have it be evaluated for the value of rowname, so I can then put that value into a string within that same function.
I'm also open to a wholly different solution. any and all suggestions are welcome.
thanks
Not sure what the problem might be, not entirely clear from your description, but if rowname is a variable, you don't need anything special, because it will evaluate to it's value anyway. Let
mat <- matrix(1:10, nrow = 5)
rownames(mat) <- letters[1:5]
mat
## [,1] [,2]
##a 1 6
##b 2 7
##c 3 8
##d 4 9
##e 5 10
and rowname <- "b", then
rowname
##[1] "b"
so
mat[rowname, 1]
##b
##2
which is the same as mat["b", 1]. It only fails, if you use mat['rowname', 1].
If you want to put this in functions, you can do something like:
getElement <- function(mat, row.name, column.index) {
mat[row.name, column.index]
}
getElement(mat, "b", 1)
##b
##2
pasteSenstence <- function(mat, row.name, col.index) {
paste("The element of row", row.name, "and column", col.index, "is",
getElement(mat, row.name, col.index))
}
pasteSentence(mat, "b", 1)
##[1] "The element of row b and column 1 is 2"
which also works with rowname <- "b"
pasteSentence(mat, rowname, 1)
##[1] "The element of row b and column 1 is 2"
This should work:
paste("the value is ", get(df['rowname',1]), "."]
If you are not familiar, 'get' in r is similar to 'eval' in python.
x=c('a', 'c', 'b')
a=2
x[1]
'a'
get(x[1])
2
I'm afraid I don't understand the question; how is your function different from the following?
foo = function(rowname = "Species", d = t(iris)){
paste("I'm selecting", d[rowname, 1])
}
foo()
# [1] "I'm selecting setosa"