2017-02-20 16 views
1

Здесь возникает ряд вопросов о повторении строк в заданном числе раз в R, но я не могу найти его для ответа на конкретный вопрос I - спросил он.Повторение блоков строк в кадре данных на основе другого значения в фрейме данных

У меня есть блок данных ответов от опроса, в котором каждый ответчик отвечает где-то между 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 

Я хотел бы повторить ответы респондента 1 дважды, как блок, а затем Респондент 2 в ответах 3 раза, как блок, и я хочу каждый блок ответов на имеют уникальный идентификатор. Другими словами, я хочу, чтобы конечный результат, чтобы выглядеть следующим образом:

 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' два раза:' df [df $ ID == 1,] [rep (seq_len (nrow (df [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

Wow. Это было намного быстрее. Спасибо! – TheChainsOfMarkov

1

Другим подходом было бы использовать data.table.

Предполагая, что вы начинаете с «DT» в качестве data.table, попробуйте:

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) 

 Смежные вопросы

  • Нет связанных вопросов^_^