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))假设我们要将表格中多列向上取整,代码如下:
dt <- tibble(a= rnorm(100,mean = 1),b=rnorm(100,mean = 1),d=rnorm(100,mean = 1))
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
starwars %>% summarise(across(c("height", "mass"), ~ mean(.x, na.rm = TRUE))) # purrr风格函数
#> # A tibble: 1 x 2
#> height mass
#> <dbl> <dbl>
#> 1 174. 97.32.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()函数。
df <- data.frame(g = c(1, 1, 2), x = c(-1, 1, 3), y = c(-1, -4, -9))
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 -92.6.2 多种函数功能
通过在第二个参数提供函数或 lambda 函数的命名列表,可是使用多个函数转换每个变量:
min_max <- list(
min = ~min(.x, na.rm = TRUE),
max = ~max(.x, na.rm = TRUE)
)
starwars %>% summarise(across(where(is.numeric), min_max))
#> # 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: 该参数的机制没有特别理解,需多练习体会,主要是运用到匿名函数时
以下是官方案例,但是报错(目前已修复):
starwars %>% summarise(across(where(is.numeric), min_max, .names = "{.fn}.{.col}"))
#> # 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修改后正常运行:
starwars %>% summarise(across(where(is.numeric), min_max, .names = "{fn}.{col}"))
#> # 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参数的使用方式问题,.加不加的问题。
starwars %>% summarise(across(where(is.numeric), min_max, .names = "{fn}——{col}"))
#> # 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的“上下文”概念类似。
该函数不是特别容易理解,需要多尝试使用加深认识。
df <- tibble(x = 1:3, y = 3:5, z = 5:7)
mult <- list(x = 1, y = 10, z = 100)
df %>% mutate(across(all_of(names(mult)), ~ .x * mult[[cur_column()]]))
#> # 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")。