【问题标题】:Simplify the code by using one of R's apply functions使用 R 的应用函数之一简化代码
【发布时间】:2013-04-15 12:47:20
【问题描述】:

我找不到令人满意的教程来解释我如何使用应用函数的所有可能性。我仍然是一个新手,但这通常可以派上用场并显着简化我的代码。所以这是我的例子...... 我有一个如下所示的数据框:

> head(p01)
   time key dwell
1   8.13   z  0.00
3   8.13   x  1.25
5   9.38   l  0.87
7  10.25   x  0.15
9  10.40   l  1.13
11 11.53   x  0.45

将其放入 R:

p01 <- structure(list(time = c(8.13, 8.13, 9.38, 10.25, 10.4, 11.53), 
key = c("z", "x", "l", "x", "l", "x"), dwell = c(0, 1.25, 
0.869, 0.15, 1.13, 0.45)), .Names = c("time", "key", "dwell"), row.names = c(1L, 3L, 5L, 7L, 9L, 11L), class = "data.frame")

现在我想统计每个字母在p01$key 中出现的次数,并在p01$occurences 中打印出来,结果如下:

    time key dwell occurences
1   8.13   z  0.00          1
3   8.13   x  1.25          3
5   9.38   l  0.87          2
7  10.25   x  0.15          3
9  10.40   l  1.13          2
11 11.53   x  0.45          3

我现在的做法是:

p01[p01$key == "l", "occurences"] <- table(p01$key)["l"]
p01[p01$key == "x", "occurences"] <- table(p01$key)["x"]
p01[p01$key == "z", "occurences"] <- table(p01$key)["z"]

...这当然不是最好的解决方案。特别是因为真实数据在p01$key(16 个不同字母之一)中包含更多可能性。

除此之外,我想计算每个字母的总数 dwell,所以我现在要做的是:

p01[p01$key == "l", "total_dwell"] <- tapply(p01$dwell, p01$key, sum)["l"]
p01[p01$key == "x", "total_dwell"] <- tapply(p01$dwell, p01$key, sum)["x"]
p01[p01$key == "z", "total_dwell"] <- tapply(p01$dwell, p01$key, sum)["z"]

为了得到:

    time key dwell total_dwell
1   8.13   z  0.00        0.00
3   8.13   x  1.25        1.85
5   9.38   l  0.87        2.00
7  10.25   x  0.15        1.85
9  10.40   l  1.13        2.00
11 11.53   x  0.45        1.85

在过去的 6 个小时里,我一直在谷歌上搜索并浏览了几本书。非常感谢一个优雅的解决方案和/或一些综合教程的链接。 我的解决方案显然有效,但这不是我第一次必须解决这样的问题,而且我的脚本文件开始看起来很荒谬!

【问题讨论】:

  • 我相信有人会为此写出答案,但this 是对此类任务的相当全面的处理。唯一的遗漏是 data.table 包,可能。
  • 我尝试描述如何将循环转换为一般的函数:github.com/hadley/devtools/wiki/Functionals

标签: r dataframe apply


【解决方案1】:

你自然可以用 tapply 解决这个问题。 请注意,这些会创建一个新对象 p01.summary,而不是添加到您的对象 p01。 另一行代码可以解决这个问题

p01.summary = with(p01, cbind(occurences=table(key),total.dwell=tapply(dwell,key,sum)))

p01.summary = with(p01, do.call(rbind,tapply(dwell,key,function(KEY){
   data.frame(occurence=length(KEY),total.dwell= sum(KEY))
}) ))

【讨论】:

    【解决方案2】:

    如果您的数据集很大,请尝试 data.table。

    library(data.table)
    DT <- data.table(p01)
    DT[,occurences:=.N,by=key]
    DT[,total_dwell:=sum(dwell),by=key]
    
        time key dwell occurences total_dwell
    1:  8.13   z 0.000          1       0.000
    2:  8.13   x 1.250          3       1.850
    3:  9.38   l 0.869          2       1.999
    4: 10.25   x 0.150          3       1.850
    5: 10.40   l 1.130          2       1.999
    6: 11.53   x 0.450          3       1.850
    

    通过引用分配的两行可以组合如下:

    DT[, `:=`(occurences = .N, total_dwell = sum(dwell)), by=key]
    

    【讨论】:

    • 当然,您也可以将data.table 用于小型数据集:)。但是plyr 语法对我来说看起来更容易学习(注意我大量使用plyr 而没有data.table)。
    • 其实习惯了data.table的语法就更容易做这种操作了。
    • 什么更容易阅读可能也是一个品味问题,但data.table 看起来像一个很棒的包。
    • 你可以同时做这两个,使用引用的:=(无法弄清楚如何在评论空间中输入),你应该使用.N而不是length(time)
    • data.table 的缺点是它的工作方式与 R 中大多数其他类型的对象完全不同,因此您必须学习两种思考方式:通常的 R 方式和 data.table大大地。优点是这样可以让 data.table 非常快,但缺点是认知开销较高。
    【解决方案3】:

    我会使用plyr:

    res = ddply(p01, .(key), transform, 
                               occurrences = length(key), 
                               total_dwell = sum(dwell))
    res
       time key dwell occurrences total_dwell
    1  9.38   l 0.869           2       1.999
    2 10.40   l 1.130           2       1.999
    3  8.13   x 1.250           3       1.850
    4 10.25   x 0.150           3       1.850
    5 11.53   x 0.450           3       1.850
    6  8.13   z 0.000           1       0.000
    

    请注意,在此之后,表格按字母顺序在key 上排序。您可以使用order 诉诸time

    res[order(res$time),]
       time key dwell occurrences total_dwell
    3  8.13   x 1.250           3       1.850
    6  8.13   z 0.000           1       0.000
    1  9.38   l 0.869           2       1.999
    4 10.25   x 0.150           3       1.850
    2 10.40   l 1.130           2       1.999
    5 11.53   x 0.450           3       1.850
    

    【讨论】:

    • +1 我真的很喜欢 plyr 和朋友中的这些单行字。我仍在学习在基础 R 上使用这些。
    • 太快了!你打我! +1 ;)
    • Plyr 非常好,是的,但如果数据变大,速度会有点慢。在这种情况下,data.table 就是答案...
    • ...只需添加 total_dwell = sum(dwell) 以包含该列。
    • 非常感谢!我接受这个答案只是因为 plyr 需要更短的输入,并且与 joran 建议的论文一起,它应该可以解决我过去和未来的许多问题:) data.table 看起来也很整洁,对于初学者来说可能更直观。但我会先给 plyr 一个机会 :) 干杯,伙计们。
    【解决方案4】:

    我认为您不想在这里使用applytable 获取频率然后使用match 将频率分配给您的表格怎么样:

    freq <- as.data.frame( table(p01$key) )
        # Var1 Freq
    #1    l    2
    #2    x    3
    #3    z    1
    
    p01$occurences <- freq[ match(p01$key , freq[,1] ) , 2 ]
    p01
    #   time key dwell occurences
    #1   8.13   z 0.000          1
    #3   8.13   x 1.250          3
    #5   9.38   l 0.869          2
    #7  10.25   x 0.150          3
    #9  10.40   l 1.130          2
    #11 11.53   x 0.450          3
    

    据我所知,与plyr 解决方案相比,此方法的唯一优势是保留了数据帧的原始顺序。我不知道你是否可以在 ddply 函数中指定它(也许你可以!)。

    【讨论】:

    • +1 分析后排序很容易确定顺序。
    • (+1) @PaulHiemstra,我认为西蒙所说的是您无法从 plyr 获得“未分类”的解决方案。但是你可以从这个中获得两者。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-24
    • 1970-01-01
    • 1970-01-01
    • 2019-08-10
    • 2023-04-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多