我经常使用ddply,但从历史上讲,它是summarize(有时是mutate),只有基本功能(例如mean()var1 - var2等)。我在一个数据集中尝试应用自定义,参与程度更高的功能,并开始尝试研究如何用ddply做到这一点。我有一个成功的解决方案,但是我不明白为什么它会像这样,而对于更多的“正常”功能却如此。

相关的

  • Custom Function not recognized by ddply {plyr}...
  • How do I pass variables to a custom function in ddply?
  • r-help: [R] Correct use of ddply with own function(我最终基于此解决方案)

  • 这是一个示例数据集:
    library(plyr)
    df <- data.frame(id = rep(letters[1:3], each = 3),
                     value = 1:9)
    

    通常,我会像这样使用ddply:
    df_ply_1 <- ddply(df, .(id), mutate, mean = mean(value))
    

    我对此的可视化是ddply根据df的组合组合将id拆分为“小型”数据帧,然后通过对mean()中存在的列名称调用df来添加新列。因此,我尝试实现一个功能扩展了这个想法:
    # actually, my logical extension of the above was to use:
    # ddply(..., mean = function(value) { mean(value) })
    df_ply_2 <- ddply(df, .(id), mutate,
                      mean = function(df) { mean(df$value) })
    
    Error: attempt to replicate an object of type 'closure'
    

    关于自定义函数的所有帮助都没有应用mutate,但是这似乎前后矛盾,或者至少让我讨厌,因为与我实现的解决方案类似:
    df_mean <- function(df) {
        temp <- data.frame(mean = rep(mean(df$value), nrow(df)))
        temp
    }
    
    df_ply_3 <- df
    df_ply_3$mean <- ddply(df, .(id), df_mean)$mean
    

    内联,看来我必须这样做:
    df_ply_4 <- df
    df_ply_4$mean <- ddply(df, .(id), function(x) {
        temp <- data.frame(mean = rep(mean(x$value), length(x$value)))
        temp})$mean
    

    为什么不能将mutate与自定义函数一起使用?仅仅是“内置”函数返回ddply可以处理的某种类,而必须踢出完整的data.frame然后仅调出我关心的列吗?

    感谢您帮助我“获得”它!

    @Gregor回答后更新

    很棒的答案,我想我明白了。确实,我对mutatesummarize的含义感到困惑...认为它们是ddply的参数,涉及如何处理结果与实际上是函数本身。因此,感谢您的深刻见解。

    另外,它确实有助于理解没有mutate/summarize,我需要返回data.frame,这就是我必须对返回的cbind中的列名进行df的原因。

    最后,如果我确实使用mutate,那么现在可以返回向量结果并获得正确的结果将非常有帮助。因此,我可以做到这一点,在阅读您的回答后,我现在已经明白了:
    # I also caught that the code above doesn't do the right thing
    # and recycles the single value returned by mean() vs. repeating it like
    # I expected. Now that I know it's taking a vector, I know I need to return
    # a vector the same length as my mini df
    custom_mean <- function(x) {
        rep(mean(x), length(x))
    }
    
    df_ply_5 <- ddply(df, .(id), mutate,
                  mean = custom_mean(value))
    

    再次感谢您的深入解答!

    根据@Gregor的最新评论更新

    嗯出于对rep(mean(x), length(x))结果的观察,我使用了df_ply_3(我第一次发表这篇文章时,并没有仔细观察它,我只是发现它并没有给我带来错误!):
    df_mean <- function(x) {
        data.frame(mean = mean(x$value))
    }
    
    df_ply_3 <- df
    df_ply_3$mean <- ddply(df, .(id), df_mean)$mean
    
    df_ply_3
      id value mean
    1  a     1    2
    2  a     2    5
    3  a     3    8
    4  b     4    2
    5  b     5    5
    6  b     6    8
    7  c     7    2
    8  c     8    5
    9  c     9    8
    

    因此,我基于3个id变量重复3次这一事实,认为我的代码实际上是一次意外。因此,实际返回等于summarize(每个id值一行),并被回收。如果我像这样更新我的数据框,则对该理论的测试似乎是正确的:
    df <- data.frame(id = c(rep(letters[1:3], each = 3), "d"),
                     value = 1:10)
    

    尝试将df_ply_3方法与df_mean()结合使用时出现错误:
    Error in `$<-.data.frame`(`*tmp*`, "mean", value = c(2, 5, 8, 10)) :
      replacement has 4 rows, data has 10
    

    因此,传递给df_mean的mini df返回df,其中meanvalue向量(返回一个值)取平均值的结果。因此,我的输出只是一个包含三个值的data.frame,每个id组一个。我在想mutate方式“记住”它已传递一个小型数据帧,然后重复单个输出以匹配其长度?

    无论如何,感谢您对df_ply_5的评论;的确,如果我删除rep()位并仅返回mean(x),那么效果很好!

    最佳答案

    你基本上是对的。 ddply确实根据石斑鱼将您的数据分解为微型数据帧,并对每个片段都应用了一个函数。

    使用ddply,所有工作都通过数据帧完成,因此.fun参数必须以一个(小型)数据帧作为输入,并返回一个数据帧作为输出。
    mutatesummarize是适合此要求的函数(它们获取和返回数据帧)。您可以查看他们的个人帮助页面,也可以在ddply之外的数据框中运行它们,例如

    mutate(mtcars, mean.mpg = mean(mpg))
    summarize(mtcars, mean.mpg = mean(mpg))
    

    如果您不使用mutatesummarize,也就是说,您使用自定义函数,那么您的函数还需要采用一个(小型)数据帧作为参数,并返回一个数据帧。

    如果使用mutatesummarize,则传递给ddply的任何其他函数都不会被ddply使用,它们只会传递给mutatesummarize使用。 mutatesummarize使用的函数作用于数据的列,而不作用于整个data.frame。这就是为什么
    ddply(mtcars, "cyl", mutate, mean.mpg = mean(mpg))
    

    请注意,我们没有将mutate传递给函数。我们不说ddply(mtcars, "cyl", mutate, mean)。我们必须告诉它要表达什么意思。在?mutate中,...的描述是“给新列定义的命名参数”,与函数无关。 (mean()与任何“自定义函数”真的不同吗?)

    因此,它不适用于匿名函数-或根本不起作用。传递一个表情!您可以预先定义一个自定义函数。
    custom_function <- function(x) {mean(x + runif(length(x))}
    ddply(mtcars, "cyl", mutate, jittered.mean.mpg = custom_function(mpg))
    ddply(mtcars, "cyl", summarize, jittered.mean.mpg = custom_function(mpg))
    

    这很好地扩展了,您可以具有接受多个参数的函数,并且可以为它们提供不同的列作为参数,但是,如果您使用mutatesummarize,则必须给其他函数提供参数;您不只是传递函数。

    您似乎想要传递ddply一个已经“知道”平均值的函数。为此,我认为您不需要使用mutatesummarize,但是您可以破解自己的版本。对于类似summarize的行为,返回带有单个值的data.frame,对于类似于mutate的行为,返回原始data.frame,并在其上加上额外的值cbind
    mean.mpg.mutate = function(df) {
        cbind.data.frame(df, mean.mpg = mean(df$mpg))
    }
    
    mean.mpg.summarize = function(df) {
        data.frame(mean.mpg = mean(df$mpg))
    }
    
    ddply(mtcars, "cyl", mean.mpg.mutate)
    ddply(mtcars, "cyl", mean.mpg.summarize)
    

    tl; dr



    恰恰相反! mutatesummarize将数据帧作为输入,并踢出数据帧作为返回。但是mutt和总结就是您要传递给ddply的函数,而不是指其他任何东西。

    Mutate和summary是便利功能,您将在ddply的99%的时间中使用它们。

    如果您不使用mutate/summaryize,则您的函数需要获取并返回一个数据帧。

    如果您确实使用了mutate/summitize,那么您就不会传递它们的功能,而是传递可以用您的(小型)数据帧求值的表达式。如果是变异的,则返回值应该是要附加到数据的向量(必要时回收)。如果是汇总,则返回值应为单个值。您没有传递像mean这样的函数;您传递一个表达式,例如mean(mpg)

    dplyr呢?

    这是在dplyr还是一件大事之前写的。 dplyr消除了此过程中的许多困惑,因为它实际上将ddply的嵌套替换为mutatesummarize作为带有顺序函数group_by后跟mutatesummarize的参数的嵌套。我的答案的dplyr版本是
    library(dplyr)
    group_by(mtcars, cyl) %>%
        mutate(mean.mpg = mean(mpg))
    

    将新的列创建直接传递给mutate(或summarize)后,对于哪个函数执行什么操作就不会感到困惑。

    09-27 20:14