重复data.frame的每一行,在列中指定的次数


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

Answers:


168

这是一种解决方案:

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 2010年

22
对于data.frame更大的效率,row.names(df)seq.int(1,nrow(df))或代替seq_len(nrow(df))
力克(Marek)2010年

这对于大数据帧来说效果非常好-150万行,5个列非常快。谢谢!
gabe 2012年

4
1:2硬编码此示例的解决方案,1:ncol(df)将适用于任意数据帧。
vladiim

70

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

1
感谢您提供的整洁解决方案。这样的解决方案通常满足“简单”和可读的标准。
D. Woods,

44

expandRows()splitstackshape包装中使用:

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

简单语法,非常快,可以在data.frame或上使用data.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:谢谢您的评论。奇怪,我只是再次尝试了,没有得到这样的错误。您是否使用dfOP的原件?我的答案更好,因为另一个答案是data.table使用data.frame语法来滥用包,请参阅FAQ 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.frames。其他三个答案与此较大的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

reprex软件包(v0.2.1)创建于2019-05-21


1

我知道不是这种情况,但是如果您需要保留原始的freq列,则可以tidyverserep以下方法一起使用:

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

reprex软件包(v0.3.0)创建于2019-12-21


或者只是.remove = FALSEuncount()
亚当
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.