2017-02-20 17 views
1

Rで事前に指定された回数だけ行を繰り返すことに関する質問がありますが、特定の質問に対処するための質問はありません。頼むよ。データフレーム内の別の値に基づいてデータフレーム内の行のブロックを繰り返す

私は、各回答者が5から10の質問の間のどこかで回答するアンケートから回答のデータフレームを持っています。おもちゃの例として:

df <- data.frame(ID = rep(1:2, each = 5), 
      Response = sample(LETTERS[1:4], 10, replace = TRUE), 
      Weight = rep(c(2,3), each = 5)) 

> df 
    ID Response Weight 
1 1  D  2 
2 1  C  2 
3 1  D  2 
4 1  D  2 
5 1  B  2 
6 2  D  3 
7 2  C  3 
8 2  B  3 
9 2  D  3 
10 2  B  3 

は、私がブロックとしてブロック、その後、被告2の答えとして、二回3回、を回答者1の答えを繰り返したい、と私は応答の各ブロックにしたいです一意のIDを持つ言い換えれば、私は最終的な結果は次のようになりたい:

 ID Response Weight 
1 11  D  2 
2 11  C  2 
3 11  D  2 
4 11  D  2 
5 11  B  2 
6 12  D  2 
7 12  C  2 
8 12  D  2 
9 12  D  2 
10 12  B  2 
11 21  D  3 
12 21  C  3 
13 21  B  3 
14 21  D  3 
15 21  B  3 
16 22  D  3 
17 22  C  3 
18 22  B  3 
19 22  D  3 
20 22  B  3 
21 23  D  3 
22 23  C  3 
23 23  B  3 
24 23  D  3 
25 23  B  3 

私はこれをやっている方法は、現在本当に不格好で、そして、私は私のデータセットで> 3000件の回答を持っていることを考えると、耐えられないほどですスロー。ここで

は私のコードです:

df.expanded <- NULL 
for(i in unique(df$ID)) { 
    x <- df[df$ID == i,] 
    y <- x[rep(seq_len(nrow(x)), x$Weight),1:3] 
    y$order <- rep(1:max(x$Weight), nrow(x)) 
    y <- y[with(y, order(order)),] 
    y$IDNew <- rep(max(y$ID)*100 + 1:max(x$Weight), each = nrow(x)) 
    df.expanded <- rbind(df.expanded, y) 
} 

これを行うにはより高速な方法はありますか?

+1

なぜこのようなタスクを実行したいのですか? – DJJ

+0

私は応答の潜在的な条件付きロジット分析に取り組んでいます(実際のデータセットでは、上記のような文字ではなく1/0です)。私が実際に分析を行っているStataでは、lclogitは重みを受け入れないので、私は逆確率の重みに戻っています。 – TheChainsOfMarkov

+1

'ID 1 'を2回繰り返します:' df [df $ ID == 1] [rep(seq_len(nrow(df $ ID == 1)])、2)、' –

答えて

1

簡単な解決策があります。あなたのコードに示されているように、Weightに基づいて行を複製するとします。

df2 <- df[rep(seq_along(df$Weight), df$Weight), ] 
df2$ID <- paste(df2$ID, unlist(lapply(df$Weight, seq_len)), sep = '') 

# sort the rows 
df2 <- df2[order(df2$ID), ] 

この方法は高速ですか?見てみましょう:

library(microbenchmark) 

microbenchmark(
    m1 = { 
     df.expanded <- NULL 
     for(i in unique(df$ID)) { 
      x <- df[df$ID == i,] 
      y <- x[rep(seq_len(nrow(x)), x$Weight),1:3] 
      y$order <- rep(1:max(x$Weight), nrow(x)) 
      y <- y[with(y, order(order)),] 
      y$IDNew <- rep(max(y$ID)*100 + 1:max(x$Weight), each = nrow(x)) 
      df.expanded <- rbind(df.expanded, y) 
     } 
    }, 
    m2 = { 
     df2 <- df[rep(seq_along(df$Weight), df$Weight), ] 
     df2$ID <- paste(df2$ID, unlist(lapply(df$Weight, seq_len)), sep = '') 

     # sort the rows 
     df2 <- df2[order(df2$ID), ] 
    } 
) 

# Unit: microseconds 
# expr  min  lq  mean median  uq  max neval 
# m1 806.295 862.460 1101.6672 921.0690 1283.387 2588.730 100 
# m2 171.731 194.199 245.7246 214.3725 283.145 506.184 100 

他のより効率的な方法があるかもしれません。

+0

うわー。これはずっと速かった。ありがとうございました! – TheChainsOfMarkov

1

もう1つの方法は、data.tableを使用することです。

してみてください、あなたはdata.tableとして「DT」で始まるしていると仮定:

library(data.table) 
DT[, list(.id = rep(seq(Weight[1]), each = .N), Weight, Response), .(ID)] 

私は一緒にID列を貼り付けたが、その代わりに、二列を作成していません。それは私に少し柔軟に思えます。


テスト用データ。 nを変更して、再生する大きなデータセットを作成します。

set.seed(1) 
n <- 5 
weights <- sample(3:15, n, TRUE) 
df <- data.frame(ID = rep(seq_along(weights), weights), 
       Response = sample(LETTERS[1:5], sum(weights), TRUE), 
       Weight = rep(weights, weights)) 
DT <- as.data.table(df) 
関連する問題