2016-05-05 7 views
8

人IDでキーが設定されていますが、多くのヌル値を持つカラムがたくさんあります(〜150)。各列は記録された状態/属性で、私は各人物のために持ち越したいと思っています。各人には10〜10,000人の観察があり、セットには約50万人がいます。 1人の人からの価値は、次の人には「出血」することができないので、私の解決策は人物IDの列とグループを適切に尊重しなければなりません。デモンストレーションの目的のためには、1つのRデータテーブル内のグループによって効率的にロケートされます。

- ここでは非常に小さなサンプル入力です:

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

それは次のようになります。

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 

私の予想出力は次のようになります。

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 

私は」私の大規模なデータセットでは非常に遅いです:012 data.table解決策を見つけました。

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

dplyrを使用して同等の解決策を見つけましたが、これは同じように遅いです。

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

私はローリング「自己」data.table機能を使用して参加を考え出すことができることを期待しましたが、私はちょうど(私はちょうど私が.Nを使用する必要があります容疑者が、私右のそれを得るように見えることはできませんそれを理解していない)。

ここでは、グループ化されたlocfを効率的に適用するためにRcppに何かを書く必要があると考えています。

私はRの新人ですが、私はC++には新しくないので、私はそれができると確信しています。 data.tableを使ってRでこれを行う効率的な方法があるはずです。

+0

私は 'DTかなり確信している[、lapply(.SD、na.locf、F)は、= idで]'私は実際にその使用を開始 – eddi

+0

速くなりますパフォーマンスが悪いことが判明しました。 –

+0

ローリング・セルフ・ジョインがここにあるように見えますが、「na.locf」とローリング・ジョインの両方の回答を覚えていますので、現在の知識ベースで答えが見つかるかもしれません。 – jangorecki

答えて

14

非常に単純なna.locfが非NA指数((!is.na(x)) * seq_along(x)を)(cummax)を転送することによって構築され、それに応じてサブセット化することができます。

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 

これは、我々は単に必要na.rm = FALSE動作を取得するために、na.rm = TRUE引数でna.locfを複製確認するために、cummaxの最初の要素はTRUEです:

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 

組み合わせる:この場合、は、私たちは、「ID」列に値が変化したアカウントにだけでなく、非NAインデックスを取ることはなく、また、指標の(注文、または発注する)必要があります上記:

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 

注、ここで我々はTRUEとの最初の要素、すなわちORというようna.rm = FALSE行動を取得、TRUEにそれを等しくします。

そして、この例のために:

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は私にはあまり明白ではないし、いくつかの説明は理解されるだろう – eddi

+1

すばらしい答えimo - これは普通の 'na.locf'のはるかに速いバージョンであるだけでなく、 (ソートされたグループを仮定して)、**実際に 'by'ループを実行しないで**(グループごとに追加の' eval'を導入し、それを遅くする)。何かが欠けていない限り、これは 'zoo'が行う' rle'の代わりに、標準の 'na.locf'実装でなければなりません。 – eddi

+0

@eddi:編集していただきありがとうございます。 'zoo :: na.locf'はより柔軟ですが、私は単純なケースでは、' cummax'バージョンの '4-5 * length(x)'スキャンはかなり簡単だと信じています。また、関数内で各カラムポインタを一度渡して、実質的に「by」グループに適用すると便利であることが確かに証明されました。 –

関連する問題