dplyr - 像rowmeans一样使用mutate()

时间:2015-10-28 21:20:09

标签: r dplyr

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

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

例如:

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)。< / p>

当然

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

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

不起作用。

你能给我一些小费吗?

此致

7 个答案:

答案 0 :(得分:25)

您正在寻找

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

答案 1 :(得分:10)

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 :(得分: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)

答案 3 :(得分:3)

我认为这是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方法慢。

答案 4 :(得分:3)

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

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

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

答案 5 :(得分:1)

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

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

或者,如果您想避免使用.,则需要在管道中添加两个输入:

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

答案 6 :(得分:0)

如果您想使用 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)