2013-04-22 17 views
14

Tôi có 2 khung dữ liệu df1df2.trừ hoạt động của khung dữ liệu

df1 <- data.frame(c1=c("a","b","c","d"),c2=c(1,2,3,4)) 
df2 <- data.frame(c1=c("c","d","e","f"),c2=c(3,4,5,6)) 
> df1 
    c1 c2 
1 a 1 
2 b 2 
3 c 3 
4 d 4 
> df2 
    c1 c2 
1 c 3 
2 d 4 
3 e 5 
4 f 6 

Tôi cần thực hiện thiết lập hoạt động của 2 khung dữ liệu này. Tôi đã sử dụng phương thức merge(df1,df2,all=TRUE)merge(df1,df2,all=FALSE) để lấy liên kết và giao điểm của các khung dữ liệu này và nhận được kết quả yêu cầu. Chức năng để có được những trừ của những khung dữ liệu, đó là tất cả các vị trí hiện có trên một khung dữ liệu nhưng không phải là khác? Tôi cần đầu ra sau.

c1 c2 
1 a 1 
2 b 2 
+1

Bạn muốn lấy các dòng trong df1 không nằm trong các dòng df2 ** và ** trong df2 không nằm trong df1? – juba

+0

@juba, tôi tin rằng điều này là nhiều hơn 'setdiff' nhưng đối với' data.frame's – Arun

+0

Vâng, đó là những gì tôi nghĩ, nhưng kết quả được đưa ra không phải là một 'setdiff'. Đó là lý do tại sao tôi đặt câu hỏi :) – juba

Trả lời

24

Tôi nhớ đã xem lại chính xác số này một vài tháng trước. Quản lý để sàng lọc thông qua một lớp lót Evernote của tôi.

Lưu ý: Đây là không phải giải pháp của tôi. Tín dụng đi đến bất cứ ai đã viết nó (mà tôi dường như không thể tìm thấy vào lúc này).

Nếu bạn đừng lo lắng về rownames sau đó bạn có thể làm:

df1[!duplicated(rbind(df2, df1))[-seq_len(nrow(df2))], ] 
# c1 c2 
# 1 a 1 
# 2 b 2 

Edit: Một data.table giải pháp:

dt1 <- data.table(df1, key="c1") 
dt2 <- data.table(df2) 
dt1[!dt2] 

hoặc tốt hơn một liner (từ v1 .9.6+):

setDT(df1)[!df2, on="c1"] 

Điều này trả về tất cả các hàng trong df1 trong đó df2$c1 không khớp với số df1$c1.

+0

tính năng này hoạt động hoàn hảo. Cảm ơn bạn –

+2

Giải pháp 'data.table' này nhanh hơn nhiều so với thực hiện tra cứu' setdiff' hoặc '% in%', cảm ơn! – daroczig

4

Bạn có thể tạo columnas nhận dạng sau đó tập hợp con:

ví dụ

df1 <- data.frame(c1=c("a","b","c","d"),c2=c(1,2,3,4), indf1 = rep("Y",4)) 
df2 <- data.frame(c1=c("c","d","e","f"),c2=c(3,4,5,6),indf2 = rep("Y",4)) 
merge(df1,df2) 
# c1 c2 indf1 indf2 
#1 c 3  Y  Y 
#2 d 4  Y  Y 

bigdf <- merge(df1,df2,all=TRUE) 
# c1 c2 indf1 indf2 
#1 a 1  Y <NA> 
#2 b 2  Y <NA> 
#3 c 3  Y  Y 
#4 d 4  Y  Y 
#5 e 5 <NA>  Y 
#6 f 6 <NA>  Y 

Sau đó tập hợp con như thế nào bạn muốn:

bigdf[is.na(bigdf$indf1) ,] 
# c1 c2 indf1 indf2 
#5 e 5 <NA>  Y 
#6 f 6 <NA>  Y 

bigdf[is.na(bigdf$indf2) ,] #<- output you requested those not in df2 
# c1 c2 indf1 indf2 
#1 a 1  Y <NA> 
#2 b 2  Y <NA> 
+0

điều này là không thể .. bcoz dữ liệu đã cho chỉ là một dataframes mẫu. khung dữ liệu chứa số lượng hàng lớn. do đó kích thước đối tượng có thể trở nên rất lớn bằng phương pháp này. –

+1

@DinoopNair Sau đó, bạn có thể hợp nhất với 'all.x = TRUE' và tập con trên' indf2'? – juba

1

Nếu bạn không có kế hoạch về việc sử dụng bất kỳ dữ liệu thực tế trong d2, sau đó bạn không cần phải merge tại tất cả:

df1[!(df1$c1 %in% df2$c1), ] 
+0

tôi cần kiểm tra các giá trị trong cả hai cột. –

7

Tôi thích gói sqldf:

require(sqldf) 
sqldf("select * from df1 except select * from df2") 

## c1 c2 
## 1 a 1 
## 2 b 2 
1

Bạn có thể kiểm tra các giá trị trong cả hai cột và tập con như thế này (chỉ cần thêm một giải pháp khác):

na.omit(df1[ sapply(1:ncol(df1) , function(x) ! df1[,x] %in% df2[,x]) , ]) 
# c1 c2 
#1 a 1 
#2 b 2 
0

Một vấn đề với https://stackoverflow.com/a/16144262/2055486 là nó giả định không phải khung dữ liệu hàng đã có đôi. Hàm sau loại bỏ giới hạn đó và cũng làm việc với các cột do người dùng định nghĩa tùy ý trong x hoặc y.

Việc triển khai sử dụng ý tưởng tương tự để triển khai duplicated.data.frame trong việc nối các cột cùng với dấu phân cách.duplicated.data.frame sử dụng "\r", điều này có thể gây ra xung đột nếu các mục nhập đã nhúng "\r" ký tự. Điều này sử dụng ASCII record separator"\30" sẽ có cơ hội xuất hiện trong dữ liệu đầu vào thấp hơn nhiều.

setdiff.data.frame <- function(x, y, 
    by = intersect(names(x), names(y)), 
    by.x = by, by.y = by) { 
    stopifnot(
    is.data.frame(x), 
    is.data.frame(y), 
    length(by.x) == length(by.y)) 

    !do.call(paste, c(x[by.x], sep = "\30")) %in% do.call(paste, c(y[by.y], sep = "\30")) 
} 

# Example usage 
# remove all 4 or 6 cylinder 4 gear cars or 8 cylinder 3 gear rows 
to_remove <- data.frame(cyl = c(4, 6, 8), gear = c(4, 4, 3)) 
mtcars[setdiff.data.frame(mtcars, to_remove), ] 
#>     mpg cyl disp hp drat wt qsec vs am gear carb 
#> Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1 
#> Valiant  18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1 
#> Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1 
#> Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2 
#> Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2 
#> Ford Pantera L 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4 
#> Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6 
#> Maserati Bora 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8 

# with differing column names 
to_remove2 <- data.frame(a = c(4, 6, 8), b = c(4, 4, 3)) 
mtcars[setdiff.data.frame(mtcars, to_remove2, by.x = c("cyl", "gear"), by.y = c("a", "b")), ] 
#>     mpg cyl disp hp drat wt qsec vs am gear carb 
#> Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1 
#> Valiant  18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1 
#> Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1 
#> Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2 
#> Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2 
#> Ford Pantera L 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4 
#> Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6 
#> Maserati Bora 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8 
0

Đây là những gì tôi đã cố gắng: Tôi có 2 dữ liệu khung df2 và df_name

df2
tuổi name2
b 10
c 20
d 30

df_name
sở thích về tên tuổi
10 nhảy
b 20 hát
c 30 vở kịch

Để tìm df_name TRỪ df2:

1.Merge 2 dataframes.
dfmerge < - merge (x = df_name, y = df2, by.x = c ("name"), by.y = c ("name2"), tất cả = TRUE)

dfmerge

tên age.x sở thích age.y
1 10 nhảy NA
2 b 20 hát 10
3 c 30 chơi 20
4 d NA 30

.210

dfmerge [is.na (dfmerge $ age.y),]

tên age.x sở thích age.y
1 10 nhảy NA