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
#1 楼
这是一种解决方案: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
#2 楼
旧问题,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
评论
感谢您提供的整洁解决方案。这样的解决方案通常满足“简单”和可读的标准。
– D.伍兹
19年2月18日在15:24
#3 楼
使用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
#4 楼
@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"]
评论
另一种选择:df [rep(seq(.N),freq)] [,freq:= NULL]
– Jaap
17年4月10日在6:04
另一个替代方法df [rep(1:.N,freq)] [,freq:= NULL]
–戴尔·库伯(Dale Kube)
19年8月11日在12:46
#5 楼
如果您必须在非常大的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
评论
我收到一个错误:rep(1,freq)中的错误:无效的'times'参数。并且鉴于此问题已经有一个data.table答案,您可能需要描述您的方法与当前data.table答案有何不同或何时更好。或者,如果没有重大区别,则可以将其作为注释添加到现有答案中。
– Sam Firke
15年7月7日在16:20
@SamFirke:谢谢您的评论。奇怪,我只是再次尝试了,没有得到这样的错误。您是否使用OP问题中的原始df?我的答案更好,因为另一个答案是某种通过使用data.frame语法滥用data.table包的方法,请参阅data.table的FAQ:“通常不建议使用数字而不是名称来引用列。”
– vonjd
2015年7月7日在16:37
感谢您的解释。您的代码在OP发布的示例df上对我有用,但是当我尝试在更大的data.frame上进行基准测试时,出现了此错误。我使用的data.frame是:set.seed(1)dfbig <-data.frame(var1 = sample(字母,1000,replace = TRUE),var2 = sample(LETTERS,1000,replace = TRUE),freq = sample (1:10,1000,replace = TRUE))在微小的data.frame上,基本答案在我的基准测试中效果很好,只是无法很好地扩展到更大的data.frames。其他三个答案与此较大的data.frame一起成功运行。
– Sam Firke
15年7月7日在17:06
@SamFirke:这确实很奇怪,它也应该在那里工作,我不知道为什么不这样做。您是否要从中提出问题?
– vonjd
15年7月7日在17:18
好主意。你能?我不知道data.table的语法,所以我不应该成为判断答案的人。
– Sam Firke
15年7月7日在17:22
#6 楼
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)
#7 楼
另一种可能是使用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
一个-liner版本的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创建
#8 楼
我知道不是这种情况,但是如果您需要保留原始的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创建
评论
或者只是在uncount()中使用.remove = FALSE
–亚当
6月30日15:35
#9 楼
事实上。使用向量和索引的方法。我们也可以达到相同的结果,并且更易于理解: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
评论
大!我总是忘记您可以使用方括号。我一直在考虑只为子集或重新排序建立索引。我有另一个解决方案,它远不那么优雅,而且效率肯定也较低。我仍然可以发帖,以便其他人可以比较。
– wkmor1
2010年5月24日上午10:30
对于大型data.frame,更有效的方法是用seq.int(1,nrow(df))或seq_len(nrow(df))替换row.names(df)。
–马雷克
10 May 25 '11:54
这对于大数据帧来说效果非常好-150万行,5列很快速。谢谢!
– Gabe
2012年11月21日下午6:16
1:2硬编码用于此示例的解决方案,1:ncol(df)将适用于任意数据帧。
–vladiim
18年8月30日在6:28