dplyr 1.0.0 之 rowwise
dplyr 1.0.0 之 rowwise
加载包
library(tidyverse, warn.conflicts = F)
<br />在 R 中 dplyr 通常是对列进行操作,然而对于行处理方面还是b比较困难,本节我们将学习通过 rowwise()
函数来对数据进行行处理,常与 c_across()
连用。<br />
<br />本节中列举了三个常见的案例:<br />
- 行水平的计算(比如,xyz 的平均值)
- 使用不同的参数调用同一个函数
- 对列表列进行操作
<br />当然这些问题我们可以通过类似 for
等循环来进行操作,但是我们可以通过管道的形式进行更便捷的操作,这里作者有一句经典的话:<br />
Of course, someone has to write loops. It doesn’t have to be you. — Jenny Bryan
<a name="a6d31b10"></a>
实战:
<br />rowwise
按行来进行分组,和 group_by()
函数一样,并不会改变数据得内容,仅仅是进行分组:<br />
df <- tibble(x = 1:2, y = 3:4, z = 5:6)
df %>% rowwise()
# 计算的是数据中所有的数值的平均值
df %>% mutate(m = mean(c(x, y, z)))
# 计算每一列的平均值
df %>% mutate(across(everything(), ~mean(.x, na.rm = T)))
# 计算的是每一行的平均值
df %>% rowwise() %>% mutate(m = mean(c(x, y, z)))
<a name="c5649d16"></a>
rowwise() 与 summarise() 函数连用
df <- tibble(name = c("Mara", "Hadley"), x = 1:2, y = 3:4, z = 5:6)
# 结果仅仅只有值
df %>%
rowwise() %>%
summarise(m = mean(c(x, y, z)))
# 可以通过加上需要处理的行作为 summarise() 的行名,可以使用 `rowwise(name)`,保留 `name` 列
df %>%
rowwise(name) %>%
summarise(m = mean(c(x, y, z)))
<a name="e2c5cace"></a>
每一行进行统计
df <- tibble(id = 1:6, w = 10:15, x = 20:25, y = 30:35, z = 40:45)
df
# 使用 `rowwise` 对数据进行行分组
rf <- df %>% rowwise(id)
rf %>% mutate(total = sum(c(w, x, y, z)))
rf %>% summarise(total = sum(c(w, x, y, z)))
<a name="b2d1310f"></a>
多列 c_across() 联合操作
rf %>% mutate(total = sum(c_across(w:z)))
rf %>% mutate(total = sum(c_across(where(is.numeric))))
<a name="c275761c"></a>
rowwise()
、c_across()
、across()
连用
ungroup()
取消分组,这里表示取消按照行进行分组
rf %>%
mutate(total = sum(c_across(w:z))) %>%
ungroup() %>%
mutate(across(w:z, ~ . / total))
<a name="b09a9c97"></a>
行处理函数总结:rowSums()
和 rowMeans()
内置行处理函数更快,对行进行操作,没有分成行、然后统计,最后连接到一起。
df %>% mutate(total = rowSums(across(where(is.numeric))))
df %>% mutate(mean = rowMeans(across(where(is.numeric))))
<a name="ec71b358"></a>
列表-列
df <- tibble(
x = list(1, 2:3, 4:6)
)
# 计算列表中向量的个数,返回的是列的长度,而不是列表中每一个向量的长度
df %>% mutate(l = length(x))
# 计算列表中单个向量的长度
df %>% mutate(l = lengths(x))
# 或者利用 sapply()、vapply() map() 函数功能实现
df %>% mutate(l = sapply(x, length))
df %>% mutate(l = purrr::map_int(x, length))
# 我们可以通过 rowwise() 函数来实现
df %>%
rowwise() %>%
mutate(l = length(x))
<a name="5d04e51c"></a>
Subsetting:取子集
df <- tibble(g = 1:2, y = list(1:3, "a"))
gf <- df %>% group_by(g)
rf <- df %>% rowwise(g)
gf %>% mutate(type = typeof(y), length = length(y))
rf %>% mutate(type = typeof(y), length = length(y))
<a name="2c6a15d0"></a>
通过 for
循环来计算列表中子向量的长度应该取 [[]]
而非 []
# grouped
out1 <- integer(2)
for (i in 1:2) {
out1[[i]] <- length(df$y[i])
}
out1
# rowwise
out2 <- integer(2)
for (i in 1:2) {
out2[[i]] <- length(df$y[[i]])
}
out2
<a name="709da5d0"></a>
当 group_by()
和 rowwise()
分组后添加新的列时,应该注意以下
gf %>% mutate(y2 = y)
# 报错,由于 rowwise 的结果是列表中子向量的值,长度不一致
# rf %>% mutate(y2 = y)
# 通过 `list()` 函数将其存储为一个 list()
rf %>% mutate(y2 = list(y))
<a name="9c427e2d"></a>
Modelling:建模(无脑运行的。)
nest_by()
分组存储为一个 list
by_cyl <- mtcars %>% nest_by(cyl)
by_cyl
<a name="2524ce58"></a>
按行线性建模
mods <- by_cyl %>% mutate(mod = list(lm(mpg ~ wt, data = data)))
mods
mods <- mods %>% mutate(pred = list(predict(mod, data)))
mods
<a name="5105b123"></a>
summarise()
函数进行统计
mods %>% summarise(rmse = sqrt(mean((pred - data$mpg) ^ 2)))
mods %>% summarise(rsq = summary(mod)$r.squared)
mods %>% summarise(broom::glance(mod))
mods %>% summarise(broom::tidy(mod))
<a name="35610ed3"></a>
重复的函数调用:按行传入变量参数
<br />rowwise()
不仅适用于返回长度为 1 的向量的函数; 如果结果是一个列表,它可以与任何函数一起连用。这意味着 rowwise()
和 mutate()
提供了一种优雅的方法,可以多次使用不同的参数调用函数,将输出存储在输入旁边。<br />
一定要用
list()
函数来将命令括起来,比如list(runif(n, min, max))
而非runif(n, min, max)
df <- tribble(
~ n, ~ min, ~ max,
1, 0, 1,
2, 10, 100,
3, 100, 1000,
)
df %>%
rowwise() %>%
mutate(data = list(runif(n, min, max)))
# 会报错
df %>%
rowwise() %>%
mutate(data = runif(n, min, max))
<a name="194c5bcf"></a>
两两多重组合:tidyr::expand_grid()
函数
# 这里就会得到 3*3 九种结果
df <- expand.grid(mean = c(-1, 0, 1), sd = c(1, 10, 100))
df %>%
rowwise() %>%
mutate(data = list(rnorm(10, mean, sd)))
<a name="bc13740f"></a>
各种功能:结合 do.call()
df <- tribble(
~rng, ~params,
"runif", list(n = 10),
"rnorm", list(n = 20),
"rpois", list(n = 10, lambda = 5),
) %>%
rowwise()
df %>%
mutate(data = list(do.call(rng, params)))
<a name="772ab8dd"></a>
以前的操作
作者建议使用
purrr
中map()
函数执行行操作,但是又会增加额外学习。