Change the argument name of a function, then draw the curve - r

For some reason I do not want to use x as the name of the variable.
mse <- function(h) {
h + (1/h)
}
The curve function in R seems to require x to be the name of the argument. So I do
cl <- quote(mse(h))
cl[[2]] <- parse(text = 'x')[[1]]
Now this works
curve(expr = eval(cl, list(x)))
But the following does not work, could anyone help explain why? Thank you.
curve(expr = function(x){eval(cl)})

As mentioned by #李哲源 and #r2evans, I will simply do:
curve(expr = mse(h), xname = 'h')

Related

RStudio and Lazy Evaluation

I am mainting the package "hdm" and I encountered the following problem.
The following code runs in plain R and used to run in RStudio, but not anymore:
library(hdm)
attach(GrowthData)
fmla= "Outcome ~ ."
fmla.y= "Outcome ~ . - gdpsh465 "
rY= rlasso(fmla.y, data =GrowthData)
Error message:
Error in exists("homoscedastic", where = penalty) : object 'n' not
found
If no penalty in the function rlasso is specified it is set by default containing the variable "n", the sample size of x, which is evaluated later.
n is gotten by lazy evaluation and it seems that in RStudio the correct environment is not found anymore.
The error occurs here, but the problem is that penalty contains n which is not know
if (!exists("homoscedastic", where = penalty)) penalty$homoscedastic = "FALSE"
Somehow I am not sure to solve this and would like to ask if you have any idea.
Thanks a lot for your efforts in advance!
Best,
Martin
When x is a character object, the problem arises because n is not defined in the environment from which rlasso.formula is called, i.e. rlasso.character(), or its parents. This is roughly what's happening:
test <- function(x, ...) {
UseMethod("test")
}
test.character <- function(x, pen = list(alpha = n)) {
test.formula(x, pen = pen)
}
test.formula <- function(x, pen = list(alpha = n)) {
n <- 2
test.default(x, pen)
}
test.default <- function(x, pen = list(alpha = n)) {
n <- 3
exists("alpha", where = pen)
}
test("y ~ x")
# Error in exists("alpha", where = pen) : object 'n' not found
test(y ~ x)
# [1] TRUE
test(123)
# [1] TRUE
A workaround is to not specify pen in the call to the formula method if it's not defined when the character method is called:
test.character <- function(x, pen = list(alpha = n)) {
if (missing(pen))
test.formula(x)
else
test.formula(x, pen = pen)
}
test("y ~ x")
# [1] TRUE

How to pass objects of previous function in R

I am creating some functions for myself and I don't know how to proceed in order to use an object (e.g. a value) returned from one function to another one, while the console is still running. As an example:
first <- function(x){
return(x)
}
second <- function(y){
z <- x + y
return(z)
}
So if you call these functions with a '+'...
first(x = 5) +
second(y = 5)
I would expect a value of 10. In this particular case, obviously the function second() can't find the object x, because the latter one was assigned in the first() environment.
This style of programming is similar to ggplot(), for example:
ggplot(aes(x = x, y = y), data = data) +
geom_point()
I know this type of programming implies the use of environments, but I can't get it work. Any suggestions?
Thanks!
EDIT
Looking to ggplot package in github I figured it out, I think:
hh_first <- function(data) {
h <- structure(list(data = data), class = c("hh"))
h
}
"+.hh" <- function(e1, e2) {
add_hh(e1, e2)
}
add_hh <- function(h, object) {
h$data <- paste(h$data, object, sep = "")
h$data
}
hh_second <- function(data) {
data
}
For example...
hh_first('Hi') +
hh_second(', how are you?')
Returns a string 'Hi, how are you?'. The plus operator in this case works with objects of class 'hh'.
Any suggestions regarding the code or perhaps possible errors that this kind of coding may produce are welcome.
Try:
first <- function(x){
return(x)
}
second <- function(x ,y){
z <- x + y
return(z)
}
second(first(5), 5)
OR
myX <- first(5)
second(myX, 5)
OR
library(magrittr) # Which uses pipes, %>%, to pass the results of a function to the first variable of the second function
first(5) %>% second(5)

pretty log scale breaks with ggplot hex plot

I want to draw a hexbin plot with ggplot, but with log scale "pretty" breaks for the frequency. Consider
df = data.frame(a=rnorm(1000)); df$b <- df$a+rnorm(1000);
I used this answer to get pretty breaks on linear scale
ggplot(df, aes(a,b)) +
geom_hex(aes(fill=cut(..value..,breaks=pretty(..value.., n=10)))) +
scale_fill_discrete("Frequency")
This works. Now say I want to use log scale pretty breaks. So I used the idea from another answer to define
base_breaks <- function(n = 10){
function(x) {
axisTicks(log10(range(x, na.rm = TRUE)), log = TRUE, n = n)
}
}
and try to do
ggplot(df, aes(a,b)) +
geom_hex(aes(fill=cut(..value..,breaks=base_breaks(n=10)(..value..))))
but it is not able to find the function. It says:
Error in cut.default(value, breaks = base_breaks(n = 10)(value)) :
could not find function "base_breaks"
Even though base_breaks is defined.
> base_breaks(n=10)(c(1:1000))
[1] 1 5 10 50 100 500 1000
How can I make my function visible in whatever environment ggplot is calling it? I even defined it as a global variable with
base_breaks <<- function(n = 10){
function(x) {
axisTicks(log10(range(x, na.rm = TRUE)), log = TRUE, n = n)
}
}
but I still get the same error.
I am not sure about it, but you could try simplifying the function like this:
base_breaks <<- function(n = 10, x){
axisTicks(log10(range(x, na.rm = TRUE)), log = TRUE, n = n)
}
Maybe the problem is that you have a function whose result is another function, and that could be causing the error. With this aproach you would have the values more directly. Check it out!
I can't check it myself, since I get an error object 'value' not found...

Default argument in R function (formal argument matched by multiple actual arguments)

Simple question, I hope. I want to write a plotting function that has a default value for the y-axis label if the user doesn't specify. I'd also like to allow the ... argument for other plotting parameters, and allow the user to set ylab manually. But I can't figure out how to do this.
# simple scatterplot function with a default ylab
scatter <- function(x,y, ...) {
plot(x, y, ylab="Default y-axis label", ...)
}
# generate data
x <- rnorm(100)
y <- x+rnorm(100)
# use the default
scatter(x,y)
# here I want to use my own label, but I get an error!
scatter(x, y, ylab="New y-axis label")
The error I get is:
Error in plot.default(x, y, ylab = "Default y-axis label", ...) :
formal argument "ylab" matched by multiple actual arguments
I understand the problem, but I don't know the best way to fix it. Thanks for the help!
EDIT: I realize I can do something like
scatter <- function(x,y,ylab = "Default y-axis label", ...) {
plot(x, y, ylab= ylab, ...)
}
...but if I'm writing a package to submit to CRAN, and I have lots of default options I'd like to fiddle with, I don't want to have to document all these standard plotting arguments because they're used in my function definition.
Try doing this instead:
scatter <- function(x,y,ylab = "Default y-axis label", ...) {
plot(x, y, ylab= ylab, ...)
}
Expanding slightly on Arun's answer, this is a sketch of one route to take if you have many arguments:
def_args <- list(ylab = "Default Label",xlab = "Default Label")
scatter <- function(x,y, ...) {
cl <- as.list(match.call())[-1L]
do.call("plot",c(cl,def_args[!names(def_args) %in% names(cl)]))
}
Some thought would be needed to decide how you want to handle partial matching of arguments (if at all). e.g. perhaps something like this:
scatter <- function(x,y, ...) {
cl <- as.list(match.call())[-1L]
names(cl) <- match.arg(names(cl),
names(formals(plot.default)),several.ok = TRUE)
do.call("plot",c(cl,def_args[!names(def_args) %in% names(cl)]))
}
would handle partial matching of arguments.
One way using match.call to check if ylab has been specified as an argument:
scatter <- function(x,y, ...) {
mcall = as.list(match.call())[-1L]
if (!"ylab" %in% names(mcall))
plot(x, y, ylab="Default y-axis label", ...)
else plot(x, y, ...)
}
As mentioned under comment list(...) is a nicer way to get just the dots argument expanded than having to get all the formal arguments with match.call.
You might also try using pmatch instead of %in% for partial matching of arguments.
I use a function to build an argument list. In my case, I do not care about partially matching argument names, which is good because this won't support it.
# Create a list of input arguments.
# Allow arguments to be specified multiple times, first definition wins.
# The resulting list is intended to be passed to do.call().
make.args <- function(..., PRE.ARGS=list(), POST.ARGS=list()) {
a <- list()
l <- c(PRE.ARGS, list(...), POST.ARGS)
for (name in unique(names(l))) {
a[[name]] <- l[[name]] # First occurrence will be found.
}
return(a)
}
An example of its use:
plot.rate <- function(col, cond=NULL, ...) {
col <- paste(col, collapse=' + ')
f <- paste(col, '~ Rate')
if (!is.null(cond)) {
cond <- paste(cond, collapse=' + ')
f <- paste(f, cond, sep='|')
}
arg.list <- make.args(...
, x = as.formula(f)
, main=col
, grid=TRUE
, scales=list(x=list(alternating=1) # bottom(/left)
, y=list(alternating=3)) # both
, xlab='x RTM'
)
do.call(xyplot, arg.list)
}

How can I include a variable name in a function call in R?

I'm trying to change the name of a variable that is included inside a for loop and function call. In the example below, I'd like column_1 to be passed to the plot function, then column_2 etc. I've tried using do.call, but it returns "object 'column_j' not found". But object column_j is there, and the plot function works if I hard-code them in. Help much appreciated.
for (j in 2:12) {
column_to_plot = paste("column_", j, sep = "")
do.call("plot", list(x, as.name(column_to_plot)))
}
I do:
x <- runif(100)
column_2 <-
column_3 <-
column_4 <-
column_5 <-
column_6 <-
column_7 <-
column_8 <-
column_9 <-
column_10 <-
column_11 <-
column_12 <- rnorm(100)
for (j in 2:12) {
column_to_plot = paste("column_", j, sep = "")
do.call("plot", list(x, as.name(column_to_plot)))
}
And I have no errors. Maybe you could provide hard-code which (according to your question) works, then will be simpler to find a reason of the error.
(I know that I can generate vectors using loop and assign, but I want to provide clear example)
You can do it without the paste() command in your for loop. Simply assign the columns via the function colnames() in your loop:
column_to_plot <- colnames(dataframeNAME)[j]
Hope that helps as a first kludge.
Are you trying to retrieve an object in the workspace by a character string? In that case, parse() might help:
for (j in 2:12) {
column_to_plot = paste("column_", j, sep = "")
plot(x, eval(parse(text=column_to_plot)))
}
In this case you could use do.call(), but it would not be required.
Edit: wrapp parse() in eval()
Here is one way to do it:
tmp.df <- data.frame(col_1=rnorm(10),col_2=rnorm(10),col_3=rnorm(10))
x <- seq(2,20,by=2)
plot(x, tmp.df$col_1)
for(j in 2:3){
name.list <- list("x",paste("col_",j,sep=""))
with(tmp.df, do.call("lines",lapply(name.list,as.name))) }
You can also do colnames(tmp.df)[j] instead of paste(..) if you'd like.

Resources