Using R function names alongside logic operators - r

In R, I have a function which takes the name of another function as a parameter. I've constructed an if-statement within the parent function to check if the input function name is the same as the name of an already existing function of the name strategy_function.
function_parent <- function(function_name){
if(function_name == strategy_function){...}
}
However, R does not appreciate this notation. Is using the name of a function in this way possible, and even if it is, is there a better way? This seems slightly sloppy.

Try quotes around strategy_function:
function_parent <- function(function_name){
if(function_name == "strategy_function"){...}
}

Using deparse(substitute(strategy_function)) in the comparison did the trick.

Related

How do I write a dplyr pipe-friendly function where a new column name is provided from a function argument?

I'm hoping to produce a pipe-friendly function where a user specifies the "name of choice" for a new column produced by the function as one of the function arguments.
In the function below, I'd like name_for_elective to be something that the user can set at will, and afterwards, the user could expect that there will be a new column in their data with the name that they provided here.
I've looked at https://dplyr.tidyverse.org/reference/dplyr_data_masking.html, the mutate() function documentation, searched here, and tried working with https://dplyr.tidyverse.org/reference/rename.html, but to no avail.
elective_open<-function(.data,name_for_elective,course,tiebreaker){
name_for_elective<-rlang::ensym(name_for_elective)
course<-rlang::ensym(course)
tiebreaker<-rlang::ensym(tiebreaker)
.data%>%
mutate(!!name_for_elective =ifelse(!!tiebreaker==max(!!tiebreaker),1,0))%>%mutate(!!name_for_elective=ifelse(!!name_for_elective==0,!!course[!!name_for_elective==1],""))%>%
filter(!(!!course %in% !!name_for_elective))
}
I've included this example function because there are several references to the desired new column name, and I'm unsure if the context in which the reference is made changes syntax.
As you can see, I was hoping !!name_for_elective would let me name our new column, but no. I've played with {{}}, not using rlang::ensym, and still haven't got this figured out.
Any solution would be greatly appreciated.
This: Use dynamic variable names in `dplyr` may be helpful, but I can't seem to figure out how to extend this to work in the case where multiple references are made to the name argument.
Example data, per a good suggestion by #MrFlick, takes the form below:
dat<-tibble(ID=c("AA","BB","AA","BB","AA","BB"),Class=c("A_Class","B_Class","C_Class","D_Class","E_Class","F_Class"),
randomNo=c(.75,.43,.97,.41,.27,.38))
The user could then run something like:
dat2<-dat%>%
elective_open(MyChosenName,Class,randomNo)
A desired result, if using the function a single time, would be:
desired_result_1<-tibble(ID=c("AA","BB","AA","BB"),
Class=c("A_Class","D_Class","E_Class","F_Class"),
randomNo=c(.75,.41,.27,.38),
MyChosenName=c("C_Class","B_Class"))
The goal would be to allow this function to be used again if so desired, with a different name specified.
In the case where a user runs:
dat3<-dat%>%
elective_open(MyChosenName,Class,randomNo)%>%
mutate(Just_Another_One=1)%>%
elective_open(SecondName,Class,randomNo)
The output would be:
desired_result_2<-tibble(ID=c("AA","BB"),
Class=c("E_Class","F_Class"),
randomNo=c(.27,.38),
MyChosenName=c("C_Class","B_Class"),
Just_Another_One=c(1,1),
SecondName=c("A_Class","D_Class"))
In reality, there may be any number of records with the same ID, and any number of Class-es.
In this case you can just stick to using the embrace {{}} option for your variables. If you want to dynamically create column names, you're going to still need to use :=. The difference here is that you can use the glue-style syntax with the embrace operator to get the name of the symbol. This works with the data provided.
elective_open <- function(.data, name_for_elective, course, tiebreaker){
.data%>%
mutate("{{name_for_elective}}" := ifelse({{tiebreaker}}==max({{tiebreaker}}),1,0)) %>%
mutate("{{name_for_elective}}" := ifelse({{name_for_elective}}==0,{{course}}[{{name_for_elective}}==1],"")) %>%
filter(!({{course}} %in% {{name_for_elective}}))
}

Is there a way make a parameter of a function be put in quotes in one life of code and not the other?

I'm not showing my actual code because it has many running parts, so let me just showcase what I am running into. I have this function:
paste <- function(d) {
print(paste0(d,"_test", sep =""))
}
What I wanted returned would be if I did paste(advising) for example is "advising_test".
The reason I am not just doing paste("advising") is because in the function I am writing, advising needs to be used as both in quotes in order to name a particular object and needs to be used as the name of a dataframe that I am doing another function on. Therefore, I need to figure out how in the code I can paste the parameter with quotes, so I can use the term both ways.
paste <- function(d) {
print(paste0(ensym(d),"_test", sep =""))
}
Edit : You can have a look at https://rlang.r-lib.org/reference/nse-defuse.html
Use substitute . No packages are used.
mypaste <- function(d) print(paste0(substitute(d), "_test"))
# test
mypaste(advising)
## [1] "advising_test"

Preserve a promise in R

I want to, essentially, pass a value untouched through a function. So in the following example (in Rstudio):
example_function <- function(datain){
as.environment("package:utils")$View(datain)
}
I want the inner function to act as if I'm passing it the original object, in particular so the name which appears in the View window will have the name of the original object (X, say) rather than datain which is what currently occurs.
With deparse(substitute(datain)) you can get the original name of the argument passed.
Then, to accomplish what you asked for, you can simply do
example_function <- function(datain){
as.environment("package:utils")$View(datain, deparse(substitute(datain)))
}
Now the View window will be titled appropriately as you wanted.
However note that "I want the inner function to act as if I'm passing it the original object" request of yours is not possible in R. R does not support pass-by-reference. There are some workarounds, but if you only needed if for naming the View, the above fix should be fine.
You can also use get for this.
example_function <- function(datain){
as.environment("package:utils")$View(get(datain),datain)
}
in this case you don't pass the variable but rather the name of the variable as a string.
example_function("X")

Regular Expression to extract function arguments in R

I have a problem to extract function arguments in R.
x="theme(legend.position='bottom',
legend.margin=(t=0,r=0,b=0,l=0,unit='mm'),
legend.background=element_rect(fill='red',size=rel(1.5)),
panel.background=element_rect(fill='red'),
legend.position='bottom')"
What I want is:
[1]legend.position='bottom'
[2]legend.margin=(t=0,r=0,b=0,l=0,unit='mm')
[3]legend.background=element_rect(fill='red',size=rel(1.5))
[4]panel.background=element_rect(fill='red')
[5]legend.position='bottom'
I tried several regular expressions without success including followings:
strsplit(x,",(?![^()]*\\))",perl=TRUE)
Please help me!
I think the best answer here might be to not attempt to use a regex to parse your function call. As the name implies, regular expressions require regular language. Your function call is not regular, because it has nested parentheses. I currently see a max nested depth of two, but who knows if that could get deeper at some point.
I would recommend writing a simple parser instead. You can use a stack here, to keep track of parentheses. And you would only split a parameter off if all parentheses were closed, implying that you are not in the middle of a parameter, excepting possibly the very first one.
Arf, I'm really sorry but i have to go work, i will continue later but for now i just let my way to solve it partially : theme\(([a-z.]*=['a-z]*)|([a-z._]*=[a-z0-9=,'_.()]*)*\,\)?
It misses only the last part..
Here the regex101 page : https://regex101.com/r/BZpcW0/2
See you later.
Thank you for all your advice. I have parsed the sentences and get the arguments as list. Here is my solution.
x<-"theme(legend.margin=margin(t=0,r=0,b=0,l=0,unit='mm'),
legend.background=element_rect(fill='red',size=rel(1.5)),
panel.background=element_rect(fill='red'),
legend.position='bottom')"
extractArgs=function(x){
result<-tryCatch(eval(parse(text=x)),error=function(e) return("error"))
if("character" %in% class(result)){
args=character(0)
} else {
if(length(names(result)>0)){
pos=unlist(str_locate_all(x,names(result)))
pos=c(sort(pos[seq(1,length(pos),by=2)]),nchar(x)+1)
args=c()
for(i in 1:(length(pos)-1)){
args=c(args,substring(x,pos[i],lead(pos)[i]-2))
}
} else{
args=character(0)
}
}
args
}

replicate a string with minor changes without a for loop

I have a couple of columns named "Lab1Date", "Lab3Date" "Lab7Date" etc and more column of the same pattern - the integer changes but not the rest of the string. I can generate a vector with such column names using a for loop easily, like
for (j in c(1,3,7,14,28)) {
newcolorder <- c(newcolorder,paste0("Lab",j,"Date"))
}
But I was wondering whether there was a more elegant, idiomatic way in R, maybe using the likes of rep().
Thanks.
You can use paste directly without a for loop as paste is vectorized.
paste0('Lab', c(1,3,7, 14,28), 'Date')

Resources