Jawaban:
rbind.fill
dari paket plyr
mungkin apa yang Anda cari.
Solusi yang lebih baru adalah dengan menggunakan dplyr
's bind_rows
fungsi yang saya asumsikan lebih efisien daripada 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
tidak dapat dikonversi dari karakter ke angka. Apakah ada cara untuk mengonversi kolom terlebih dahulu?
Anda dapat menggunakan smartbind
dari gtools
paket.
Contoh:
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
dengan dua frame data besar (total sekitar 3 * 10 ^ 6 baris) dan dibatalkan setelah 10 menit.
Jika kolom dalam df1 adalah himpunan bagian dari yang ada di df2 (dengan nama kolom):
df3 <- rbind(df1, df2[, names(df1)])
Alternatif dengan 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
juga akan berfungsi data.table
selama objek dikonversi menjadi data.table
objek, jadi
rbind(setDT(df1), setDT(df2), fill=TRUE)
juga akan bekerja dalam situasi ini. Ini bisa lebih disukai ketika Anda memiliki beberapa data.tabel dan tidak ingin membangun daftar.
intersect
pendekatan, hanya berfungsi untuk 2 kerangka data dan tidak mudah digeneralisasi.
Sebagian besar jawaban R dasar mengatasi situasi di mana hanya satu data.frame memiliki kolom tambahan atau bahwa data.frame yang dihasilkan akan memiliki persimpangan kolom. Karena OP menulis saya berharap untuk mempertahankan kolom yang tidak cocok setelah bind , jawaban yang menggunakan metode dasar R untuk mengatasi masalah ini mungkin patut diposkan.
Di bawah ini, saya menyajikan dua metode dasar R: Satu yang mengubah data.frame asli, dan yang tidak. Selain itu, saya menawarkan metode yang menggeneralisasi metode non-destruktif ke lebih dari dua data.frame.
Pertama, mari kita ambil beberapa sampel data.
# 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])
Dua data.frame, ubah aslinya
Untuk mempertahankan semua kolom dari kedua data.frame dalam rbind
(dan memungkinkan fungsi untuk bekerja tanpa menghasilkan kesalahan), Anda menambahkan kolom NA ke setiap data.frame dengan nama-nama yang hilang yang sesuai diisi menggunakan setdiff
.
# fill in non-overlapping columns with NAs
df1[setdiff(names(df2), names(df1))] <- NA
df2[setdiff(names(df1), names(df2))] <- NA
Sekarang, 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
Perhatikan bahwa dua baris pertama mengubah data.frame asli, df1 dan df2, menambahkan set kolom lengkap untuk keduanya.
Dua data.frame, jangan ubah dokumen asli
Untuk membiarkan data.frame asli tetap utuh, pertama-tama periksalah nama-nama yang berbeda, kembalikan vektor bernama NAS yang digabungkan ke dalam daftar dengan menggunakan data.frame c
. Kemudian, data.frame
ubah hasilnya menjadi data.frame yang sesuai untuk 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)))
)
Banyak data.frame, jangan ubah aslinya
Dalam contoh Anda memiliki lebih dari dua data.frame, Anda bisa melakukan hal berikut.
# 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)))))
Mungkin sedikit lebih baik untuk tidak melihat nama-nama baris data.frame asli? Lalu lakukan ini.
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)
. Ini harus membangun objek daftar yang tidak menumbuhkan ukuran lingkungan Anda, tetapi hanya menunjuk ke setiap elemen daftar (selama Anda tidak mengubah konten apa pun sesudahnya). Setelah operasi, hapus daftar objek, hanya agar aman.
Anda juga bisa mencabut nama kolom yang umum.
> cols <- intersect(colnames(df1), colnames(df2))
> rbind(df1[,cols], df2[,cols])
Saya menulis fungsi untuk melakukan ini karena saya suka kode saya untuk memberi tahu saya jika ada sesuatu yang salah. Fungsi ini secara eksplisit akan memberi tahu Anda nama kolom mana yang tidak cocok dan jika Anda memiliki tipe ketidakcocokan. Maka itu akan melakukan yang terbaik untuk menggabungkan data.frame. Batasannya adalah Anda hanya bisa menggabungkan dua data.frame sekaligus.
### 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)
}
Mungkin saya benar-benar salah membaca pertanyaan Anda, tetapi "Saya berharap untuk mempertahankan kolom yang tidak cocok setelah ikatan" membuat saya berpikir Anda sedang mencari left join
atau right join
mirip dengan permintaan SQL. R memiliki merge
fungsi yang memungkinkan Anda menentukan gabungan kiri, kanan, atau dalam mirip dengan bergabung dengan tabel di SQL.
Sudah ada pertanyaan dan jawaban yang bagus tentang topik ini di sini: Bagaimana cara bergabung (menggabungkan) bingkai data (dalam, luar, kiri, kanan)?
gtools / smartbind tidak suka bekerja dengan Tanggal, mungkin karena as.vectoring. Jadi, inilah solusi saya ...
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)
}
Hanya untuk dokumentasi. Anda dapat mencoba Stack
perpustakaan dan fungsinya Stack
dalam bentuk berikut:
Stack(df_1, df_2)
Saya juga memiliki kesan bahwa itu lebih cepat daripada metode lain untuk set data yang besar.
Anda juga bisa menggunakan sjmisc::add_rows()
, yang menggunakan dplyr::bind_rows()
, tetapi tidak seperti bind_rows()
, add_rows()
mempertahankan atribut dan karenanya berguna untuk data berlabel .
Lihat contoh berikut dengan dataset berlabel. The frq()
-fungsi mencetak tabel frekuensi dengan label nilai, jika data yang diberi label.
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
danbind_rows()
keduanya diam-diam menjatuhkan nama sandi.