当前位置: 首页>>代码示例 >>用法及示例精选 >>正文


R dplyr across 跨多列应用一个或多个函数


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() 分别访问当前列和分组键。

...

[Deprecated]

.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

[Experimental]

可选地, .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/across.R

相关用法


注:本文由纯净天空筛选整理自Hadley Wickham等大神的英文原创作品 Apply a function (or functions) across multiple columns。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。