2016-06-23 3 views
3

Beispieldaten:Anti kommt aber nur zurückkehren „vorherige“ fehlende Zeile

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) 

Ich mag, finden, wenn es vorhanden ist, die letzte Zeile in jedem zusammenhängenden Block (Reihenfolge mit timestamp) im Datenrahmen anti_join(y,x) .

Unter Verwendung der Beispieldaten, konstruieren die anti_join:

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

die

> 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 

gibt, gebe ich die Lösung sein möchte:

name timestamp 
    Alice   3 
    Alice   9 
    Alice  15 
    ... 

Die Lösung schneller

sein muss als Berechnung anti_join(y,x), die beiübermäßig langsam istsind groß.

+0

die drei zusammenhängenden Blöcke Zeitstempel für Alice sind (2,3), (6,7,8,9) und (12,13,14,15) – Alex

+0

'y% >% anti_join (x)%>% group_by (Name)%>% filter (! Zeitstempel% in% (Zeitstempel - 1))%>% ordnen (Name, Zeitstempel) '? – alistaire

+0

@alistaire sollten Sie das in eine Antwort setzen, nett –

Antwort

2

Dies verbessert die Geschwindigkeit der Anti-Join und verwendet eine Schleife, um die Zeilen, die Sie wollen, aber es gibt sicherlich einen besseren Weg, um die Zeilen als meine hack-ish-Schleife.

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

Eine einfache Möglichkeit, die letzte Zeile einer arithmetischen Sequenz Filter zu filtern, die Reihen in der Sequenz verschoben, um eine Rücklaufzeit, das heißt ihre minus Differenz (hier nur 1). Dies vereinfacht zwar den Übergang von A nach B, beschleunigt jedoch nicht anti_join, was ein größerer Job ist, es sei denn, Sie können die Muster Ihrer Daten nutzen.

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 

Wenn Sie möchten, können Sie mit filter manuell die anti-Join tun, die für die Beispieldaten während schneller kann schlecht skalieren; %in% ist nicht besonders effizient.

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

oder ohne %in%:

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

Die Ergebnisse sind identisch.

1

Eine data.table Möglichkeit. Keine Ahnung von der Relativgeschwindigkeit:

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