Можно ли связать две привязки двух кадров данных, которые не имеют одинакового набора столбцов? Я надеюсь сохранить столбцы, которые не совпадают после привязки.Объединить два кадра данных по строкам (rbind), когда они имеют разные наборы столбцов
ответ
rbind.fill
из пакета plyr
может быть то, что вы ищете.
Вы можете использовать smartbind
от gtools
.
Пример:
library(gtools)
df1 <- data.frame(a = c(1:5), b = c(6:10))
df2 <- data.frame(a = c(11:15), b = c(16:20), c = LETTERS[1:5])
smartbind(df1, df2)
# result
a b c
1.1 1 6 <NA>
1.2 2 7 <NA>
1.3 3 8 <NA>
1.4 4 9 <NA>
1.5 5 10 <NA>
2.1 11 16 A
2.2 12 17 B
2.3 13 18 C
2.4 14 19 D
2.5 15 20 E
Я попробовал «smartbind» с двумя большими кадрами данных (всего примерно 3 * 10^6 строк) и прервал его через 10 минут. – Joe
Вы также можете просто вытащить общие имена столбцов.
> cols <- intersect(colnames(df1), colnames(df2))
> rbind(df1[,cols], df2[,cols])
Если столбцы в df1 является подмножеством тех, кто в df2 (по именам столбцов):
df3 <- rbind(df1, df2[, names(df1)])
Может быть, я совершенно неправильно ваш вопрос, но «я надеюсь сохранить столбцы которые не совпадают после привязки «заставляет меня думать, что вы ищете left join
или right join
, похожие на SQL-запрос. R имеет функцию merge
, которая позволяет указывать левые, правые или внутренние соединения, похожие на объединение таблиц в SQL.
Существует уже большой вопрос и ответ на эту тему здесь: How to join (merge) data frames (inner, outer, left, right)?
Я написал функцию, чтобы сделать это, потому что мне нравится мой код, чтобы сказать мне, если что-то не так. Эта функция явно укажет вам, какие имена столбцов не совпадают, и если у вас есть несоответствие типа. Тогда он сделает все возможное, чтобы объединить data.frames в любом случае. Ограничение состоит в том, что вы можете комбинировать только два кадра данных за один раз.
### combines data frames (like rbind) but by matching column names
# columns without matches in the other data frame are still combined
# but with NA in the rows corresponding to the data frame without
# the variable
# A warning is issued if there is a type mismatch between columns of
# the same name and an attempt is made to combine the columns
combineByName <- function(A,B) {
a.names <- names(A)
b.names <- names(B)
all.names <- union(a.names,b.names)
print(paste("Number of columns:",length(all.names)))
a.type <- NULL
for (i in 1:ncol(A)) {
a.type[i] <- typeof(A[,i])
}
b.type <- NULL
for (i in 1:ncol(B)) {
b.type[i] <- typeof(B[,i])
}
a_b.names <- names(A)[!names(A)%in%names(B)]
b_a.names <- names(B)[!names(B)%in%names(A)]
if (length(a_b.names)>0 | length(b_a.names)>0){
print("Columns in data frame A but not in data frame B:")
print(a_b.names)
print("Columns in data frame B but not in data frame A:")
print(b_a.names)
} else if(a.names==b.names & a.type==b.type){
C <- rbind(A,B)
return(C)
}
C <- list()
for(i in 1:length(all.names)) {
l.a <- all.names[i]%in%a.names
pos.a <- match(all.names[i],a.names)
typ.a <- a.type[pos.a]
l.b <- all.names[i]%in%b.names
pos.b <- match(all.names[i],b.names)
typ.b <- b.type[pos.b]
if(l.a & l.b) {
if(typ.a==typ.b) {
vec <- c(A[,pos.a],B[,pos.b])
} else {
warning(c("Type mismatch in variable named: ",all.names[i],"\n"))
vec <- try(c(A[,pos.a],B[,pos.b]))
}
} else if (l.a) {
vec <- c(A[,pos.a],rep(NA,nrow(B)))
} else {
vec <- c(rep(NA,nrow(A)),B[,pos.b])
}
C[[i]] <- vec
}
names(C) <- all.names
C <- as.data.frame(C)
return(C)
}
rbind.ordered=function(x,y){
diffCol = setdiff(colnames(x),colnames(y))
if (length(diffCol)>0){
cols=colnames(y)
for (i in 1:length(diffCol)) y=cbind(y,NA)
colnames(y)=c(cols,diffCol)
}
diffCol = setdiff(colnames(y),colnames(x))
if (length(diffCol)>0){
cols=colnames(x)
for (i in 1:length(diffCol)) x=cbind(x,NA)
colnames(x)=c(cols,diffCol)
}
return(rbind(x, y[, colnames(x)]))
}
gtools/smartbind не понравилось работать с датами, вероятно, потому, что он был as.vectoring. Так вот мое решение ...
sbind = function(x, y, fill=NA) {
sbind.fill = function(d, cols){
for(c in cols)
d[[c]] = fill
d
}
x = sbind.fill(x, setdiff(names(y),names(x)))
y = sbind.fill(y, setdiff(names(x),names(y)))
rbind(x, y)
}
Более недавнее решение использовать dplyr
«s bind_rows
функцию, которую я предполагаю, является более эффективным, чем smartbind
.
Альтернативным с data.table
:
library(data.table)
df1 = data.frame(a = c(1:5), b = c(6:10))
df2 = data.frame(a = c(11:15), b = c(16:20), c = LETTERS[1:5])
rbindlist(list(df1, df2), fill = TRUE)
rbind
также будет работать в data.table
до тех пор, как объекты преобразуются в data.table
объекты, так
rbind(setDT(df1), setDT(df2), fill=TRUE)
также будет работать в этой ситуации , Это может быть предпочтительнее, если у вас есть несколько data.tables и вы не хотите создавать список.
Я понимаю вопрос, как:
a = data.frame(
x = c(1,2,3),
y = c(5,2,3)
)
b = data.frame(
u = c(6,2,3),
v = c(19,13,12)
)
dd=cbind(a, b)
str(dd)
'data.frame': 3 obs. of 4 variables:
$ x: num 1 2 3
$ y: num 5 2 3
$ u: num 6 2 3
$ v: num 19 13 12
К сожалению, ваше понимание кажется неправильным, поскольку это можно увидеть, прочитав принятый ответ. Вопрос о привязке строк * к двум кадрам data.frames, если они не имеют одинакового набора столбцов.Ваш пост имеет дело с привязкой * column * с использованием 'cbind()'. Пожалуйста, подумайте о том, чтобы пересмотреть свой пост с уникальным ответом на вопрос или удалить его. Спасибо. – Uwe
Только для документации.Вы можете попробовать Stack
библиотеку и ее функции Stack
в следующем виде:
Stack(df_1, df_2)
У меня есть также впечатление, что это быстрее, чем другие методы для больших наборов данных.
Основная часть базы R отвечает на ситуацию, когда только один data.frame имеет дополнительные столбцы или что результирующий data.frame будет иметь пересечение столбцов. Поскольку OP пишет , я надеюсь сохранить столбцы, которые не совпадают после привязки, ответ с использованием базовых методов R для решения этой проблемы, вероятно, стоит опубликовать.
Ниже я представляю два базовых метода R: один, который изменяет исходные кадры data.frames, и тот, который этого не делает. Кроме того, я предлагаю метод, который обобщает неразрушающий метод на более чем два data.frames.
Прежде всего, давайте возьмем некоторые данные образца.
# sample data, variable c is in df1, variable d is in df2
df1 = data.frame(a=1:5, b=6:10, d=month.name[1:5])
df2 = data.frame(a=6:10, b=16:20, c = letters[8:12])
Два data.frames, изменять оригиналы
Для того, чтобы сохранить все столбцы из обеих data.frames в rbind
(и позволяют функцию работать, не приводя к ошибке), вам добавьте столбцы NA в каждый файл data.frame с соответствующими пропущенными именами, заполненными с использованием setdiff
.
# fill in non-overlapping columns with NAs
df1[setdiff(names(df2), names(df1))] <- NA
df2[setdiff(names(df1), names(df2))] <- NA
Теперь rbind
-em
rbind(df1, df2)
a b d c
1 1 6 January <NA>
2 2 7 February <NA>
3 3 8 March <NA>
4 4 9 April <NA>
5 5 10 May <NA>
6 6 16 <NA> h
7 7 17 <NA> i
8 8 18 <NA> j
9 9 19 <NA> k
10 10 20 <NA> l
Обратите внимание, что первые две строки изменяют первоначальную data.frames, DF1 и df2, добавив полный набор столбцов для обоих.
Два data.frames, не изменяет оригиналы
Чтобы оставить оригинальную data.frames нетронутыми, первый цикл по именам, которые отличаются, возвращать названный вектор ВПЛ, которые объединяются в список с data.frame с использованием c
. Затем data.frame
преобразует результат в соответствующий файл data.frame для rbind
.
rbind(
data.frame(c(df1, sapply(setdiff(names(df2), names(df1)), function(x) NA))),
data.frame(c(df2, sapply(setdiff(names(df1), names(df2)), function(x) NA)))
)
Многие data.frames, не изменяют оригиналы
В том случае, что у вас есть более чем в два data.frames, вы можете сделать следующее.
# put data.frames into list (dfs named df1, df2, df3, etc)
mydflist <- mget(ls(pattern="df\\d+")
# get all variable names
allNms <- unique(unlist(lapply(mydflist, names)))
# put em all together
do.call(rbind,
lapply(mydflist,
function(x) data.frame(c(x, sapply(setdiff(allNms, names(x)),
function(y) NA)))))
Может быть, немного лучше не видеть имена строк исходных данных.фреймы? Тогда сделайте это.
do.call(rbind,
c(lapply(mydflist,
function(x) data.frame(c(x, sapply(setdiff(allNms, names(x)),
function(y) NA)))),
make.row.names=FALSE))
'rbind.fill' и' bind_rows() 'оба молчат падают имена ростов. – MERose
@MERose Hadley: «Да, все методы dplyr игнорируют имена ростов». – zx8754