2016-05-05 42 views
8

Bir kullanıcı kimliği ile anahtarlanmış, ancak çok sayıda null değere sahip çok sayıda sütun (~ 150) içeren geniş, geniş data.table (20m satır) var. Her sütun, her bir kişi için ileriye taşımak istediğim kayıtlı bir durum/özelliktir. Her bir kişi 10 ila 10,000 gözlemden herhangi birine sahip olabilir ve sette yaklaşık 500.000 kişi var. Bir kişiden elde edilen değerler aşağıdaki kişiye 'kanama' yapamaz, bu yüzden çözümüm kişinin kimlik sütununa ve gruba uygun şekilde saygı göstermelidir.Tek bir R data.table içinde grupların verimli bir şekilde konumlandırılabilir

DT = data.table(
    id=c(1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3), 
    aa=c("A", NA, "B", "C", NA, NA, "D", "E", "F", NA, NA, NA), 
    bb=c(NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA), 
    cc=c(1, NA, NA, NA, NA, 4, NA, 5, 6, NA, 7, NA) 
) 

Bu şuna benzer: - Gösteri amaçlı

burada çok küçük bir örnek girdi var

id aa bb cc 
1: 1 A NA 1 
2: 1 NA NA NA 
3: 1 B NA NA 
4: 1 C NA NA 
5: 2 NA NA NA 
6: 2 NA NA 4 
7: 2 D NA NA 
8: 2 E NA 5 
9: 3 F NA 6 
10: 3 NA NA NA 
11: 3 NA NA 7 
12: 3 NA NA NA 

My beklenen çıkışı şöyle görünür:

id aa bb cc 
1: 1 A NA 1 
2: 1 A NA 1 
3: 1 B NA 1 
4: 1 C NA 1 
5: 2 NA NA NA 
6: 2 NA NA 4 
7: 2 D NA 4 
8: 2 E NA 5 
9: 3 F NA 6 
10: 3 F NA 6 
11: 3 F NA 7 
12: 3 F NA 7 

Ben ve çalıştığı bir data.table çözüm buldu, ancak büyük veri kümelerimde çok yavaş:

DT[, na.locf(.SD, na.rm=FALSE), by=id] 

Eşit derecede yavaş olan dplyr kullanarak eşdeğer çözümler buldum.

GRP = DT %>% group_by(id) 
data.table(GRP %>% mutate_each(funs(blah=na.locf(., na.rm=FALSE)))) 

ben ben .N kullanmak gerekir şüpheli ama sadece ben (yuvarlanan 'öz' ile gelip data.table işlevini kullanarak katılmak, ama sadece doğru olsun gibi olamaz ki ümitli olduğunu bunu anlamadım).

Bu noktada, gruplandırılmış locayı verimli bir şekilde uygulamak için Rcpp'de bir şeyler yazmam gerektiğini düşünüyorum.

R için yeniyim, fakat C++ için yeni değilim - bu yüzden yapabilirim. Sadece data.table kullanarak R'de bunu yapmak için verimli bir yol olmalı.

x = c(1, NA, NA, 6, 4, 5, 4, NA, NA, 2) 
x[cummax((!is.na(x)) * seq_along(x))] 
# [1] 1 1 1 6 4 5 4 4 4 2 

Bu biz sadece ihtiyaç na.rm = FALSE davranışı elde etmek, bir na.rm = TRUE argümanla na.locf çoğaltır:

+0

ben [= id tarafından, lapply (.SD, na.locf F)] DT 'eminim' hızlı olacaktır Aslında o ile başladı – eddi

+0

ve performansın daha kötü olduğu ortaya çıktı. –

+0

Rolling self join burada noktaya bakar gibi görünüyor, hem 'na.locf' hem de haddeleme birleştirme cevapları var bazı soruları hatırlıyorum, bu yüzden mevcut SO bilgi tabanında cevap bulabilirsiniz düşünüyorum. – jangorecki

cevap

14

Çok basit na.locf (cummax) olmayan NA indeksleri ((!is.na(x)) * seq_along(x)) ve buna göre subsetting ileterek inşa edilebilir emin olmak için cummax ilk unsur TRUE geçerli:

x = c(NA, NA, 1, NA, 2) 
x[cummax(c(TRUE, tail((!is.na(x)) * seq_along(x), -1)))] 
#[1] NA NA 1 1 2 
id = c(10, 10, 11, 11, 11, 12, 12, 12, 13, 13) 
c(TRUE, id[-1] != id[-length(id)]) 
# [1] TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE TRUE FALSE 

birleştiren: Bu durumda, biz "id" sütun değeri değişir dikkate sadece dışı NA endeks almaya ama, aynı zamanda, endekslerin (sıralı veya sipariş edilmesi) ihtiyaç yukarıda:

id = c(10, 10, 11, 11, 11, 12, 12, 12, 13, 13) 
x = c(1, NA, NA, 6, 4, 5, 4, NA, NA, 2) 

x[cummax(((!is.na(x)) | c(TRUE, id[-1] != id[-length(id)])) * seq_along(x))] 
# [1] 1 1 NA 6 4 5 4 4 NA 2 

Not, işte TRUE ile ilk elemanı, yani OR o nedenle na.rm = FALSE davranışı alma TRUE için eşit hale.

Ve

bu örneğin:

id_change = DT[, c(TRUE, id[-1] != id[-.N])] 
DT[, lapply(.SD, function(x) x[cummax(((!is.na(x)) | id_change) * .I)])] 
# id aa bb cc 
# 1: 1 A NA 1 
# 2: 1 A NA 1 
# 3: 1 B NA 1 
# 4: 1 C NA 1 
# 5: 2 NA NA NA 
# 6: 2 NA NA 4 
# 7: 2 D NA 4 
# 8: 2 E NA 5 
# 9: 3 F NA 6 
#10: 3 F NA 6 
#11: 3 F NA 7 
#12: 3 F NA 7 
+5

downvote benim için çok açık değil, ve bazı açıklamalar takdir olurdu – eddi

+1

Büyük cevap imo - bu sadece normal 'na.locf' çok daha hızlı bir versiyonu değil, aynı zamanda grup başına bunu yapmak için bir değişiklik ekler (sıralı grupları varsayarak), ** ** aslında bir 'by' döngüsünü yapmadan (grup başına ekstra 'eval''in getireceği ve bunu yavaşlatacağı). Bir şey özlemediğim sürece - bu 'hayvanat bahçesi' nin yaptığı 'rle' şey yerine standart 'na.locf' uygulaması olmalıdır. – eddi

+0

@eddi: düzenlemeler için teşekkürler. Sanırım zoo :: na.locf' daha esnektir, ancak basit durumlarda, 'cummax' sürümünün 4-5 * uzunluk (x)' taramalarının oldukça basit olacağına inanıyorum. Ve ayrıca, her bir sütun işaretçisini işlevde bir kez geçirmenin ve hemen hemen "by" grubunun uygulanmasının uygun olduğu kanıtlandı. –

İlgili konular