2013-04-19 6 views
13

は、私は2つのデータフレームそうのようにあります:私は(all.x=T付き)dfto.mergeをマージしたいR - A、B、*に最も近い* 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) 

よう:

  • df$x == to.merge$x
  • df$y == to.merge$y
  • abs(df$time - to.merge$time) <= 1。満足する複数のto.mergeの場合、この距離を最小にするものを選ぶ。

どうすればいいですか?

だから、私の所望の結果(これはちょうど一致する行のために添加to.mergeの対応value列とdf)である:

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た:

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 

注 - (2 (X、Y)=(2,1)の場合、time 17がdf$time 11から1以上離れていたため、dfに一致しませんでした。 。

はまた、そこdfに一致させるための条件を満たしto.mergeに2列であった年代(2、1、11)行が、しかし 『そのtimeであったため、C B『列』行が代わりに選ばれました』最後に、to.mergeには、dfに一致しない行が存在する可能性があります。to.mergeには、dfの何も一致しません。私はと感じてい

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行to.mergeが持っている〜250kの行を持っている)作品


一つの方法は、forループであるが、それは私のデータのためにはるかに時間がかかりすぎます何とかのように、マージを行うことができます。

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) 

しかし、これは(2, 1, 11.5, c)ためto.merge$closest_time_in_dfが12であるため、(2, 1, 11)行をマージしますが、時間はありませんdfの12は(x、y)=(2,5)ではないため、マージは失敗します。

答えて

5

)参加二転からmerge倍のカップルと一度aggregateを使用して、ここでそれを行う方法です。

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

' df'の時刻が6で、 'to.merge'の時刻が2で、1より大きいため、行9はそこにはありません。 –

+0

@ mathematical.coffeeは答えを編集しました –

+0

よかった!複数の 'merge'を使うと非常に巧妙です。私は決して' aggregate'を使用していませんe。また、 'all.x'は私が信じる最初の' merge'では必要ありません。 –

13

使用data.tableroll='nearest'または1に制限する、roll = 1, rollends = c(TRUE,TRUE)

例えば

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 

あなたがroll=-1rollends = 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 
を設定することで、楽しみにあなたの自己を制限し、バック(1)することができます

または、最初にロールを1回掛けてからロールを-1にして、結果を結合することができます(val.1の列を整理するnは

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

入力データが異なりますか?あなたの出力は、OPの希望出力と一致しません。 –

+0

あなたの入力データは私のものとは異なって見えます。しかし、私は自分の入力データを試しましたが、あなたの解決策は 'DT'(2,4,6)行を' tm'(2、4、2)行とマージします。なぜなら、ここでは時間が1以上です(質問に記載されています) –

+0

@geektrader。良いキャッチ。私は 'set.seed(1)'を実行していませんでした。私はまた彼の質問への実際の答え(:赤面:)を含めました。 – mnel

関連する問題