【问题标题】:Fastest by column sort in RR中最快的按列排序
【发布时间】:2012-07-19 10:43:55
【问题描述】:

我有一个数据框full,我想从中获取最后一列和一列v。然后我想以最快的方式对v 上的两列进行排序。 full 是从 csv 读取的,但这可用于测试(包括一些真实性的 NA):

n <- 200000
full <- data.frame(A = runif(n, 1, 10000), B = floor(runif(n, 0, 1.9)))
full[sample(n, 10000), 'A'] <- NA
v <- 1

我这里有v,但实际上它可能会改变,full 有很多列。


我尝试使用ordersort.list 对数据框、数据表和矩阵进行排序(一些想法来自this thread)。所有这些的代码:

# DATA FRAME

ord_df <- function() {
  a <- full[c(v, length(full))]
  a[with(a, order(a[1])), ]
}

sl_df <- function() {
  a <- full[c(v, length(full))]
  a[sort.list(a[[1]]), ] 
}


# DATA TABLE

require(data.table)

ord_dt <- function() {
  a <- as.data.table(full[c(v, length(full))])
  colnames(a)[1] <- 'values'
  a[order(values)]
}

sl_dt <- function() {
 a <- as.data.table(full[c(v, length(full))])
 colnames(a)[1] <- 'values'
 a[sort.list(values)]
}


# MATRIX

ord_mat <- function() {
  a <- as.matrix(full[c(v, length(full))])
  a[order(a[, 1]), ] 
}

sl_mat <- function() {
  a <- as.matrix(full[c(v, length(full))])
  a[sort.list(a[, 1]), ] 
}

时间结果:

         ord_df  sl_df    ord_dt   sl_dt   ord_mat sl_mat
Min.     0.230   0.1500   0.1300   0.120   0.140   0.1400
Median   0.250   0.1600   0.1400   0.140   0.140   0.1400
Mean     0.244   0.1610   0.1430   0.136   0.142   0.1450
Max.     0.250   0.1700   0.1600   0.140   0.160   0.1600

或使用microbenchmark(结果以毫秒为单位):

             min      lq       median   uq       max
1  ord_df() 243.0647 248.2768 254.0544 265.2589 352.3984
2  ord_dt() 133.8159 140.0111 143.8202 148.4957 181.2647
3 ord_mat() 140.5198 146.8131 149.9876 154.6649 191.6897
4   sl_df() 152.6985 161.5591 166.5147 171.2891 194.7155
5   sl_dt() 132.1414 139.7655 144.1281 149.6844 188.8592
6  sl_mat() 139.2420 146.8578 151.6760 156.6174 186.5416

似乎订购数据表获胜。 ordersort.list 之间没有太大区别,除非使用 sort.list 更快的数据帧。

在数据表版本中,我还尝试将v 设置为键(因为它是根据文档进行排序的),但由于v 的内容不是整数,我无法让它工作。

理想情况下,我希望尽可能加快速度,因为对于不同的v 值,我必须多次执行此操作。有谁知道我如何能够进一步加快这个过程?是否值得尝试Rcpp 实现?谢谢。


这是我用来计时的代码,如果它对任何人有用的话:

sortMethods <- list(ord_df, sl_df, ord_dt, sl_dt, ord_mat, sl_mat)

require(plyr)
timings <- raply(10, sapply(sortMethods, function(x) system.time(x())[[3]]))
colnames(timings) <- c('ord_df', 'sl_df', 'ord_dt', 'sl_dt', 'ord_mat', 'sl_mat')
apply(timings, 2, summary) 

require(microbenchmark)
mb <- microbenchmark(ord_df(), sl_df(), ord_dt(), sl_dt(), ord_mat(), sl_mat())
plot(mb)

【问题讨论】:

  • 我不知道您的确切规格,但如果他们允许,请考虑在您的排序功能之外运行一次像 full &lt;- as.matrix(full)full &lt;- as.data.table(full) 这样的东西。这些转换可能会对您的计算时间产生重大影响,而它们并没有真正进行排序。
  • 好吧,full 最初是一个数据框,在进行任何排序之前(我无法更改),因此将其转换为矩阵或数据表是排序方法的一部分,因此需要包括在我认为的时间安排中。
  • 请升级到 data.table v1.8.2,现在在 CRAN 上。一条评论是您无法在非整数列上设置键 - 现在在 1.8.2 中可以,请参阅 NEWS。在 v 上设置一个键应该更快,是的。此外,您似乎在排序时间中包括了转换为 data.table 的时间。这公平吗?但是,您可能会发现在 1.8.2 中转换的时间比在 1.8.0 中要快得多。 +1顺便说一句。好问题。
  • 您可能还需要超过 200,000 行。每当我看到报告的无关紧要的时间有显着差异时,我通常都会翻白眼。您是说 data.table 以 0.13 秒和 0.23 秒为基础获胜?我会说这是微不足道的。请参阅 vignette("datatable-timings") 了解我认为是好的基准。 8.8 秒减少到 0.013(快 678 倍),22.6 秒减少到 1.1 秒(胖 19 倍)。这些是重要时期的重大差异。您(几乎)有足够时间在等待时泡杯茶的任务。
  • 对于ord_df,不需要with。您的速度差异是由于订购数据帧order(a[1]) 而不是矢量order(a[[1]])order(a[[1]]) 就像 sort.list(a[[1]])。如果要排序的列实际上是整数值,那么 sort.list(..., method="radix") 非常快(比 data.table 快?)。

标签: r data.table


【解决方案1】:

我不知道将这种东西作为编辑放入是否更好,但它似乎更像是答案,所以这里会做。更新测试功能:

n <- 1e7
full <- data.frame(A = runif(n, 1, 10000), B = floor(runif(n, 0, 1.9)))
full[sample(n, 100000), 'A'] <- NA

fdf <- full
fma <- as.matrix(full)
fdt <- as.data.table(full)
setnames(fdt, colnames(fdt)[1], 'values')

# DATA FRAME
ord_df <- function() { fdf[order(fdf[1]), ] }
sl_df <- function() { fdf[sort.list(fdf[[1]]), ] }

# DATA TABLE
require(data.table)
ord_dt <- function() { fdt[order(values)] }

key_dt <- function() {
  setkey(fdt, values) 
  fdt
}

# MATRIX
ord_mat <- function() { fma[order(fma[, 1]), ] }
sl_mat <- function() { fma[sort.list(fma[, 1]), ] }

结果(使用不同的计算机,R 2.13.1 和data.table 1.8.2):

         ord_df  sl_df   ord_dt  key_dt  ord_mat sl_mat
Min.     37.56   20.86   2.946   2.249   20.22   20.21
1st Qu.  37.73   21.15   2.962   2.255   20.54   20.59
Median   38.43   21.74   3.002   2.280   21.05   20.82
Mean     38.76   21.75   3.074   2.395   21.09   20.95
3rd Qu.  39.85   22.18   3.151   2.445   21.48   21.42
Max.     40.36   23.08   3.330   2.797   22.41   21.84

所以 data.table 是明显的赢家。使用键比排序更快,并且我认为它的语法也更好。谢谢大家的帮助。

【讨论】:

  • 在您之前的基准测试中,ord_dtord_mat 大致相当,而现在有很大的不同。我很想知道在升级您的 R 版本和包之间,或者从您的计算中删除数据类转换之间,什么贡献最大。
  • isn't ord_df 仍然未正确实现,使用 [ 而不是 [[ 子设置?
  • @flodel 我之前使用了 200,000 行,现在使用了 1000 万行。可能没有足够的数据来区分这两种方法。此外,我使用的是相当旧版本的 data.table。我认为班级转换没有太大影响。转换是微不足道的(as.data.table()full 上花费的时间不到 100 毫秒,在整体所需的 2-3 秒中并不多)。
  • @MartinMorgan order(fdf[[1]])sort.list(fdf[[1]]) 是等价的(及时),因此无需同时包含两者。
  • +1 这看起来更像我的预期。感谢您如此迅速地升级和报告新结果。顺便说一句,如果你也增加列数,速度差距应该会进一步扩大。这是因为一旦确定了排序顺序,setkey 通过引用重用少量工作内存来重新排序其他列。而其他方法复制整个表。有 1000 万行和(仅)2 列,它只有 1e7 * 2 * 8 / 1024^3 = 0.15 GB。一旦对象大小显着达到许多 GB(比如 64 位 20GB),这就是我们所说的大。
猜你喜欢
  • 2019-10-31
  • 2021-07-14
  • 1970-01-01
  • 2013-10-14
  • 1970-01-01
  • 1970-01-01
  • 2019-10-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多