Реплицируйте каждую строку data.frame и укажите количество реплик для каждой строки

df <- data.frame(var1=c('a', 'b', 'c'), var2=c('d', 'e', 'f'), freq=1:3)

Каков самый простой способ развернуть первые два столбца файла data.frame выше, так что в каждой строке отображается количество раз, указанное в столбце "freq"?

Другими словами, идите отсюда:

df
  var1 var2 freq
1    a    d    1
2    b    e    2
3    c    f    3

Для этого:

df.expanded
  var1 var2
1    a    d
2    b    e
3    b    e
4    c    f
5    c    f
6    c    f

Ответ 1

Здесь одно решение:

df.expanded <- df[rep(row.names(df), df$freq), 1:2]

Результат:

    var1 var2
1      a    d
2      b    e
2.1    b    e
3      c    f
3.1    c    f
3.2    c    f

Ответ 2

Используйте expandRows() из пакета splitstackshape:

library(splitstackshape)
expandRows(df, "freq")

Простой синтаксис, очень быстрый, работает на data.frame или data.table.

Результат:

    var1 var2
1      a    d
2      b    e
2.1    b    e
3      c    f
3.1    c    f
3.2    c    f

Ответ 3

старый вопрос, новый глагол в tidyverse:

library(tidyr) # version >= 0.8.0
df <- data.frame(var1=c('a', 'b', 'c'), var2=c('d', 'e', 'f'), freq=1:3)
df %>% 
  uncount(freq)

    var1 var2
1      a    d
2      b    e
2.1    b    e
3      c    f
3.1    c    f
3.2    c    f

Ответ 4

Решение @neilfws отлично работает для data.frame, но не для data.table так как им не row.names свойства row.names. Этот подход работает для обоих:

df.expanded <- df[rep(seq(nrow(df)), df$freq), 1:2]

Код для data.table - это data.table средство:

# convert to data.table by reference
setDT(df)
df.expanded <- df[rep(seq(.N), freq), !"freq"]

Ответ 5

Если вам нужно выполнить эту операцию на очень больших data.frames, я бы рекомендовал преобразовать ее в таблицу data.table и использовать следующее, которое должно выполняться намного быстрее:

library(data.table)
dt <- data.table(df)
dt.expanded <- dt[ ,list(freq=rep(1,freq)),by=c("var1","var2")]
dt.expanded[ ,freq := NULL]
dt.expanded

Посмотрите, насколько быстрее это решение:

df <- data.frame(var1=1:2e3, var2=1:2e3, freq=1:2e3)
system.time(df.exp <- df[rep(row.names(df), df$freq), 1:2])
##    user  system elapsed 
##    4.57    0.00    4.56
dt <- data.table(df)
system.time(dt.expanded <- dt[ ,list(freq=rep(1,freq)),by=c("var1","var2")])
##    user  system elapsed 
##    0.05    0.01    0.06

Ответ 6

Другая возможность - использовать tidyr::expand:

library(dplyr)
library(tidyr)

df %>% group_by_at(vars(-freq)) %>% expand(temp = 1:freq) %>% select(-temp)
#> # A tibble: 6 x 2
#> # Groups:   var1, var2 [3]
#>   var1  var2 
#>   <fct> <fct>
#> 1 a     d    
#> 2 b     e    
#> 3 b     e    
#> 4 c     f    
#> 5 c     f    
#> 6 c     f

Однолинейная версия ответа vonjd:

library(data.table)

setDT(df)[ ,list(freq=rep(1,freq)),by=c("var1","var2")][ ,freq := NULL][]
#>    var1 var2
#> 1:    a    d
#> 2:    b    e
#> 3:    b    e
#> 4:    c    f
#> 5:    c    f
#> 6:    c    f

Создано 2019-05-21 пакетом представлением (v0.2.1)

Ответ 7

Другой dplyr альтернатива с slice где мы повторяем каждый номер строки freq раз

library(dplyr)

df %>%  
  slice(rep(seq_len(n()), freq)) %>% 
  select(-freq)

#  var1 var2
#1    a    d
#2    b    e
#3    b    e
#4    c    f
#5    c    f
#6    c    f

seq_len(n()) может быть заменена любой из следующих.

df %>% slice(rep(1:nrow(df), freq)) %>% select(-freq)
#Or
df %>% slice(rep(row_number(), freq)) %>% select(-freq)
#Or
df %>% slice(rep(seq_len(nrow(.)), freq)) %>% select(-freq)