按照列中指定的次数重复 data.frame 的每一行

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
115465 次浏览

这里有一个解决方案:

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

@Neilfws的解决方案非常适合data.frame,但不适合data.table,因为它们缺少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"]

使用splitstackshape程序包中的expandRows()

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

如果您必须在非常大的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

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

另一种可能性是使用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创建

另一种dplyr,其具有slice,其中我们将每个行号__ABC__重复2次

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)

我知道情况并非如此,但如果您需要保留原始FREQ列,则可以将另一种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

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

事实上。使用向量和索引的方法。我们也可以达到同样的结果,并且更容易理解:

rawdata <- data.frame('time' = 1:3,
'x1' = 4:6,
'x2' = 7:9,
'x3' = 10:12)


rawdata[rep(1, time=2), ] %>% remove_rownames()
#  time x1 x2 x3
# 1    1  4  7 10
# 2    1  4  7 10




我提供了一个更多的补充,这个美妙的线索的好答案!将tidyr包(包含在tidyverse中)用于单行解决方案:

df %>% tidyr::uncount(weights = freq)