ทำซ้ำแต่ละแถวของข้อมูลกำหนดจำนวนครั้งที่ระบุในคอลัมน์


150
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

คำตอบ:


169

นี่คือทางออกหนึ่ง:

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

ที่ดี! ฉันลืมเสมอว่าคุณสามารถใช้วงเล็บเหลี่ยมได้ ฉันคิดถึงการทำดัชนีสำหรับการย่อยหรือการเรียงลำดับใหม่ ฉันมีวิธีแก้ไขปัญหาอื่นที่ไม่ค่อยสง่างามและไม่ต้องสงสัยเลยว่ามีประสิทธิภาพน้อยลง ฉันอาจโพสต์ต่อไปเพื่อให้ผู้อื่นสามารถเปรียบเทียบได้
wkmor1

22
ขนาดใหญ่data.frameมีประสิทธิภาพมากขึ้นเพื่อแทนที่row.names(df)ด้วยหรือseq.int(1,nrow(df)) seq_len(nrow(df))
Marek

สิ่งนี้ได้ผลอย่างน่าอัศจรรย์สำหรับกรอบข้อมูลขนาดใหญ่ - 1.5 ล้านแถว, 5 คอลัมน์, รวดเร็วมาก ขอบคุณ!
gabe

4
รหัสฮาร์ด 1: 2 วิธีแก้ปัญหาสำหรับตัวอย่างนี้ 1: ncol (df) จะใช้ได้กับดาต้าเฟรมที่ไม่มีกฎเกณฑ์
vladiim

71

คำถามเก่า, คำกริยาใหม่ใน 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

2
ขอบคุณสำหรับวิธีการแก้ปัญหาเรียบร้อย โซลูชั่นดังกล่าวมักจะตรงตามเกณฑ์ของ "ง่าย" และอ่านได้
D. Woods

45

ใช้expandRows()จากsplitstackshapeแพ็คเกจ:

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

ไวยากรณ์ง่ายมากได้อย่างรวดเร็วทำงานในหรือdata.framedata.table

ผลลัพธ์:

    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 ใช้งานได้ดีสำหรับdata.frames แต่ไม่ใช่สำหรับdata.tables เนื่องจากไม่มีrow.namesคุณสมบัติ วิธีนี้ใช้ได้ผลกับทั้งคู่:

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

รหัสสำหรับdata.tableคือตัวทำความสะอาดแบบตาด:

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

4
ทางเลือกอื่น:df[rep(seq(.N), freq)][, freq := NULL]
Jaap

ทางเลือกอื่นdf[rep(1:.N, freq)][, freq:=NULL]
Dale Kube

4

ในกรณีที่คุณต้องทำการดำเนินการนี้กับ 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

ฉันได้รับข้อผิดพลาด: Error in rep(1, freq) : invalid 'times' argument. และเนื่องจากว่ามี data.table คำตอบสำหรับคำถามนี้คุณอาจต้องการอธิบายว่าวิธีการของคุณแตกต่างกันอย่างไรหรือเมื่อมันดีกว่า data.table ปัจจุบันคำตอบ หรือหากไม่มีความแตกต่างที่สำคัญคุณสามารถเพิ่มเป็นความคิดเห็นในคำตอบที่มีอยู่แทน
Sam Firke

@ SamFirke: ขอบคุณสำหรับความคิดเห็นของคุณ แปลกฉันเพิ่งลองอีกครั้งและฉันไม่ได้รับข้อผิดพลาดดังกล่าว คุณใช้ต้นฉบับไหมdfจากคำถามของ OP หรือไม่? คำตอบของฉันดีกว่าเพราะคำตอบอื่น ๆ คือการใช้data.tableแพคเกจในทางที่ผิดโดยใช้data.frameไวยากรณ์ดูคำถามที่พบบ่อยของdata.table: "โดยทั่วไปแล้วมันเป็นการปฏิบัติที่ไม่ดีในการอ้างถึงคอลัมน์ตามจำนวนแทนที่จะเป็นชื่อ"
vonjd

1
ขอบคุณสำหรับคำอธิบาย รหัสของคุณใช้ได้กับฉันในตัวอย่างที่dfโพสต์โดย OP แต่เมื่อฉันพยายามเปรียบเทียบกับ data.frame ที่ใหญ่ขึ้นฉันได้รับข้อผิดพลาดนั้น data.frame ที่ฉันใช้คือ: 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)) ใน data.frame ขนาดเล็กคำตอบพื้นฐานทำได้ดีในการเปรียบเทียบของฉัน อีกสามคำตอบก็ประสบความสำเร็จใน data.frame ที่ใหญ่กว่านี้
Sam Firke

@ SamFirke: นี่มันแปลกจริง ๆ มันควรทำงานที่นั่นด้วยและฉันก็ไม่รู้ว่าทำไมมันถึงไม่เป็นเช่นนั้น คุณต้องการที่จะสร้างคำถามจากมันหรือฉันจะ?
vonjd

ความคิดที่ดี. คุณสามารถ? ฉันไม่รู้data.tableไวยากรณ์ดังนั้นฉันจึงไม่ควรเป็นคนตัดสินคำตอบ
Sam Firke

4

อีก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)

2

ความเป็นไปได้อีกอย่างคือการใช้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 โดยแพ็คเกจ reprex (v0.2.1)


1

ฉันรู้ว่านี่ไม่ใช่กรณี แต่ถ้าคุณต้องการเก็บคอลัมน์ความถี่เดิมคุณสามารถใช้tidyverseวิธีอื่นร่วมกับ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 โดยแพ็คเกจ reprex (v0.3.0)


หรือใช้.remove = FALSEในuncount()
อาดัม
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.