【问题标题】:Subset a data.frame into a list in a more efficient way以更有效的方式将 data.frame 子集到列表中
【发布时间】:2023-03-14 19:21:01
【问题描述】:

我有一个 data.frame,其中 2 列代表 2 个基因之间的相互作用。 data.frame 的外观示例:

head(df)
V1       V2
A1BG     A1BG
A1BG    CRISP3
A1CF     A1CF
A1CF   APOBEC1
A1CF    CUGBP2
A1CF     KHSRP

我想根据第一列的值拆分 data.frame,我使用了以下命令:

out <- split(df, df$V1)

期望的输出应该是:

out
$A1BG
[1] A1BG CRISP3

$A1CF
[2] A1CF APOBEC1 CUGBP2 KHSRP

但是,使用拆分的过程需要很长时间,因为我的文件太大(大约 200,000 行)

非常感谢

【问题讨论】:

  • 确保您的列不是因素。加载您的文件或使用参数stringsAsFactors=FALSE 创建您的data.frame。然后执行:split(df$V2, df$V1)。它应该很快。我尝试了 1e5 data.frame,它在 0.07 秒内完成。
  • @Arun 不同之处在于您使用的是split.default,而split.data.frame 是针对OP 的数据调用的。在内部,split.default 必须创建一个因子,如果 f 不是一个因子,所以我看不出在提供非因子 f 时它如何更快。
  • 当我尝试这个字符时,这两种情况下的时间都比因素长:char &lt;- rep(c("a", "b"), each = 10000); fac &lt;- factor(char); library(rbenchmark); benchmark(split(char, char), split(fac, fac)); DF.char &lt;- data.frame(char, stringsAsFactors = FALSE); DF.fac &lt;- data.frame(fac); benchmark(split(DF.char, char), split(DF.fac, fac))
  • @JoshO'Brien 不,我认为这与 Arun 所建议的因素 f 无关。
  • @Arun 我明白你现在的意思了——完全不清楚你在原始评论中指的是要拆分的变量。您的后续评论使用了带有x 和y 的数据,但显示了带有df$V1 和df$V2 的使用代码,而没有说明x 和y 是哪个。我想我必须测试与你所做的相反的事情。因此,我完全同意论点x,如果是一个因素,不如非因素x 快。我以为我们在讨论争论f。

标签: r


【解决方案1】:

为了加快速度,特别是如果您只需要在 df$V1 的基础上拆分 df$V2,请在对 split 的调用中仅使用该向量,而不是整个数据框 df。例如:

## Dummy data
df <- read.table(text = "V1       V2
A1BG     A1BG
A1BG    CRISP3
A1CF     A1CF
A1CF   APOBEC1
A1CF    CUGBP2
A1CF     KHSRP", header = TRUE)
## make it big!
df <- with(df, cbind.data.frame(V1 = rep(V1, length.out = 1e5),
                                V2 = rep(V2, length.out = 1e5)))
# time it
system.time(sp1 <- split(df, df$V1))

system.time(sp2 <- split(df$V2, df$V1))

> system.time(sp1 <- split(df, df$V1))
   user  system elapsed 
  0.024   0.000   0.016 
> system.time(sp2 <- split(df$V2, df$V1))
   user  system elapsed 
  0.008   0.000   0.005

这是一个级别很少的示例。对于非常多的级别,拆分整个数据帧的低效率开始严重影响计算时间,例如对于大约 10000 个级别的因子:

df2 <- data.frame(V1 = factor(sample(10000, 1e5, replace = TRUE)),
                  V2 = rnorm(1e5))

system.time(sp3 <- split(df2, df2$V1))

system.time(sp4 <- split(df2$V2, df2$V1))

> system.time(sp3 <- split(df2, df2$V1))
   user  system elapsed 
  5.332   0.000   4.216 
> 
> system.time(sp4 <- split(df2$V2, df2$V1))
   user  system elapsed 
  0.008   0.000   0.005

原因是在split(df, df$V1) 的情况下,调用了split.data.frame 方法,该方法在向量1:nrow(df) 上执行lapply(),它本身被f (df$V2) 分成组,并对每个组件应用一个函数 (function(ind) x[ind, , drop = FALSE]))。因此,随着级别数量的增加,对该匿名函数的函数调用次数也会增加并增加计算时间。

在split(df$V2, df$v1) 的情况下使用split.default 方法,如果使用因子f 调用该方法,基本上只需要调用split 的快速C 实现。因此,它不会产生任何调用匿名函数的开销,也不会重复调用[。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-22
    • 1970-01-01
    • 2017-04-17
    • 2012-02-04
    • 2017-01-30
    • 2015-02-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多