Використовуйте значення з попереднього рядка в обчисленні таблиці даних R.


81

Я хочу створити новий стовпець у таблиці даних. Розрахований на основі поточного значення одного стовпця та попереднього іншого. Чи можна отримати доступ до попередніх рядків?

Наприклад:

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

Правильна відповідь повинна бути

> 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

Зазвичай я встановлюю ключ до моїх data.tables:DT <- data.table(A=..., key = "A")
PatrickT

Відповіді:


103

З shift()реалізацією у версії 1.9.6 це досить просто.

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

З НОВИН :

  1. Нові функціональні shift()знаряддя швидко lead/lagз векторних , списку , data.frames або data.tables . Він приймає typeаргумент, який може бути або "lag" (за замовчуванням), або "lead" . Це дозволяє дуже зручно використовувати разом з :=або set(). Наприклад: DT[, (cols) := shift(.SD, 1L), by=id]. Будь ласка, подивіться на ?shiftдодаткову інформацію.

Подивіться історію попередніх відповідей.


Тут міститься .Nномер поточного рядка чи щось інше? Вибачте, що питаю тут, але, здається, я не можу знайти це у файлах довідки ...
SlowLearner

7
@SlowLearner: Ви також можете виявити .Iкорисним, що містить індекси рядків для рядків у поточній групі.
Steve Lianoglou

7
Використовуйте seq_len (.N - 1) замість 1 :(. N-1). Це дозволяє уникнути проблем, пов’язаних з 1: 0.
mnel

1
+1 для .SDприкладу - я намагався використати a lapplyі отримував прикольні результати. це набагато простіше.
MichaelChirico

Де я можу знайти оновлений pdf із усією цією новою інформацією? Офіційні віньєтки 1.9.4 та вебмінари це не включають. А віньєтки Rmd 1.9.5 не зручні, і їх теж не включають.
скан

44

За допомогою dplyrви можете зробити:

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

Що дає:

#   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

22

Кілька людей відповіли на конкретне питання. Дивіться код нижче для загальної функції, яку я використовую у подібних ситуаціях, які можуть бути корисними. Замість того, щоб просто отримати попередній рядок, ви можете перенести скільки завгодно рядків у "минулому" або "майбутньому".

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

Це блискуча відповідь, мене дратує, що я вже підтримав інші відповіді, оскільки це набагато більш загальна відповідь. Насправді я збираюся використовувати його у своєму пакеті генеорами (якщо ви не проти).
geneorama

Звичайно, продовжуйте. Я сподівався отримати трохи вільного часу і подати його як запит на витяг до data.tableпакету, але на жаль ...
dnlbrky

Подібна функція, що називається shift, була додана до data.tableверсії 1.9.5. Дивіться оновлену відповідь від @Arun.
dnlbrky

12

На основі коментаря @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

І уникати використання seq_lenабо headбудь-якої іншої функції.


2
Приємно - проте це не спрацює, якщо ви хочете знайти попереднього в групі.
Matthew

1
@Matthew ти маєш рацію. Якщо підгрупувати за групою, я б замінив .Iнаseq_len(.N)
Гері Вайсман

9

Після рішення Аруна подібні результати можна отримати, не посилаючись на .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

Чи є причина віддавати перевагу одному методу іншому? Або це просто естетична різниця?
Короне

Я думаю, що в цьому сценарії (тобто там, де .Nце легко доступно) це переважно естетичний вибір. Я не знаю жодної важливої ​​різниці.
Ryogi


1

Ось моє інтуїтивне рішення:

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

Тут invshiftкількість рядків мінус 1, дорівнює 4. nrow(df)надає вам кількість рядків у кадрі даних або у векторі. Подібним чином, якщо ви хочете взяти ще більш ранні значення, відніміть від nrow 2, 3, ... тощо, а також поставте NA на початку відповідно.


-2

це можна зробити в циклі.

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

Використовуючи for, ви навіть можете використовувати попереднє значення рядка цього нового стовпця DT[(i-1), 4]

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.