[R] Multiple if function

Berend Hasselman bhh at xs4all.nl
Thu Sep 17 10:17:11 CEST 2015


> On 17 Sep 2015, at 01:42, Dénes Tóth <toth.denes at ttk.mta.hu> wrote:
> 
> 
> 
> On 09/16/2015 04:41 PM, Bert Gunter wrote:
>> Yes! Chuck's use of mapply is exactly the split/combine strategy I was
>> looking for. In retrospect, exactly how one should think about it.
>> Many thanks to all for a constructive discussion .
>> 
>> -- Bert
>> 
>> 
>> Bert Gunter
>> 
>>>>> 
>>>>> Use mapply like this on large problems:
>>>>> 
>>>>> unsplit(
>>>>>   mapply(
>>>>>       function(x,z) eval( x, list( y=z )),
>>>>>       expression( A=y*2, B=y+3, C=sqrt(y) ),
>>>>>       split( dat$Flow, dat$ASB ),
>>>>>       SIMPLIFY=FALSE),
>>>>>   dat$ASB)
>>>>> 
>>>>> Chuck
>>>>> 
> 
> 
> Is there any reason not to use data.table for this purpose, especially if efficiency is of concern?
> 
> ---
> 
> # load data.table and microbenchmark
> library(data.table)
> library(microbenchmark)
> #
> # prepare data
> DF <- data.frame(
>    ASB = rep_len(factor(LETTERS[1:3]), 3e5),
>    Flow = rnorm(3e5)^2)
> DT <- as.data.table(DF)
> DT[, ASB := as.character(ASB)]
> #
> # define functions
> #
> # Chuck's version
> fnSplit <- function(dat) {
>    unsplit(
>        mapply(
>            function(x,z) eval( x, list( y=z )),
>            expression( A=y*2, B=y+3, C=sqrt(y) ),
>            split( dat$Flow, dat$ASB ),
>            SIMPLIFY=FALSE),
>        dat$ASB)
> }
> #
> # data.table-way (IMHO, much easier to read)
> fnDataTable <- function(dat) {
>    dat[,
>        result :=
>            if (.BY == "A") {
>                2 * Flow
>            } else if (.BY == "B") {
>                3 + Flow
>            } else if (.BY == "C") {
>                sqrt(Flow)
>            },
>        by = ASB]
> }
> #
> # benchmark
> #
> microbenchmark(fnSplit(DF), fnDataTable(DT))
> identical(fnSplit(DF), fnDataTable(DT)[, result])
> 
> ---
> 
> Actually, in Chuck's version the unsplit() part is slow. If the order is not of concern (e.g., DF is reordered before calling fnSplit), fnSplit is comparable to the DT-version.
> 

But David’s version is faster than Chuck’s fnSplit. I modified David’s solution slightly to get a result that is identical to fnSplit.

# David's version
# my modification to return a vector just like fnSplit
fnDavid <- function(dat) {
    z <- mapply(
          function(x,z) eval( x, list( y=z )),
          expression(A= y*2, B=y+3, C=sqrt(y) ),
          split( dat$Flow, dat$ASB ),
          USE.NAMES=FALSE, SIMPLIFY=TRUE
        )
    as.vector(t(z))
}

Added this to Dénes's code.
Benchmarking  with R package rbenchmark and testing result like this

library(rbenchmark)
benchmark(fnSplit(DF), fnDataTable(DT),fnDavid(DF))
identical(fnSplit(DF), fnDataTable(DT)[, result])
identical(fnSplit(DF), fnDavid(DF))

gave this:

             test replications elapsed relative user.self sys.self user.child
2 fnDataTable(DT)          100   0.829    1.000     0.762    0.066          0
3     fnDavid(DF)          100   1.615    1.948     1.515    0.098          0
1     fnSplit(DF)          100   2.878    3.472     2.685    0.190          0
  sys.child
2         0
3         0
1         0

> identical(fnSplit(DF), fnDataTable(DT)[, result])
[1] TRUE
> identical(fnSplit(DF), fnDavid(DF))
[1] TRUE
 

Berend



More information about the R-help mailing list