【问题标题】:dplyr - using mutate() like rowmeans()dplyr - 使用 mutate() 像 rowmeans()
【发布时间】:2016-01-28 20:38:47
【问题描述】:

我在任何地方都找不到答案。

我想根据行的平均值计算数据框的新变量。

例如:

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)) 

没用。

你能给我一些建议吗?

问候

【问题讨论】:

  • rowMeans 用于矩阵,而不是 args 向量。我建议data %&gt;% mutate(c = Reduce("+",.)/length(.))
  • 谢谢 - 它有效,但如何只选择特定的行(例如 p1 到 p32)?以及如何处理 NA?
  • 您能否修改您的问题以说明您的意思?
  • 好的,我做到了。现在清楚了吗? ;-)

标签: r dplyr


【解决方案1】:

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')

两种情况下的结果都是一样的。从概念上讲,我更喜欢第二种变体。

【讨论】:

  • 顺便说一句:对于非常大的表,重塑数据可能效率低下,但我使用具有数百万行的 data.frame 的等效代码,它仍然可以正常工作。
  • 我忘记了 dplyr 数据应该是整洁的 - 但事实上,就我的目的而言,这种转换似乎是非常倾斜的方式......但现在我明白了! :)
  • 在我看来,OP 的问题更多地与 mean.default 的尴尬签名(默认后的点)与 dplyr 的参数映射交互而不是 dplyr 无法处理“不整齐”的数据。
【解决方案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的可能性,函数必须被丑化:

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 方法慢。

【讨论】:

  • my_rowmeans = function(...) Reduce(+, list(...))/length(list(...)) 这是我的问题的非常接近的解决方案。但是如何处理 NA? na.rm 参数会非常有用 ;-)
  • @TomaszWojtas 已更新。如果您的初始帖子也反映了这一点会更好(而不是在 cmets 中扩展问题)。
【解决方案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))

【讨论】:

  • 啊。一点也不差。不幸的是,rowwise 的文档很糟糕(“rowwise 在某些情况下会做一些事情。这是一个无法概括的单一特殊情况的非描述性示例。”)所以我最终从未使用它。 :-(
【解决方案4】:

另一种使用少量代码的简单可能性是:

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)

【讨论】:

    【解决方案5】:

    还有另外几种方法,如果您有要汇总的列的数字位置或向量名称,则很有用:

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

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

    【讨论】:

    • 与我的回答非常相似,但我喜欢你不需要使用 data.frame() 那种总是困扰我的方式。谢谢。
    • 这是最好的解决方案,但通过在管道数据帧上使用 select 变得更简单。已添加答案。
    【解决方案6】:

    我认为建议使用 data.frame 或在 . 上切片的答案是最好的,但可以像这样变得更简单和更 dplyr-ish:

    data %>% mutate(c = rowMeans(select(., a,b)))
    

    或者,如果您想避免 .,但您的管道有两个输入:

    data %>% mutate(c = rowMeans(select(data, a,b)))
    

    【讨论】:

    • 是的。使用select 确实增加了选择要求和的变量的灵活性。
    【解决方案7】:

    如果您想使用pivot_longer() 风格的解决方案:

    data%>%
    pivot_longer(cols=-id)%>%
    group_by(id)%>%
    mutate(mean=mean(value))%>%
    pivot_wider(names_from=name, values_from=value)
    

    请注意,这需要 tidyr 包。

    这是我的偏好,因为我只需要键入我的 ID 列的名称,而不必担心列索引或名称。适合快速复制并指向不同数据的解决方案,尽管此处的其他答案也可以这样说。也适用于您可能有多个包含分类信息的列并且尚未创建单个唯一标识符列的情况。

    对于它的价值,我发现这个解决方案很容易修改为忽略 NA 值,只需在平均计算中添加 na.rm=TRUE

    例如:

    data <- data.frame(id=c(101,102,103), a=c(NA,2,3), b=c(2,2,2), c=c(3,3,3))
    
    
    data%>%
    pivot_longer(cols=-id)%>%
    group_by(id)%>%
    mutate(mean=mean(value,na.rm=TRUE))%>%
    pivot_wider(names_from = name, values_from=value)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-19
      • 2018-11-17
      • 2021-07-27
      • 2017-12-23
      • 2015-03-20
      • 2015-03-20
      相关资源
      最近更新 更多