2017-08-18 2 views
2

Angenommen, ich habe einen Datenrahmen wie den folgenden, und ich muss jede Zeile identifizieren, in der einem oder mehreren fehlenden Werten (NA) mindestens ein gültiger Wert (beliebig numerisch) folgt). Kannst du mir helfen?Suchen Sie Zeilen mit einer Folge von aufeinander folgenden Spaltenwerten

a <- c(1, 'S06.4', 6.7, 7.0, 6.5, 7.0, 7.2, NA, NA, 6.6,6.7) 
b <- c(2 ,'S06.2' ,5.0, NA, 4.9, 7.8, 9.3, 8.0, 7.8, 8.0,NA) 
c <- c(3, 'S06.5', 7.0, 5.5, NA, NA, 7.2, 8.0, 7.6, NA,6.7) 
d <- c(4, 'S06.5', 7.0, 7.0, 7.0, 6.9, 6.8, 9.0, 6.0, 6.6,6.7) 
e <- c(5, 'S06.1', 6.7, NA, NA, NA, NA, NA, NA, NA,NA) 

df <- data.frame(rbind(a,b,c,d,e)) 
colnames(df) <- c('id','dx','dia01','dia02','dia03','dia04','dia05','dia06','dia07','dia08','dia09') 
+0

Ich versuche Ihr Problem zu verstehen. Also ist die letzte Reihe nicht das, wonach du suchst, oder? –

+0

Was ich brauche ist, die Zeilen zu identifizieren, die eine Sequenz wie c (, any_value) in irgendeiner Spalte entsprechen. In diesem df würden die Zeilen 1, 2 und 3 mit dieser Sequenz übereinstimmen. – ezequielm

Antwort

7

mit:

df[rowSums(is.na(df[,3:10]) * !is.na(df[,4:11])) > 0,] 

Sie erhalten:

id dx dia01 dia02 dia03 dia04 dia05 dia06 dia07 dia08 dia09 
a 1 S06.4 6.7  7 6.5  7 7.2 <NA> <NA> 6.6 6.7 
b 2 S06.2  5 <NA> 4.9 7.8 9.3  8 7.8  8 <NA> 
c 3 S06.5  7 5.5 <NA> <NA> 7.2  8 7.6 <NA> 6.7 

Was das bedeutet:

  • is.na(df[,3:10]) überprüfen, welcher der Werte in den Spalten dia01 bis dia08NA ist und gibt eine logische Matrix zurück.
  • !is.na(df[,4:11]) macht das gleiche für die nächsten Werte in jeder Reihe der df[,3:10] und gibt auch eine logische Matrix
  • diese beiden Matrizen Multipliziert man gibt eine logische Matrix für die erforderliche Bedingung.
  • Mit rowSums überprüfen Sie in jeder Zeile mindestens einmal, ob die Bedingungen erfüllt sind.

Als Antwort auf Ihren Kommentar: Wenn Sie durch einen numerischen Wert gefolgt, um sicherzustellen, wollen die NA, Sie obige Lösung verändern könnten:

# first convert the 'dia*''-columns to numeric 
df[-c(1,2)] <- lapply(df[-c(1,2)], function(x) as.numeric(as.character(x))) 
# then do the same because values that can't converted to numeric will give NA 
df[rowSums(is.na(df[,3:10]) * !is.na(df[,4:11])) > 0,] 

oder ohne numerische konvertieren zuerst:

df[rowSums(is.na(df[,3:10]) * !is.na(sapply(df[4:11], function(x) as.numeric(as.character(x))))) > 0,] 

HINWEIS:

Mit der Methode, die Sie zum Erstellen Ihrer Beispieldaten verwendet haben, werden Sie mit allen Faktorspalten enden. Ich nehme an, du willst das nicht.

A möglicherweise korrekt formatiert Beispiel-Datensatz wäre:

df <- structure(list(id = c("1", "2", "3", "4", "5"), 
        dx = c("S06.4", "S06.2", "S06.5", "S06.5", "S06.1"), 
        dia01 = c(6.7, 5, 7, 7, 6.7), 
        dia02 = c(7, NA, 5.5, 7, NA), 
        dia03 = c(6.5, 4.9, NA, 7, NA), 
        dia04 = c(7, 7.8, NA, 6.9, NA), 
        dia05 = c(7.2, 9.3, 7.2, 6.8, NA), 
        dia06 = c(NA, 8, 8, 9, NA), 
        dia07 = c(NA, 7.8, 7.6, 6, NA), 
        dia08 = c(6.6, 8, NA, 6.6, NA), 
        dia09 = c(6.7, NA, 6.7, 6.7, NA)), 
       .Names = c("id", "dx", "dia01", "dia02", "dia03", "dia04", "dia05", "dia06", "dia07", "dia08", "dia09"), 
       row.names = c("a", "b", "c", "d", "e"), 
       class = "data.frame") 

Das vorgeschlagene Verfahren auf das so gut funktioniert.



Wie in den Kommentaren von @Frank erwähnt, ist es meist besser sind Ihre Daten im langen Format zu speichern. Mit:

library(data.table) 
setDT(df)[, 3:11 := lapply(.SD, function(x) as.numeric(as.character(x))), .SDcols = 3:11][] 
melt(df, id = 1:2)[, if(any(is.na(value) & !is.na(shift(value, type = 'lead')))) .SD, by = .(id, dx)] 

Sie erhalten:

id dx variable value 
1: 1 S06.4 dia01 6.7 
2: 1 S06.4 dia02 7.0 
3: 1 S06.4 dia03 6.5 
4: 1 S06.4 dia04 7.0 
5: 1 S06.4 dia05 7.2 
6: 1 S06.4 dia06 NA 
7: 1 S06.4 dia07 NA 
8: 1 S06.4 dia08 6.6 
9: 1 S06.4 dia09 6.7 
10: 2 S06.2 dia01 5.0 
11: 2 S06.2 dia02 NA 
12: 2 S06.2 dia03 4.9 
13: 2 S06.2 dia04 7.8 
14: 2 S06.2 dia05 9.3 
15: 2 S06.2 dia06 8.0 
16: 2 S06.2 dia07 7.8 
17: 2 S06.2 dia08 8.0 
18: 2 S06.2 dia09 NA 
19: 3 S06.5 dia01 7.0 
20: 3 S06.5 dia02 5.5 
21: 3 S06.5 dia03 NA 
22: 3 S06.5 dia04 NA 
23: 3 S06.5 dia05 7.2 
24: 3 S06.5 dia06 8.0 
25: 3 S06.5 dia07 7.6 
26: 3 S06.5 dia08 NA 
27: 3 S06.5 dia09 6.7 

Eine weitere Alternative ist:

setDT(df)[, 3:11 := lapply(.SD, function(x) as.numeric(as.character(x))), .SDcols = 3:11][] 
df[unique(melt(df, id = 1:2)[, .I[is.na(value) & !is.na(shift(value, type = 'lead'))], by = .(id, dx)], by = 'id')[,'id'], on = 'id'] 

Das Ergebnis dieses Ansatzes im Wide-Format jedoch nach wie vor, wie im ersten Teil dieser Antwort dargestellt ist .

+0

Vielen Dank, aber wenn Sie versuchen, wird dies auch mit jeder Zeile übereinstimmen, die einen NA-Wert enthält, dem kein gültiger Wert folgt. Was ich identifizieren muss, ist eine NA gefolgt von einem gültigen Wert (numerisch). – ezequielm

+0

@ezequielm siehe das Update, HTH – Jaap

+0

ja, das hat es geschafft! Danke! – ezequielm

3

Eine weitere Idee ist apply mit Rand verwenden 1 über jede Zeile zu gehen und die Mindestzahl von NA mit dem maximalen Index des nicht-NA zu vergleichen, dh

#convert to numeric first to capture only valid numbers (as in @Jaap's answer) 
df[-c(1,2)] <- lapply(df[-c(1,2)], function(x) as.numeric(as.character(x))) 

apply(d1, 1, function(i) min(which(is.na(i))) < max(which(!is.na(i)))) 
# a  b  c  d  e 
# TRUE TRUE TRUE FALSE FALSE 

#or 

df[apply(d1, 1, function(i) min(which(is.na(i))) < max(which(!is.na(i)))),] 

die gibt,

id dx dia01 dia02 dia03 dia04 dia05 dia06 dia07 dia08 dia09 
a 1 S06.4 6.7  7 6.5  7 7.2 <NA> <NA> 6.6 6.7 
b 2 S06.2  5 <NA> 4.9 7.8 9.3  8 7.8  8 <NA> 
c 3 S06.5  7 5.5 <NA> <NA> 7.2  8 7.6 <NA> 6.7 
2

Hier ist eine Lösung mit rle():
(ich die Daten definiti verwendet aus der Antwort von Jaap)

df <- structure(list(id = c("1", "2", "3", "4", "5"), 
        dx = c("S06.4", "S06.2", "S06.5", "S06.5", "S06.1"), 
        dia01 = c(6.7, 5, 7, 7, 6.7), 
        dia02 = c(7, NA, 5.5, 7, NA), 
        dia03 = c(6.5, 4.9, NA, 7, NA), 
        dia04 = c(7, 7.8, NA, 6.9, NA), 
        dia05 = c(7.2, 9.3, 7.2, 6.8, NA), 
        dia06 = c(NA, 8, 8, 9, NA), 
        dia07 = c(NA, 7.8, 7.6, 6, NA), 
        dia08 = c(6.6, 8, NA, 6.6, NA), 
        dia09 = c(6.7, NA, 6.7, 6.7, NA)), 
       .Names = c("id", "dx", "dia01", "dia02", "dia03", "dia04", "dia05", "dia06", "dia07", "dia08", "dia09"), 
       row.names = c("a", "b", "c", "d", "e"), 
       class = "data.frame") 

R <- apply(is.na(df[-(1:2)]), 1, rle) 
id.row <- function(r) { 
    first.na <- which(r$value)[1] 
    if (is.na(first.na)) return(FALSE) 
    if (first.na==length(r$value)) return(FALSE) 
    return(TRUE) 
} 
sapply(R, id.row) 
#> sapply(R, id.row) 
# a  b  c  d  e 
# TRUE TRUE TRUE FALSE FALSE 
Verwandte Themen