2010-05-24 5 views
76
df <- data.frame(var1=c('a', 'b', 'c'), var2=c('d', 'e', 'f'), freq=1:3) 

Quelle est la façon la plus simple d'étendre les deux premières colonnes du data.frame ci-dessus, de sorte que chaque ligne apparaît le nombre de fois spécifié dans la colonne 'freq'?Répliquer chaque rangée de data.frame et spécifier le nombre de réplications pour chaque ligne

En d'autres termes, passer de ceci:

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

à ceci:

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

Répondre

106

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 
+0

Great! J'oublie toujours que vous pouvez utiliser des crochets de cette façon. Je continue à penser à l'indexation juste pour les sous-ensembles ou les réordonnances. J'avais une autre solution beaucoup moins élégante et sans doute moins efficace. Je pourrais poster de toute façon afin que les autres puissent comparer. – wkmor1

+14

Pour un grand 'data.frame', il est plus efficace de remplacer' row.names (df) 'par' seq.int (1, nrow (df)) 'ou' seq_len (nrow (df)) '. – Marek

+0

Cela a fonctionné de manière fantastique pour un grand cadre de données - 1,5 million de lignes, 5 cols, est allé très vite. Merci! – gabe

13

@ solution de neilfws fonctionne très bien pour data.frame s, mais pas pour data.table s car ils ne disposent pas de la propriété row.names. Cette approche fonctionne pour les deux:

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

Pour data.table si vous aurez besoin d'ajouter with=F et peut éliminer éventuellement df$:

dt <- data.table(df) 
dt.expanded <- dt[rep(seq(.N), freq), !"freq", with=F] 
+6

'.N' est maintenant accessible dans le premier argument de' dt [] ', donc vous pouvez faire' dt [rep (seq (.N), freq),! "Freq", avec = FALSE] 'ou similaire. – Frank

+0

Merci, mis à jour. –

+1

une autre alternative: 'df [rep (seq (.N), freq)] [, freq: = NULL]' – Jaap

33

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 
2

Dans le cas où vous devez faire cette opération sur de très grandes data.frames je recommande la transformer en un data.table et utiliser ce qui suit, ce qui devrait beaucoup plus vite:

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 plus vite cette solution est:

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 
+0

Je reçois une erreur: 'Erreur dans rep (1, freq): argument 'times' invalide'. Et étant donné qu'il y a déjà une donnée.réponse de table à cette question, vous pouvez vouloir décrire comment votre approche est différente ou quand elle est meilleure que la réponse actuelle de data.table. Ou s'il n'y a pas de différence majeure, vous pouvez l'ajouter comme commentaire à la réponse existante à la place. –

+0

@SamFirke: Merci pour votre commentaire. Étrange, je l'ai juste essayé encore et je ne reçois pas une telle erreur. Utilisez-vous l'original 'df' de la question de l'OP? Ma réponse est meilleure parce que l'autre réponse est un peu abusive du paquet 'data.table' en utilisant la syntaxe' data.frame', voir la FAQ de 'data.table':" Il est généralement de mauvaise pratique de se référer aux colonnes par numéro plutôt que de nommer. " – vonjd

+1

Merci pour l'explication. Votre code fonctionne pour moi sur l'échantillon 'df 'posté par l'OP, mais quand j'ai essayé de comparer cela sur un plus grand data.frame j'ai eu cette erreur. Le fichier data.frame que j'ai utilisé était: 'set.seed (1) dfbig <- data.frame (var1 = échantillon (letters, 1000, replace = TRUE), var2 = sample (LETTRES, 1000, replace = TRUE), freq = sample (1:10, 1000, replace = TRUE)) 'Sur le minuscule data.frame, la réponse de base fonctionne bien dans mon benchmarking, elle ne s'adapte pas bien aux plus gros data.frames. Les trois autres réponses ont fonctionné avec succès avec ce data.frame plus grand. –

4

vieille question, nouveau verbe 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) 

e

Questions connexes