首页 文章

dplyr - 像rowmeans一样使用mutate()

提问于
浏览
13

我无法在任何地方找到答案 .

我想计算基于行的平均值的数据帧的新变量 .

例如:

data <- data.frame(id=c(101,102,103), a=c(1,2,3), b=c(2,2,2), c=c(3,3,3))

我想使用mutate来创建变量d,它是a,b和c的意思 . 我希望能够通过选择d = mean(a,b,c)的方式来实现这一点,并且我还需要使用变量范围(例如dplyr)d = mean(a:c) .

而且当然

mutate(data, c=mean(a,b))

要么

mutate(data, c=rowMeans(a,b))

不起作用 .

你能给我一些小费吗?

问候

5 回答

  • 3

    您正在寻找

    data %>% 
        rowwise() %>% 
        mutate(c=mean(c(a,b)))
    
    #      id     a     b     c
    #   (dbl) (dbl) (dbl) (dbl)
    # 1   101     1     2   1.5
    # 2   102     2     2   2.0
    # 3   103     3     2   2.5
    

    要么

    library(purrr)
    data %>% 
        rowwise() %>% 
        mutate(c=lift_vd(mean)(a,b))
    
  • 9

    dplyr非常适合对这类数据进行操作,因为它采用tidy data格式,并且 - 对于有问题的问题 - 您的数据是不整洁的 .

    你当然可以先整理它:

    tidy_data = tidyr::gather(data, name, value, -id)
    

    看起来像这样:

    id name value
    1 101    a     1
    2 102    a     2
    3 103    a     3
    4 101    b     2
    5 102    b     2
    6 103    b     2
        …
    

    然后:

    tidy_data %>% group_by(id) %>% summarize(mean = mean(value))
    
    name  mean
      (fctr) (dbl)
    1      a     2
    2      b     2
    3      c     3
    

    当然这会丢弃原始数据 . 您可以使用 mutate 而不是 summarize 来避免这种情况 . 最后,您可以再次解开数据:

    tidy_data %>%
        group_by(id) %>%
        mutate(mean = mean(value)) %>%
        tidyr::spread(name, value)
    
    id     mean     a     b     c
      (dbl)    (dbl) (dbl) (dbl) (dbl)
    1   101 2.000000     1     2     3
    2   102 2.333333     2     2     3
    3   103 2.666667     3     2     3
    

    或者,您可以汇总然后将结果与原始表合并:

    tidy_data %>%
        group_by(id) %>%
        summarize(mean = mean(value)) %>%
        inner_join(data, by = 'id')
    

    在任何一种情况下结果都是相同的 . 我在概念上更喜欢第二种变体 .

  • 2

    我认为这是dplyr-ish方式 . 首先,我创建一个函数:

    my_rowmeans = function(...) Reduce(`+`, list(...))/length(list(...))
    

    然后,它可以在mutate内部使用:

    data %>% mutate(rms = my_rowmeans(a, b))
    
    #    id a b c rms
    # 1 101 1 2 3 1.5
    # 2 102 2 2 3 2.0
    # 3 103 3 2 3 2.5
    
    # or
    
    data %>% mutate(rms = my_rowmeans(a, b, c))
    
    #    id a b c      rms
    # 1 101 1 2 3 2.000000
    # 2 102 2 2 3 2.333333
    # 3 103 3 2 3 2.666667
    

    为了处理 NAs 的可能性,该函数必须被uglified:

    my_rowmeans = function(..., na.rm=TRUE){
      x = 
        if (na.rm) lapply(list(...), function(x) replace(x, is.na(x), as(0, class(x)))) 
        else       list(...)
    
      d = Reduce(function(x,y) x+!is.na(y), list(...), init=0)
    
      Reduce(`+`, x)/d
    } 
    
    # alternately...
    
    my_rowmeans2 = function(..., na.rm=TRUE) rowMeans(cbind(...), na.rm=na.rm)
    
    # new example
    
    data$b[2] <- NA  
    data %>% mutate(rms = my_rowmeans(a,b,na.rm=FALSE))
    
       id a  b c rms
    1 101 1  2 3 1.5
    2 102 2 NA 3  NA
    3 103 3  2 3 2.5
    
    data %>% mutate(rms = my_rowmeans(a,b))
    
       id a  b c rms
    1 101 1  2 3 1.5
    2 102 2 NA 3 2.0
    3 103 3  2 3 2.5
    

    my_rowmeans2 的缺点是它强制转换为矩阵 . 不过,我不确定这总是比 Reduce 方法慢 .

  • 3

    还有另外两种方法,如果你有要汇总的列的数字位置或矢量名称,这将非常有用:

    data %>% mutate(d = rowMeans(.[, 2:4]))
    

    要么

    data %>% mutate(d = rowMeans(.[, c("a","b","c")]))
    
  • 19

    代码很少的另一个简单可能性是:

    data %>%
        mutate(c= rowMeans(data.frame(a,b)))
    
     #     id a b   c
     #  1 101 1 2 1.5
     #  2 102 2 2 2.0
     #  3 103 3 2 2.5
    

    由于rowMeans需要类似矩阵或data.frame的东西,因此可以使用 data.frame(var1, var2, ...) 而不是 c(var1, var2, ...) . 如果您的数据中有NA,则需要告诉R要做什么,例如删除它们: rowMeans(data.frame(a,b), na.rm=TRUE)

相关问题