Użyj wartości z poprzedniego wiersza w obliczeniu tabeli R data.table

81

Chcę utworzyć nową kolumnę w tabeli data.table obliczoną na podstawie bieżącej wartości jednej kolumny i poprzedniej w drugiej. Czy jest możliwy dostęp do poprzednich rzędów?

Na przykład:

> 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?

Prawidłowa odpowiedź powinna brzmieć

> 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
źródło
Zwykle ustawiam klucz do moich danych. Tabele:DT <- data.table(A=..., key = "A")
PatrickT

Odpowiedzi:

103

Po shift()zaimplementowaniu w wersji 1.9.6 jest to dość proste.

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

Z WIADOMOŚCI :

  1. Nowa funkcja shift()narzędzia szybko lead/lagz wektorowej , liście , data.frames lub data.tables . Pobiera typeargument, którym może być „lag” (domyślnie) lub „lead” . Umożliwia bardzo wygodne użytkowanie wraz z :=lub set(). Na przykład: DT[, (cols) := shift(.SD, 1L), by=id]. Zajrzyj na stronę, ?shiftaby uzyskać więcej informacji.

Zobacz historię poprzednich odpowiedzi.

Bieg
źródło
Czy to zawiera .Naktualny numer wiersza czy coś? Przepraszam, że pytam, ale nie mogę go znaleźć w plikach pomocy ...
SlowLearner
7
@SlowLearner: Przydatne może być również .Iprzechowywanie indeksów wierszy dla wierszy w bieżącej grupie.
Steve Lianoglou
7
Użyj seq_len (.N - 1) zamiast 1 :(. N-1). Pozwala to uniknąć problemów związanych z 1: 0.
mnel
1
Na .SDprzykład +1 - próbowałem użyć a lapplyi otrzymałem fajne wyniki. to jest dużo prostsze.
MichaelChirico
Gdzie mogę znaleźć zaktualizowany plik PDF ze wszystkimi nowymi informacjami? Oficjalne winiety 1.9.4 i webminaria go nie zawierają. A winiety Rmd 1.9.5 nie są wygodne i też ich nie zawierają.
skan
44

Używając dplyrmożesz zrobić:

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

Co daje:

#   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é
źródło
22

Kilka osób odpowiedziało na konkretne pytanie. Zobacz poniższy kod, aby zapoznać się z funkcją ogólnego przeznaczenia, której używam w takich sytuacjach, która może być pomocna. Zamiast pobierać tylko poprzedni wiersz, możesz przejść do dowolnej liczby wierszy w „przeszłości” lub „przyszłości”.

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
źródło
To genialna odpowiedź, denerwuję się, że inne odpowiedzi już przegłosowałem, ponieważ jest to znacznie bardziej ogólna odpowiedź. Właściwie użyję go w moim pakiecie geneorama (jeśli nie masz nic przeciwko).
geneorama
Jasne, zrób to. Miałem nadzieję, że zdobędę trochę wolnego czasu i data.table
prześlę
Podobna funkcja o nazwie shiftzostała dodana data.tablew wersji 1.9.5. Zobacz zaktualizowaną odpowiedź od @Arun.
dnlbrky
12

Na podstawie powyższego komentarza @Steve Lianoglou, dlaczego nie tylko:

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

I unikaj używania seq_lenlub headlub jakiejkolwiek innej funkcji.

Gary Weissman
źródło
2
Fajnie - jednak to nie zadziała, jeśli chcesz znaleźć poprzednią w grupie.
Matthew
1
@Matthew masz rację. W przypadku podziału na grupy zastąpiłbym .Igoseq_len(.N)
Gary Weissman,
9

Po rozwiązaniu Arun podobne wyniki można uzyskać bez odwoływania się do .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
źródło
Czy istnieje powód, aby preferować jedną metodę od drugiej? A może to po prostu różnica estetyczna?
Korone
Myślę, że w tym scenariuszu (tj. Gdzie .Njest łatwo dostępny) jest to głównie wybór estetyczny. Nie znam żadnej istotnej różnicy.
Ryogi
1

Oto moje intuicyjne rozwiązanie:

#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))`

Tutaj invshiftliczba wierszy pomniejszona o 1 wynosi 4. nrow(df)zapewnia liczbę wierszy w ramce danych lub w wektorze. Podobnie, jeśli chcesz wziąć jeszcze wcześniejsze wartości, odejmij od nrow 2, 3, ... itd., A także umieść odpowiednio NA na początku.

Abdullah Al Mahmud
źródło
-2

można to zrobić w pętli.

# 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]   
  }
}

Używając for, możesz nawet użyć poprzedniej wartości wiersza tej nowej kolumny DT[(i-1), 4]

Rafael Braga
źródło