2017-05-03 3 views
0

Ich habe einen Datenrahmen, sagenentfernen Fälle nach bestimmten anderen Fällen

df = data.frame(x = c("a","a","b","b","b","c","d","t","c","b","t","c","t","a","a","b","d","t","t","c"), 
       y = c(2,4,5,2,6,2,4,5,2,6,2,4,5,2,6,2,4,5,2,6)) 

ich nur die Zeilen, in denen ein oder mehr t s sind direkt zwischen einem d und einem c, in allen anderen Fällen entfernen möge Ich möchte die Fälle behalten. Also für dieses Beispiel möchte ich die t s in Reihe 8, 18 und 19 entfernen, aber die anderen behalten. Ich habe über Tausende von Fällen, also wäre dies ein echter Horror. Jede Hilfe wird sehr geschätzt.

+1

Sie Zeilen bedeuten ** 8 **, 18, 19 ...? – Sotos

+0

Sie können mit 'regexec (" dt + c ", ...)' auf 'paste0 (df $ x, collapse =" ")' arbeiten, um die Position des Musters zu bestimmen. Nachdem man gefunden hat, muss man die Zeichenkette manipulieren, um das nächste Auftreten des Musters zu finden. – jogo

+0

@Sotos, das ist in der Tat, was ich meinte, tut mir leid –

Antwort

1

Eine Option wäre rle zu verwenden, läuft der gleiche Zeichenfolge zu bekommen und dann können Sie eine sapply verwenden vorwärts/rückwärts zu überprüfen und zurück, alle Positionen, die Sie löschen möchten:

rle_vals <- rle(as.character(df$x)) 

drop <- unlist(sapply(2:length(rle_vals$values), #loop over values 
         function(i, vals, lengths) { 
         if(vals[i] == "t" & vals[i-1] == "d" & vals[i+1] == "c"){#Check if value is "t", previous is "d" and next is "c" 
          (sum(lengths[1:i-1]) + 1):sum(lengths[1:i]) #Get row #s 
         } 
         },vals = rle_vals$values, lengths = rle_vals$lengths)) 

drop 
#[1] 8 18 19 

df[-drop,] 
# x y 
#1 a 2 
#2 a 4 
#3 b 5 
#4 b 2 
#5 b 6 
#6 c 2 
#7 d 4 
#9 c 2 
#10 b 6 
#11 t 2 
#12 c 4 
#13 t 5 
#14 a 2 
#15 a 6 
#16 b 2 
#17 d 4 
#20 c 6 
+0

Dies scheint wie ein Charme zu arbeiten, die Sie zu einem wahren Timing speichern Held macht! Ich danke dir sehr! Natürlich habe ich Ihren Kommentar aufgewertet, aber es ist nicht visuell, da ich nicht so lange hier bin. –

1

Dies funktioniert auch , durch Zusammenfassen zu einem String, Identifizieren von Gruppen von t zwischen d und c (oder c und d - nicht sicher, ob Sie diese Option auch wollten), dann herauszufinden, wo sie sind und die Zeilen wie geeignet entfernen.

df =  data.frame(x=c("a","a","b","b","b","c","d","t","c","b","t","c","t","a","a","b","d","t","t","c"), 
       y=c(2,4,5,2,6,2,4,5,2,6,2,4,5,2,6,2,4,5,2,6),stringsAsFactors = FALSE) 

dfs <- paste0(df$x,collapse="") #collapse to a string 
dfs2 <- do.call(rbind,lapply(list(gregexpr("dt+c",dfs),gregexpr("ct+d",dfs)), 
       function(L) data.frame(x=L[[1]],y=attr(L[[1]],"match.length")))) 
dfs2 <- dfs2[dfs2$x>0,] #remove any -1 values (if string not found) 
drop <- unlist(mapply(function(a,b) (a+1):(a+b-2),dfs2$x,dfs2$y)) 
df2 <- df[-drop,] 
+0

@Adrew Das funktioniert auch super! Vielen Dank für deine Zeit :) –

0

ist hier eine andere Lösung mit Basis R:

df = data.frame(x = c("a","a","b","b","b","c","d","t","c","b","t","c","t","a","a","b","d","t","t","c"), 
       y = c(2,4,5,2,6,2,4,5,2,6,2,4,5,2,6,2,4,5,2,6)) 

# 
s <- paste0(df$x, collapse="") 
L <- c(NA, NA) 
while (TRUE) { 
    r <- regexec("dt+c", s)[[1]] 
    if (r[1]==-1) break 
    L <- rbind(L, c(pos=r[1]+1, length=attr(r, "match.length")-2)) 
    s <- sub("d(t+)c", "x\\1x", s) 
} 
L <- L[-1,] 
drop <- unlist(apply(L,1, function(x) seq(from=x[1], len=x[2]))) 
df[-drop, ] 
# > drop 
# 8 18 19 
# > df[-drop, ] 
# x y 
# 1 a 2 
# 2 a 4 
# 3 b 5 
# 4 b 2 
# 5 b 6 
# 6 c 2 
# 7 d 4 
# 9 c 2 
# 10 b 6 
# 11 t 2 
# 12 c 4 
# 13 t 5 
# 14 a 2 
# 15 a 6 
# 16 b 2 
# 17 d 4 
# 20 c 6 

Mit gregexpr() es kürzer:

s <- paste0(df$x, collapse="") 
g <- gregexpr("dt+c", s)[[1]] 
L <- data.frame(pos=g+1, length=attr(g, "match.length")-2) 
drop <- unlist(apply(L,1, function(x) seq(from=x[1], len=x[2]))) 
df[-drop, ] 
+0

Das funktioniert auch! Danke @Jogo! –

Verwandte Themen