Câu trả lời:
rbind.fill
từ gói plyr
có thể là những gì bạn đang tìm kiếm.
Một giải pháp gần đây hơn là sử dụng dplyr
's bind_rows
chức năng mà tôi giả định là hiệu quả hơn 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
Không thể chuyển đổi cột từ ký tự sang số. Có cách nào để chuyển đổi các cột đầu tiên?
Bạn có thể sử dụng smartbind
từ gtools
gói.
Thí dụ:
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
với hai khung dữ liệu lớn (tổng cộng khoảng 3 * 10 ^ 6 hàng) và hủy bỏ nó sau 10 phút.
Nếu các cột trong df1 là tập hợp con của các cột trong df2 (theo tên cột):
df3 <- rbind(df1, df2[, names(df1)])
Một thay thế với 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
cũng sẽ hoạt động data.table
miễn là các đối tượng được chuyển đổi thành data.table
các đối tượng, vì vậy
rbind(setDT(df1), setDT(df2), fill=TRUE)
cũng sẽ làm việc trong tình huống này. Điều này có thể thích hợp hơn khi bạn có một vài data.tables và không muốn xây dựng một danh sách.
intersect
cách tiếp cận, chỉ hoạt động cho 2 datafram và không dễ dàng khái quát hóa.
Hầu hết các câu trả lời cơ sở R giải quyết tình huống trong đó chỉ có một data.frame có các cột bổ sung hoặc data.frame kết quả sẽ có giao điểm của các cột. Vì OP viết nên tôi hy vọng giữ lại các cột không khớp sau liên kết , nên một câu trả lời sử dụng các phương pháp cơ sở R để giải quyết vấn đề này có lẽ đáng để đăng.
Dưới đây, tôi trình bày hai phương thức R cơ bản: Một phương pháp thay đổi dữ liệu gốc.frames và một phương thức không. Ngoài ra, tôi cung cấp một phương pháp tổng quát hóa phương thức không phá hủy cho hơn hai data.frames.
Đầu tiên, hãy lấy một số dữ liệu mẫu.
# 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])
Hai data.frames, thay đổi bản gốc
Để giữ lại tất cả các cột từ cả hai data.frames trong một rbind
(và cho phép hàm hoạt động mà không gây ra lỗi), bạn thêm các cột NA vào mỗi data.frame với các tên bị thiếu phù hợp được điền vào sử dụng setdiff
.
# fill in non-overlapping columns with NAs
df1[setdiff(names(df2), names(df1))] <- NA
df2[setdiff(names(df1), names(df2))] <- NA
Bây giờ, 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
Lưu ý rằng hai dòng đầu tiên thay đổi data.frames ban đầu, df1 và df2, thêm bộ cột đầy đủ cho cả hai.
Hai data.frames, không thay đổi bản gốc
Để giữ nguyên data.frames, vòng lặp đầu tiên thông qua các tên khác nhau, trả về một vectơ NA được đặt tên được nối vào danh sách với data.frame bằng cách sử dụng c
. Sau đó, data.frame
chuyển đổi kết quả thành một data.frame thích hợp cho 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)))
)
Nhiều data.frames, không thay đổi bản gốc
Trong trường hợp bạn có nhiều hơn hai data.frames, bạn có thể làm như sau.
# 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)))))
Có lẽ đẹp hơn một chút để không thấy tên hàng của data.frames gốc? Sau đó làm điều này.
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)
. Điều này sẽ xây dựng một đối tượng danh sách không tăng kích thước môi trường của bạn, mà chỉ trỏ đến từng thành phần của danh sách (miễn là bạn không thay đổi bất kỳ nội dung nào sau đó). Sau khi hoạt động, loại bỏ các đối tượng danh sách, chỉ để được an toàn.
Bạn cũng có thể chỉ cần kéo ra các tên cột phổ biến.
> cols <- intersect(colnames(df1), colnames(df2))
> rbind(df1[,cols], df2[,cols])
Tôi đã viết một hàm để làm điều này bởi vì tôi thích mã của tôi để cho tôi biết nếu có gì đó không đúng. Hàm này sẽ cho bạn biết rõ tên cột nào không khớp và nếu bạn có kiểu không khớp. Sau đó, nó sẽ làm hết sức mình để kết hợp data.frames. Hạn chế là bạn chỉ có thể kết hợp hai data.frames tại một thời điểm.
### 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)
}
Có thể tôi đã hoàn toàn đọc sai câu hỏi của bạn, nhưng "Tôi hy vọng giữ lại các cột không khớp sau liên kết" khiến tôi nghĩ rằng bạn đang tìm kiếm một left join
hoặc right join
tương tự như một truy vấn SQL. R có merge
chức năng cho phép bạn chỉ định các phép nối trái, phải hoặc bên trong tương tự như nối các bảng trong SQL.
Đã có một câu hỏi và câu trả lời tuyệt vời về chủ đề này ở đây: Làm thế nào để nối (hợp nhất) khung dữ liệu (bên trong, bên ngoài, bên trái, bên phải)?
gtools / smartbind không thích làm việc với Ngày, có lẽ vì đó là as.vectoring. Vì vậy, đây là giải pháp của tôi ...
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)
}
Chỉ cần cho các tài liệu. Bạn có thể thử Stack
thư viện và chức năng của nó Stack
theo mẫu sau:
Stack(df_1, df_2)
Tôi cũng có ấn tượng rằng nó nhanh hơn các phương thức khác cho các tập dữ liệu lớn.
Bạn cũng có thể sử dụng sjmisc::add_rows()
, sử dụng dplyr::bind_rows()
, nhưng không giống như bind_rows()
, add_rows()
bảo tồn các thuộc tính và do đó rất hữu ích cho dữ liệu được dán nhãn .
Xem ví dụ sau với một tập dữ liệu được dán nhãn. Các frq()
chức năng in bảng tần số với nhãn giá trị, nếu dữ liệu được dán nhãn.
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
vàbind_rows()
cả hai âm thầm thả tên.