Use a function as input in another function - r

I am trying to fix the code below. The below code is a simplified version of my real code
fa=function(x){
x+y
}
fb=function(x,func){
y=rnorm(1)
func(x)
}
fb(3,fa)
Running the code returns the error:
"Error in func(x) : object 'y' not found".
I can fix it by moving y=rnorm(1) to the global environment but I need y to be in function since in my real code there is a for loop and y changes in each iteration.
Really appreciate any help to fix this problem

You could use match.fun to pass function as an argument. I'm not sure about what is the best approach for this particular question though.
fa = function(x, y){
x + y
}
set.seed(42)
fb = function(x, func){
y = rnorm(1)
match.fun(func)(x, y)
}
fb(3, fa)
#[1] 4.370958

Related

Formal Arguments Evaluation and Lexical Scoping in R

I have been reading Hadley Wickham's Advanced R in order to gain a better understanding of the mechanism or R and how it works behind the scene. I have so far enjoyed and everything is quite clear, there is one question that occupy s my mind for which I have not yet found an explanation.
I am quite familiar with the scoping rules of R which determines how values are assigned to FREE VARIABLES, However I have been grappling with the idea that why R cannot find the value of a formal argument through lexical scoping in the first case? consider the following example:
y <- 4
f1 <- function(x = 2, y) {
x*2 + y
}
f1(x = 3)
I normally throws and error cause I didn't assign a default value for argument y, however if I create a local variable y in the body of the function it won't throw any error:
f1 <- function(x = 2, y) {
y <- 4
x*2 + y
}
f1(x = 3)
Thank you very much in advance
You have defined y in f1(), you just haven't bound a value to it. The y that is in the global environment is a completely different variable.
You have specified an argument y for the function, but not providing any value when asked for a value in return. So this will work
f1(x = 3, y)
[1] 10
Here it takes y your defined variable as an input for the second argument which incidentally is also named y and returns a value.
even this will also work. As you have defined a default value to this function
y1 <- 4
f1 <- function(x = 2, y= y1) {
x*2 + y
}
f1(x=3)
#> [1] 10
f1(x = 3, 5)
#> [1] 11
Created on 2021-05-03 by the reprex package (v2.0.0)
If you want to evaluate any function without giving any value for any argument, you have to define that in function itself.

Passing `...` to functions taking different arguments (including `...`)

I understand how to use ellipses when I only want one function to receive those arguments:
ok <- function(x, ...){
sum(x, ...)
}
x.in <- c(1:9, NA)
ok(x.in, na.rm=TRUE)
I start getting confused when some functions need only certain parts of .... I was thinking of using something like names(formals(cor)) to test for which arguments in ... to send where, but I don't see how to do this for sum or plot. In general, I want to write functions similar to the following:
yikes <- function(x, ...){
plot(x, ...)
sum(x, ...) + cor(x, ...)
}
x.in <- c(1:9, NA)
x.in.jitter <- jitter(x.in)
yikes(x.in, y=x.in.jitter, na.rm=TRUE, use="na.or.complete", type="o")
Ideally, yikes() would do the following:
plot(x=x.in, y=x.in.jitter, type="o")
sum(x.in, na.rm=TRUE) + cor(x=x.in, y=x.in.jitter, use="na.or.complete")
I suspect part of the solution may use match.call. How would I get a function like yikes() to work? Is this simply a bad idea?
Edit: The second link in the first comment goes a long way to answering this question given the situation where you know/ are willing to explicitly describe what parameters get passed to what functions. Using the arguments of functions called directly (e.g., cor, plot, sum in yikes), or indirectly (par via plot) to indicate what arguments supplied via ... should be used for a particular function is what I am searching for. I understand the case for cor, e.g.; but how would one do this for plot, without explicitly mentioning par or the arguments it takes?

R: custom ggplot2 color-transform gives error in labels

Basically, i have a dataframe with 3 numeric vectors(x,y,z), and lets say i wanna make a scatter plot of x,y colored by z. I want to transform the colorscale with a squareroot that respects sign, so i made my own with trans_new. Here is a simple dataset, but with the actual transform.
library(ggplot2)
library(scales)
set.seed(1)
plot<-data.frame(x=rnorm(100),y=rnorm(100),z=rnorm(100))
super_trans <- function(){
trans_new('super', function(X) sapply(X,function(x) {if(x>0){x^0.5} else{-(- x)^0.5}}), function(X) sapply(X,function(x){ if(x>0){x^2} else{-x^2}}))
}
ggplot(plot,aes(x,y))+geom_point(aes(colour=z))+scale_colour_gradient(trans="super")
It gives an error,
Error in if (x > 0) { : missing value where TRUE/FALSE needed
I don't understand it. I tried to backtrack the mistake, and my guess is that the error happens when trans_new tries to make breaks.
However, i do not understand how the "breaks" parameter works in trans_new.
Is there a ggplot2/Scales hero out there, that can help me transform my color-scale correctly?
It may be relevant that only some datasets gives errors.
There is a vectorized if, called ifelse. It also seems you are missing an extra minus.
super_trans <- function() {
trans_new('super',
function(x) ifelse(x>0, x^0.5, -(-x)^0.5),
function(x) ifelse(x>0, x^2, -(-x)^2))
}

Plotting a data.frame from within a function with ggplot2

I have this function to take an object returned by the IRT package sirt and plot item response functions for a set of items that the user can specify:
plotRaschIRF <- function(x,items=NULL,thl=-5,thu=5,thi=.01,D=1.7) {
if (!class(x)=="rasch.mml") stop("Object must be of class rasch.mml")
thetas <- seq(thl,thu,thi)
N <- length(thetas)
n <- length(x$item$b)
tmp <- data.frame(item=rep(1:n,each=N),theta=rep(thetas,times=n),b=rep(x$item$b,each=N))
probs <- exp(D*(tmp[,2]-tmp[,3]))/(1+exp(D*(tmp[,2]-tmp[,3])))
dat <- data.frame(item=rep(1:n,each=N),theta=rep(thetas,times=n),b=rep(x$item$b,each=N),p=probs)
#dat$item <- factor(dat$item,levels=1:n,labels=paste0("Item",1:n))
if (is.null(items)) {
m <- min(10,n)
items <- 1:m
if (10<n) warning("By default, this function will plot only the first 10 items")
}
if (length(items)==1) {
title="Item Response Function"
} else {
title="Item Response Functions"
}
dat2 <- subset(dat,eval(quote(eval(item,dat) %in% items)))
dat2$item <- factor(dat2$item,levels=unique(dat2$item),labels=paste0("Item",unique(dat2$item)))
out <- ggplot(dat2,aes(x=theta,y=p,group=item)) +
geom_line(aes(color=dat2$item),lwd=1) + guides(col=guide_legend(title="Items")) +
theme_bw() + ggtitle(title) + xlab(expression(theta)) +
ylab("Probability") + scale_x_continuous(breaks=seq(thl,thu,1))
print(out)
}
But it seems to be getting stuck at either the line just before I start using ggplot2 (where I convert one column of dat2 to a factor) or at the ggplotting itself -- not really sure which. I get the error message "Error in eval(expr, envir, enclos) : object 'dat2' not found".
I tried reading through this as was suggested here but either this is a different problem or I'm just not getting it. The function works fine when I step through it line by line. Any help is greatly appreciated!
Based on your comments, the error is almost certainly in geom_line(aes(color=dat2$item)). Get rid of dat2$ and it should work fine (i.e. geom_line(aes(color=item))). Stuff in aes is evaluated in the data argument (dat2 here), with the global environment as the enclosure. Notably this means stuff in the function environment is not available for use by aes unless it is part of the data (dat2 here). Since dat2 doesn't exist inside dat2, and dat2 doesn't exist in the global environment, you get that error.

Wrapping a mathematical function inside an R function

I would like to write a function that takes any user-provided mathematical function (e.g., x^2) and do different things with it, for example:
#-----------------nonworking code---------------------
foo <- function(FUN, var){
math_fun <- function(x){
FUN
}
curve(math_fun, -5, 5) #plot the mathematical function
y = math_func(var) #compute the function based on a user provided x value.
points(x=var, y=y) #plot the value from the last step.
}
#A user can use the function defined above in a way as shown below:
Function <- x^2 + x
foo(FUN=Function, var = 2)
But obviously this function doesn't work:
First of all, if I run this function, I get Error in math_fun(x) : object 'x' not found.
Second of all, even if the function did work, I am assuming that the variable is x, but the user can make use of any letter.
For this second problem, one potential solution is to also ask the user to specify the letter they use as the variable.
foo <- function(FUN, var, variable){
math_fun <- function(variable){
FUN
}
curve(math_fun, -5, 5)
y = math_func(var)
points(x=var, y=y)
}
But I am at loss as to how exactly I can implement this... If someone can help me solve at least part of the problem, that would be great. Thanks!
It is a lot simpler than that. The user defined function should contain the arguments in its definition, e.g. function(x) x^2 + x instead of x^2 + x. Then it can be passed and called directly:
foo <- function(math_fun, var){
curve(math_fun, -5, 5) #plot the mathematical function
y = math_fun(var) #compute the function based on a user provided x value
points(x=var, y=y) #plot the value from the last step.
}
#A user can use the function defined above in a way as shown below:
Function <- function(x) x^2 + x
foo(Function, var = 2)

Resources