across()
可以轻松地将相同的转换应用于多个列,允许您在 "data-masking" 函数(如 summarise()
和 mutate()
)中使用 select()
语义。有关更多详细信息,请参阅vignette("colwise")
。
if_any()
和 if_all()
对选定的列应用相同的谓词函数,并将结果合并到单个逻辑向量中:当任何选定列的谓词为 TRUE
时,if_any()
为 TRUE
;当所有选定列的谓词为 TRUE
时,if_all()
为 TRUE
。
如果您只需要选择列而不对每个列应用转换,那么您可能需要使用pick()
。
across()
取代 "scoped variants" 系列,例如 summarise_at()
、 summarise_if()
和 summarise_all()
。
用法
across(.cols, .fns, ..., .names = NULL, .unpack = FALSE)
if_any(.cols, .fns, ..., .names = NULL)
if_all(.cols, .fns, ..., .names = NULL)
参数
- .cols
-
<
tidy-select
> 要转换的列。您无法选择分组列,因为它们已由动词自动处理(即summarise()
或mutate()
)。 - .fns
-
应用于每个选定列的函数。可能的值为:
-
一个函数,例如
mean
。 -
purrr-style lambda,例如
~ mean(.x, na.rm = TRUE)
-
函数或 lambda 的命名列表,例如
list(mean = mean, n_miss = ~ sum(is.na(.x))
。每个函数都应用于每一列,并通过使用.names
中的粘合规范将函数名称和列名称组合起来来命名输出。
在这些函数中,您可以使用
cur_column()
和cur_group()
分别访问当前列和分组键。 -
- ...
-
.fns
中函数调用的附加参数在...
中不再接受,因为不清楚何时应计算它们:每个across()
一次还是每个组一次?相反,使用 lambda 直接在.fns
中提供附加参数。例如,不要写across(a:b, mean, na.rm = TRUE)
,而是写across(a:b, ~ mean(.x, na.rm = TRUE))
。 - .names
-
说明如何命名输出列的粘合规范。这可以使用
{.col}
代表所选的列名称,并使用{.fn}
代表正在应用的函数的名称。默认值 (NULL
) 相当于单函数情况下的"{.col}"
和.fns
使用列表的情况下的"{.col}_{.fn}"
。 - .unpack
-
可选地,
.fns
中的函数返回 unpack 数据帧,它将 df-columns 扩展为单独的列,保留数据帧中的行数。-
如果是
FALSE
,默认情况下,不进行解包。 -
如果
TRUE
,则使用默认胶合规范"{outer}_{inner}"
完成解包。 -
否则,可以提供单个粘合规范来说明如何命名未填充的列。这可以使用
{outer}
来引用.names
最初生成的名称,并使用{inner}
来引用要解包的数据帧的名称。
-
值
across()
通常为 .cols
中的每一列和 .fns
中的每个函数返回一个包含一列的 tibble。如果使用 .unpack
,则可能会返回更多列,具体取决于 .fns
结果的解包方式。
if_any()
和 if_all()
返回逻辑向量。
评估时间
dplyr 动词中的 R 代码通常每组评估一次。然而,在 across()
内部,对于每个列和组的组合,代码都会计算一次。如果评估时间很重要,例如,如果您要生成随机变量,请考虑它应该何时发生并将代码放在结果中。
gdf <-
tibble(g = c(1, 1, 2, 3), v1 = 10:13, v2 = 20:23) %>%
group_by(g)
set.seed(1)
# Outside: 1 normal variate
n <- rnorm(1)
gdf %>% mutate(across(v1:v2, ~ .x + n))
#> # A tibble: 4 x 3
#> # Groups: g [3]
#> g v1 v2
#> <dbl> <dbl> <dbl>
#> 1 1 9.37 19.4
#> 2 1 10.4 20.4
#> 3 2 11.4 21.4
#> 4 3 12.4 22.4
# Inside a verb: 3 normal variates (ngroup)
gdf %>% mutate(n = rnorm(1), across(v1:v2, ~ .x + n))
#> # A tibble: 4 x 4
#> # Groups: g [3]
#> g v1 v2 n
#> <dbl> <dbl> <dbl> <dbl>
#> 1 1 10.2 20.2 0.184
#> 2 1 11.2 21.2 0.184
#> 3 2 11.2 21.2 -0.836
#> 4 3 14.6 24.6 1.60
# Inside `across()`: 6 normal variates (ncol * ngroup)
gdf %>% mutate(across(v1:v2, ~ .x + rnorm(1)))
#> # A tibble: 4 x 3
#> # Groups: g [3]
#> g v1 v2
#> <dbl> <dbl> <dbl>
#> 1 1 10.3 20.7
#> 2 1 11.3 21.7
#> 3 2 11.2 22.6
#> 4 3 13.5 22.7
也可以看看
c_across()
用于返回向量的函数
例子
# For better printing
iris <- as_tibble(iris)
# across() -----------------------------------------------------------------
# Different ways to select the same set of columns
# See <https://tidyselect.r-lib.org/articles/syntax.html> for details
iris %>%
mutate(across(c(Sepal.Length, Sepal.Width), round))
#> # A tibble: 150 × 5
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> <dbl> <dbl> <dbl> <dbl> <fct>
#> 1 5 4 1.4 0.2 setosa
#> 2 5 3 1.4 0.2 setosa
#> 3 5 3 1.3 0.2 setosa
#> 4 5 3 1.5 0.2 setosa
#> 5 5 4 1.4 0.2 setosa
#> 6 5 4 1.7 0.4 setosa
#> 7 5 3 1.4 0.3 setosa
#> 8 5 3 1.5 0.2 setosa
#> 9 4 3 1.4 0.2 setosa
#> 10 5 3 1.5 0.1 setosa
#> # ℹ 140 more rows
iris %>%
mutate(across(c(1, 2), round))
#> # A tibble: 150 × 5
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> <dbl> <dbl> <dbl> <dbl> <fct>
#> 1 5 4 1.4 0.2 setosa
#> 2 5 3 1.4 0.2 setosa
#> 3 5 3 1.3 0.2 setosa
#> 4 5 3 1.5 0.2 setosa
#> 5 5 4 1.4 0.2 setosa
#> 6 5 4 1.7 0.4 setosa
#> 7 5 3 1.4 0.3 setosa
#> 8 5 3 1.5 0.2 setosa
#> 9 4 3 1.4 0.2 setosa
#> 10 5 3 1.5 0.1 setosa
#> # ℹ 140 more rows
iris %>%
mutate(across(1:Sepal.Width, round))
#> # A tibble: 150 × 5
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> <dbl> <dbl> <dbl> <dbl> <fct>
#> 1 5 4 1.4 0.2 setosa
#> 2 5 3 1.4 0.2 setosa
#> 3 5 3 1.3 0.2 setosa
#> 4 5 3 1.5 0.2 setosa
#> 5 5 4 1.4 0.2 setosa
#> 6 5 4 1.7 0.4 setosa
#> 7 5 3 1.4 0.3 setosa
#> 8 5 3 1.5 0.2 setosa
#> 9 4 3 1.4 0.2 setosa
#> 10 5 3 1.5 0.1 setosa
#> # ℹ 140 more rows
iris %>%
mutate(across(where(is.double) & !c(Petal.Length, Petal.Width), round))
#> # A tibble: 150 × 5
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> <dbl> <dbl> <dbl> <dbl> <fct>
#> 1 5 4 1.4 0.2 setosa
#> 2 5 3 1.4 0.2 setosa
#> 3 5 3 1.3 0.2 setosa
#> 4 5 3 1.5 0.2 setosa
#> 5 5 4 1.4 0.2 setosa
#> 6 5 4 1.7 0.4 setosa
#> 7 5 3 1.4 0.3 setosa
#> 8 5 3 1.5 0.2 setosa
#> 9 4 3 1.4 0.2 setosa
#> 10 5 3 1.5 0.1 setosa
#> # ℹ 140 more rows
# Using an external vector of names
cols <- c("Sepal.Length", "Petal.Width")
iris %>%
mutate(across(all_of(cols), round))
#> # A tibble: 150 × 5
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> <dbl> <dbl> <dbl> <dbl> <fct>
#> 1 5 3.5 1.4 0 setosa
#> 2 5 3 1.4 0 setosa
#> 3 5 3.2 1.3 0 setosa
#> 4 5 3.1 1.5 0 setosa
#> 5 5 3.6 1.4 0 setosa
#> 6 5 3.9 1.7 0 setosa
#> 7 5 3.4 1.4 0 setosa
#> 8 5 3.4 1.5 0 setosa
#> 9 4 2.9 1.4 0 setosa
#> 10 5 3.1 1.5 0 setosa
#> # ℹ 140 more rows
# If the external vector is named, the output columns will be named according
# to those names
names(cols) <- tolower(cols)
iris %>%
mutate(across(all_of(cols), round))
#> # A tibble: 150 × 7
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species sepal.length
#> <dbl> <dbl> <dbl> <dbl> <fct> <dbl>
#> 1 5.1 3.5 1.4 0.2 setosa 5
#> 2 4.9 3 1.4 0.2 setosa 5
#> 3 4.7 3.2 1.3 0.2 setosa 5
#> 4 4.6 3.1 1.5 0.2 setosa 5
#> 5 5 3.6 1.4 0.2 setosa 5
#> 6 5.4 3.9 1.7 0.4 setosa 5
#> 7 4.6 3.4 1.4 0.3 setosa 5
#> 8 5 3.4 1.5 0.2 setosa 5
#> 9 4.4 2.9 1.4 0.2 setosa 4
#> 10 4.9 3.1 1.5 0.1 setosa 5
#> # ℹ 140 more rows
#> # ℹ 1 more variable: petal.width <dbl>
# A purrr-style formula
iris %>%
group_by(Species) %>%
summarise(across(starts_with("Sepal"), ~ mean(.x, na.rm = TRUE)))
#> # A tibble: 3 × 3
#> Species Sepal.Length Sepal.Width
#> <fct> <dbl> <dbl>
#> 1 setosa 5.01 3.43
#> 2 versicolor 5.94 2.77
#> 3 virginica 6.59 2.97
# A named list of functions
iris %>%
group_by(Species) %>%
summarise(across(starts_with("Sepal"), list(mean = mean, sd = sd)))
#> # A tibble: 3 × 5
#> Species Sepal.Length_mean Sepal.Length_sd Sepal.Width_mean
#> <fct> <dbl> <dbl> <dbl>
#> 1 setosa 5.01 0.352 3.43
#> 2 versicolor 5.94 0.516 2.77
#> 3 virginica 6.59 0.636 2.97
#> # ℹ 1 more variable: Sepal.Width_sd <dbl>
# Use the .names argument to control the output names
iris %>%
group_by(Species) %>%
summarise(across(starts_with("Sepal"), mean, .names = "mean_{.col}"))
#> # A tibble: 3 × 3
#> Species mean_Sepal.Length mean_Sepal.Width
#> <fct> <dbl> <dbl>
#> 1 setosa 5.01 3.43
#> 2 versicolor 5.94 2.77
#> 3 virginica 6.59 2.97
iris %>%
group_by(Species) %>%
summarise(across(starts_with("Sepal"), list(mean = mean, sd = sd), .names = "{.col}.{.fn}"))
#> # A tibble: 3 × 5
#> Species Sepal.Length.mean Sepal.Length.sd Sepal.Width.mean
#> <fct> <dbl> <dbl> <dbl>
#> 1 setosa 5.01 0.352 3.43
#> 2 versicolor 5.94 0.516 2.77
#> 3 virginica 6.59 0.636 2.97
#> # ℹ 1 more variable: Sepal.Width.sd <dbl>
# If a named external vector is used for column selection, .names will use
# those names when constructing the output names
iris %>%
group_by(Species) %>%
summarise(across(all_of(cols), mean, .names = "mean_{.col}"))
#> # A tibble: 3 × 3
#> Species mean_sepal.length mean_petal.width
#> <fct> <dbl> <dbl>
#> 1 setosa 5.01 0.246
#> 2 versicolor 5.94 1.33
#> 3 virginica 6.59 2.03
# When the list is not named, .fn is replaced by the function's position
iris %>%
group_by(Species) %>%
summarise(across(starts_with("Sepal"), list(mean, sd), .names = "{.col}.fn{.fn}"))
#> # A tibble: 3 × 5
#> Species Sepal.Length.fn1 Sepal.Length.fn2 Sepal.Width.fn1
#> <fct> <dbl> <dbl> <dbl>
#> 1 setosa 5.01 0.352 3.43
#> 2 versicolor 5.94 0.516 2.77
#> 3 virginica 6.59 0.636 2.97
#> # ℹ 1 more variable: Sepal.Width.fn2 <dbl>
# When the functions in .fns return a data frame, you typically get a
# "packed" data frame back
quantile_df <- function(x, probs = c(0.25, 0.5, 0.75)) {
tibble(quantile = probs, value = quantile(x, probs))
}
iris %>%
reframe(across(starts_with("Sepal"), quantile_df))
#> # A tibble: 3 × 2
#> Sepal.Length$quantile $value Sepal.Width$quantile $value
#> <dbl> <dbl> <dbl> <dbl>
#> 1 0.25 5.1 0.25 2.8
#> 2 0.5 5.8 0.5 3
#> 3 0.75 6.4 0.75 3.3
# Use .unpack to automatically expand these packed data frames into their
# individual columns
iris %>%
reframe(across(starts_with("Sepal"), quantile_df, .unpack = TRUE))
#> # A tibble: 3 × 4
#> Sepal.Length_quantile Sepal.Length_value Sepal.Width_quantile
#> <dbl> <dbl> <dbl>
#> 1 0.25 5.1 0.25
#> 2 0.5 5.8 0.5
#> 3 0.75 6.4 0.75
#> # ℹ 1 more variable: Sepal.Width_value <dbl>
# .unpack can utilize a glue specification if you don't like the defaults
iris %>%
reframe(across(starts_with("Sepal"), quantile_df, .unpack = "{outer}.{inner}"))
#> # A tibble: 3 × 4
#> Sepal.Length.quantile Sepal.Length.value Sepal.Width.quantile
#> <dbl> <dbl> <dbl>
#> 1 0.25 5.1 0.25
#> 2 0.5 5.8 0.5
#> 3 0.75 6.4 0.75
#> # ℹ 1 more variable: Sepal.Width.value <dbl>
# This is also useful inside mutate(), for example, with a multi-lag helper
multilag <- function(x, lags = 1:3) {
names(lags) <- as.character(lags)
purrr::map_dfr(lags, lag, x = x)
}
iris %>%
group_by(Species) %>%
mutate(across(starts_with("Sepal"), multilag, .unpack = TRUE)) %>%
select(Species, starts_with("Sepal"))
#> # A tibble: 150 × 9
#> # Groups: Species [3]
#> Species Sepal.Length Sepal.Width Sepal.Length_1 Sepal.Length_2
#> <fct> <dbl> <dbl> <dbl> <dbl>
#> 1 setosa 5.1 3.5 NA NA
#> 2 setosa 4.9 3 5.1 NA
#> 3 setosa 4.7 3.2 4.9 5.1
#> 4 setosa 4.6 3.1 4.7 4.9
#> 5 setosa 5 3.6 4.6 4.7
#> 6 setosa 5.4 3.9 5 4.6
#> 7 setosa 4.6 3.4 5.4 5
#> 8 setosa 5 3.4 4.6 5.4
#> 9 setosa 4.4 2.9 5 4.6
#> 10 setosa 4.9 3.1 4.4 5
#> # ℹ 140 more rows
#> # ℹ 4 more variables: Sepal.Length_3 <dbl>, Sepal.Width_1 <dbl>,
#> # Sepal.Width_2 <dbl>, Sepal.Width_3 <dbl>
# if_any() and if_all() ----------------------------------------------------
iris %>%
filter(if_any(ends_with("Width"), ~ . > 4))
#> # A tibble: 3 × 5
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> <dbl> <dbl> <dbl> <dbl> <fct>
#> 1 5.7 4.4 1.5 0.4 setosa
#> 2 5.2 4.1 1.5 0.1 setosa
#> 3 5.5 4.2 1.4 0.2 setosa
iris %>%
filter(if_all(ends_with("Width"), ~ . > 2))
#> # A tibble: 23 × 5
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> <dbl> <dbl> <dbl> <dbl> <fct>
#> 1 6.3 3.3 6 2.5 virginica
#> 2 7.1 3 5.9 2.1 virginica
#> 3 6.5 3 5.8 2.2 virginica
#> 4 7.6 3 6.6 2.1 virginica
#> 5 7.2 3.6 6.1 2.5 virginica
#> 6 6.8 3 5.5 2.1 virginica
#> 7 5.8 2.8 5.1 2.4 virginica
#> 8 6.4 3.2 5.3 2.3 virginica
#> 9 7.7 3.8 6.7 2.2 virginica
#> 10 7.7 2.6 6.9 2.3 virginica
#> # ℹ 13 more rows
相关用法
- R dplyr arrange_all 通过选择的变量排列行
- R dplyr arrange 使用列值对行进行排序
- R dplyr group_trim 修剪分组结构
- R dplyr slice 使用行的位置对行进行子集化
- R dplyr copy_to 将本地数据帧复制到远程src
- R dplyr sample_n 从表中采样 n 行
- R dplyr consecutive_id 为连续组合生成唯一标识符
- R dplyr row_number 整数排名函数
- R dplyr band_members 乐队成员
- R dplyr mutate-joins 变异连接
- R dplyr nth 从向量中提取第一个、最后一个或第 n 个值
- R dplyr coalesce 找到第一个非缺失元素
- R dplyr group_split 按组分割 DataFrame
- R dplyr mutate 创建、修改和删除列
- R dplyr order_by 用于排序窗口函数输出的辅助函数
- R dplyr context 有关“当前”组或变量的信息
- R dplyr percent_rank 比例排名函数
- R dplyr recode 重新编码值
- R dplyr starwars 星球大战人物
- R dplyr desc 降序
- R dplyr between 检测值落在指定范围内的位置
- R dplyr cumall 任何、全部和平均值的累积版本
- R dplyr group_map 对每个组应用一个函数
- R dplyr do 做任何事情
- R dplyr nest_join 嵌套连接
注:本文由纯净天空筛选整理自Hadley Wickham等大神的英文原创作品 Apply a function (or functions) across multiple columns。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。