代码之家  ›  专栏  ›  技术社区  ›  jay.sf

如何用lappy定义多个变量?

  •  7
  • jay.sf  · 技术社区  · 7 年前

    我想将具有多个不同值的变量的函数应用于列表。我知道如何使用一个变化的变量

    sapply(c(1:10), function(x) x * 2)
    # [1]  2  4  6  8 10 12 14 16 18 20
    

    但不是两个人。我首先手动向您显示我想要什么(实际上我使用 lapply() 但是 sapply() 在SO中更具概括性):

    # manual
    a <- sapply(c(1:10), function(x, y=2) x * y)
    b <- sapply(c(1:10), function(x, y=3) x * y)
    c <- sapply(c(1:10), function(x, y=4) x * y)
    c(a, b, c)
    # [1]  2  4  6  8 10 12 14 16 18 20  3  6  9 12 15 18 21 24 27 30  4  8 12 
    # [24]  16 20 24 28 32 36 40
    

    这是我的尝试,我试图定义两者 x y .

    # attempt
    X <- list(x = 1:10, y = 2:4)
    sapply(c(1:10, 2:4), function(x, y) x * y)
    # Error in FUN(X[[i]], ...) : argument "y" is missing, with no default
    

    解决方案基准

    library(microbenchmark)
    microbenchmark(sapply = as.vector(sapply(1:10, function(x, y) x * y, 2:4)), 
                   mapply = mapply( FUN = function(x, y) x * y, 1:10, rep( x = 2:4, each = 10)),
                   sapply2 = as.vector(sapply(1:10, function(y) sapply(2:4, function(x) x * y))),
                   outer = c(outer(1:10, 2:4, function(x, y) x * y)))
    # Unit: microseconds
    # expr        min       lq      mean   median       uq      max neval
    # sapply   34.212  36.3500  62.44864  39.1295  41.9090 2304.542   100
    # mapply   62.008  65.8570  87.82891  70.3470  76.5480 1283.342   100
    # sapply2 196.714 203.9835 262.09990 223.6550 232.2080 3344.129   100
    # outer     7.698  10.4775  13.02223  12.4020  13.4715   53.883   100
    
    4 回复  |  直到 7 年前
        1
  •  6
  •   G. Grothendieck    7 年前

    一般解决方案

    尝试 outer :

    c(outer(1:10, 2:4, Vectorize(function(x, y) x*y)))
    ##  [1]  2  4  6  8 10 12 14 16 18 20  3  6  9 12 15 18 21 24 27 30  4  8 12 16 20
    ## [26] 24 28 32 36 40
    

    如果函数已矢量化

    如果函数已经矢量化,就像这里一样,那么我们可以省略 Vectorize :

    c(outer(1:10, 2:4, function(x, y) x * y))
    ##  [1]  2  4  6  8 10 12 14 16 18 20  3  6  9 12 15 18 21 24 27 30  4  8 12 16 20
    ## [26] 24 28 32 36 40
    

    所示的特定示例

    事实上,在这种特殊情况下,显示的匿名函数是默认函数,因此这将起作用:

    c(outer(1:10, 2:4))
    ##  [1]  2  4  6  8 10 12 14 16 18 20  3  6  9 12 15 18 21 24 27 30  4  8 12 16 20
    ## [26] 24 28 32 36 40
    

    在这种特殊情况下,我们还可以使用:

    c(1:10 %o% 2:4)
    ##  [1]  2  4  6  8 10 12 14 16 18 20  3  6  9 12 15 18 21 24 27 30  4  8 12 16 20
    ## [26] 24 28 32 36 40
    

    如果输入为列表X

    如果您的起点是列表 X 如问题所示:

    c(outer(X[[1]], X[[2]], Vectorize(function(x, y) x * y)))
    ##  [1]  2  4  6  8 10 12 14 16 18 20  3  6  9 12 15 18 21 24 27 30  4  8 12 16 20
    ## [26] 24 28 32 36 40
    

    c(do.call("outer", c(unname(X), Vectorize(function(x, y) x*y))))
    ##  [1]  2  4  6  8 10 12 14 16 18 20  3  6  9 12 15 18 21 24 27 30  4  8 12 16 20
    ## [26] 24 28 32 36 40
    

    如果前面的章节适用于缩短它,如果适用的话。

        2
  •  6
  •   Cristian E. Nuno    7 年前

    使用mapply()

    mapply() 将函数应用于多个列表或向量参数。

    rep() 也用于重复值2、3和4。在中指定10 each 参数 代表() 重复的每个元素 x 10次。

    这是必要的,因为 mapply() -1:10-长度为10。

    # supply the function first, followed by the
    # arguments in the order in which they are called in `FUN`
    mapply( FUN = function(x, y) x * y
            , 1:10
            , rep( x = 2:4, each = 10)
    )
    
    # [1]   2  4  6  8 10 12 14 16 18 20  3  6  9 12 15 18 21 24 27 30  4  8 12 16 20
    # [26] 24 28 32 36 40
    
        3
  •  6
  •   Joris Meys    7 年前

    首先,你可以用 lapply() 如果您的函数是矢量化的。在这种情况下,它是:

    x <- 1:10
    unlist(lapply(2:4, function(y) x*y))
    # OR
    unlist(lapply(2:4, function(x=x,y) x*y))
    

    其次,如果需要对两个向量的每个组合应用函数,请使用 outer() :

    xf <- 1:10
    yf <- 2:4
    c(xf %o% yf)
    # OR spelled out for any function:
    c(outer(xf,yf,FUN = `*`))
    

    如果使用mapply,则可以使用参数 MoreArgs 避免使用 rep 要构造参数,请执行以下操作:

    xf <- 1:10
    yf <- 2:4
    mapply(function(x,y) x*y,
           y = yf,
           MoreArgs = list(x = xf))
    

    这与 lapply() 我在上面展示的构造。生成的矩阵也可以使用 SIMPLIFY = FALSE unlist() :

    unlist(mapply(function(x,y) x*y,
                  y = yf,
                  MoreArgs = list(x = xf),
                  SIMPLIFY = FALSE))
    

    哪种解决方案最方便,取决于您的实际用例。就时间而言,它们都具有可比性,可能在最近的R版本中 外部() 将比其他解决方案慢一点。

    标杆管理

    为了说明结果如何因对象的大小和顺序而大不相同,我提供了以下基准测试结果(下面的代码和输出)。这表明:

    1. 外部() 不一定是最快的解决方案,尽管它通常是最快的解决方案之一。
    2. 手动重复中的一个向量 mapply() 增加了太多的开销 sapply() 打电话要快得多。

    代码: 警告:这将运行一段时间

    fx <- sample(1e4)
    fy <- sample(1e3)
    library(microbenchmark)
    microbenchmark(sapply = as.vector(sapply(fx, function(x, y) x * y, fy)), 
                   mapply = mapply( FUN = function(x, y) x * y, fx, rep( fy, each = 1e4)),
                   sapply2 = as.vector(sapply(fx, function(y) sapply(fy, function(x) x * y))),
                   outer = c(outer(fx, fy, function(x, y) x * y)),
                   mapply2 = mapply(function(x,y) x*y, x=fx, MoreArgs = list(y = fy)),
                   mapply3 = mapply(function(x,y) x*y, y=fy, MoreArgs = list(x = fx)),
                   times = 15)
    

    我的机器上的输出:

    Unit: milliseconds
        expr         min          lq       mean      median          uq        max neval cld
      sapply    89.52318    92.98653   344.1538   117.11280   239.64887  1485.3178    15 a  
      mapply 20471.02137 22925.42757 24478.5985 24650.29055 25627.31232 28840.3494    15   c
     sapply2  7472.02251  8268.04696  9519.8016  8707.19193  9528.46181 14182.7537    15  b 
       outer    77.62331    85.94651   189.5107    91.83722   182.08506  1119.6620    15 a  
     mapply2    77.76871    79.71924   143.9484    81.24168    84.53247   971.1792    15 a  
     mapply3    65.21709    71.85662   107.9586    73.80779   124.21141   242.0760    15 a  
    
        4
  •  3
  •   www    7 年前

    另一个想法是使用 sapply 两次

    as.vector(sapply(2:4, function(y) sapply(1:10, function(x) x * y)))
    [1]  2  4  6  8 10 12 14 16 18 20  3  6  9 12 15 18 21 24 27 30  4  8 12 16 20 24 28 32 36 40
    

    或者我们可以使用 map2_int purrr 包裹 map2\u int 可以循环遍历两个长度相同的向量,并确保输出为整数。所以我们需要使用 rep(a, length(b)) rep(b, each = length(a)) 确保每个元素成对。 ~.x * .y 是在中指定函数的简洁方法 呼噜声 .

    library(purrr)
    
    a <- 1:10
    b <- 2:4
    map2_int(rep(a, length(b)), rep(b, each = length(a)), ~.x * .y)
    # [1]  2  4  6  8 10 12 14 16 18 20  3  6  9 12 15 18 21 24 27 30  4  8 12 16 20 24 28 32 36 40