Her bir veri satırını yineleyin. Bir sütunda belirtilen sayıda çerçeveye alın


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

Yukarıdaki data.frame'in ilk iki sütununu her satırı genişletmenin en basit yolu nedir, böylece her satır 'freq' sütununda belirtilen sayıda tekrarlanır?

Başka bir deyişle, şundan gidin:

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

Buna:

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

Yanıtlar:


175

İşte bir çözüm:

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

Sonuç:

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

Harika! Köşeli parantezleri bu şekilde kullanabileceğinizi her zaman unutuyorum. Yalnızca alt kümeleme veya yeniden sıralama için indekslemeyi düşünmeye devam ediyorum. Çok daha az zarif ve hiç şüphesiz daha az verimli olan başka bir çözümüm vardı. Başkalarının karşılaştırabilmesi için yine de gönderebilirim.
wkmor1

24
Daha büyük için data.framedaha verimli veya row.names(df)ile değiştirmektir . seq.int(1,nrow(df))seq_len(nrow(df))
Marek

Bu, büyük bir veri çerçevesi için harika bir şekilde çalıştı - 1,5 milyon satır, 5 sütun, çok hızlı gitti. Teşekkürler!
gabe

4
Bu örneğin çözümünü 1: 2 sabit kodlar; 1: ncol (df), rastgele bir veri çerçevesi için çalışacaktır.
vladiim

76

eski soru, tidyverse'te yeni fiil:

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

2
Düzenli bir çözüm için teşekkürler. Bu tür çözümler tipik olarak "basit" ve okunabilirlik kriterlerini karşılar.
D. Woods

45

Kullanım expandRows()gelen splitstackshapepaketin:

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

Basit sözdizimi, çok hızlı, data.frameveya üzerinde çalışır data.table.

Sonuç:

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

23

@ neilfws'in çözümü data.frames için harika çalışıyor , ancak özelliği olmadığı için data.tables için değil row.names. Bu yaklaşım her ikisi için de işe yarar:

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

Kod data.tablebiraz daha temiz:

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

5
başka bir alternatif:df[rep(seq(.N), freq)][, freq := NULL]
Jaap

başka bir alternatifdf[rep(1:.N, freq)][, freq:=NULL]
Dale Kube

4

Her satır numarasını defalarca tekrarladığımız başka bir dplyralternatifslicefreq

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()) parça aşağıdakilerden herhangi biri ile değiştirilebilir.

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)

3

Bu işlemi çok büyük data.frames üzerinde yapmanız gerekiyorsa, bunu bir data.table'a dönüştürmenizi ve aşağıdakileri kullanmanızı tavsiye ederim, bu çok daha hızlı çalışması gerekir:

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

Bu çözümün ne kadar hızlı olduğunu görün:

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

Bir hata alıyorum: Error in rep(1, freq) : invalid 'times' argument. Ve bu soruya zaten data.table bir cevap olduğu göz önüne alındığında, yaklaşımınızın nasıl farklı olduğunu veya mevcut data.table cevaptan daha iyi olduğunu açıklamak isteyebilirsiniz. Veya büyük bir fark yoksa, bunu mevcut cevaba yorum olarak ekleyebilirsiniz.
Sam Firke

@SamFirke: Yorumunuz için teşekkürler. Garip, tekrar denedim ve böyle bir hata almadım. dfOP'nin sorusundaki orijinali kullanıyor musunuz ? Cevabım daha iyi çünkü diğer cevap, sözdizimi data.tablekullanarak paketi kötüye kullanmaktır, data.frameşu SSS'ye bakın data.table: "Sütunlara addan ziyade numaraya göre atıfta bulunmak genellikle kötü bir uygulamadır."
vonjd

1
Açıklama için teşekkürler. Kodunuz dfOP tarafından yayınlanan örnekte benim için çalışıyor , ancak bunu daha büyük bir data.frame üzerinde karşılaştırmaya çalıştığımda bu hatayı aldım. Kullandığım data.frame şuydu: set.seed(1) dfbig <- data.frame(var1=sample(letters, 1000, replace = TRUE), var2=sample(LETTERS, 1000, replace = TRUE), freq=sample(1:10, 1000, replace = TRUE)) Minik data.frame'de, temel cevap kıyaslamamda iyi işliyor, sadece daha büyük data.frame'lere ölçeklenmiyor. Diğer üç cevap, bu daha büyük data.frame ile başarılı bir şekilde çalıştı.
Sam Firke

@SamFirke: Bu gerçekten tuhaf, orada da çalışmalı ve neden çalışmadığını bilmiyorum. Bundan bir soru mu yaratmak istiyorsunuz yoksa ben mi yapayım?
vonjd

İyi bir fikir. Yapabilir misin? data.tableSözdizimini bilmiyorum, bu yüzden cevapları yargılayan ben olmamalıyım.
Sam Firke

3

Başka bir olasılık kullanmaktır 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'nin cevabının tek satırlık versiyonu :

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

Reprex paketi (v0.2.1) tarafından 2019-05-21 tarihinde oluşturuldu


3

Durumun böyle olmadığını biliyorum, ancak orijinal frekans sütununu tutmanız gerekiyorsa, aşağıdakilerle tidyversebirlikte başka bir yaklaşım kullanabilirsiniz rep:

library(purrr)

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

df %>% 
  map_df(., rep, .$freq)
#> # A tibble: 6 x 3
#>   var1  var2   freq
#>   <fct> <fct> <int>
#> 1 a     d         1
#> 2 b     e         2
#> 3 b     e         2
#> 4 c     f         3
#> 5 c     f         3
#> 6 c     f         3

2019-12-21 tarihinde reprex paketi tarafından oluşturuldu (v0.3.0)


1
Ya da sadece kullanmak .remove = FALSEiçindeuncount()
Adam
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.