2016-05-27 10 views
1

Ich habe einen Datensatz mit "Athleten" spielt "Matches" ("Match" == 1) auf zufällige "Dates". Zum Beispiel:Zeilenpositionen relativ zu einer bestimmten Bedingung in R

df <- data.frame(matrix(nrow = 80, ncol = 5)) 
colnames(df) <- c("Athlete", "Date", "Match", "DaysAfter", "DaysBefore") 
df[,"Athlete"] <- c(rep(1, 20), rep(2,20), rep(3, 20), rep(4, 20)) 
df[,"Date"] <- rep(1:20, 4) 
df[,"Match"] <- c(0,0,0,0,1,0,0,1,0,0) 

Ich möchte zwei Variablen machen:

df$DaysAfter <- # number of days after last "Match" (for each "Athlete"). 
df$DaysBefore <- # number of days before next "Match" (for each "Athlete"). 

PS! Wenn "Match" == 1, dann sollten "DaysAfter" und "DaysBefore" 0 sein. Wenn es keine Übereinstimmungen vorher in "DaysAfter" und danach in "DaysBefore" gibt, zeige NA (siehe Beispiel).

Ich möchte der Datensatz wie folgt aussehen:

Ath Dat Mat DA DB 
1 1 0 NA -4 
1 2 0 NA -3 
1 3 0 NA -2 
1 4 0 NA -1 
1 5 1 0 0 
1 6 0 1 -2 
1 7 0 2 -1 
1 8 1 0 0 
1 9 0 1 -4 
1 10 0 2 -3 
1 11 0 3 -2 
1 12 0 4 -1 
1 13 1 0 0 
1 14 0 1 -2 
1 15 0 2 -1 
1 16 1 0 0 
1 17 0 1 NA 
1 18 0 2 NA 
1 19 0 3 NA 
1 20 0 4 NA 
2 1 0 NA -4 
2 2 0 NA -3 
etc. 

Wie kann ich das erreichen?

Antwort

2

Wir data.table verwenden können. Konvertieren Sie den 'data.frame' in 'data.table' (setDT(df)), gruppiert nach 'Athlet' und eine andere Gruppierungsvariable, die basierend auf der Position 1 in 'Match' (cumsum(Match == 1)) erstellt wurde, erstellen wir zwei Spalten:

1) DA - Da wir NA für alle Elemente bis zur ersten 1 in 'Übereinstimmung' benötigen, erstellen Sie eine logische Bedingung mit if/else, so dass all die Elemente, die in 'Match' 0 sind, mit 'NA' (NA * any) multipliziert werden Zahl gibt NA zurück. Wie wir die Gruppierung von cumsum gemacht haben, hat nur die erste Gruppe alle Elemente als 0, so dass Teil gelöst wurde. Die else Bedingung erhält die Reihenfolge der Zeilen und subtrahiere 1 davon (`.seq_len (.N) -1).

2) DB - Wir multiplizieren das 'Match' mit der Anzahl der Zeilen (.N) und subtrahieren von der umgekehrten Sequenz (.N:1). Sobald wir das erledigt haben, beinhaltet der letzte Teil das Erstellen von NA für die Elemente in der Spalte nach der letzten 1 in 'Match'. Gruppiert durch "Athlet" erhalten wir den Zeilenindex (.I) der Sequenz von der letzten 1 in "Übereinstimmung" (nächstes Element) zur Anzahl der Zeilen (.N) und weisen (:=) den "DB" der NA zu auf diesem Index.

library(data.table) 
df1 <- setDT(df)[, c("DA", "DB") := list(if(all(!Match)) NA*Match else 
    seq_len(.N)-1,Match*(.N) -(.N:1)) , by = .(cumsum(Match==1), Athlete)] 
df1[df1[, .I[(max(which(Match==1))+1):.N] , by = Athlete]$V1, DB:= NA][] 
# Athlete Date Match DA DB 
# 1:  1 1  0 NA -4 
# 2:  1 2  0 NA -3 
# 3:  1 3  0 NA -2 
# 4:  1 4  0 NA -1 
# 5:  1 5  1 0 0 
# 6:  1 6  0 1 -2 
# 7:  1 7  0 2 -1 
# 8:  1 8  1 0 0 
# 9:  1 9  0 1 -6 
#10:  1 10  0 2 -5 
#11:  1 11  0 3 -4 
#12:  1 12  0 4 -3 
#13:  1 13  0 5 -2 
#14:  1 14  0 6 -1 
#15:  1 15  1 0 0 
#16:  1 16  0 1 -2 
#17:  1 17  0 2 -1 
#18:  1 18  1 0 0 
#19:  1 19  0 1 NA 
#20:  1 20  0 2 NA 
#21:  2 1  0 NA -4 
#22:  2 2  0 NA -3 
#23:  2 3  0 NA -2 
#24:  2 4  0 NA -1 
#25:  2 5  1 0 0 
#26:  2 6  0 1 -2 
#27:  2 7  0 2 -1 
#28:  2 8  1 0 0 
#29:  2 9  0 1 -6 
#30:  2 10  0 2 -5 
#31:  2 11  0 3 -4 
#32:  2 12  0 4 -3 
#33:  2 13  0 5 -2 
#34:  2 14  0 6 -1 
#35:  2 15  1 0 0 
#36:  2 16  0 1 -2 
#37:  2 17  0 2 -1 
#38:  2 18  1 0 0 
#39:  2 19  0 1 NA 
#40:  2 20  0 2 NA 
#41:  3 1  0 NA -4 
#42:  3 2  0 NA -3 
#43:  3 3  0 NA -2 
#44:  3 4  0 NA -1 
#45:  3 5  1 0 0 
#46:  3 6  0 1 -2 
#47:  3 7  0 2 -1 
#48:  3 8  1 0 0 
#49:  3 9  0 1 -6 
#50:  3 10  0 2 -5 
#51:  3 11  0 3 -4 
#52:  3 12  0 4 -3 
#53:  3 13  0 5 -2 
#54:  3 14  0 6 -1 
#55:  3 15  1 0 0 
#56:  3 16  0 1 -2 
#57:  3 17  0 2 -1 
#58:  3 18  1 0 0 
#59:  3 19  0 1 NA 
#60:  3 20  0 2 NA 
#61:  4 1  0 NA -4 
#62:  4 2  0 NA -3 
#63:  4 3  0 NA -2 
#64:  4 4  0 NA -1 
#65:  4 5  1 0 0 
#66:  4 6  0 1 -2 
#67:  4 7  0 2 -1 
#68:  4 8  1 0 0 
#69:  4 9  0 1 -6 
#70:  4 10  0 2 -5 
#71:  4 11  0 3 -4 
#72:  4 12  0 4 -3 
#73:  4 13  0 5 -2 
#74:  4 14  0 6 -1 
#75:  4 15  1 0 0 
#76:  4 16  0 1 -2 
#77:  4 17  0 2 -1 
#78:  4 18  1 0 0 
#79:  4 19  0 1 NA 
#80:  4 20  0 2 NA 
+0

Dies zeigt nicht genau das gewünschte Ergebnis. Zeile 19 und 20 haben ein falsches Ergebnis.NA wäre korrekt 19: 1 19 0 1 NA 1 -2 20: 1 20 0 2 NA 2 -1 21: 2 1 0 NA -4 NA -4 22: 2 2 0 NA -3 NA - 3 –

+0

@Otto_K Können Sie bitte bestätigen, ob der aktualisierte Ausgang korrekt ist – akrun

+1

Ja, jetzt ist es korrekt. –

1

sollten Dieser Code funktioniert:

unique_list<-(unique(df$Athlete)) 
for(k in (1:length(unique_list))){ 
index<-c(1:dim(df)[1])[df$Athlete==unique_list[k]] 
count=NA 
for(j in index){ 
    if(df$Mat[j]==1){ 
     count=0 
     }else{ 
    count=count+1 
    } 
    df$DaysAfter[j]=count 
    } 
    count=NA 
    for(j in index[c(length(index):1)]){ 
    if(df$Mat[j]==1){ 
     count=0 
     }else{ 
    count=count-1 
    } 
    df$DaysBefore[j]=count 
    } 

} 
+0

Danke. Es ist fast richtig. Ich brauche den Code, der getrennt für jeden Athleten ausgeführt wird, damit es keine Überschneidungen zwischen Athleten gibt. Ist es möglich? – havard

+0

Funktioniert es jetzt? –

0

Ich schrieb einmal die folgende Funktion:

cumsum.r <- function (vals, restart) 
{ 
    if (!is.vector(vals) || !is.vector(restart)) 
     stop("expect vectors") 
    if (length(vals) != length(restart)) 
     stop("different length") 
    len = length(vals) 
    restart[1] = T 
    ind = which(restart) 
    ind = rep(ind, c(ind[-1], len + 1) - ind) 
    vals.c = cumsum(vals) 
    vals.c - vals.c[ind] + vals[ind] 
} 

Es cumsum führt, sondern beginnt bei Null, wenn Neustart = TRUE.

Für "Tage nach", müssen Sie

new.ath <- c(TRUE, df$Ath[-1]==df$Ath[-length(df$Ath)]) 
restart <- df$Math==1 | new.ath 
days.after <- cumsum.r(1-restart, restart) 

für days.before Sie

rr <- rev(restart) 
days.before <- -rev(cumsum.r(1-rr, rr)) 

benötigen (Dies gilt nicht NAs setzen, aber Sie können diese cumsum.r für NAs verwenden zu .)

Verwandte Themen