读取大型Excel xlsx文件的最快方法?是否并行化?

时间:2019-04-04 10:02:09

标签: r parallel-processing readxl

我的问题是:

  • 将大型(ish).xlsx Excel文件读入R最快的方法是什么? 10到200 MB的xlsx文件,有多张纸。

  • 是否可以使用某种并行处理,例如每个核心阅读 多页Excel文件的另一页?

  • 还有其他可以执行的优化吗?

到目前为止,我所了解的(以及我尚未了解的):

  • 如果从旋转磁盘读取数据,正如我将要进行的,并行处理实际上可能会减慢读取速度,因为多个进程试图从同一文件读取数据。但是,并行处理可能有助于转换和推断数据类型?不知道readxl从磁盘读取(我认为是IO绑定)还是转换数据类型(我估计是CPU绑定)花了多少钱。
  • 这可能与SSD驱动器不同。如果有很大的改进,我可能会将数据复制到SSD驱动器并从那里读取。
  • data.table :: fread可以加快文本文件的读取速度(尽管我不完全理解为什么),但是它不能用于excel文件-可以吗?
  • 据此answer我了解到readxl往往比openxlsx

我只对表格数据感兴趣;我对Excel格式,图表,文本标签或任何其他类型的数据都不感兴趣。

我可能希望导入tidyverse拼写,但不一定。然后,我需要将表导出到Microsoft SQL Server中。

某些背景:我主要使用Python,并且对R完全陌生。用Python读取大型Excel文件非常缓慢。我已经看到R的readxl比Python快得多pandas(在15页xlsx上,每页有10,000行32列:readxl 5.6秒,熊猫33秒),太好了!但是,我仍然想了解是否有任何方法可以使导入速度更快。我可以使用R读取文件,将其导出到SQL,然后使用从SQL读取Python继续我的工作流程的其余部分。

我认为转换为CSV并不是最好的选择,尤其是当readxl仍然比Python快得多时;基本上转换为csv所需的时间可能比我从csv读取而不是excel节省​​的时间要长。另外,至少对于Python(我不太了解R是否足以使用readxl对其进行全面测试)而言,使用xlsx推断数据类型要比使用csv更好。

我的代码(欢迎提出任何批评或建议)

library(readxl)
library(tidyverse)
library(tictoc)


this.dir <- dirname(parent.frame(2)$ofile)
setwd(this.dir)

tic("readxl")

path <- "myfile.xlsx"
sheetnames <- excel_sheets(path)
mylist <- lapply(excel_sheets(path), read_excel, path = path)

names(mylist) <- sheetnames
toc()

2 个答案:

答案 0 :(得分:4)

您可以尝试使用parallel软件包并行运行它,但是要估算没有样本数据的速度将有些困难:

library(parallel)
library(readxl)

excel_path <- ""
sheets <- excel_sheets(excel_path)

创建具有指定核心数的集群:

cl <- makeCluster(detectCores() - 1)

使用parLapplyLB浏览excel表格并使用负载平衡并行读取它们:

parLapplyLB(cl, sheets, function(sheet, excel_path) {
  readxl::read_excel(excel_path, sheet = sheet)
}, excel_path)

您可以使用软件包microbenchmark测试某些选项的运行速度:

library(microbenchmark)

microbenchmark(
  lapply = {lapply(sheets, function(sheet) {
    read_excel(excel_path, sheet = sheet)
  })},
  parralel = {parLapplyLB(cl, sheets, function(sheet, excel_path) {
    readxl::read_excel(excel_path, sheet = sheet)
  }, excel_path)},
  times = 10
)

就我而言,并行版本更快:

Unit: milliseconds
     expr       min        lq     mean    median        uq      max neval
   lapply 133.44857 167.61801 179.0888 179.84616 194.35048 226.6890    10
 parralel  58.94018  64.96452 118.5969  71.42688  80.48588 316.9914    10

测试文件包含6张纸,每张纸都包含此表:

    test test1 test3 test4 test5
 1     1     1     1     1     1
 2     2     2     2     2     2
 3     3     3     3     3     3
 4     4     4     4     4     4
 5     5     5     5     5     5
 6     6     6     6     6     6
 7     7     7     7     7     7
 8     8     8     8     8     8
 9     9     9     9     9     9
10    10    10    10    10    10
11    11    11    11    11    11
12    12    12    12    12    12
13    13    13    13    13    13
14    14    14    14    14    14
15    15    15    15    15    15

注意: 您可以在过程完成后使用stopCluster(cl)关闭工作程序。

答案 1 :(得分:1)

我看到了@clemens的回答,但是由于我已经准备了一些东西,所以无论如何我都将其发布。除了@clemens答案以外,我使用更大的测试数据,并使用furrr::future_map()运行更简单的多核选项,这最终并不会带来任何性能提升...

数据生成

这将创建10张10000 * 15数据,其中包含浮点数,整数和字符。在我的磁盘上,文件大小为13.2MB。

library(writexl)
library(tidyverse)

n <- 1e4
sample_data <- map(seq(10), function(x) {
  sample_data <-
    map(1:5, function(x){
      data_frame(
        num_var = rnorm(n),
        int_var = as.integer(sample(1e5:9e5, n, replace = T)),
        char_var = sample(letters, n, replace = T)
      ) %>% rename_all(funs(paste0(., x)))
    }) %>% bind_cols()
  return(sample_data)
})
fn <- tempfile(tmpdir = "~/Desktop/temp",fileext = ".xlsx")
write_xlsx(sample_data, path = fn)

基准

parallel部分是从@clemens借来的。

library(parallel)
library(readxl)

sheets <- excel_sheets(fn)

cl <- makeCluster(detectCores() - 1)
excel_path <- fn

microbenchmark::microbenchmark(
  map = map(sheets, function(x) read_xlsx(fn, sheet = x)) ,
  future_map = furrr::future_map(sheets, function(x) read_xlsx(fn, sheet = x)),
  parLapplyLB = {parLapplyLB(cl, sheets, function(sheet, excel_path) {
    readxl::read_xlsx(excel_path, sheet = sheet)
  }, excel_path)},
  times = 10
)

基准测试结果如下:

Unit: milliseconds
        expr       min        lq      mean    median       uq      max neval
         map 1258.2643 1272.2354 1351.8371 1291.2474 1430.211 1556.992    10
  future_map 1276.4125 1302.2022 1469.8349 1436.5356 1616.146 1702.494    10
 parLapplyLB  809.2697  863.3299  951.1041  914.9503 1014.907 1189.897    10

我的CPU相对较弱,因此在其他环境中获得的收益一定很大,但是最后,编写SQL部分可能会成为瓶颈,因为对于read_xlsx而言,读取速度确实非常快。

注意

我还尝试了其他软件包,例如gdataxlsx。这些过程非常缓慢,因此不值得考虑。