2015-10-05 17 views
10

Đây là những dữ liệu mẫu của tôi:tổng đơn giản nếu biểu

dt <- data.table(id=c("a","a","a","a","b","b"), monthsinarrears=c(0,1,0,0,1,0), date=c(2013,2014,2015,2016,2014,2015)) 

Bảng trông như thế này:

> dt 
    id monthsinarrears date 
1: a    0 2013 
2: a    1 2014 
3: a    0 2015 
4: a    0 2016 
5: b    1 2014 
6: b    0 2015 

Bây giờ tôi muốn tạo ra một cột bổ sung được gọi là "EverinArrears" mà sẽ được chỉ định với "1" nếu id đã từng bị nợ (theo lịch sử) và "0" nếu không. Do đó, đầu ra tôi muốn có được là:

id monthsinarrears date EverinArrears 
1: a    0 2013    0 
2: a    1 2014    1 
3: a    0 2015    1 
4: a    0 2016    1 
5: b    1 2014    1 
6: b    0 2015    1 

Lưu ý rằng id vay a là không lịch sử trong nợ vào năm 2013 (điều này xảy ra vào năm 2014) vì vậy đó là lý do tại sao EverinArrears được một 0 zero cũng trong năm 2013.

+2

kết quả của bạn không khớp với dữ liệu bạn đã cung cấp! –

+0

tại sao? Tôi chỉ muốn thêm cột bổ sung này vào EverinArrears dựa trên bảng đầu tiên mà tôi đã cung cấp và các điều kiện tôi đã cung cấp. –

+0

nhìn vào dt ban đầu của bạn, và những gì bạn đã viết ... điều này hoàn toàn khác, ngay cả khi câu hỏi của bạn là dễ hiểu tổng thể;) –

Trả lời

14

Bạn có thể làm như sau (nhờ @Roland cho gợi ý để tránh số> 1):

dt[, EverinArrears := as.integer(as.logical(cumsum(monthsinarrears))), by=id] 

Output:

# id monthsinarrears date EA 
#1: a    0 2013 0 
#2: a    1 2014 1 
#3: a    0 2015 1 
#4: a    0 2016 1 
#5: b    1 2014 1 
#6: b    0 2015 1 

Lưu ý: nếu bạn thích một mã ngắn hơn , bạn cũng có thể làm

dt[, EverinArrears := +(!!(cumsum(monthsinarrears))), by=id] 

mặc dù không phải là "thực hành tốt" là as.integer(as.logical(...))

Như đã đề cập bởi @Jaap, bạn cũng có thể làm:

dt[, EverinArrears := +(cumsum(monthsinarrears) > 0), by = id] 

hoặc, đối với thực hành tốt hơn:

dt[, EverinArrears := as.integer(cumsum(monthsinarrears) > 0), by = id] 

Theo đề nghị của @Arun trong các bình luận , cách khác, đơn giản hơn, cách:

dt[, EverinArrears := cummax(monthsinarrears), by = id] 
+0

điều này không làm việc là có nhiều hơn 1 trong monthsinarrears mỗi id ... –

+0

@ColonelBeauvel phải, tốt bắt ... – Cath

+2

Chỉ cần bọc trong 'as.logical'. – Roland

3

Bạn có thể sử dụng ave:

dt$EverinArrears = as.integer(!!ave(dt$monthsinarrears, dt$id, FUN=cumsum)) 

Hoặc cách tiếp cận tốt với data.table:

dt[, EverinArrears := +(!!cumsum(monthsinarrears)), id][] 
+2

Không sử dụng 'ave' với dữ liệu. Đó là không cần thiết chậm. – Roland

+0

Tôi đã đăng giải pháp 'ave' với' !! cumsum() + 0L' không hoạt động với dữ liệu. Đầu tiên :) –

+1

Sử dụng 'as.logical' và' as.integer' là thực hành tốt hơn (mã rõ ràng hơn và nhỏ hơn) nhanh hơn). – Roland

2

Sử dụng gói dplyr:

library(dplyr) 

dt %>% 
    group_by(id) %>% 
    arrange(date) %>% 
    mutate(EverinArrears = +as.logical(cumsum(monthsinarrears))) %>% 
    data.table 

    id monthsinarrears date EverinArrears 
1: a    0 2013    0 
2: a    1 2014    1 
3: a    0 2015    1 
4: a    0 2016    1 
5: b    1 2014    1 
6: b    0 2015    1 
5

Đây là sự thay đổi nhỏ về câu trả lời của những người khác:

dt[, newcol := cummax(monthsinarrears > 0), by=id] 

Bằng cách sử dụng cummax thay vì cumsum, chúng ta có thể tiết kiệm một số tính toán.


Và đây là một cách để so sánh với vị trí của mục đầu tiên với tháng tích cực trong nợ:

dt[, newcol := { 
    z = which(monthsinarrears > 0) 
    if (!length(z)) rep(0L,.N) 
    else   replace(rep(1L,.N), 1:.N < z[1], 0L) 
}, by=id] 

Không chắc rằng có thể là bất kỳ hiệu quả hơn; nó chắc chắn phụ thuộc vào dữ liệu ở một mức độ nào đó.

Các vấn đề liên quan