2.6 列操作
在多列上执行相同的函数是常有的操作,但是通过复制和粘贴代码,麻烦并且容易错,如下所示:
%>%
df group_by(g1, g2) %>%
summarise(a = mean(a), b = mean(b), c = mean(c), d = mean(d))
通过across()
函数可以更简洁地重写上面代码:
%>%
df group_by(g1, g2) %>%
summarise(across(a:d, mean))
假设我们要将表格中多列向上取整,代码如下:
<- tibble(a= rnorm(100,mean = 1),b=rnorm(100,mean = 1),d=rnorm(100,mean = 1))
dt %>%
dt mutate(across(a:d,ceiling))
#> # A tibble: 100 x 3
#> a b d
#> <dbl> <dbl> <dbl>
#> 1 2 2 1
#> 2 1 1 1
#> 3 1 3 1
#> 4 1 2 1
#> 5 1 2 1
#> 6 1 1 3
#> # ... with 94 more rows
函数across()
通过与summarise()
和mutate()
结合,很容易将某函数运用到多列上。函数across()
取代了summarise_all()
,summarise_at()
,summarise_if()
函数。
%>%
starwars summarise_at(c("height", "mass"), mean, na.rm = TRUE)
#> # A tibble: 1 x 2
#> height mass
#> <dbl> <dbl>
#> 1 174. 97.3
%>% summarise(across(c("height", "mass"), ~ mean(.x, na.rm = TRUE))) # purrr风格函数
starwars #> # A tibble: 1 x 2
#> height mass
#> <dbl> <dbl>
#> 1 174. 97.3
2.6.1 基础用法
across() 有两个主要参数:
第一个参数,.cols 选择要操作的列。它使用
tidyr
的方式选择(例如select()),因此您可以按位置,名称和类型选择变量。第二个参数,.fns是要应用于每一列的一个函数或函数列表。也可以是 purrr 样式的公式(或公式列表),例如~ .x / 2。
%>%
starwars summarise(across(where(is.character), ~ length(unique(.x))))
#> # A tibble: 1 x 8
#> name hair_color skin_color eye_color sex gender homeworld species
#> <int> <int> <int> <int> <int> <int> <int> <int>
#> 1 87 13 31 15 5 3 49 38
%>%
starwars group_by(species) %>%
filter(n() > 1) %>%
summarise(across(c(sex, gender, homeworld), ~ length(unique(.x))))
#> # A tibble: 9 x 4
#> species sex gender homeworld
#> <chr> <int> <int> <int>
#> 1 Droid 1 2 3
#> 2 Gungan 1 1 1
#> 3 Human 2 2 16
#> 4 Kaminoan 2 2 1
#> 5 Mirialan 1 1 1
#> 6 Twi'lek 2 2 1
#> # ... with 3 more rows
%>%
starwars group_by(homeworld) %>%
filter(n() > 1) %>%
summarise(across(where(is.numeric), ~ mean(.x, na.rm = TRUE)))
#> # A tibble: 10 x 4
#> homeworld height mass birth_year
#> <chr> <dbl> <dbl> <dbl>
#> 1 Alderaan 176. 64 43
#> 2 Corellia 175 78.5 25
#> 3 Coruscant 174. 50 91
#> 4 Kamino 208. 83.1 31.5
#> 5 Kashyyyk 231 124 200
#> 6 Mirial 168 53.1 49
#> # ... with 4 more rows
~ .x / 2是purrr包提供的函数式编程风格,等效于 function(x) (x/2)
across()
不会选择分组变量,如下所示:group_by()
中的变量g
不会被选中执行sum()
函数。
<- data.frame(g = c(1, 1, 2), x = c(-1, 1, 3), y = c(-1, -4, -9))
df %>%
df group_by(g) %>%
summarise(across(where(is.numeric), sum))
#> # A tibble: 2 x 3
#> g x y
#> <dbl> <dbl> <dbl>
#> 1 1 0 -5
#> 2 2 3 -9
2.6.2 多种函数功能
通过在第二个参数提供函数或 lambda 函数的命名列表,可是使用多个函数转换每个变量:
<- list(
min_max min = ~min(.x, na.rm = TRUE),
max = ~max(.x, na.rm = TRUE)
)%>% summarise(across(where(is.numeric), min_max))
starwars #> # A tibble: 1 x 6
#> height_min height_max mass_min mass_max birth_year_min birth_year_max
#> <int> <int> <dbl> <dbl> <dbl> <dbl>
#> 1 66 264 15 1358 8 896
通过.names
参数控制名称:
Note: 该参数的机制没有特别理解,需多练习体会,主要是运用到匿名函数时
以下是官方案例,但是报错(目前已修复):
%>% summarise(across(where(is.numeric), min_max, .names = "{.fn}.{.col}"))
starwars #> # A tibble: 1 x 6
#> min.height max.height min.mass max.mass min.birth_year max.birth_year
#> <int> <int> <dbl> <dbl> <dbl> <dbl>
#> 1 66 264 15 1358 8 896
修改后正常运行:
%>% summarise(across(where(is.numeric), min_max, .names = "{fn}.{col}"))
starwars #> # A tibble: 1 x 6
#> min.height max.height min.mass max.mass min.birth_year max.birth_year
#> <int> <int> <dbl> <dbl> <dbl> <dbl>
#> 1 66 264 15 1358 8 896
区别主要是.names
参数的使用方式问题,.
加不加的问题。
%>% summarise(across(where(is.numeric), min_max, .names = "{fn}——{col}"))
starwars #> # A tibble: 1 x 6
#> `min——height` `max——height` `min——mass` `max——mass` `min——birth_year`
#> <int> <int> <dbl> <dbl> <dbl>
#> 1 66 264 15 1358 8
#> # ... with 1 more variable: max——birth_year <dbl>
2.6.3 当前列
如果需要,可以通过调用访问内部的“当前”列的名称cur_column()
,仅在across()
使用。和 Excel 中power pivot的“上下文”概念类似。
该函数不是特别容易理解,需要多尝试使用加深认识。
<- tibble(x = 1:3, y = 3:5, z = 5:7)
df <- list(x = 1, y = 10, z = 100)
mult
%>% mutate(across(all_of(names(mult)), ~ .x * mult[[cur_column()]]))
df #> # A tibble: 3 x 3
#> x y z
#> <dbl> <dbl> <dbl>
#> 1 1 30 500
#> 2 2 40 600
#> 3 3 50 700
代码解释:代码实现的是数据框 df 中列和 mult 中同名元素相乘得到新列。mult[[cur_column()]]
依次返回mult[[“x”]],mult[[“y”]],mult[[“z”]]。
以上部分是关于列操作的内容,详情查看vignette("colwise")
。