2016-06-23 6 views
3

例のデータが不足して返します。それが存在する場合はアンチ参加するだけで「前」行

library(dplyr) 
x <- data.frame(name = c(rep('Alice', 4), rep('Bob', 3)), 
       timestamp = c(1, 5, 10, 11, 1, 3, 4), stringsAsFactors = F) 

y <- 
    base::merge(I(c('Alice', 'Bob')), c(1:3, 5:15)) # note missing time stamp = 4 
names(y) <- names(x) 
y <- 
    y %>% 
    arrange(name,timestamp) 

私は、データフレームanti_join(y,x)に各連続ブロック(timestampを使用して注文した)で、見つけることが最後の行をしたいと思います。

anti_joinを構築し、サンプルデータを使用:

> head(x_missing, 11) 
    name timestamp 
1 Alice   2 
2 Alice   3 
3 Alice   6 
4 Alice   7 
5 Alice   8 
6 Alice   9 
7 Alice  12 
8 Alice  13 
9 Alice  14 
10 Alice  15 
11 Bob   2 

を与える

x_missing <- 
    dplyr::anti_join(y, x) %>% 
    arrange(name,timestamp) 

私がするソリューションをしたいと思います:

name timestamp 
    Alice   3 
    Alice   9 
    Alice  15 
    ... 

ソリューションは、より高速にする必要がありますanti_join(y,x)の計算よりも遅くなります。x,yは大きいです。

+0

アリスのための3つの連続したタイムスタンプブロックが(2,3)、(6,7,8,9)及び(12,13,14,15) – Alex

+0

'のY%です>%anti_join(x)%>%group_by(name)%>%filter(%timestamp - 1)%>%arrange(name、timestamp) '? – alistaire

+0

@alistaireあなたはそれを答えに入れるべきです –

答えて

2

これはアンチ・ジョイントの速度を向上させ、ループを使用して必要な行を取得しますが、ハック・アイ・ループを使用するよりも行を選択するほうが良い方法があります。

library(dplyr) 
x <- data.frame(name = c(rep('Alice', 4), rep('Bob', 3)), 
       timestamp = c(1, 5, 10, 11, 1, 3, 4), stringsAsFactors = F) 

y <- 
    base::merge(I(c('Alice', 'Bob')), c(1:3, 5:15)) # note missing time stamp = 4 
names(y) <- names(x) 


y <- 
    y %>% 
    arrange(name,timestamp) 

x$nt <- paste(x$name,x$timestamp) 
y$nt <- paste(y$name,y$timestamp) 

ynt <- y[!y$nt %in% x$nt,] # should be faster 

tmp <- data.frame(name=NA,timestamp=NA) 
for(i in 2:nrow(ynt)){ 
     if((ynt[i-1,2]+1) < (ynt[i,2])){tmp <- rbind(ynt[i-1,1:2],tmp)} 
     if(!((ynt[i-1,1]) == (ynt[i,1]))){tmp <- rbind(ynt[i-1,1:2],tmp)} 
     if(i == nrow(ynt)){tmp <- rbind(ynt[i,1:2],tmp)} 
} 

tmp <- tmp[order(tmp$name,tmp$timestamp),]; tmp <- tmp[!is.na(tmp$name),] 
tmp 

    name timestamp 
    Alice   3 
    Alice   9 
    Alice  15 
    ... 
2

演算シーケンスの最後の行にフィルタリングするための簡単な方法は、配列内にある行にフィルタリングすることである用語、すなわちマイナスの差(ここでは単に1)バックシフト。これはAからBへの移行を簡素化しますが、データのパターンを活用することができない限り、より大きな仕事であるanti_joinのスピードアップはありません。あなたが好きなら

y %>% anti_join(x) %>% 
    group_by(name) %>% 
    filter(!timestamp %in% (timestamp - 1)) %>% 
    arrange(name, timestamp) 

# Source: local data frame [5 x 2] 
# Groups: name [2] 
# 
#  name timestamp 
# (AsIs)  (int) 
# 1 Alice   3 
# 2 Alice   9 
# 3 Alice  15 
# 4 Bob   2 
# 5 Bob  15 

、あなたはサンプルデータのための速い一方で、不十分スケーリングすることができるfilter、使って手動で抗参加行うことができます。 %in%はあまり効率的ではありません。

y %>% group_by(name) %>% 
    filter(!timestamp %in% x[x$name == unique(name), 'timestamp']) %>% 
    filter(!timestamp %in% (timestamp - 1)) 

または%in%なし:

y %>% anti_join(x) %>% 
    group_by(name) %>% 
    arrange(timestamp) %>% 
    filter(c(diff(timestamp), 0) != 1) 

結果は同じです。

1

data.table可能。相対速度約考えいいえ:

library(data.table) 
setDT(x) 
setDT(y) 
keycols = c("name","timestamp") 
setkeyv(x,keycols) 
setkeyv(y,keycols) 

y[!x][c(diff(timestamp)!= 1,TRUE)] 

# name timestamp 
#1: Alice   3 
#2: Alice   9 
#3: Alice  15 
#4: Bob   2 
#5: Bob  15 
関連する問題