2

这个问题把我难住了。

我有以下数据框:

library(dplyr)

# approximation of data frame
x <- data.frame(doy = sample(c(seq(200, 300)), 20, replace = T),
                year = sample(c("2000", "2005"), 20, replace = T), 
                phase = sample(c("pre", "post"), 20, replace = T))

和一个简单的“汇总”函数,它将列名作为变量接收,并且运行良好:

 getStats <- function(df, col) {
      col <- as.name(col)
      df %>% 
        group_by(year, phase) %>% 
        summarize(n = sum(!is.na(col)), 
                  mean = mean(col, na.rm = T),
                  sd = sd(col, na.rm = T),
                  se = sd/sqrt(n))
 }

> getStats(x, "doy")
Source: local data frame [4 x 6]
Groups: year [?]

    year  phase     n    mean       sd       se
  <fctr> <fctr> <int>   <dbl>    <dbl>    <dbl>
1   2000   post     8 248.625 30.42526 10.75695
2   2000    pre     2 290.000 14.14214 10.00000
3   2005   post     5 231.400 32.86031 14.69558
4   2005    pre     5 274.200 29.79429 13.32441

但是,如果我修改函数以获取中位数,则会返回错误:

 getStats <- function(df, col) {
      col <- as.name(col)
      df %>% 
        group_by(year, phase) %>% 
        summarize(n = sum(!is.na(col)), 
                  mean = mean(col, na.rm = T),
                  med = median(col, na.rm = T), # new line 
                  sd = sd(col, na.rm = T),
                  se = sd/sqrt(n))
    }

> getStats(x, "doy")

Error in median (doy, na.rm = TRUE): object "doy" not found

我尝试了许多名称和位置更改,但都产生相同的结果:'median' 不接受列名作为传递的变量。我想我错过了一些非常基本的东西,当有人向我指出时我会做一个手掌,但在此期间我觉得我正在失去理智。我很感激任何见解!

4

1 回答 1

3

您的近端问题可能是median没有...争论,而mean有(我不确定为什么sd会起作用……也许是方法和之间的交互...?)

无论如何,IMO 处理此类问题的正确方法是使用标准评估(即,不是非标准评估,即使用summarise_而不是summarise,如图所示vignette("nse",package="dplyr")):

说明它是如何在全局环境中而不是在函数内部工作的,但我认为这不重要......

col <- "doy"
funs <- c("n","mean","stats::median","sd","se")
## put together function calls
dots <- c(sprintf("sum(!is.na(%s))",col),
      sprintf("%s(%s,na.rm=TRUE)",funs[2:4],col),
      "sd/sqrt(n)")
names(dots) <- gsub("^.*::","",funs)  ## ugh
dots 
##                              n                            mean 
##              "sum(!is.na(doy))"          "mean(doy,na.rm=TRUE)" 
##                        median                              sd 
## "stats::median(doy,na.rm=TRUE)"            "sd(doy,na.rm=TRUE)" 
##                              se 
##                    "sd/sqrt(n)" 

x %>% 
    group_by(year, phase) %>% 
    summarise_(.dots=dots)

这里唯一令人讨厌的是,由于某种原因dplyr找不到median,除非我将其称为stats::median,这意味着我们必须更加努力地获得漂亮的列名。标准评估方法有点丑陋,但这就是您为这种灵活性付出的代价。

将它嵌入一个函数中,我可能会getStats在不同的地方中断,即

 getStats <- function(data,col) {
   ## if you want to pass a string argument instead, remove
   ## the next line
   col <- deparse(substitute(col))
   funs <- c("n","mean","stats::median","sd","se")
   dots <- c(sprintf("sum(!is.na(%s))",col),
      sprintf("%s(%s,na.rm=TRUE)",funs[2:4],col),
      "sd/sqrt(n)")
   names(dots) <- gsub("^.*::","",funs)  ## ugh
   summarise_(data,.dots=dots)
}

x %>% group_by(year,phase) %>% getStats(doy)

这使您可以更灵活地进行不同的分组...

于 2016-11-25T20:16:35.810 回答