对列的子集执行 dplyr mutate
Performing dplyr mutate on subset of columns
我有一个这样的data.frame(真实的数据集有更多的行和列)
set.seed(15)
dd <- data.frame(id=letters[1:4], matrix(runif(5*4), nrow=4))
# id X1 X2 X3 X4 X5
# 1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437
# 2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670
# 3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871
# 4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125
我希望能够编写一个 dplyr 语句,我可以在其中 select 列的子集并改变它们。 (我正在尝试做一些类似于在 data.table 中使用 .SDcols 的事情)。
作为一个简化的示例,这是我希望能够编写的函数,用于为偶数 "X" 列的总和和均值添加列,同时保留所有其他列。使用基数 R 的所需输出是
(cols<-paste0("X", c(2,4)))
# [1] "X2" "X4"
cbind(dd,evensum=rowSums(dd[,cols]),evenmean=rowMeans(dd[,cols]))
# id X1 X2 X3 X4 X5 evensum evenmean
# 1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623 0.4380811
# 2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878 0.8477439
# 3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071 0.8387535
# 4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535 0.5478768
但我想使用类似 dplyr 的链来做同样的事情。在一般情况下,我希望能够使用 select()
的任何辅助函数,例如 starts_with
、ends_with
、matches
等以及任何函数。这是我试过的
library(dplyr)
partial_mutate1 <- function(x, colspec, ...) {
select_(x, .dots=list(lazyeval::lazy(colspec))) %>%
transmute_(.dots=lazyeval::lazy_dots(...)) %>%
cbind(x,.)
}
dd %>% partial_mutate1(num_range("X", c(2,4)),
evensum=rowSums(.), evenmean=rowMeans(.))
但是,这会引发一个错误
Error in rowSums(.) : 'x' must be numeric
这似乎是因为 .
似乎指的是整个 date.frame 而不是 selected 子集。 (与 rowSums(dd)
相同的错误)。但是,请注意,这会产生所需的输出
partial_mutate2 <- function(x, colspec) {
select_(x, .dots=list(lazyeval::lazy(colspec))) %>%
transmute(evensum=rowSums(.), evenmean=rowMeans(.)) %>%
cbind(x,.)
}
dd %>% partial_mutate2(seq(2,ncol(dd),2))
我猜这是某种环境问题?关于如何将参数传递给 partial_mutate1
以便 .
正确地从 "select()-ed" 数据集中获取值的任何建议?
我是不是遗漏了什么,或者这会按预期工作吗:
cols <- paste0("X", c(2,4))
dd %>% mutate(evensum = rowSums(.[cols]), evenmean = rowMeans(.[cols]))
# id X1 X2 X3 X4 X5 evensum evenmean
#1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623 0.4380811
#2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878 0.8477439
#3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071 0.8387535
#4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535 0.5478768
或者您是否正在专门寻找自定义函数来执行此操作?
不完全是您要查找的内容,但如果您想在管道内执行此操作,您可以在 mutate
内显式使用 select
,如下所示:
dd %>% mutate(xy = select(., num_range("X", c(2,4))) %>% rowSums)
# id X1 X2 X3 X4 X5 xy
#1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623
#2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878
#3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071
#4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535
但是,如果要应用多个函数,就有点复杂了。您可以按照 (..not thoroughly tested..):
使用辅助函数
f <- function(x, ...) {
n <- nrow(x)
x <- lapply(list(...), function(y) if (length(y) == 1L) rep(y, n) else y)
matrix(unlist(x), nrow = n, byrow = FALSE)
}
然后像这样应用它:
dd %>% mutate(xy = select(., num_range("X", c(2,4))) %>% f(., rowSums(.), max(.)))
# id X1 X2 X3 X4 X5 xy.1 xy.2
#1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623 0.9888592
#2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878 0.9888592
#3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071 0.9888592
#4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535 0.9888592
使用 dplyr 的列数不可知方法:
dd %>%
select(-id) %>%
mutate(evensum = rowSums(.[,1:length(.[1,])%%2==0]),
evenmean = rowMeans(.[,1:length(.[1,])%%2==0])) %>%
cbind(id=dd[,1],.)
id X1 X2 X3 X4 X5 evensum evenmean
1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623 0.4380812
2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878 0.8477439
3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071 0.8387535
4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535 0.5478767
tidyr::nest()
理解与 dplyr::select()
相同的选择器语法,因此一种方法是将感兴趣的列合并到单个数据帧列中,对该列执行必要的操作- of-dataframes,并 unnest 取回平面数据框:
library( tidyverse )
dd %>% nest( X2, X4, .key="Slice" ) %>%
mutate( evensum = map(Slice, rowSums),
evenmean = map(Slice, rowMeans),
evensd = map(Slice, pmap_dbl, lift_vd(sd)) ) %>%
unnest
# id X1 X3 X5 evensum evenmean evensd X2 X4
# 1 a 0.602 0.687 0.447 0.876 0.438 0.100 0.367 0.509
# 2 b 0.195 0.831 0.965 1.70 0.848 0.200 0.989 0.707
# 3 c 0.966 0.105 0.141 1.68 0.839 0.0333 0.815 0.862
# 4 d 0.651 0.646 0.777 1.10 0.548 0.416 0.254 0.842
由于数据框基本上是列表,这种方法自然适用于使用 purrr::pmap()
系列函数将任意函数(例如上面的 sd
)应用于任意一组列。
旁注:由于 sd
适用于矢量,我们使用 purrr::lift_vd
将其接口转换为适合 pmap
:
sd( c(0.367, 0.509) ) # 0.100
lift_vd(sd)( 0.367, .509 ) # 0.100
在较新版本的 dplyr 中,您可以使用新的 mutate_at()
函数
mutate_at(dd, vars(starts_with("X")), somefunction)
另一种选择是使用 rowwise()
加 c_across()
。这种类型的操作不适用于 rowSums
或 rowMeans
,但适用于常规的 sum()
和 mean()
函数。 c_across()
函数 returns 多列作为一个简单的向量。它还接受任何 tidyselect 辅助函数。所以例如你可以做
dd %>%
rowwise() %>%
mutate(
evensum = sum( c_across(all_of(cols)) ),
evenmean = mean( c_across(all_of(cols)) )
)
我有一个这样的data.frame(真实的数据集有更多的行和列)
set.seed(15)
dd <- data.frame(id=letters[1:4], matrix(runif(5*4), nrow=4))
# id X1 X2 X3 X4 X5
# 1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437
# 2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670
# 3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871
# 4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125
我希望能够编写一个 dplyr 语句,我可以在其中 select 列的子集并改变它们。 (我正在尝试做一些类似于在 data.table 中使用 .SDcols 的事情)。
作为一个简化的示例,这是我希望能够编写的函数,用于为偶数 "X" 列的总和和均值添加列,同时保留所有其他列。使用基数 R 的所需输出是
(cols<-paste0("X", c(2,4)))
# [1] "X2" "X4"
cbind(dd,evensum=rowSums(dd[,cols]),evenmean=rowMeans(dd[,cols]))
# id X1 X2 X3 X4 X5 evensum evenmean
# 1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623 0.4380811
# 2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878 0.8477439
# 3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071 0.8387535
# 4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535 0.5478768
但我想使用类似 dplyr 的链来做同样的事情。在一般情况下,我希望能够使用 select()
的任何辅助函数,例如 starts_with
、ends_with
、matches
等以及任何函数。这是我试过的
library(dplyr)
partial_mutate1 <- function(x, colspec, ...) {
select_(x, .dots=list(lazyeval::lazy(colspec))) %>%
transmute_(.dots=lazyeval::lazy_dots(...)) %>%
cbind(x,.)
}
dd %>% partial_mutate1(num_range("X", c(2,4)),
evensum=rowSums(.), evenmean=rowMeans(.))
但是,这会引发一个错误
Error in rowSums(.) : 'x' must be numeric
这似乎是因为 .
似乎指的是整个 date.frame 而不是 selected 子集。 (与 rowSums(dd)
相同的错误)。但是,请注意,这会产生所需的输出
partial_mutate2 <- function(x, colspec) {
select_(x, .dots=list(lazyeval::lazy(colspec))) %>%
transmute(evensum=rowSums(.), evenmean=rowMeans(.)) %>%
cbind(x,.)
}
dd %>% partial_mutate2(seq(2,ncol(dd),2))
我猜这是某种环境问题?关于如何将参数传递给 partial_mutate1
以便 .
正确地从 "select()-ed" 数据集中获取值的任何建议?
我是不是遗漏了什么,或者这会按预期工作吗:
cols <- paste0("X", c(2,4))
dd %>% mutate(evensum = rowSums(.[cols]), evenmean = rowMeans(.[cols]))
# id X1 X2 X3 X4 X5 evensum evenmean
#1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623 0.4380811
#2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878 0.8477439
#3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071 0.8387535
#4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535 0.5478768
或者您是否正在专门寻找自定义函数来执行此操作?
不完全是您要查找的内容,但如果您想在管道内执行此操作,您可以在 mutate
内显式使用 select
,如下所示:
dd %>% mutate(xy = select(., num_range("X", c(2,4))) %>% rowSums)
# id X1 X2 X3 X4 X5 xy
#1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623
#2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878
#3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071
#4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535
但是,如果要应用多个函数,就有点复杂了。您可以按照 (..not thoroughly tested..):
使用辅助函数f <- function(x, ...) {
n <- nrow(x)
x <- lapply(list(...), function(y) if (length(y) == 1L) rep(y, n) else y)
matrix(unlist(x), nrow = n, byrow = FALSE)
}
然后像这样应用它:
dd %>% mutate(xy = select(., num_range("X", c(2,4))) %>% f(., rowSums(.), max(.)))
# id X1 X2 X3 X4 X5 xy.1 xy.2
#1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623 0.9888592
#2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878 0.9888592
#3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071 0.9888592
#4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535 0.9888592
使用 dplyr 的列数不可知方法:
dd %>%
select(-id) %>%
mutate(evensum = rowSums(.[,1:length(.[1,])%%2==0]),
evenmean = rowMeans(.[,1:length(.[1,])%%2==0])) %>%
cbind(id=dd[,1],.)
id X1 X2 X3 X4 X5 evensum evenmean
1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623 0.4380812
2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878 0.8477439
3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071 0.8387535
4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535 0.5478767
tidyr::nest()
理解与 dplyr::select()
相同的选择器语法,因此一种方法是将感兴趣的列合并到单个数据帧列中,对该列执行必要的操作- of-dataframes,并 unnest 取回平面数据框:
library( tidyverse )
dd %>% nest( X2, X4, .key="Slice" ) %>%
mutate( evensum = map(Slice, rowSums),
evenmean = map(Slice, rowMeans),
evensd = map(Slice, pmap_dbl, lift_vd(sd)) ) %>%
unnest
# id X1 X3 X5 evensum evenmean evensd X2 X4
# 1 a 0.602 0.687 0.447 0.876 0.438 0.100 0.367 0.509
# 2 b 0.195 0.831 0.965 1.70 0.848 0.200 0.989 0.707
# 3 c 0.966 0.105 0.141 1.68 0.839 0.0333 0.815 0.862
# 4 d 0.651 0.646 0.777 1.10 0.548 0.416 0.254 0.842
由于数据框基本上是列表,这种方法自然适用于使用 purrr::pmap()
系列函数将任意函数(例如上面的 sd
)应用于任意一组列。
旁注:由于 sd
适用于矢量,我们使用 purrr::lift_vd
将其接口转换为适合 pmap
:
sd( c(0.367, 0.509) ) # 0.100
lift_vd(sd)( 0.367, .509 ) # 0.100
在较新版本的 dplyr 中,您可以使用新的 mutate_at()
函数
mutate_at(dd, vars(starts_with("X")), somefunction)
另一种选择是使用 rowwise()
加 c_across()
。这种类型的操作不适用于 rowSums
或 rowMeans
,但适用于常规的 sum()
和 mean()
函数。 c_across()
函数 returns 多列作为一个简单的向量。它还接受任何 tidyselect 辅助函数。所以例如你可以做
dd %>%
rowwise() %>%
mutate(
evensum = sum( c_across(all_of(cols)) ),
evenmean = mean( c_across(all_of(cols)) )
)