使用ddply + mutate和自定义函数?

Hen*_*ndy 4 r plyr

ddply经常使用,但历史上summarize(偶尔mutate)和基本功能,如mean(),var1 - var2等等.我有一个数据集,我正在尝试应用一个自定义,更多涉及的功能,并开始尝试深入研究如何使用ddply.我有一个成功的解决方案,但我不明白为什么它会像这样工作而不是更"正常"的功能.

有关

这是一个示例数据集:

library(plyr)
df <- data.frame(id = rep(letters[1:3], each = 3),
                 value = 1:9)
Run Code Online (Sandbox Code Playgroud)

通常情况下,我会这样使用ddply:

df_ply_1 <- ddply(df, .(id), mutate, mean = mean(value))
Run Code Online (Sandbox Code Playgroud)

我的这种可视化是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'
Run Code Online (Sandbox Code Playgroud)

所有关于自定义函数的帮助都不适用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
Run Code Online (Sandbox Code Playgroud)

在线,看起来我必须这样做:

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
Run Code Online (Sandbox Code Playgroud)

为什么我不能使用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))
Run Code Online (Sandbox Code Playgroud)

再次感谢您的深入解答!


根据@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
Run Code Online (Sandbox Code Playgroud)

所以,我认为我的代码实际上是一个事故,因为我有3个id变量重复3次.因此,实际回报相当于summarize(每个id值一行),并且可以回收.如果我更新我的数据框,测试该理论看起来是准确的:

df <- data.frame(id = c(rep(letters[1:3], each = 3), "d"),
                 value = 1:10)
Run Code Online (Sandbox Code Playgroud)

尝试使用该df_ply_3方法时遇到错误df_mean():

Error in `$<-.data.frame`(`*tmp*`, "mean", value = c(2, 5, 8, 10)) : 
  replacement has 4 rows, data has 10
Run Code Online (Sandbox Code Playgroud)

因此,传递的迷你df df_mean返回一个dfwhere mean,如果value向量(返回一个值)取平均值的结果.所以,我的输出只是data.frame三个值中的一个,id每组一个.我想的是mutate那种"记得"它是通过一个迷你数据帧,然后重复单个输出以匹配它的长度?

无论如何,感谢您的评论df_ply_5; 事实上,如果我删除该rep()位并返回mean(x),它的效果很好!

Gre*_*gor 11

你大部分都是对的.ddply确实会根据石斑鱼将您的数据分解为迷你数据框,并将功能应用于每个部分.

使用ddply,所有工作都是使用数据帧完成的,因此.fun参数必须将(迷你)数据帧作为输入并返回数据帧作为输出.

mutate并且summarize是适合此法案的函数(它们采用并返回数据框).您可以查看其各自的帮助页面,或在外部的数据框上运行它们ddply,例如

mutate(mtcars, mean.mpg = mean(mpg))
summarize(mtcars, mean.mpg = mean(mpg))
Run Code Online (Sandbox Code Playgroud)

如果您使用mutate或者summarize,即您使用自定义函数,那么您的函数还需要将(迷你)数据框作为参数,并返回数据框.

如果你确实使用了mutate或者summarize你传递给的任何其他函数ddply都没有被使用ddply,那么它们只是传递给mutateor使用summarize.函数使用的函数mutatesummarize作用于数据的列,而不是整个data.frame.这就是为什么

ddply(mtcars, "cyl", mutate, mean.mpg = mean(mpg))
Run Code Online (Sandbox Code Playgroud)

请注意,我们不传递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))
Run Code Online (Sandbox Code Playgroud)

这延长了好了,你可以有多个参数的功能,你可以给他们不同的列作为参数,但如果你正在使用的mutatesummarize,你必须给其他函数的参数; 你不只是传递这些功能.

您似乎想要传递ddply一个已经"知道"哪个列的函数.对于这一点,我认为你需要使用mutatesummarize,但你可以砍你自己的版本.对于summarize类似行为,返回带有单个值的data.frame,类似于mutate行为,返回带有额外值cbinded 的原始data.frame

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)
Run Code Online (Sandbox Code Playgroud)

TL;博士

为什么我不能在自定义函数中使用mutate?只是"内置"函数返回某种类,ddply可以处理这个类而不必踢出一个完整的data.frame然后只调出我关心的列吗?

恰恰相反!mutate并将summarize数据帧作为输入,并将数据帧作为返回.但变异和总结你传递给ddply的功能,而不是意思或其他任何东西.

变异和总结是您在99%的时间内使用的便利功能ddply.

如果你不使用mutate/summarize,那么你的函数需要获取并返回一个数据框.

如果你确实使用mutate/summarize,那么你不传递它们的函数,你传递它们可以使用你的(迷你)数据框计算的表达式.如果它是变异的,则返回应该是要附加到数据的向量(根据需要回收).如果总结,则返回值应为单个值.你没有通过一个函数,比如mean; 你传递一个表达式,比如mean(mpg).


怎么样dplyr

之前写的dplyr是一件事,或者至少是一件大事.dplyr从这个过程中消除了很多混淆,因为它实际上取代了ddplywith mutatesummarize作为参数的嵌套,顺序函数group_by后跟mutatesummarize.在dplyr我的答案的版本将是

library(dplyr)
group_by(mtcars, cyl) %>%
    mutate(mean.mpg = mean(mpg))
Run Code Online (Sandbox Code Playgroud)

随着新列创建直接传递给mutate(或summarize),没有关于哪个函数做什么的混淆.

  • 也许`mutate`应该只产生错误信息:"不要向我发送功能." (2认同)