2013-04-19 28 views
13

İki dataframes şöyle var: I (all.x=T ile) df içine to.merge birleştirmek istediğinizR - A, B ve * en yakın * C?

set.seed(1) 
df <- cbind(expand.grid(x=1:3, y=1:5), time=round(runif(15)*30)) 
to.merge <- data.frame(x=c(2, 2, 2, 3, 2), 
         y=c(1, 1, 1, 5, 4), 
         time=c(17, 12, 11.6, 22.5, 2), 
         val=letters[1:5], 
         stringsAsFactors=F) 

öyle ki:

  • df$x == to.merge$x VE
  • df$y == to.merge$y VE
  • abs(df$time - to.merge$time) <= 1; tatmin eden çoklu to.merge durumunda, bu mesafeleri en aza indiren birini seçeriz.

Bunu nasıl yapabilirim?

Yani benim istenen sonuç (bu sadece eşleşen satırlar için eklenen to.merge karşılık gelen value sütun ile df olan) 'dir:

x y time val 
1 1 1 8 NA 
2 2 1 11 c 
3 3 1 17 NA 
4 1 2 27 NA 
5 2 2 6 NA 
6 3 2 27 NA 
7 1 3 28 NA 
8 2 3 20 NA 
9 3 3 19 NA 
10 1 4 2 NA 
11 2 4 6 NA 
12 3 4 5 NA 
13 1 5 21 NA 
14 2 5 12 NA 
15 3 5 23 d 

to.merge oldu:

x y time val 
1 2 1 17.0 a 
2 2 1 12.0 b 
3 2 1 11.6 c 
4 3 5 22.5 d 
5 2 4 2.0 e 

Not - (2 , 1, 17, a) df ile eşleşmedi, çünkü time 17 (X, Y) = (2, 1) için df$time 11'den 1'den fazla oldu .

Ayrıca df uyan şartını yerine to.merge iki sıra bulunmaktadır 's (2, 1, 11) arka arkaya, fakat 'de time olduğu için b ve c 'satır' satır yerine çekilmiş' En yakın olanı 11

Son olarak, to.merge numaralı satırda df numaralı öğeyle eşleşmeyen satırlar olabilir. Ben hissediyorum

df$value <- NA 
for (i in 1:nrow(df)) { 
    row <- df[i, ] 
    idx <- which(row$x == to.merge$x & 
       row$y == to.merge$y & 
       abs(row$time - to.merge$time) <= 1) 
    if (length(idx)) { 
     j <- idx[which.min(row$time - to.merge$time[idx])] 
     df$val[i] <- to.merge$val[j] 
    } 
} 

(df ~ 12k satır ve to.merge sahiptir ~ 250k satır vardır) çalışır


bir yolu için döngü, ama benim veriler için çok uzun sürer bir şekilde böyle bir birleştirme, yapabilirsiniz:

to.merge$closest_time_in_df <- sapply(to.merge$time, 
            function (tm) { 
            dts <- abs(tm - df$time) 
            # difference must be at most 1 
            if (min(dts) <= 1) { 
             df$time[which.min(dts)] 
            } else { 
             NA 
            } 
            }) 
merge(df, to.merge, 
     by.x=c('x', 'y', 'time'), 
     by.y=c('x', 'y', 'closest_time_in_df'), 
     all.x=T) 

ama (2, 1, 11.5, c) için to.merge$closest_time_in_df 12 olduğu için bu (2, 1, 11) satır birleştirme değil, ama bir zaman içinde df belgesinde 12, (x, y) = (2, 5) 'e (2, 1) karşılık gelmez, dolayısıyla birleştirme başarısız olur.

cevap

5

burada, bir kez merge birkaç kez ve aggregate Kullanılması bunu nasıl olduğunu) katıldı.

set.seed(1) 
df <- cbind(expand.grid(x = 1:3, y = 1:5), time = round(runif(15) * 30)) 
to.merge <- data.frame(x = c(2, 2, 2, 3, 2), y = c(1, 1, 1, 5, 4), time = c(17, 12, 11.6, 22.5, 2), val = letters[1:5], stringsAsFactors = F) 

#Find rows that match by x and y 
res <- merge(to.merge, df, by = c("x", "y"), all.x = TRUE) 
res$dif <- abs(res$time.x - res$time.y) 
res 
## x y time.x val time.y dif 
## 1 2 1 17.0 a  11 6.0 
## 2 2 1 12.0 b  11 1.0 
## 3 2 1 11.6 c  11 0.6 
## 4 2 4 2.0 e  6 4.0 
## 5 3 5 22.5 d  23 0.5 

#Find rows that need to be merged 
res1 <- merge(aggregate(dif ~ x + y, data = res, FUN = min), res) 
res1 
## x y dif time.x val time.y 
## 1 2 1 0.6 11.6 c  11 
## 2 2 4 4.0 2.0 e  6 
## 3 3 5 0.5 22.5 d  23 

#Finally merge the result back into df 
final <- merge(df, res1[res1$dif <= 1, c("x", "y", "val")], all.x = TRUE) 
final 
## x y time val 
## 1 1 1 8 <NA> 
## 2 1 2 27 <NA> 
## 3 1 3 28 <NA> 
## 4 1 4 2 <NA> 
## 5 1 5 21 <NA> 
## 6 2 1 11 c 
## 7 2 2 6 <NA> 
## 8 2 3 20 <NA> 
## 9 2 4 6 <NA> 
## 10 2 5 12 <NA> 
## 11 3 1 17 <NA> 
## 12 3 2 27 <NA> 
## 13 3 3 19 <NA> 
## 14 3 4 5 <NA> 
## 15 3 5 23 d 
+0

Satıcınız 9 orada olmamalı, çünkü df'de geçen süre 6 ve to.merge'de 2 zamanı, ve bunlar 1 –

+0

@ mathematical.coffee değerinden daha fazla farklılık gösterdi. –

+0

, çoklu "birleştirme" yi kullanarak çok akıllıca davrandı ve ben asla "agrega" kullanmadım. e. Ayrıca, 'all.x' ilk inanıyorum 'birleştirme' inanıyorum. –

13

Kullanım data.table ve roll='nearest' veya 1'e sınırlamak için roll = 1, rollends = c(TRUE,TRUE)

örn

library(data.table) 
# create data.tables with the same key columns (x, y, time) 
DT <- data.table(df, key = names(df)) 
tm <- data.table(to.merge, key = key(DT)) 

# use join syntax with roll = 'nearest' 


tm[DT, roll='nearest'] 

#  x y time val 
# 1: 1 1 8 NA 
# 2: 1 2 27 NA 
# 3: 1 3 28 NA 
# 4: 1 4 2 NA 
# 5: 1 5 21 NA 
# 6: 2 1 11 c 
# 7: 2 2 6 NA 
# 8: 2 3 20 NA 
# 9: 2 4 6 e 
# 10: 2 5 12 NA 
# 11: 3 1 17 NA 
# 12: 3 2 27 NA 
# 13: 3 3 19 NA 
# 14: 3 4 5 NA 
# 15: 3 5 23 d 

Sen (1) roll=-1 ve rollends = c(TRUE,TRUE)

new <- tm[DT, roll=-1, rollends =c(TRUE,TRUE)] 
new 
    x y time val 
1: 1 1 8 NA 
2: 1 2 27 NA 
3: 1 3 28 NA 
4: 1 4 2 NA 
5: 1 5 21 NA 
6: 2 1 11 c 
7: 2 2 6 NA 
8: 2 3 20 NA 
9: 2 4 6 NA 
10: 2 5 12 NA 
11: 3 1 17 NA 
12: 3 2 27 NA 
13: 3 3 19 NA 
14: 3 4 5 NA 
15: 3 5 23 d 
ayarlayarak geriye ileriye dönük ve kendini sınırlayabilir

Ya da ilk önce = 1, sonra rulo = -1, sonra sonuçları birleştirin (val.1 sütunu toplama). n ikinci haddeleme gelen

new <- tm[DT, roll = 1][tm[DT,roll=-1]][is.na(val), val := ifelse(is.na(val.1),val,val.1)][,val.1 := NULL] 
new 
    x y time val 
1: 1 1 8 NA 
2: 1 2 27 NA 
3: 1 3 28 NA 
4: 1 4 2 NA 
5: 1 5 21 NA 
6: 2 1 11 c 
7: 2 2 6 NA 
8: 2 3 20 NA 
9: 2 4 6 NA 
10: 2 5 12 NA 
11: 3 1 17 NA 
12: 3 2 27 NA 
13: 3 3 19 NA 
14: 3 4 5 NA 
15: 3 5 23 d 
+0

Giriş verileriniz farklı mı? Çıktınız OP'nin istenen çıkışı ile eşleşmiyor. –

+0

Girdi verileriniz benimkilerden farklı görünüyor. Ancak giriş verilerimle denedim ve çözümünüz hala 'DT' (2, 4, 6) satırını tm '(2, 4, 2) satırı ile birleştiriyor, ki bu fark olmamalı çünkü Burada zamanlar 1'den fazla (soruda belirtildiği gibi) –

+0

@geektrader. İyi yakalama. Set.seed (1) 'yi çalıştırmadım. Ben de şimdi onun sorusuna gerçek cevabı dahil ettik (: allık :) – mnel

İlgili konular