使用plyr计算组间同比增长的初学者技巧

5

我是一个新手,对plyr(和R)一无所知,需要帮助入门。以棒球数据集为例,如何计算按联盟和队伍(lg和team)的年度“打数”变化(yoy)?

library(plyr)
df1 <- aggregate(ab~year+lg+team, FUN=sum, data=baseball)

对数据框进行了一些聚合以简化数据,数据现在如下所示:

head(df1)

  year lg team   ab
  1884 UA  ALT  108
  1997 AL  ANA 1703
  1998 AL  ANA 1502
  1999 AL  ANA  660
  2000 AL  ANA   85
  2001 AL  ANA  219

我希望您最终能得到这样的结果。
  year lg team   ab yoy
  1997 AL  ANA 1703  NA
  1998 AL  ANA 1502  -201
  1999 AL  ANA  660  -842
  2000 AL  ANA   85  -575
  2001 AL  ANA  219  134

我开始编写以下函数,但我认为它是错误的:

yoy.func <- function(df) {
  lag <- c(df$ab[-1],0)
  cur <- c(df$ab[1],0)
  df$yoy <- cur -lag
  return(df)
}

尝试无果后,我使用以下代码来尝试返回年同比变化率。

df2 <- ddply(df1, .(lg, team), yoy.func)

任何指导将不胜感激。
谢谢。

1
做得好,提供了可重现的例子和出色的第一次尝试!问题在于函数中两个向量的长度。lag将等于nrow(df),而cur将等于nrow(df) + 1。如果你只是减去df$ab - lag,那么你就可以解决问题了。(唯一缺少的部分是确认你是否有连续的年份。)我还会看看zoo包中的rollapply - Justin
Justin,感谢你建议看一下rollapply。但是,为了澄清,你是在建议它作为确保年份连续的工具吗? - MikeTP
2个回答

6

我知道你要求一个“plyr”特定的解决方案,但是出于分享的目的,这里提供一种基于R语言的替代方法。在我看来,我发现基于R语言的方法同样“可读性”很高。而且,在这个特定的情况下,它要快得多!

output <- within(df1, {
  yoy <- ave(ab, team, lg, FUN = function(x) c(NA, diff(x)))
})
head(output)
#   year lg team   ab  yoy
# 1 1884 UA  ALT  108   NA
# 2 1997 AL  ANA 1703   NA
# 3 1998 AL  ANA 1502 -201
# 4 1999 AL  ANA  660 -842
# 5 2000 AL  ANA   85 -575
# 6 2001 AL  ANA  219  134

library(rbenchmark)

benchmark(DDPLY = {
  ddply(df1, .(team, lg), mutate ,
        yoy = c(NA, diff(ab)))
}, WITHIN = {
  within(df1, {
    yoy <- ave(ab, team, lg, FUN = function(x) c(NA, diff(x)))
  })
}, columns = c("test", "replications", "elapsed", 
               "relative", "user.self"))
#     test replications elapsed relative user.self
# 1  DDPLY          100  10.675    4.974    10.609
# 2 WITHIN          100   2.146    1.000     2.128

更新:data.table

如果你的数据非常大,可以考虑使用data.table。即使在此示例中,相对而言也能得到很好的加速。此外,语法非常紧凑,我认为易于阅读。

library(plyr)
df1 <- aggregate(ab~year+lg+team, FUN=sum, data=baseball)
library(data.table)
DT <- data.table(df1)
DT
#       year lg team   ab
#    1: 1884 UA  ALT  108
#    2: 1997 AL  ANA 1703
#    3: 1998 AL  ANA 1502
#    4: 1999 AL  ANA  660
#    5: 2000 AL  ANA   85
#   ---                  
# 2523: 1895 NL  WSN  839
# 2524: 1896 NL  WSN  982
# 2525: 1897 NL  WSN 1426
# 2526: 1898 NL  WSN 1736
# 2527: 1899 NL  WSN  787

现在,看看这个简洁的解决方案:
DT[, yoy := c(NA, diff(ab)), by = "team,lg"]
DT
#       year lg team   ab  yoy
#    1: 1884 UA  ALT  108   NA
#    2: 1997 AL  ANA 1703   NA
#    3: 1998 AL  ANA 1502 -201
#    4: 1999 AL  ANA  660 -842
#    5: 2000 AL  ANA   85 -575
#   ---                       
# 2523: 1895 NL  WSN  839  290
# 2524: 1896 NL  WSN  982  143
# 2525: 1897 NL  WSN 1426  444
# 2526: 1898 NL  WSN 1736  310
# 2527: 1899 NL  WSN  787 -949

谢谢Amanda,好主意。我对R还不是很熟悉,正在尝试学习Plyr,但并不一定要使用它,所以感谢你的建议。有没有来自Plyr阵营的人能提供任何关于为什么Plyr可能是更好的解决方案的原因? - MikeTP
@MikeTP,我担心如果“data.table”阵营的某个人带来了一个data.table解决方案(这将非常容易),你会再次更改接受的答案!我认为这真的取决于语法的偏好和一致性。 plyrdata.table都有自己的语法,可能与基本的R方法有些不同,对于一些人来说,这些结构可能只是更自然的。 - A5C1D2H2I1M1N2O1R2T1
@MikeTP,我也通常喜欢在可能的情况下添加基本的R解决方案,因为我有时发现它们被忽视了。 - A5C1D2H2I1M1N2O1R2T1
@阿曼达。哈哈,谢谢。作为一个初学者,有这么多的包,有时很难决定我应该先学哪些包/解决方案。我已经使用相当多的ggplot,所以我认为学习plyr将是一个很好的附加基础包。然而,你的回答确实很有说服力。 - MikeTP
@MikeTP,我为你添加了data.table。如果你愿意的话可以进行一些基准测试,或者至少用system.time()包装一下命令来看看它与其他方法相比如何。顺便说一下,我的名字是ANanda(实际上是男性名字!),不是AManda ;) - A5C1D2H2I1M1N2O1R2T1
@Ananda,谢谢。对于反复打错你的名字,我深表歉意。 - MikeTP

5

使用diff()如何呢:

df <- read.table(header = TRUE, text = '  year lg team   ab
  1884 UA  ALT  108
  1997 AL  ANA 1703
  1998 AL  ANA 1502
  1999 AL  ANA  660
  2000 AL  ANA   85
  2001 AL  ANA  219')
require(plyr)
ddply(df, .(team, lg), mutate ,
      yoy = c(NA, diff(ab)))
#   year lg team   ab  yoy
1 1884 UA  ALT  108   NA
2 1997 AL  ANA 1703   NA
3 1998 AL  ANA 1502 -201
4 1999 AL  ANA  660 -842
5 2000 AL  ANA   85 -575
6 2001 AL  ANA  219  134

这是一个不错、简洁的回答。EDi说得完全正确。请注意,您必须使用“NA”值连接'diff'函数,否则会出现奇怪的错误。如果有帮助的话,您可以继续使用'ddply'函数封装器,并像这样加载数据:df <- ddply(baseball, .(year, lg, team), summarize, ab=sum(ab)) - Dinre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接