Constructing functions from symbols using 'bquote' (or alternatives to doing so) - r

Let's say I have an object of type "symbol" representing the name of a function. For example:
nm <- quote(mean)
I want to construct a function f whose body uses the function named by the symbol nm. For example:
f <- function(x, do = c("something", "nothing")) {
switch(match.arg(do), something = mean(x), nothing = x)
}
I want to construct this function identically, which implies that I would not be satisfied with the following approach:
factory <- function(name) {
func <- match.fun(name)
function(x, do = c("something", "nothing")) {
switch(match.arg(do), something = func(x), nothing = x)
}
}
g <- factory(nm)
since the body of g is not body(f) and the environment of g is not environment(f).
One approach that I've considered is bquote:
h <- eval(bquote({
function(x, do = c("something", "nothing")) {
switch(match.arg(do), something = .(nm)(x), nothing = x)
}
}))
bquote gets me most of the way there, but one issue is that the print output of h doesn't contain the substituted value of nm by default:
h
## function(x, do = c("something", "nothing")) {
## switch(match.arg(do), something = .(nm)(x), nothing = x)
## }
print(h, useSource = FALSE)
## function (x, do = c("something", "nothing"))
## {
## switch(match.arg(do), something = mean(x), nothing = x)
## }
The cause seems to be the srcref attribute of h:
identical(f, h)
## [1] TRUE
identical(f, h, ignore.srcref = FALSE)
## [1] FALSE
My question is: How might one approach the general problem of constructing f from nm?
My conditions on the constructed function h are that identical(f, h) should be TRUE and that the output of print(h) should contain the substituted value of nm, similar to print(f).
I would welcome answers improving on my existing bquote approach, or answers suggesting a new approach, or answers explaining why what I want to do is not actually possible...

Not especially elegant, but a parse(deparse( seems to work:
nm <- quote(mean)
f <- function(x, do = c("something", "nothing")) {
switch(match.arg(do), something = mean(x), nothing = x)
}
eval(parse(text=deparse(bquote(h <- function(x, do = c("something", "nothing")) {
switch(match.arg(do), something = .(nm)(x), nothing = x)
}))))
identical(f, h)
#> [1] TRUE
print(f)
#> function(x, do = c("something", "nothing")) {
#> switch(match.arg(do), something = mean(x), nothing = x)
#> }
print(h)
#> function(x, do = c("something", "nothing")) {
#> switch(match.arg(do), something = mean(x), nothing = x)
#> }
srcref is not identical, as expected:
identical(f, h, ignore.srcref = FALSE)
#> [1] FALSE
attributes(attributes(f)$srcref)$srcfile$lines
#> [1] "f <- function(x, do = c(\"something\", \"nothing\")) {"
#> [2] " switch(match.arg(do), something = mean(x), nothing = x)"
#> [3] "}"
attributes(attributes(h)$srcref)$srcfile$lines
#> [1] "h <- function(x, do = c(\"something\", \"nothing\")) {"
#> [2] " switch(match.arg(do), something = mean(x), nothing = x)"
#> [3] "}"

Reading through ?srcref, it seems that there are two idiomatic ways to improve the bquote approach. The first uses removeSource to recursively clean a function that preserves its source code:
h <- removeSource(eval(bquote({
function(x, do = c("something", "nothing")) {
switch(match.arg(do), something = .(nm)(x), nothing = x)
}
})))
h
function (x, do = c("something", "nothing"))
{
switch(match.arg(do), something = mean(x), nothing = x)
}
The second avoids preserving the source code altogether:
op <- options(keep.source = FALSE)
h <- eval(bquote({
function(x, do = c("something", "nothing")) {
switch(match.arg(do), something = .(nm)(x), nothing = x)
}
}))
options(op)
h
function (x, do = c("something", "nothing"))
{
switch(match.arg(do), something = mean(x), nothing = x)
}
Actually, ?options states that the default value of keep.source is interactive(), so both approaches are somewhat redundant in non-interactive contexts.

Related

r how to keep print method for custom class

i have defined a method for printing a vector with the class test:
print.test <- function(x, ...) {
x <- formatC(
as.numeric(x),
format = "f",
big.mark = ".",
decimal.mark = ",",
digits = 1
)
x[x == "NA"] <- "-"
x[x == "NaN"] <- "-"
print.default(x)
}
which works fine for the following
a <- c(1000.11, 2000.22, 3000.33)
class(a) <- c("test", class(a))
print(a)
[1] "1.000,11" "2.000,22" "3.000,33"
this also works:
round(a)
[1] "1.000,0" "2.000,0" "3.000,0"
this does not:
median(a)
[1] 2000.22
class(median(a))
[1] "numeric"
now my question is: do i need to write a custom method for this class to use median e.g. and if so what would it look like or is there another way (as i simply would like this class to print the data in a certain format)?
The problem is that median.default returns an object of class numeric therefore autoprinting of the returned object does not call your custom print method.
The following will do so.
median.test <- function(x, na.rm = FALSE, ...){
y <- NextMethod(x, na.rm = na.rm, ...)
class(y) <- c("test", class(y))
y
}
median(a)
#[1] "2.000,2"
As for the handling of NA values, I will first define another method for a base R function. It is not strictly needed but save some code lines if objects of class test are used frequently.
c.test <- function(x, ...){
y <- NextMethod(x, ...)
class(y) <- c("test", class(y))
y
}
b <- c(a, NA)
class(b)
#[1] "test" "numeric"
median(b)
#[1] "-"
median(b, na.rm = TRUE)
#[1] "2.000,2"
EDIT.
The following defines a generic function wMedian, a default method and a method for objects of class "currency", as requested by the OP in a comment.
Note that there must be a method print.currency, which I don't redefine since it's exactly the same as print.test above. As for the other methods, I have made them simpler with the help of a new function, as.currency.
median.currency <- function(x, na.rm = FALSE, ...){
y <- NextMethod(x, na.rm = na.rm, ...)
as.currency(y)
}
c.currency <- function(x, ...){
y <- NextMethod(x, ...)
as.currency(y)
}
as.currency <- function(x){
class(x) <- c("currency", class(x))
x
}
wMedian <- function(x, ...) UseMethod("wMedian")
wMedian.default <- function(x, ...){
matrixStats::weightedMedian(x, ...)
}
wMedian.currency <- function(x, w = NULL, idxs = NULL, na.rm = FALSE, interpolate = is.null(ties), ties = NULL, ...) {
y <- NextMethod(x, w = w, idxs = idxs, na.rm = na.rm, interpolate = interpolate, ties = ties, ... )
as.currency(y)
}
set.seed(1)
x <- rnorm(10)
wMedian(x, w = (1:10)/10)
#[1] 0.4084684
wMedian(as.currency(x), w = (1:10)/10)
#[1] "0,4"

R put for loop into vector

I would like to put the output of the following for loop into a single vector.
test=c("A","B","C","D")
for(i in 1:3)
{e=runif(1,5,10);
f=round(e);
g=sample(test,f,TRUE);
h=paste(g,collapse = "");
print(h)}
Output:
[1] "BDCCABD"
[1] "DDBAADBBAA"
[1] "DACCAB"
I would like to get a vector like:
i=c("BDCCABD","DDBAADBBAA","DACCAB")
Thank you for your help
Just a slight adaptation of your code will do it.
set.seed(8632) # make the results reproducible
i <- sapply(1:3, function(x){
e = runif(1, 5, 10)
f = round(e)
g = sample(test, f, TRUE)
h = paste(g, collapse = "")
print(h)
h
})
i
#[1] "CACDAABCC" "ADDAACA" "ACCDAACAB"
Do you really need to print(h)?
EDIT.
I've just tested it and the following simplification gives exactly the same result.
set.seed(8632) # make the results reproducible
j <- sapply(1:3, function(x){
f <- sample(5:10, 1) # this is equivalent to your original code
g = sample(test, f, TRUE)
h = paste(g, collapse = "")
print(h)
h
})
j
#[1] "CACDAABCC" "ADDAACA" "ACCDAACAB"
identical(i, j)
#[1] TRUE
You mention vector, then let us using vector
V=vector()
test=c("A","B","C","D")
for(i in 1:3)
{e=runif(1,5,10);
f=round(e);
g=sample(test,f,TRUE);
h=paste(g,collapse = "");
V[i]=h}
V
[1] "BCCAD" "CCDCACBAD" "ADCDBCBCC"
V[1]
[1] "BCCAD"
I think something like this:
test=c("A","B","C","D")
h_final<-0
for(i in 1:3){e=runif(1,5,10);
f=round(e);
g=sample(test,f,TRUE);
h=paste(g,collapse = "");
h_final[i]<-h
if(i==3){print(h_final)}
}
something like this?
j <- character()
test = c("A", "B", "C", "D")
for (i in 1:3) {
e = runif(1, 5, 10)
f = round(e)
g = sample(test, f, TRUE)
h = paste(g, collapse = "")
j <- c(j, h)
}
print(j)
> print(j)
[1] "DDDBADBCD" "ABBCBCC" "BBCAA"
EDIT: Even simpler
test = c("A", "B", "C", "D")
for (i in 1:3) {
e = runif(1, 5, 10)
f = round(e)
g = sample(test, f, TRUE)
h[i] = paste(g, collapse = "")
}
> print(h)
[1] "DBDADDD" "AABDA" "CDBDDABC"
Not the most elegant way especially if you have lots of iterations but it works:
test=c("A","B","C","D")
k = NA
for(i in 1:3)
{e=runif(1,5,10)
f=round(e)
g=sample(test,f,TRUE)
h=paste(g,collapse = "")
k = append(k,h)
print(h)}
k <- na.omit(k)
You should take a look at vectors in R
You need to initialize an empty vector, let's call it test_vector
test_vector = c()
test=c("A","B","C","D")
for(i in 1:3)
{e=runif(1,5,10);
f=round(e);
g=sample(test,f,TRUE);
h=paste(g,collapse = "");
print(h)
test_vector <- c(test_vector,h)
}
Note that you could apply a function to your test vector directly without using a for loop.

R object's name carrying through multiple functions

In line with my reading of Hadley's advice on building S3 objects I am using a helper function, a constructor function, and a validator function. A simple reproducible example:
test_object <- function(x, y, z) {
new_test_object(x, y, z)
}
new_test_object <- function(x, y, z) {
structure(list(x = x,
y = y,
z = z,
x_name = deparse(substitute(x))),
class = "test_object")
}
validate_test_object <- function(test_object) {
# Anything goes
test_object
}
I would like the resulting object to include a value with the original name that the item passed in had ($x_name in the above example). The deparse(substitute(...)) trick works if I call the constructor directly:
alpha = "a"
test_constructor <- new_test_object(x = alpha, y = "b", z = "c")
test_constructor$x_name
# [1] "alpha"
But not if I use the helper function:
test_helper <- test_object(x = alpha, y = "b", z = "c")
test_helper$x_name
# [1] "x"
I would like test_helper$x_name to also return [1] "alpha".
Short of doing the deparse(substitute(...)) step at the helper stage, is there any way of the constructor function (new_test_object()) accessing the 'original' name of the object x if it has come via the helper? Or to ensure that its name passes through with it as the helper function passes it to the constructor?
What's really the purpose here? If you are just using one function as a wrapper to another, then there are better ways of preserving arguments. For example
test_object <- function(x, y, z) {
call <- match.call()
call[[1]] <- quote(new_test_object)
eval(call)
}
But in general relying on deparse() to get information from names of variables isn't a very reliable method. It would be better to have such pieces of information be proper parameters that you can set if you like. This makes your functions much more flexible.
test_object <- function(x, y, z, xname=deparse(substitute(x))) {
new_test_object(x, y, z, xname=xname)
}
new_test_object <- function(x, y, z, xname=deparse(substitute(x))) {
structure(list(x = x,
y = y,
z = z,
x_name = xname),
class = "test_object")
}
Here is a not beautifull fix: you add ... argument to pass the name when you are calling it from another function
test_object <- function(x, y, z) {
x_name = deparse(substitute(x))
new_test_object(x, y, z, x_name = x_name)
}
new_test_object <- function(x, y, z, ...) {
args <- list(...)
if(is.null(args[["x_name"]])){
structure(list(x = x,
y = y,
z = z,
x_name = deparse(substitute(x))),
class = "test_object")
}
else{
structure(list(x = x,
y = y,
z = z,
x_name = args[["x_name"]]),
class = "test_object")
}
}
And here is the result:
test_helper <- test_object(x = alpha, y = "b", z = "c")
test_helper$x_name
# [1] "alpha"

Running aggregate function within dmapply (ddR package)

I would like to run the aggregate function within the dmapply function as offered through the ddR package.
Desired results
The desired results reflect a simple output generated via aggregate in base:
aggregate(
x = mtcars$mpg,
FUN = function(x) {
mean(x, na.rm = TRUE)
},
by = list(trans = mtcars$am)
)
which produces:
trans x
1 0 17.14737
2 1 24.39231
Attempt - ddmapply
I would like to arrive at the same results while utilising ddmapply, as attempted below:
# ddR
require(ddR)
# ddR object creation
distMtcars <- as.dframe(mtcars)
# Aggregate / ddmapply
dmapply(
FUN = function(x, y) {
aggregate(FUN = mean(x, na.rm = TRUE),
x = x,
by = list(trans = y))
},
distMtcars$mpg,
y = distMtcars$am,
output.type = "dframe",
combine = "rbind"
)
The code fails:
Error in match.fun(FUN) : 'mean(x, na.rm = TRUE)' is not a
function, character or symbol Called from: match.fun(FUN)
Updates
Fixing error pointed out by #Mike removes the error, however, does not produce the desired result. The code:
# Avoid namespace conflict with other packages
ddR::collect(
dmapply(
FUN = function(x, y) {
aggregate(
FUN = function(x) {
mean(x, na.rm = TRUE)
},
x = x,
by = list(trans = y)
)
},
distMtcars$mpg,
y = distMtcars$am,
output.type = "dframe",
combine = "rbind"
)
)
yields:
[1] trans x
<0 rows> (or 0-length row.names)
It works fine for me if you change your aggregate function to be consistent with the one you call earlier: FUN = function(x) mean(x, na.rm = T). The reason it can't find mean(x, na.rm = T) is because it isn't a function (it's a function call), rather mean is a function.
Also it will give you NA results unless you change your x = distMtcars$mpg to x = collect(distMtcars)$mpg. Same for y. With all that said, I think this should work for you:
res <-dmapply(
FUN = function(x, y) {
aggregate(FUN = function(x) mean(x, na.rm = TRUE),
x = x,
by = list(trans = y))
},
x = list(collect(distMtcars)$mpg),
y = list(collect(distMtcars)$am),
output.type = "dframe",
combine = "rbind"
)
Then you can do collect(res) to see the result.
collect(res)
# trans x
#1 0 17.14737
#2 1 24.39231

Clean, simple function factories in R

Short example. I am exploring the behavior of a function by testing it with different "specs", f(spec). I wrote down one spec by hand, spec1, and am creating new specs as variations on it. To do this, I decided to write a function:
spec1 = list(fy = list(a = 1), fx = list(f1 = function(x) 10-x, f2 = function(x) 2-x))
make_spec = function(f = function(x) 10-x, xtheta = 2)
list(fy = list(a = 1), fx = list(f1 = f, f2 = function(x) xtheta-x))
res1 = make_spec()
# first problem: they don't match
all.equal(res1,spec1)
# [1] "Component “fx”: Component “f2”: target, current do not match when deparsed"
# ^ this happens, even though...
res1$fx$f2(4) == spec1$fx$f2(4)
# TRUE
# second problem: res1 is fugly
res1
# $fy
# $fy$a
# [1] 1
#
#
# $fx
# $fx$f1
# function (x)
# 10 - x
# <environment: 0x000000000f8f2e20>
#
# $fx$f2
# function (x)
# xtheta - x
# <environment: 0x000000000f8f2e20>
str(res1)
# even worse
My goals for make_spec are...
all.equal(spec1, res1) and/or identical(spec1, res1)
for str(res1) to be human-readable (no <environment: ptr> tags or srcfilecopy)
to avoid substitute and eval altogether if possible (not a high priority)
to avoid writing out the second arg of substitute (see "full" example below)
Is there an idiomatic way to achieve some or all of these goals?
Full example. I'm not sure if the example above fully covers my use case, so here's the latter:
spec0 = list(
v_dist = list(
pdf = function(x) 1,
cdf = function(x) x,
q = function(x) x,
supp = c(0,1)
)
,
ucondv_dist = {
ucondv_dist = list()
ucondv_dist$condmean = function(v) 10-v
ucondv_dist$pdf = function(u,v) dnorm(u, ucondv_dist$condmean(v), 50)
ucondv_dist$cdf = function(u,v) pnorm(u, ucondv_dist$condmean(v), 50)
ucondv_dist
}
)
make_spec = function(ycondx_condmean = function(x) 10-x, ycondx_sd = 50){
s = substitute(list(
x_dist = list(
pdf = function(x) 1,
cdf = function(x) x,
q = function(x) x,
supp = c(0,1)
)
,
ycondx_dist = {
ycondx_dist = list()
ycondx_dist$condmean = ycondx_condmean
ycondx_dist$pdf = function(u,v) dnorm(u, ycondx_dist$condmean(v), ycondx_sd)
ycondx_dist$cdf = function(u,v) pnorm(u, ycondx_dist$condmean(v), ycondx_sd)
ycondx_dist
}
)
, list(ycondx_condmean=ycondx_condmean, ycondx_sd = ycondx_sd))
eval(s, .GlobalEnv)
}
res0 = make_spec()
Side note. I don't know if "function factory" is the right term here, since I am not a computer scientist, but it seems related. I found only a paragraph on the concept related to R.
The enclosing environments of the functions are different leading to the difference in output/difference in deparsing. So, there are two things to do to get the desired output:
make the environments the same
substitute the variables from the enclosing environments into the function bodies.
However, doing it this way you get a double dose of the eval/substitute you didn't want, so maybe there would be an alternative.
make_spec <- function(f = function(x) 10-x, xtheta = 2) {
e <- parent.frame()
fixClosure <- function(func)
eval(eval(substitute(substitute(func)), parent.frame()), e)
list(fy = list(a = 1), fx = list(
f1 = fixClosure(f),
f2 = fixClosure(function(x) xtheta-x)
))
}
spec1 <- list(fy = list(a = 1), fx = list(f1 = function(x) 10-x, f2 = function(x) 2-x))
res1 <- make_spec()
all.equal(res1, spec1)
[1] TRUE

Resources