2017-02-20 3 views
1

Es gibt eine Reihe von Fragen über das Wiederholen von Zeilen mit einer vorgegebenen Anzahl von Malen in R, aber ich kann keine finden, um die spezifische Frage I anzusprechen frag ich.Wiederholte Blöcke von Zeilen in einem Datenrahmen basierend auf einem anderen Wert im Datenrahmen

Ich habe einen Datenrahmen von Antworten aus einer Umfrage, in der jeder Befragte irgendwo zwischen 5 und 10 Fragen beantwortet. Als Spielzeug Beispiel:

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 

würde Ich mag Antworten Befragte 1 der zweimal wiederholen, als Block und dann Antworten des Befragten 2 3 mal, als Block, und ich möchte jeden Block von Antworten auf habe eine eindeutige ID. Mit anderen Worten, ich möchte das Endergebnis wie folgt aussehen:

 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 

So wie ich das tue ist derzeit wirklich klobig, und gegeben, dass ich> 3000 Befragten in meinem Datensatz ist unerträglich langsam.

Hier ist mein Code:

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

Gibt es einen schnelleren Weg, dies zu tun?

+1

Darf ich fragen, warum Sie so eine Aufgabe ausführen möchten? – DJJ

+0

Sicher. Ich arbeite an einer bedingten Logit-Analyse der Antworten der latenten Klasse (die im realen Datensatz 1/0 sind, keine Buchstaben wie oben). In Stata, wo ich gerade die Analyse mache, akzeptiert "lclogit" keine Gewichte, also unterstütze ich die inversen Wahrscheinlichkeitsgewichte, die ich habe. – TheChainsOfMarkov

+1

Wiederhole 'ID 1' zweimal: 'df [df $ ID == 1,] [rep (seq_len (nwer (df [df $ ID == 1,]))), 2),]' –

Antwort

1

Es gibt eine einfachere Lösung. Ich nehme an, dass Sie Zeilen basierend auf Weight duplizieren möchten, wie in Ihrem Code gezeigt.

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

Ist diese Methode schneller? Mal sehen:

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 

Es könnte andere effizientere Möglichkeiten geben.

+0

Wow. Das war _so_ viel schneller. Vielen Dank! – TheChainsOfMarkov

1

Ein anderer Ansatz wäre, data.table zu verwenden.

Sie beginnen mit „DT“ als data.table Unter der Annahme, versuchen:

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

Ich habe die ID-Spalten zusammen nicht geklebt, sondern stattdessen eine sekundäre Spalte erstellt. Das scheint mir etwas flexibler zu sein.


Daten zum Testen. Ändern Sie n, um einen größeren Datensatz zum Spielen zu erstellen.

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