An embedded and charset-unspecified text was scrubbed... Name: not available URL: <https://stat.ethz.ch/pipermail/r-devel/attachments/20100702/71f7f50d/attachment.pl>
Attributes of 1st argument in ...
10 messages · Daniel Murphy, Olaf Mersmann, Hadley Wickham +2 more
Hi Daniel,
On 02.07.2010, at 23:26, Daniel Murphy wrote:
I am trying to get an attribute of the first argument in a call to a
function whose formal arguments consist of dots only and do something, e.g.,
call 'cbind', based on the attribute
f<- function(...) {get first attribute; maybe or maybe not call 'cbind'}
I thought of (ignoring "deparse.level" for the moment)
f<-function(...) {x <- attr(list(...)[[1L]], "foo"); if (x=="bar")
cbind(...) else x}
what about using the somewhat obscure ..1 syntax? This version runs quite a bit faster for me:
g <- function(...) {
x <- attr(..1, "foo")
if (x == "bar")
cbind(...)
else
x
}
but it will be hard to quantify how this pans out for your unless we know how many and what size and type the arguments are.
Cheers,
Olaf
Hi Daniel,
On 02.07.2010, at 23:26, Daniel Murphy wrote:
I am trying to get an attribute of the first argument in a call to a
function whose formal arguments consist of dots only and do something, e.g.,
call 'cbind', based on the attribute
f<- function(...) {get first attribute; maybe or maybe not call 'cbind'}
I thought of (ignoring "deparse.level" for the moment)
f<-function(...) {x <- attr(list(...)[[1L]], "foo"); if (x=="bar")
cbind(...) else x}
what about using the somewhat obscure ..1 syntax? This version runs quite a bit faster for me:
g <- function(...) {
x <- attr(..1, "foo")
if (x == "bar")
cbind(...)
else
x
}
but it will be hard to quantify how this pans out for your unless we know how many and what size and type the arguments are.
Cheers,
Olaf
Hi Dan,
Is there a reason you can't change the function to
f <- function(x, ...) {}
?
Hadley
On Fri, Jul 2, 2010 at 4:26 PM, Daniel Murphy <chiefmurphy at gmail.com> wrote:
R-Devel:
I am trying to get an attribute of the first argument in a call to a
function whose formal arguments consist of dots only and do something, e.g.,
call 'cbind', based on the attribute
f<- function(...) {get first attribute; maybe or maybe not call 'cbind'}
I thought of (ignoring "deparse.level" for the moment)
f<-function(...) {x <- attr(list(...)[[1L]], "foo"); if (x=="bar")
cbind(...) else x}
but I feared my solution might do some extra copying, with a performance
penalty if the dotted objects in the actual call to "f' are very large.
I thought the following alternative might avoid a potential performance hit
by evaluating the attribute in the parent.frame (and therefore avoid extra
copying?):
f<-function(...)
{
? L<-match.call(expand.dots=FALSE)[[2L]]
? x <- eval(substitute(attr(x,"foo"), list(x=L[[1L]])))
? if (x=="bar") cbind(...) else x
}
system.time tests showed this second form to be only marginally faster.
Is my fear about extra copying unwarranted? If not, is there a better way to
get the "foo" attribute of the first argument other than my two
alternatives?
Thanks,
Dan Murphy
? ? ? ?[[alternative HTML version deleted]]
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
Assistant Professor / Dobelman Family Junior Chair Department of Statistics / Rice University http://had.co.nz/
An embedded and charset-unspecified text was scrubbed... Name: not available URL: <https://stat.ethz.ch/pipermail/r-devel/attachments/20100703/ab50b490/attachment.pl>
I think you have missed the use of ..1 etc: see e.g. cBind() in package Matrix. So x <- attr(list(...)[[1L]], "foo") can be x <- attr(..1, "foo") As for 'extra copying', it all depends on exactly what you are doing, but compare
foo1 <- function(...) length(..1) foo2 <- function(...) length(list(...)[[1L]]) tracemem(x <- runif(1000))
[1] "<0x1b27800>"
foo1(x)
[1] 1000
tracemem(x <- runif(1000))
[1] "<0x1b29800>"
foo2(x)
tracemem[0x1b29800 -> 0x10a2200]: foo2 [1] 1000
On Sat, 3 Jul 2010, Daniel Murphy wrote:
Hi Hadley,
My actual goal is to have a cbind method in the mondate package that behaves
just like the base cbind function: class and shape of the result, names,
etc. Perhaps it's due to the fact that 'cbind' uses its own internal
dispatching, but I have not found a way to implement a "true" S3-style cbind
method. (This is probably ancient news to the development team.) An S4 cbind
method will utilize callNextMethod with just setGeneric("cbind"), which has
no 'x' in the formal arguments. With no 'x', there's no "first argument" on
which to dispatch a "mondate" method. I can make the cbind of mondates also
be a mondate with an all-encompassing setMethod("cbind","ANY", etc) method,
but that wrests dispatch control from cbind which makes no sense whatsoever.
So, to make a long story even longer, I settled for a "cbindmondate
function" that utilizes the speed of base::cbind and (with one exception)
gives me the hoped-for "base cbind behavior."
I can send examples of my trial-and-error attempts under separate email if
you're interested.
Best regards,
Dan
On Sat, Jul 3, 2010 at 9:17 AM, Hadley Wickham <hadley at rice.edu> wrote:
Hi Dan,
Is there a reason you can't change the function to
f <- function(x, ...) {}
?
Hadley
On Fri, Jul 2, 2010 at 4:26 PM, Daniel Murphy <chiefmurphy at gmail.com>
wrote:
R-Devel: I am trying to get an attribute of the first argument in a call to a function whose formal arguments consist of dots only and do something,
e.g.,
call 'cbind', based on the attribute
f<- function(...) {get first attribute; maybe or maybe not call 'cbind'}
I thought of (ignoring "deparse.level" for the moment)
f<-function(...) {x <- attr(list(...)[[1L]], "foo"); if (x=="bar")
cbind(...) else x}
but I feared my solution might do some extra copying, with a performance
penalty if the dotted objects in the actual call to "f' are very large.
I thought the following alternative might avoid a potential performance
hit
by evaluating the attribute in the parent.frame (and therefore avoid
extra
copying?):
f<-function(...)
{
L<-match.call(expand.dots=FALSE)[[2L]]
x <- eval(substitute(attr(x,"foo"), list(x=L[[1L]])))
if (x=="bar") cbind(...) else x
}
system.time tests showed this second form to be only marginally faster.
Is my fear about extra copying unwarranted? If not, is there a better way
to
get the "foo" attribute of the first argument other than my two
alternatives?
Thanks,
Dan Murphy
[[alternative HTML version deleted]]
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
-- Assistant Professor / Dobelman Family Junior Chair Department of Statistics / Rice University http://had.co.nz/
[[alternative HTML version deleted]]
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
Brian D. Ripley, ripley at stats.ox.ac.uk Professor of Applied Statistics, http://www.stats.ox.ac.uk/~ripley/ University of Oxford, Tel: +44 1865 272861 (self) 1 South Parks Road, +44 1865 272866 (PA) Oxford OX1 3TG, UK Fax: +44 1865 272595
An embedded and charset-unspecified text was scrubbed... Name: not available URL: <https://stat.ethz.ch/pipermail/r-devel/attachments/20100704/bc6422b9/attachment.asc>
"DM" == Daniel Murphy <chiefmurphy at gmail.com>
on Sun, 4 Jul 2010 11:11:43 -0700 writes:
DM> Thank you, Professor, for drawing my attention to the nifty tracemem
DM> function. I'm using the ..1 syntax to check the properties of the S4 class
DM> at function call.
DM> The Description at ?"cBind" tells me that I'm not alone in this predicament.
DM> Just as Matrix needs its own cBind function, my package will need its own
DM> cbindMondate function. Alas, subclasses of mondate will also need their own
DM> binding functions, thus defeating one of the purposes of the class paradigm
DM> (for binding, anyway).
I don't think you see the full picture:
Matrix::cBind() builds on methods::cbind2()
and cbind2 / rbind2 are there (in package methods) for you to
use setMethod() on them.
BTW: When Matrix::cBind() -- and cbind2 / rbind2 in package
methods were written,
R did not have the possibility yet to write methods for "...",
which it now does.
In the package Rmpfr (arbitrary-precision number ["mpfr"] computing),
where I also define methods for matrices of such "mpfr" numbers
(classes "mpfrMatrix" and "mpfrArray"),
I use the new feature of defining methods for "..." :
setGeneric("cbind", signature = "...")
setMethod("cbind", "Mnumber",
function(..., deparse.level = 1) {
args <- list(...)
if(all(sapply(args, is.atomic)))
return( base::cbind(..., deparse.level = deparse.level) )
## else: at least one is "mpfr(Matrix/Array)"
if(any(sapply(args, is.character))) {
## result will be <character> matrix !
isM <- sapply(args, is, class2 = "mpfr")
args[isM] <- lapply(args[isM], as, Class = "character")
return(do.call(base::cbind,
c(args, list(deparse.level=deparse.level))))
} else if(any(sapply(args, is.complex))) {
## result will be <complex> matrix;
## in the future <complex_mpfr> ???
stop("cbind(...) of 'complex' and 'mpfr' objects is not implemented")
## give at least warning !!
}
## else
..........
..........
})
where I use a useful class union
setClassUnion("Mnumber",
members = c("array_or_vector", # *but* must be numeric-like
"mpfr", "mpfrArray", "mpfrMatrix"))
----
As always, I'd recommend to read the R source, rather than just
investigate the installed package.
You can get the *source* tarbal, i.e., the *.tar.gz file from
CRAN, or, as it's all on R-forge,
http://rmpfr.r-forge.r-project.org/
you can get the source tarball here,
https://r-forge.r-project.org/R/?group_id=386
or browse the source at
https://r-forge.r-project.org/scm/viewvc.php/pkg/?root=rmpfr
But if I were you I'd get it via
svn checkout svn://svn.r-forge.r-project.org/svnroot/rmpfr/pkg Rmpfr
DM> As an aside, I wonder why, on around line 75, cBind uses 'rep.int("",
DM> ncol(r))' rather than the slightly faster 'character(ncol(r))'.
Well, I would not remember, but the first one is a more self-explaining,
... and I would guess strongly that time difference is
irrelevant in the context where it's used...
... but then thanks for your hint :-)
Martin
DM> Thanks again,
DM> Dan
DM> On Sun, Jul 4, 2010 at 4:36 AM, Prof Brian Ripley <ripley at stats.ox.ac.uk>wrote:
>> I think you have missed the use of ..1 etc: see e.g. cBind() in package
>> Matrix.
>>
>> So x <- attr(list(...)[[1L]], "foo") can be x <- attr(..1, "foo")
>>
>> As for 'extra copying', it all depends on exactly what you are doing, but
>> compare
>>
>> foo1 <- function(...) length(..1)
>>> foo2 <- function(...) length(list(...)[[1L]])
>>> tracemem(x <- runif(1000))
>>>
>> [1] "<0x1b27800>"
>>
>>> foo1(x)
>>>
>> [1] 1000
>>
>>> tracemem(x <- runif(1000))
>>>
>> [1] "<0x1b29800>"
>>
>>> foo2(x)
>>>
>> tracemem[0x1b29800 -> 0x10a2200]: foo2
>> [1] 1000
>>
>>
>>
>> <snip>
>> --
>> Brian D. Ripley, ripley at stats.ox.ac.uk
>> Professor of Applied Statistics, http://www.stats.ox.ac.uk/~ripley/
>> University of Oxford, Tel: +44 1865 272861 (self)
>> 1 South Parks Road, +44 1865 272866 (PA)
>> Oxford OX1 3TG, UK Fax: +44 1865 272595
>>
DM> [[alternative HTML version deleted]]
DM> ______________________________________________
DM> R-devel at r-project.org mailing list
DM> https://stat.ethz.ch/mailman/listinfo/r-devel
An embedded and charset-unspecified text was scrubbed... Name: not available URL: <https://stat.ethz.ch/pipermail/r-devel/attachments/20100705/ad9b4dbc/attachment.pl>
An embedded and charset-unspecified text was scrubbed... Name: not available URL: <https://stat.ethz.ch/pipermail/r-devel/attachments/20100705/0d42b535/attachment.pl>