Verwenden Sie einen Wert aus der vorherigen Zeile in einer R-Datentabellenberechnung

81

Ich möchte eine neue Spalte in einer Datentabelle erstellen, die aus dem aktuellen Wert einer Spalte und dem vorherigen einer anderen berechnet wird. Ist es möglich, auf vorherige Zeilen zuzugreifen?

Z.B:

> DT <- data.table(A=1:5, B=1:5*10, C=1:5*100)
> DT
   A  B   C
1: 1 10 100
2: 2 20 200
3: 3 30 300
4: 4 40 400
5: 5 50 500
> DT[, D := C + BPreviousRow] # What is the correct code here?

Die richtige Antwort sollte sein

> DT
   A  B   C   D
1: 1 10 100  NA
2: 2 20 200 210
3: 3 30 300 320
4: 4 40 400 430
5: 5 50 500 540
Korone
quelle
Normalerweise setze ich einen Schlüssel für meine data.tables:DT <- data.table(A=..., key = "A")
PatrickT

Antworten:

103

Mit der shift()Implementierung in Version 1.9.6 ist dies recht einfach.

DT[ , D := C + shift(B, 1L, type="lag")]
# or equivalently, in this case,
DT[ , D := C + shift(B)]

Aus NEWS :

  1. Neue Funktion shift()implementiert schnell lead/lagvon Vektor , Liste , data.frames oder data.tables . Es wird ein typeArgument verwendet, das entweder "lag" (Standard) oder "lead" sein kann . Es ermöglicht eine sehr bequeme Verwendung zusammen mit :=oder set(). Zum Beispiel : DT[, (cols) := shift(.SD, 1L), by=id]. Bitte schauen Sie ?shiftfür weitere Informationen.

Siehe Antworten für frühere Antworten.

Arun
quelle
Enthält das .Ndie aktuelle Zeilennummer oder so? Tut mir leid, hier zu fragen, aber ich kann es nicht in den Hilfedateien finden ...
SlowLearner
7
@SlowLearner: Möglicherweise finden Sie auch .Inützliche Zeilenindizes für die Zeilen in der aktuellen Gruppe.
Steve Lianoglou
7
Verwenden Sie seq_len (.N - 1) anstelle von 1 :(. N-1). Dies vermeidet Probleme im Zusammenhang mit 1: 0.
mnel
1
+1 für das .SDBeispiel - Ich habe versucht, ein zu verwenden lapplyund funky Ergebnisse zu erhalten. das ist viel einfacher.
MichaelChirico
Wo finde ich ein aktualisiertes PDF mit all diesen neuen Informationen? Die offiziellen 1.9.4-Vignetten und Webminare enthalten dies nicht. Und die Rmd 1.9.5-Vignetten sind nicht bequem und enthalten sie auch nicht.
Skan
43

Mit dplyrkönnten Sie tun:

mutate(DT, D = lag(B) + C)

Welches gibt:

#   A  B   C   D
#1: 1 10 100  NA
#2: 2 20 200 210
#3: 3 30 300 320
#4: 4 40 400 430
#5: 5 50 500 540
Steven Beaupré
quelle
22

Mehrere Leute haben die spezifische Frage beantwortet. Im folgenden Code finden Sie eine allgemeine Funktion, die ich in solchen Situationen verwende, die hilfreich sein können. Anstatt nur die vorherige Zeile abzurufen, können Sie in der "Vergangenheit" oder "Zukunft" so viele Zeilen verschieben, wie Sie möchten.

rowShift <- function(x, shiftLen = 1L) {
  r <- (1L + shiftLen):(length(x) + shiftLen)
  r[r<1] <- NA
  return(x[r])
}

# Create column D by adding column C and the value from the previous row of column B:
DT[, D := C + rowShift(B,-1)]

# Get the Old Faithul eruption length from two events ago, and three events in the future:
as.data.table(faithful)[1:5,list(eruptLengthCurrent=eruptions,
                                 eruptLengthTwoPrior=rowShift(eruptions,-2), 
                                 eruptLengthThreeFuture=rowShift(eruptions,3))]
##   eruptLengthCurrent eruptLengthTwoPrior eruptLengthThreeFuture
##1:              3.600                  NA                  2.283
##2:              1.800                  NA                  4.533
##3:              3.333               3.600                     NA
##4:              2.283               1.800                     NA
##5:              4.533               3.333                     NA
dnlbrky
quelle
Dies ist eine brillante Antwort. Ich ärgere mich, dass ich die anderen Antworten bereits positiv bewertet habe, da dies eine weitaus allgemeinere Antwort ist. Tatsächlich werde ich es in meinem Genorama-Paket verwenden (wenn es Ihnen nichts ausmacht).
Genorama
Klar, mach es. Ich hatte gehofft, etwas Freizeit zu haben und es als Pull-Anfrage an das data.tablePaket zu
senden
Eine ähnliche Funktion namens shiftwurde ab data.tableVersion 1.9.5 hinzugefügt. Siehe die aktualisierte Antwort von @Arun.
dnlbrky
12

Warum nicht einfach, basierend auf dem obigen Kommentar von @Steve Lianoglou:

DT[, D:= C + c(NA, B[.I - 1]) ]
#    A  B   C   D
# 1: 1 10 100  NA
# 2: 2 20 200 210
# 3: 3 30 300 320
# 4: 4 40 400 430
# 5: 5 50 500 540

Verwendung und vermeiden seq_lenoder headoder eine andere Funktion.

Gary Weissman
quelle
2
Schön - dies würde jedoch nicht funktionieren, wenn Sie das vorherige innerhalb einer Gruppe finden möchten.
Matthew
1
@ Matthew, du hast recht. Wenn durch Gruppe subsetting würde ich ersetzen .Imitseq_len(.N)
Gary Weissman
9

Nach Aruns Lösung können ähnliche Ergebnisse erhalten werden, ohne auf etwas Bezug zu nehmen .N

> DT[, D := C + c(NA, head(B, -1))][]
   A  B   C   D
1: 1 10 100  NA
2: 2 20 200 210
3: 3 30 300 320
4: 4 40 400 430
5: 5 50 500 540
Ryogi
quelle
Gibt es einen Grund, eine Methode einer anderen vorzuziehen? Oder ist es einfach ein ästhetischer Unterschied?
Korone
Ich denke, dass es in diesem Szenario (dh wo .Nes leicht verfügbar ist) hauptsächlich eine ästhetische Wahl ist. Mir ist kein wichtiger Unterschied bekannt.
Ryogi
1

Ich fügte ein Auffüllargument hinzu, änderte einige Namen und nannte es shift. https://github.com/geneorama/geneorama/blob/master/R/shift.R

Genorama
quelle
1
Vielen Dank für den Hinweis. Ich werde danach Ausschau halten und es höchstwahrscheinlich verwenden und meine Genorama-Version ablehnen.
Genorama
1

Hier ist meine intuitive Lösung:

#create data frame
df <- data.frame(A=1:5, B=seq(10,50,10), C=seq(100,500, 100))`
#subtract the shift from num rows
shift  <- 1 #in this case the shift is 1
invshift <- nrow(df) - shift
#Now create the new column
df$D <- c(NA, head(df$B, invshift)+tail(df$C, invshift))`

Hier invshiftbeträgt die Anzahl der Zeilen minus 1 4. nrow(df)Sie erhalten die Anzahl der Zeilen in einem Datenrahmen oder in einem Vektor. Wenn Sie noch frühere Werte verwenden möchten, subtrahieren Sie in ähnlicher Weise von nrow 2, 3, ... usw. und setzen Sie die NAs entsprechend am Anfang.

Abdullah Al Mahmud
quelle
-2

Dies kann in einer Schleife erfolgen.

# Create the column D
DT$D <- 0
# for every row in DT
for (i in 1:length(DT$A)) {
  if(i==1) {
    #using NA at first line
    DT[i,4] <- NA
  } else {
    #D = C + BPreviousRow
    DT[i,4] <- DT[i,3] + DT[(i-1), 2]   
  }
}

Mit einem for können Sie sogar den vorherigen Wert der Zeile dieser neuen Spalte verwenden DT[(i-1), 4]

Rafael Braga
quelle