【问题标题】:Adding maximum values from different levels to a new column in a data.frame将不同级别的最大值添加到 data.frame 中的新列
【发布时间】:2013-04-24 14:48:59
【问题描述】:

我有以下 R 问题。我做了一个实验,观察了一些汽车的速度。我有一张桌子,上面有汽车(其中数字 1 表示例如保时捷、2 沃尔沃等)和它们的速度。一辆车可以被带入观察不止一次。因此,例如,Porche 被观察树次,Volvo 被观察两次。

exp<-data.frame(car=c(1,1,1,2,2,3),speed=c(10,20,30,40,50,60))

我想添加第三列,其中计算每行/每辆车的最大速度。所以它看起来像这样:

exp<-data.frame(car=c(1,1,1,2,2,3),speed=c(10,20,30,40,50,60), maxSpeed=c(30,30,30,50,50,60))

观察到的保时捷的最大速度是 30,因此保时捷的每一行都会得到 maxSpeed = 30。

我知道它应该是 apply/sapply 函数,但不知道如何实现它。任何人? :)

【问题讨论】:

    标签: r matrix dataframe apply


    【解决方案1】:

    @Arun 这是我在更大样本(1000 条记录)中的结果。中位数的比率现在(实际上)是 0.82:

    exp <- data.frame(car=sample(1:10, 1000, T),speed=rnorm(1000, 20, 5))
    
    f1 <- function() mutate(exp, maxSpeed = ave(speed, car, FUN=max))
    f2 <- function() transform(exp, maxSpeed = ave(speed, car, FUN=max))
    
    library(microbenchmark)
    library(plyr)
    > microbenchmark(f1(), f2(), times=1000)
    Unit: microseconds
     expr     min      lq  median       uq      max neval
     f1() 551.321 565.112 570.565 589.9680 27866.23  1000
     f2() 662.933 683.138 689.552 713.7665 28510.24  1000
    

    plyr 文档本身说 Mutate seems to be considerably faster than transform for large data frames.

    但是,对于这种情况,您可能是对的。如果我放大样本:

    > exp <- data.frame(car=sample(1:1000, 100000, T),speed=rnorm(100000, 20, 5))
    > microbenchmark(f1(), f2(), times=100)
    Unit: milliseconds
     expr      min       lq   median       uq      max neval
     f1() 37.92438 39.00056 40.66607 41.18115 77.41645   100
     f2() 39.47731 40.28650 43.11927 43.70779 78.34878   100
    

    比率接近一。老实说,我很确定plyr 的性能(在我的代码中总是依赖它),这就是我在评论中“声明”的原因。可能在不同的情况下它表现更好..

    编辑:

    使用@Arun 评论中的f3()

    > microbenchmark(f1(), f2(), f3(), times=100)
    Unit: milliseconds
     expr      min       lq   median       uq      max neval
     f1() 38.76050 39.57129 41.48728 42.14812 76.94338   100
     f2() 40.38913 41.19767 44.12329 44.78782 79.94021   100
     f3() 38.63606 39.58700 40.24272 42.04902 76.07551   100
    

    是的!稍微快一点...移动更少的数据?

    【讨论】:

    • (+1) 太棒了!非常感谢您这样做。我以前不知道这一点。怎么样:f3 &lt;- function() { exp[["maxSpeed"]] &lt;- with(exp, ave(speed, car, FUN=max)); exp }.
    • 再次感谢!我只是在自己编辑它。它在小数据和大数据上都更快。我猜transform 中的函数调用重载和exp$maxSpeed 中的$ 重载(使用exp[["maxSpeed"]])都有助于改善很多
    • 检查标记为[data.table] 的帖子(使用方括号搜索标签)并观看魔术:)。
    • @Arun,我会搜索 [r] data.table,因为这也会给出未由 data.table 标记但已在答案中使用它的问题。 Michele,您可能还想搜索 `[r] data.table plyr',您会发现几个速度提升的示例
    【解决方案2】:

    data.table 非常直接

    library(data.table)
    
    exp <- data.table(exp)
    exp[, maxSpeed := max(speed), by=car]
    

    给出:

    exp
       car speed maxSpeed
    1:   1    10       30
    2:   1    20       30
    3:   1    30       30
    4:   2    40       50
    5:   2    50       50
    6:   3    60       60
    

    【讨论】:

    • 霹雳!!我将您的代码与我的答案中的代码进行了比较,您的代码采用4.983214 milliseconds
    • @Michele,很明显data.table 会更快。 data.table 预分配列(max(2*ncol(dt), 100) IIRC)。键入truelength(exp),您将获得 100(100 列)。 := 也通过引用分配值。它会非常快。但是,我想提供一个 base 解决方案。
    • @Arun 我知道!就像问题一样简单(简单来说,我的意思是base 是任何人首先知道的第一个包)。无论如何,在这样的时刻,我很高兴加入这个网站,感谢您的“低级编程”课程!还有很多东西要学!
    • @Michele,正如 Arun 指出的那样,data.table 的魔力在于它努力不制作多余的对象副本并尽可能通过引用进行分配。当数据变大时,这允许疯狂的速度。
    • 谢谢大家的回答!他们都工作,R疯了! :)
    【解决方案3】:
    transform(exp, maxSpeed = ave(speed, car, FUN=max))
    

    使用split的另一种方式:

    exp$maxSpeed <- exp$speed
    split(exp$maxSpeed, exp$car) <- lapply(split(exp$maxSpeed, exp$car), max)
    exp
    

    【讨论】:

    • +1 ave 的最佳实践。而使用plyr 中的mutate 会快30%。使用microbenchmark 包测试。
    • 这让我很惊讶。您介意发布 plye 解决方案和基准测试结果(使用相对较大的数据)吗?
    • 刚刚回答了性能细节。你的惊喜实际上是合理的! :-)
    • 谢谢大家的回答!他们都工作,R疯了! :)
    猜你喜欢
    • 1970-01-01
    • 2016-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-25
    • 1970-01-01
    • 1970-01-01
    • 2010-12-30
    相关资源
    最近更新 更多