R 中 rbind() 和 bind_rows() 的区别
Difference between rbind() and bind_rows() in R
在网上,我发现rbind()
用于合并两个数据框,[=12=执行相同的任务]函数。
那我不明白这两个函数有什么区别,哪个用起来效率更高??
除了一些差异之外,使用 bind_rows
而不是 rbind
的主要原因之一是合并具有不同列数的两个数据帧。 rbind
在这种情况下会抛出错误,而 bind_rows
将“NA
”分配给其中一个数据帧中缺少的列行,其中数据帧未提供值。
尝试以下代码以查看差异:
a <- data.frame(a = 1:2, b = 3:4, c = 5:6)
b <- data.frame(a = 7:8, b = 2:3, c = 3:4, d = 8:9)
两次调用结果如下:
rbind(a, b)
> rbind(a, b)
Error in rbind(deparse.level, ...) :
numbers of columns of arguments do not match
library(dplyr)
bind_rows(a, b)
> bind_rows(a, b)
a b c d
1 1 3 5 NA
2 2 4 6 NA
3 7 2 3 8
4 8 3 4 9
由于这里的 none 答案对 base::rbind
和 dplyr::bind_rows
之间的差异进行了系统的回顾,@bob 关于性能的答案是不正确的,我决定添加以下。
让我们来一些测试数据框:
df_1 = data.frame(
v1_dbl = 1:1000,
v2_lst = I(as.list(1:1000)),
v3_fct = factor(sample(letters[1:10], 1000, replace = TRUE)),
v4_raw = raw(1000),
v5_dtm = as.POSIXct(paste0("2019-12-0", sample(1:9, 1000, replace = TRUE)))
)
df_1$v2_lst = unclass(df_1$v2_lst) #remove the AsIs class introduced by `I()`
1。 base::rbind
以不同方式处理列表输入
rbind(list(df_1, df_1))
[,1] [,2]
[1,] List,5 List,5
# You have to combine it with `do.call()` to achieve the same result:
head(do.call(rbind, list(df_1, df_1)), 3)
v1_dbl v2_lst v3_fct v4_raw v5_dtm
1 1 1 b 00 2019-12-02
2 2 2 h 00 2019-12-08
3 3 3 c 00 2019-12-09
head(dplyr::bind_rows(list(df_1, df_1)), 3)
v1_dbl v2_lst v3_fct v4_raw v5_dtm
1 1 1 b 00 2019-12-02
2 2 2 h 00 2019-12-08
3 3 3 c 00 2019-12-09
2。 base::rbind
可以应对(部分)混合类型
虽然 base::rbind
和 dplyr::bind_rows
都在尝试绑定时失败,例如。 raw 或 datetime 列转换为其他类型的列,base::rbind
可以处理某种程度的差异。
组合列表和非列表列会生成列表列。结合一个因素和其他因素会产生警告但不会产生错误:
df_2 = data.frame(
v1_dbl = 1,
v2_lst = 1,
v3_fct = 1,
v4_raw = raw(1),
v5_dtm = as.POSIXct("2019-12-01")
)
head(rbind(df_1, df_2), 3)
v1_dbl v2_lst v3_fct v4_raw v5_dtm
1 1 1 b 00 2019-12-02
2 2 2 h 00 2019-12-08
3 3 3 c 00 2019-12-09
Warning message:
In `[<-.factor`(`*tmp*`, ri, value = 1) : invalid factor level, NA generated
# Fails on the lst, num combination:
head(dplyr::bind_rows(df_1, df_2), 3)
Error: Column `v2_lst` can't be converted from list to numeric
# Fails on the fct, num combination:
head(dplyr::bind_rows(df_1[-2], df_2), 3)
Error: Column `v3_fct` can't be converted from factor to numeric
3。 base::rbind
保留行名
Tidyverse 提倡将行名放入专用列中,因此它的函数会删除它们。
rbind(mtcars[1:2, 1:4], mtcars[3:4, 1:4])
mpg cyl disp hp
Mazda RX4 21.0 6 160 110
Mazda RX4 Wag 21.0 6 160 110
Datsun 710 22.8 4 108 93
Hornet 4 Drive 21.4 6 258 110
dplyr::bind_rows(mtcars[1:2, 1:4], mtcars[3:4, 1:4])
mpg cyl disp hp
1 21.0 6 160 110
2 21.0 6 160 110
3 22.8 4 108 93
4 21.4 6 258 110
4。 base::rbind
无法处理缺失的列
只是为了完整性,因为 Abhilash Kandwal 已经在他们的回答中这么说了。
5。 base::rbind
以不同方式处理命名参数
虽然 base::rbind
将参数名称添加到行名前,但 dplyr::bind_rows
可以选择添加专用 ID 列:
rbind(hi = mtcars[1:2, 1:4], bye = mtcars[3:4, 1:4])
mpg cyl disp hp
hi.Mazda RX4 21.0 6 160 110
hi.Mazda RX4 Wag 21.0 6 160 110
bye.Datsun 710 22.8 4 108 93
bye.Hornet 4 Drive 21.4 6 258 110
dplyr::bind_rows(hi = mtcars[1:2, 1:4], bye = mtcars[3:4, 1:4], .id = "my_id")
my_id mpg cyl disp hp
1 hi 21.0 6 160 110
2 hi 21.0 6 160 110
3 bye 22.8 4 108 93
4 bye 21.4 6 258 110
6. base::rbind
将矢量参数放入行中(并回收它们)
相比之下,dplyr::bind_rows
添加列(因此需要命名 x 的元素):
rbind(mtcars[1:2, 1:4], x = 1:2))
mpg cyl disp hp
Mazda RX4 21 6 160 110
Mazda RX4 Wag 21 6 160 110
x 1 2 1 2
dplyr::bind_rows(mtcars[1:2, 1:4], x = c(a = 1, b = 2))
mpg cyl disp hp a b
1 21 6 160 110 NA NA
2 21 6 160 110 NA NA
3 NA NA NA NA 1 2
7。 base::rbind
速度较慢并且需要更多 RAM
要绑定一百个中等大小的数据帧(1k 行),base::rbind
需要多五十倍的 RAM,并且速度要慢 15 倍以上:
dfs = rep(list(df_1), 100)
bench::mark(
"base::rbind" = do.call(rbind, dfs),
"dplyr::bind_rows" = dplyr::bind_rows(dfs)
)[, 1:5]
# A tibble: 2 x 5
expression min median `itr/sec` mem_alloc
<bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt>
1 base::rbind 47.23ms 48.05ms 20.0 104.48MB
2 dplyr::bind_rows 3.69ms 3.75ms 261. 2.39MB
因为我需要绑定很多小数据框,这里也有一个基准。两者的速度,尤其是 RAM 的差异是相当惊人的:
dfs = rep(list(df_1[1:2, ]), 10^4)
bench::mark(
"base::rbind" = do.call(rbind, dfs),
"dplyr::bind_rows" = dplyr::bind_rows(dfs)
)[, 1:5]
# A tibble: 2 x 5
expression min median `itr/sec` mem_alloc
<bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt>
1 base::rbind 1.65s 1.65s 0.605 1.56GB
2 dplyr::bind_rows 19.31ms 20.21ms 43.7 566.69KB
最后,help("rbind")
和 help("bind_rows")
也很有趣。
在网上,我发现rbind()
用于合并两个数据框,[=12=执行相同的任务]函数。
那我不明白这两个函数有什么区别,哪个用起来效率更高??
除了一些差异之外,使用 bind_rows
而不是 rbind
的主要原因之一是合并具有不同列数的两个数据帧。 rbind
在这种情况下会抛出错误,而 bind_rows
将“NA
”分配给其中一个数据帧中缺少的列行,其中数据帧未提供值。
尝试以下代码以查看差异:
a <- data.frame(a = 1:2, b = 3:4, c = 5:6)
b <- data.frame(a = 7:8, b = 2:3, c = 3:4, d = 8:9)
两次调用结果如下:
rbind(a, b)
> rbind(a, b)
Error in rbind(deparse.level, ...) :
numbers of columns of arguments do not match
library(dplyr)
bind_rows(a, b)
> bind_rows(a, b)
a b c d
1 1 3 5 NA
2 2 4 6 NA
3 7 2 3 8
4 8 3 4 9
由于这里的 none 答案对 base::rbind
和 dplyr::bind_rows
之间的差异进行了系统的回顾,@bob 关于性能的答案是不正确的,我决定添加以下。
让我们来一些测试数据框:
df_1 = data.frame(
v1_dbl = 1:1000,
v2_lst = I(as.list(1:1000)),
v3_fct = factor(sample(letters[1:10], 1000, replace = TRUE)),
v4_raw = raw(1000),
v5_dtm = as.POSIXct(paste0("2019-12-0", sample(1:9, 1000, replace = TRUE)))
)
df_1$v2_lst = unclass(df_1$v2_lst) #remove the AsIs class introduced by `I()`
1。 base::rbind
以不同方式处理列表输入
rbind(list(df_1, df_1))
[,1] [,2]
[1,] List,5 List,5
# You have to combine it with `do.call()` to achieve the same result:
head(do.call(rbind, list(df_1, df_1)), 3)
v1_dbl v2_lst v3_fct v4_raw v5_dtm
1 1 1 b 00 2019-12-02
2 2 2 h 00 2019-12-08
3 3 3 c 00 2019-12-09
head(dplyr::bind_rows(list(df_1, df_1)), 3)
v1_dbl v2_lst v3_fct v4_raw v5_dtm
1 1 1 b 00 2019-12-02
2 2 2 h 00 2019-12-08
3 3 3 c 00 2019-12-09
2。 base::rbind
可以应对(部分)混合类型
虽然 base::rbind
和 dplyr::bind_rows
都在尝试绑定时失败,例如。 raw 或 datetime 列转换为其他类型的列,base::rbind
可以处理某种程度的差异。
组合列表和非列表列会生成列表列。结合一个因素和其他因素会产生警告但不会产生错误:
df_2 = data.frame(
v1_dbl = 1,
v2_lst = 1,
v3_fct = 1,
v4_raw = raw(1),
v5_dtm = as.POSIXct("2019-12-01")
)
head(rbind(df_1, df_2), 3)
v1_dbl v2_lst v3_fct v4_raw v5_dtm
1 1 1 b 00 2019-12-02
2 2 2 h 00 2019-12-08
3 3 3 c 00 2019-12-09
Warning message:
In `[<-.factor`(`*tmp*`, ri, value = 1) : invalid factor level, NA generated
# Fails on the lst, num combination:
head(dplyr::bind_rows(df_1, df_2), 3)
Error: Column `v2_lst` can't be converted from list to numeric
# Fails on the fct, num combination:
head(dplyr::bind_rows(df_1[-2], df_2), 3)
Error: Column `v3_fct` can't be converted from factor to numeric
3。 base::rbind
保留行名
Tidyverse 提倡将行名放入专用列中,因此它的函数会删除它们。
rbind(mtcars[1:2, 1:4], mtcars[3:4, 1:4])
mpg cyl disp hp
Mazda RX4 21.0 6 160 110
Mazda RX4 Wag 21.0 6 160 110
Datsun 710 22.8 4 108 93
Hornet 4 Drive 21.4 6 258 110
dplyr::bind_rows(mtcars[1:2, 1:4], mtcars[3:4, 1:4])
mpg cyl disp hp
1 21.0 6 160 110
2 21.0 6 160 110
3 22.8 4 108 93
4 21.4 6 258 110
4。 base::rbind
无法处理缺失的列
只是为了完整性,因为 Abhilash Kandwal 已经在他们的回答中这么说了。
5。 base::rbind
以不同方式处理命名参数
虽然 base::rbind
将参数名称添加到行名前,但 dplyr::bind_rows
可以选择添加专用 ID 列:
rbind(hi = mtcars[1:2, 1:4], bye = mtcars[3:4, 1:4])
mpg cyl disp hp
hi.Mazda RX4 21.0 6 160 110
hi.Mazda RX4 Wag 21.0 6 160 110
bye.Datsun 710 22.8 4 108 93
bye.Hornet 4 Drive 21.4 6 258 110
dplyr::bind_rows(hi = mtcars[1:2, 1:4], bye = mtcars[3:4, 1:4], .id = "my_id")
my_id mpg cyl disp hp
1 hi 21.0 6 160 110
2 hi 21.0 6 160 110
3 bye 22.8 4 108 93
4 bye 21.4 6 258 110
6. base::rbind
将矢量参数放入行中(并回收它们)
相比之下,dplyr::bind_rows
添加列(因此需要命名 x 的元素):
rbind(mtcars[1:2, 1:4], x = 1:2))
mpg cyl disp hp
Mazda RX4 21 6 160 110
Mazda RX4 Wag 21 6 160 110
x 1 2 1 2
dplyr::bind_rows(mtcars[1:2, 1:4], x = c(a = 1, b = 2))
mpg cyl disp hp a b
1 21 6 160 110 NA NA
2 21 6 160 110 NA NA
3 NA NA NA NA 1 2
7。 base::rbind
速度较慢并且需要更多 RAM
要绑定一百个中等大小的数据帧(1k 行),base::rbind
需要多五十倍的 RAM,并且速度要慢 15 倍以上:
dfs = rep(list(df_1), 100)
bench::mark(
"base::rbind" = do.call(rbind, dfs),
"dplyr::bind_rows" = dplyr::bind_rows(dfs)
)[, 1:5]
# A tibble: 2 x 5
expression min median `itr/sec` mem_alloc
<bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt>
1 base::rbind 47.23ms 48.05ms 20.0 104.48MB
2 dplyr::bind_rows 3.69ms 3.75ms 261. 2.39MB
因为我需要绑定很多小数据框,这里也有一个基准。两者的速度,尤其是 RAM 的差异是相当惊人的:
dfs = rep(list(df_1[1:2, ]), 10^4)
bench::mark(
"base::rbind" = do.call(rbind, dfs),
"dplyr::bind_rows" = dplyr::bind_rows(dfs)
)[, 1:5]
# A tibble: 2 x 5
expression min median `itr/sec` mem_alloc
<bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt>
1 base::rbind 1.65s 1.65s 0.605 1.56GB
2 dplyr::bind_rows 19.31ms 20.21ms 43.7 566.69KB
最后,help("rbind")
和 help("bind_rows")
也很有趣。