Разница между rbind () и bind_rows () в R

В Интернете я обнаружил, что rbind() используется для объединения двух фреймов данных, и одна и та же задача выполняется функцией bind_rows().

Тогда я не понимаю, в чем разница между этими двумя функциями и какая из них более эффективна?


person asad_hussain    schedule 19.03.2017    source источник
comment
У него есть дополнительные аргументы, такие как .id и т. Д. В bind_rows. bind_rows может связывать несколько наборов данных в list, тогда как rbind выполняет только 2 набора данных, если вы не сделаете do.call. Что касается эффективности, легче проверить с помощью system.time или microbenchmark на большом наборе данных, когда у вас будет время   -  person akrun    schedule 19.03.2017


Ответы (3)


Помимо нескольких отличий, одной из основных причин использования 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
person Abhilash Kandwal    schedule 25.07.2017

Поскольку ни один из ответов здесь не предлагает систематического обзора различий между 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 терпят неудачу при попытке привязки, например. необработанный столбец или столбец 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 сохраняет rownames

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 не может справиться с отсутствующими столбцами

Просто для полноты, поскольку Абхилаш Кандвал уже сказал об этом в своем ответе.

5. base::rbind по-разному обрабатывает именованные аргументы

В то время как base::rbind добавляет имена аргументов к именам строк, dplyr::bind_rows имеет возможность добавить специальный столбец идентификатора:

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 работает медленнее и требует больше ОЗУ

Чтобы связать сотню кадров данных среднего размера (1k строк), base::rbind требуется в 50 раз больше ОЗУ и более чем в 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

Поскольку мне нужно было связать множество небольших фреймов данных, вот и эталон для этого. Разница в скорости и особенно в оперативной памяти весьма разительна:

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") тоже интересно читать.

person jakub    schedule 25.12.2019

Хотя bind_rows() более функциональный в том смысле, что он объединяет фреймы данных с разным количеством столбцов (присваивая NA строкам с отсутствующими столбцами), если вы комбинируете фреймы данных с теми же столбцами, я бы рекомендовал rbind().

rbind() гораздо более эффективен с точки зрения вычислений в случаях, когда данные, которые вы объединяете, отформатированы одинаково, и просто выдает ошибку, когда количество столбцов другое. Это сэкономит вам много времени при работе с большими наборами данных. Я очень рекомендую rbind() в таких ситуациях. Тем не менее, если ваши данные имеют разные столбцы, вам необходимо использовать bind_rows().

person bob    schedule 14.04.2019