Sử dụng một giá trị từ hàng trước đó trong một phép tính R data. Bảng


81

Tôi muốn tạo một cột mới trong data.table được tính từ giá trị hiện tại của một cột và giá trị trước đó của cột khác. Có thể truy cập các hàng trước đó không?

Ví dụ:

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

Câu trả lời đúng phải là

> 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

Tôi thường đặt khóa cho data.tables của mình:DT <- data.table(A=..., key = "A")
PatrickT

Câu trả lời:


103

Với được shift()triển khai trong v1.9.6 , điều này khá đơn giản.

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

Từ TIN TỨC :

  1. Chức năng mới shift()cụ nhanh chóng lead/lagcủa vector , danh sách , data.frames hoặc data.tables . Nó nhận một typeđối số có thể là "lag" (mặc định) hoặc "lead" . Nó cho phép sử dụng rất thuận tiện cùng với :=hoặc set(). Ví dụ: DT[, (cols) := shift(.SD, 1L), by=id]. Vui lòng xem ?shiftđể biết thêm thông tin.

Xem lịch sử để biết câu trả lời trước.


Điều đó có .Ngiữ số hàng hiện tại hay thứ gì đó không? Xin lỗi khi hỏi ở đây, nhưng dường như tôi không thể tìm thấy nó trong tệp trợ giúp ...
SlowLearner

7
@SlowLearner: Bạn cũng có thể thấy .Ihữu ích, nó giữ các chỉ số hàng cho các hàng trong nhóm Curren.
Steve Lianoglou

7
Sử dụng seq_len (.N - 1) thay vì 1 :(. N-1). Điều này tránh các vấn đề liên quan đến 1: 0.
mnel

1
+1 cho .SDví dụ - Tôi đã cố gắng sử dụng a lapplyvà nhận được kết quả thú vị. điều này đơn giản hơn nhiều.
MichaelChirico

Tôi có thể tìm bản pdf cập nhật với tất cả thông tin mới này ở đâu? Các họa tiết 1.9.4 chính thức và trình kết thúc web không bao gồm nó. Và họa tiết Rmd 1.9.5 không thoải mái và cũng không bao gồm nó.
skan

43

Sử dụng dplyrbạn có thể làm:

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

Cái nào mang lại:

#   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

Một số người đã trả lời câu hỏi cụ thể. Xem đoạn mã dưới đây để biết một hàm mục đích chung mà tôi sử dụng trong những trường hợp như thế này có thể hữu ích. Thay vì chỉ nhận được hàng trước, bạn có thể đi bao nhiêu hàng trong "quá khứ" hoặc "tương lai" tùy thích.

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

Đây là một câu trả lời tuyệt vời, tôi khó chịu vì tôi đã ủng hộ các câu trả lời khác vì đây là một câu trả lời chung chung hơn nhiều. Trên thực tế, tôi sẽ sử dụng nó trong gói geneorama của mình (nếu bạn không phiền).
geneorama

Chắc chắn, đi cho nó. Tôi đã hy vọng để có được một số thời gian rảnh rỗi và nộp nó như là một yêu cầu kéo đến data.tableđóng gói, nhưng than ôi ...
dnlbrky

Một chức năng tương tự được gọi shiftđã được thêm vào data.tablekể từ phiên bản 1.9.5. Xem câu trả lời được cập nhật từ @Arun.
dnlbrky 19/02/15

12

Dựa trên nhận xét của @Steve Lianoglou ở trên, tại sao không chỉ:

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

Và tránh sử dụng seq_lenhoặc headhoặc bất kỳ chức năng nào khác.


2
Tốt - tuy nhiên điều này sẽ không hoạt động nếu bạn muốn tìm cái trước trong một nhóm.
Matthew

1
@Matthew bạn nói đúng. Nếu đặt con theo nhóm, tôi sẽ thay thế .Ibằngseq_len(.N)
Gary Weissman

9

Theo giải pháp của Arun, có thể thu được kết quả tương tự mà không cần tham khảo .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

Có lý do gì để thích phương pháp này hơn phương pháp khác không? Hay đơn giản là sự khác biệt về mặt thẩm mỹ?
Korone

Tôi nghĩ rằng trong kịch bản này (tức là ở những nơi .Ncó sẵn), nó chủ yếu là sự lựa chọn thẩm mỹ. Tôi không nhận thức được bất kỳ sự khác biệt quan trọng nào.
Ryogi


1

Đây là giải pháp trực quan của tôi:

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

Ở đây invshift, số hàng trừ đi 1, là 4. nrow(df)cung cấp cho bạn số hàng trong khung dữ liệu hoặc trong một vectơ. Tương tự, nếu bạn muốn lấy các giá trị trước đó, hãy trừ từ nrow 2, 3, ... vv và cũng đặt NA tương ứng ở đầu.


-2

nó có thể được thực hiện trong một vòng lặp.

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

Sử dụng for, bạn thậm chí có thể sử dụng giá trị trước đó của hàng của cột mới này DT[(i-1), 4]

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.