我在任何地方都找不到答案。
我想计算基于行平均值的数据框的新变量。
例如:
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))
不行。
你能给我一些提示吗?
问候
你正在寻找
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))
dplyr 不适合对此类数据进行操作,因为它假定整洁的数据格式,并且对于所讨论的问题,您的数据不整洁。
你当然可以先整理一下:
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 %>% mutate(d = rowMeans(.[, 2:4]))
或
data %>% mutate(d = rowMeans(.[, c("a","b","c")]))
我认为建议使用data.frame
或.
切片的答案是最好的,但可以做得更简单、更像这样
data %>% mutate(c = rowMeans(select(., a,b)))
或者,如果您想避免.
,则代价是管道有两个输入:
data %>% mutate(c = rowMeans(select(data, a,b)))
我认为这是dplyr式的方式。首先,我将创建一个函数:
my_rowmeans = function(...) Reduce(`+`, list(...))/length(list(...))
然后,它可以在突变体内使用:
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
方法慢。
代码很少的另一个简单可能性是:
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)
如果要使用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)
您可以在rowMeans()
周围使用包装器函数,以使其更易于使用。下面的一个允许您指定 na.rm
,如果需要,您可以使用 tidyselect 来选择您的列。
# This is the wrapper function
means <- function(..., na.rm = FALSE) {
rowMeans(data.frame(...), na.rm = na.rm)
}
library(dplyr)
# Example data
iris2 <- iris %>%
head() %>%
transmute(Sepal.Length = replace(Sepal.Length,
sample(c(TRUE, FALSE), nrow(.),
replace = TRUE),
NA),
Sepal.Width,
Petal.Length,
Petal.Width) %>%
print()
#> Sepal.Length Sepal.Width Petal.Length Petal.Width
#> 1 NA 3.5 1.4 0.2
#> 2 NA 3.0 1.4 0.2
#> 3 NA 3.2 1.3 0.2
#> 4 4.6 3.1 1.5 0.2
#> 5 NA 3.6 1.4 0.2
#> 6 5.4 3.9 1.7 0.4
# Basic usage
iris2 %>%
mutate(mean_sepal = means(Sepal.Length, Sepal.Width))
#> Sepal.Length Sepal.Width Petal.Length Petal.Width mean_sepal
#> 1 NA 3.5 1.4 0.2 NA
#> 2 NA 3.0 1.4 0.2 NA
#> 3 NA 3.2 1.3 0.2 NA
#> 4 4.6 3.1 1.5 0.2 3.85
#> 5 NA 3.6 1.4 0.2 NA
#> 6 5.4 3.9 1.7 0.4 4.65
# If you want to exclude NAs
iris2 %>%
mutate(mean_sepal = means(Sepal.Length, Sepal.Width, na.rm = TRUE))
#> Sepal.Length Sepal.Width Petal.Length Petal.Width mean_sepal
#> 1 NA 3.5 1.4 0.2 3.50
#> 2 NA 3.0 1.4 0.2 3.00
#> 3 NA 3.2 1.3 0.2 3.20
#> 4 4.6 3.1 1.5 0.2 3.85
#> 5 NA 3.6 1.4 0.2 3.60
#> 6 5.4 3.9 1.7 0.4 4.65
# You can also use select() and choose columns using tidyselect
iris2 %>%
mutate(mean_sepal = means(select(., contains("Sepal")), na.rm = TRUE))
#> Sepal.Length Sepal.Width Petal.Length Petal.Width mean_sepal
#> 1 NA 3.5 1.4 0.2 3.50
#> 2 NA 3.0 1.4 0.2 3.00
#> 3 NA 3.2 1.3 0.2 3.20
#> 4 4.6 3.1 1.5 0.2 3.85
#> 5 NA 3.6 1.4 0.2 3.60
#> 6 5.4 3.9 1.7 0.4 4.65
创建于 2022-01-13 由 reprex 软件包 (v2.0.1(