Ответы:
rbind.fill
из пакета plyr
может быть то, что вы ищете.
Более свежее решение использовать dplyr
«s bind_rows
функцию , которую я предполагаю , является более эффективным , чем smartbind
.
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])
dplyr::bind_rows(df1, df2)
a b c
1 1 6 <NA>
2 2 7 <NA>
3 3 8 <NA>
4 4 9 <NA>
5 5 10 <NA>
6 11 16 A
7 12 17 B
8 13 18 C
9 14 19 D
10 15 20 E
ABC
невозможно преобразовать столбец из символьного в числовой. Есть ли способ сначала преобразовать столбцы?
Вы можете использовать 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 минут.
Если столбцы в df1 являются подмножеством столбцов в df2 (по именам столбцов):
df3 <- rbind(df1, df2[, names(df1)])
Альтернатива с 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 и вы не хотите создавать список.
intersect
подход, работают только для двух информационных фреймов и не легко обобщаются.
Большинство базовых ответов R относятся к ситуации, когда только один data.frame имеет дополнительные столбцы или что результирующий data.frame будет иметь пересечение столбцов. Так как OP пишет, я надеюсь сохранить столбцы, которые не совпадают после привязки , ответ с использованием базовых методов R для решения этой проблемы, вероятно, стоит опубликовать.
Ниже я представляю два базовых метода R: один, который изменяет исходные данные. Frame, а другой - нет. Кроме того, я предлагаю метод, который обобщает неразрушающий метод для более чем двух 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
-ем
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 нетронутыми, сначала выполните цикл по разным именам, верните именованный вектор NA, которые объединены в список с использованием 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)))))
Может быть, немного приятнее не видеть имена строк оригинальных data.frames? Тогда сделай это.
do.call(rbind,
c(lapply(mydflist,
function(x) data.frame(c(x, sapply(setdiff(allNms, names(x)),
function(y) NA)))),
make.row.names=FALSE))
mydflist <- list(as, dr, kr, hyt, ed1, of)
. Это должно создать объект списка, который не увеличивает размер вашей среды, а просто указывает на каждый элемент списка (до тех пор, пока вы не измените какое-либо содержимое впоследствии). После операции удалите объект списка, просто чтобы быть в безопасности.
Вы также можете просто извлечь общие имена столбцов.
> cols <- intersect(colnames(df1), colnames(df2))
> rbind(df1[,cols], df2[,cols])
Я написал функцию, чтобы сделать это, потому что мне нравится мой код, чтобы сказать мне, если что-то не так. Эта функция явно скажет вам, какие имена столбцов не совпадают, и если у вас есть несоответствие типов. Тогда он сделает все возможное, чтобы объединить 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)
}
Может быть, я полностью неправильно понял ваш вопрос, но «я надеюсь сохранить столбцы, которые не совпадают после привязки», заставляет меня думать, что вы ищете left join
или right join
похож на SQL-запрос. В R есть merge
функция, которая позволяет вам указывать левое, правое или внутреннее соединение, аналогичное соединению таблиц в SQL.
Здесь уже есть большой вопрос и ответ по этой теме: Как объединить (объединить) фреймы данных (внутренний, внешний, левый, правый)?
gtools / smartbind не нравилось работать с датами, вероятно, потому что это было как .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)
}
Просто для документации. Вы можете попробовать Stack
библиотеку и ее функции Stack
в следующей форме:
Stack(df_1, df_2)
У меня также сложилось впечатление, что это быстрее, чем другие методы для больших наборов данных.
Вы также можете использовать sjmisc::add_rows()
, который использует dplyr::bind_rows()
, но в отличие от него bind_rows()
, add_rows()
сохраняет атрибуты и, следовательно, полезен для помеченных данных .
Смотрите следующий пример с помеченным набором данных. frq()
-Функции печатают таблицы частот с метками значений, если данные помечены.
library(sjmisc)
library(dplyr)
data(efc)
# select two subsets, with some identical and else different columns
x1 <- efc %>% select(1:5) %>% slice(1:10)
x2 <- efc %>% select(3:7) %>% slice(11:20)
str(x1)
#> 'data.frame': 10 obs. of 5 variables:
#> $ c12hour : num 16 148 70 168 168 16 161 110 28 40
#> ..- attr(*, "label")= chr "average number of hours of care per week"
#> $ e15relat: num 2 2 1 1 2 2 1 4 2 2
#> ..- attr(*, "label")= chr "relationship to elder"
#> ..- attr(*, "labels")= Named num 1 2 3 4 5 6 7 8
#> .. ..- attr(*, "names")= chr "spouse/partner" "child" "sibling" "daughter or son -in-law" ...
#> $ e16sex : num 2 2 2 2 2 2 1 2 2 2
#> ..- attr(*, "label")= chr "elder's gender"
#> ..- attr(*, "labels")= Named num 1 2
#> .. ..- attr(*, "names")= chr "male" "female"
#> $ e17age : num 83 88 82 67 84 85 74 87 79 83
#> ..- attr(*, "label")= chr "elder' age"
#> $ e42dep : num 3 3 3 4 4 4 4 4 4 4
#> ..- attr(*, "label")= chr "elder's dependency"
#> ..- attr(*, "labels")= Named num 1 2 3 4
#> .. ..- attr(*, "names")= chr "independent" "slightly dependent" "moderately dependent" "severely dependent"
bind_rows(x1, x1) %>% frq(e42dep)
#>
#> # e42dep <numeric>
#> # total N=20 valid N=20 mean=3.70 sd=0.47
#>
#> val frq raw.prc valid.prc cum.prc
#> 3 6 30 30 30
#> 4 14 70 70 100
#> <NA> 0 0 NA NA
add_rows(x1, x1) %>% frq(e42dep)
#>
#> # elder's dependency (e42dep) <numeric>
#> # total N=20 valid N=20 mean=3.70 sd=0.47
#>
#> val label frq raw.prc valid.prc cum.prc
#> 1 independent 0 0 0 0
#> 2 slightly dependent 0 0 0 0
#> 3 moderately dependent 6 30 30 30
#> 4 severely dependent 14 70 70 100
#> NA NA 0 0 NA NA
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)]))
}
rbind.fill
иbind_rows()
оба молча отбрасывают имена строк.