Lặp lại mỗi hàng dữ liệu. Tạo số lần được chỉ định trong một cột


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

Cách đơn giản nhất để mở rộng mỗi hàng hai cột đầu tiên của data.frame ở trên, sao cho mỗi hàng được lặp lại số lần được chỉ định trong cột 'freq'?

Nói cách khác, đi từ đây:

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

Về điều này:

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

Câu trả lời:


169

Đây là một giải pháp:

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

Kết quả:

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

Tuyệt quá! Tôi luôn quên bạn có thể sử dụng dấu ngoặc vuông theo cách đó. Tôi tiếp tục nghĩ về việc lập chỉ mục cho việc đặt lại hoặc sắp xếp lại. Tôi đã có một giải pháp khác ít thanh lịch hơn và không nghi ngờ gì là kém hiệu quả. Tôi có thể đăng bài để mọi người có thể so sánh.
wkmor1

22
Đối với data.framehiệu quả lớn hơn là thay thế row.names(df)bằng seq.int(1,nrow(df))hoặc seq_len(nrow(df)).
Marek

Điều này đã làm việc tuyệt vời cho một khung dữ liệu lớn - 1,5 triệu hàng, 5 cols, đã đi rất nhanh. Cảm ơn!
gabe

4
Mã cứng 1: 2 giải pháp cho ví dụ này, 1: ncol (df) sẽ hoạt động cho một khung dữ liệu tùy ý.
vladiim

71

câu hỏi cũ, động từ mới trong 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
Cảm ơn cho một giải pháp gọn gàng. Các giải pháp như vậy thường đáp ứng các tiêu chí "đơn giản" và dễ đọc.
D. Rừng

45

Sử dụng expandRows()từ splitstackshapegói:

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

Cú pháp đơn giản, rất nhanh, hoạt động trên data.framehoặc data.table.

Kết quả:

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

23

Giải pháp của @ neilfws hoạt động tuyệt vời cho data.frames, nhưng không phải cho data.tables vì họ thiếu row.namestài sản. Cách tiếp cận này hiệu quả cho cả hai:

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

Mã cho data.tablelà một trình dọn dẹp tad:

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

4
một cách khác:df[rep(seq(.N), freq)][, freq := NULL]
Jaap

một lựa chọn khácdf[rep(1:.N, freq)][, freq:=NULL]
Dale Kube

4

Trong trường hợp bạn phải thực hiện thao tác này trên data.frames rất lớn, tôi khuyên bạn nên chuyển đổi nó thành data.table và sử dụng các thao tác sau, để chạy nhanh hơn nhiều:

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

Xem giải pháp này nhanh hơn bao nhiêu:

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

Tôi gặp lỗi : Error in rep(1, freq) : invalid 'times' argument. Và cho rằng đã có một câu trả lời data.table cho câu hỏi này, bạn có thể muốn mô tả cách tiếp cận của bạn khác nhau hoặc khi nó tốt hơn câu trả lời data.table hiện tại. Hoặc nếu không có sự khác biệt lớn, bạn có thể thêm nó dưới dạng một nhận xét cho câu trả lời hiện có.
Sam Firke 7/07/2015

@SamFirke: Cảm ơn bạn đã bình luận. Thật kỳ lạ, tôi vừa thử lại và tôi không gặp lỗi như vậy. Bạn có sử dụng bản gốc dftừ câu hỏi của OP không? Câu trả lời của tôi tốt hơn bởi vì câu trả lời khác là sử dụng sai data.tablegói bằng cách sử dụng data.framecú pháp, xem Câu hỏi thường gặp về data.table: "Nói chung là thực tế xấu khi tham chiếu các cột theo số thay vì tên."
vonjd 7/07/2015

1
Cảm ơn đã giải thích. Mã của bạn hoạt động với tôi trên mẫu dfđược đăng bởi OP, nhưng khi tôi cố gắng điểm chuẩn này trên dữ liệu lớn hơn. Tôi đã gặp lỗi đó. Data.frame tôi đã sử dụng là: 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)) Trên data.frame nhỏ, câu trả lời cơ bản thực hiện tốt trong điểm chuẩn của tôi, nó chỉ không mở rộng tốt cho data.frames lớn hơn. Ba câu trả lời khác đã chạy thành công với data.frame lớn hơn này.
Sam Firke 7/07/2015

@SamFirke: Điều này thực sự kỳ lạ, nó cũng hoạt động ở đó và tôi không biết tại sao nó lại không. Bạn có muốn tạo một câu hỏi từ nó không?
vonjd 7/07/2015

Ý tưởng tốt. Bạn có thể? Tôi không biết data.tablecú pháp vì vậy tôi không nên là người đánh giá câu trả lời.
Sam Firke 7/07/2015

4

Một dplyrcách khác với slicenơi chúng tôi lặp lại mỗi freqlần số hàng

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()) một phần có thể được thay thế bằng bất kỳ điều sau đây.

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

Một khả năng khác là sử dụng 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

Phiên bản một lót của câu trả lời của 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

Được tạo vào ngày 2019-05-21 bởi gói reprex (v0.2.1)


1

Tôi biết đây không phải là trường hợp nhưng nếu bạn cần giữ cột freq ban đầu, bạn có thể sử dụng một tidyversecách tiếp cận khác cùng với 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

Được tạo vào ngày 2019-12-21 bởi gói reprex (v0.3.0)


Hoặc chỉ sử dụng .remove = FALSEtronguncount()
Adam
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.