Объедините (rbind) кадры данных и создайте столбец с именем исходных кадров данных
У меня есть несколько кадров данных, которые я хочу объединить по ряду. В результирующем одиночном кадре данных я хочу создать новую переменную, идентифицирующую, с каких данных установлено наблюдение.
# original data frames
df1 <- data.frame(x = c(1, 3), y = c(2, 4))
df2 <- data.frame(x = c(5, 7), y = c(6, 8))
# desired, combined data frame
df3 <- data.frame(x = c(1, 3, 5, 7), y = c(2, 4, 6, 8),
source = c("df1", "df1", "df2", "df2")
# x y source
# 1 2 df1
# 3 4 df1
# 5 6 df2
# 7 8 df2
Как я могу это достичь?
Спасибо заранее!
Ответы
Ответ 1
Это не совсем то, что вы просили, но это довольно близко. Поместите свои объекты в именованный список и используйте do.call(rbind...)
> do.call(rbind, list(df1 = df1, df2 = df2))
x y
df1.1 1 2
df1.2 3 4
df2.1 5 6
df2.2 7 8
Обратите внимание, что имена строк теперь отражают источник data.frame
s.
Обновление: используйте cbind
и rbind
Другой вариант - сделать базовую функцию следующим образом:
AppendMe <- function(dfNames) {
do.call(rbind, lapply(dfNames, function(x) {
cbind(get(x), source = x)
}))
}
Затем эта функция берет вектор-символ имен data.frame
, которые вы хотите "стекать", следующим образом:
> AppendMe(c("df1", "df2"))
x y source
1 1 2 df1
2 3 4 df1
3 5 6 df2
4 7 8 df2
Обновление 2: используйте combine
из пакета "gdata"
> library(gdata)
> combine(df1, df2)
x y source
1 1 2 df1
2 3 4 df1
3 5 6 df2
4 7 8 df2
Обновление 3: используйте rbindlist
из "data.table"
Другим подходом, который можно использовать сейчас, является использование rbindlist
из "data.table". При этом подход может быть:
> rbindlist(mget(ls(pattern = "df\\d+")), idcol = TRUE)
.id x y
1: df1 1 2
2: df1 3 4
3: df2 5 6
4: df2 7 8
Обновление 4: используйте map_df
из "purrr"
Подобно rbindlist
, вы также можете использовать map_df
из "purrr" с помощью I
или c
в качестве функции, применяемой к каждому элементу списка.
> mget(ls(pattern = "df\\d+")) %>% map_df(I, .id = "src")
Source: local data frame [4 x 3]
src x y
(chr) (int) (int)
1 df1 1 2
2 df1 3 4
3 df2 5 6
4 df2 7 8
Ответ 2
Я не уверен, что такая функция уже существует, но это похоже на трюк:
bindAndSource <- function(df1, df2) {
df1$source <- as.character(match.call())[[2]]
df2$source <- as.character(match.call())[[3]]
rbind(df1, df2)
}
Результаты:
bindAndSource(df1, df2)
1 1 2 df1
2 3 4 df1
3 5 6 df2
4 7 8 df2
Предостережение: это не будет работать в *aply
-подобных вызовах
Ответ 3
Смесь двух других ответов:
df1 <- data.frame(x = 1:3,y = 1:3)
df2 <- data.frame(x = 4:6,y = 4:6)
> foo <- function(...){
args <- list(...)
result <- do.call(rbind,args)
result$source <- rep(as.character(match.call()[-1]),times = sapply(args,nrow))
result
}
> foo(df1,df2,df1)
x y source
1 1 1 df1
2 2 2 df1
3 3 3 df1
4 4 4 df2
5 5 5 df2
6 6 6 df2
7 1 1 df1
8 2 2 df1
9 3 3 df1
Если вы хотите избежать бизнеса match.call
, вы всегда можете ограничиться наименованием аргументов функции (т.е. df1 = df1, df2 = df2
) и использовать names(args)
для доступа к именам.
Ответ 4
Другой подход с использованием dplyr
:
df1 <- data.frame(x = c(1,3), y = c(2,4))
df2 <- data.frame(x = c(5,7), y = c(6,8))
df3 <- dplyr::bind_rows(list(df1=df1, df2=df2), .id = 'source')
df3
Source: local data frame [4 x 3]
source x y
(chr) (dbl) (dbl)
1 df1 1 2
2 df1 3 4
3 df2 5 6
4 df2 7 8
Ответ 5
Другим обходным решением для этого является использование ldply в пакете plyr...
df1 <- data.frame(x = c(1,3), y = c(2,4))
df2 <- data.frame(x = c(5,7), y = c(6,8))
list = list(df1 = df1, df2 = df2)
df3 <- ldply(list)
df3
.id x y
df1 1 2
df1 3 4
df2 5 6
df2 7 8
Ответ 6
Несмотря на то, что здесь есть некоторые отличные ответы, я просто хотел добавить тот, который я использовал. Это база R
, поэтому она может быть менее ограничивающей, если вы хотите использовать ее в пакете, и она немного быстрее, чем некоторые другие базовые решения R
.
dfs <- list(df1 = data.frame("x"=c(1,2), "y"=2),
df2 = data.frame("x"=c(2,4), "y"=4),
df3 = data.frame("x"=2, "y"=c(4,5,7)))
> microbenchmark(cbind(do.call(rbind,dfs),
rep(names(dfs), vapply(dfs, nrow, numeric(1)))), times = 1001)
Unit: microseconds
min lq mean median uq max neval
393.541 409.083 454.9913 433.422 453.657 6157.649 1001
Первая часть, do.call(rbind, dfs)
связывает строки кадров данных в единый фрейм данных. vapply(dfs, nrow, numeric(1))
находит, сколько строк каждого кадра данных передано в rep
в rep(names(dfs), vapply(dfs, nrow, numeric(1)))
, чтобы повторить имя кадра данных один раз для каждой строки фрейма данных. cbind
помещает их все вместе.
Это похоже на ранее опубликованное решение, но примерно в 2 раза быстрее.
> microbenchmark(do.call(rbind,
lapply(names(dfs), function(x) cbind(dfs[[x]], source = x))),
times = 1001)
Unit: microseconds
min lq mean median uq max neval
844.558 870.071 1034.182 896.464 1210.533 8867.858 1001
Я не уверен на 100%, но я считаю, что ускорение вызвано однократным вызовом cbind
вместо одного кадра данных.