web-dev-qa-db-fra.com

Répliquez chaque ligne de data.frame et spécifiez le nombre de réplications pour chaque ligne.

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

Quel est le moyen le plus simple de développer les deux premières colonnes du fichier data.frame ci-dessus, afin que chaque ligne apparaisse le nombre de fois spécifié dans la colonne 'freq'?

En d'autres termes, allez de ceci:

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

Pour ça:

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

Voici une solution:

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

Résultat:

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

Utilisez expandRows() du package splitstackshape:

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

La syntaxe simple, très rapide, fonctionne sur data.frame ou data.table.

Résultat:

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

vieille question, nouveau verbe dans 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
39
einar

La solution de @ neilfws fonctionne très bien pour data.frames, mais pas pour data.tables puisqu'ils n'ont pas le row.names propriété. Cette approche fonctionne à la fois pour:

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

Le code pour data.table est un nettoyant pour tad:

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

Si vous devez effectuer cette opération sur des data.frames très volumineuses, je vous recommande de la convertir en data.table et d'utiliser les éléments suivants, qui devraient s'exécuter beaucoup plus rapidement:

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

Voyez combien cette solution est rapide:

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
3
vonjd

Une autre alternative dplyr avec slice où nous répétons chaque numéro de ligne freq fois

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()) pièce peut être remplacée par l'une des options suivantes.

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)
2
Ronak Shah

Une autre possibilité consiste à utiliser 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

Version à une ligne de réponse de 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

Créé le 2019-05-21 par le package reprex (v0.2.1)

1
M--