基本的R读取多个CSV文件比readr更快

14

有很多文档介绍如何读取多个CSV文件并将它们绑定到一个数据框中。我有5000多个CSV文件需要读取并绑定到一个数据结构中。

特别是我已经遵循了这里的讨论:Issue in Loading multiple .csv files into single dataframe in R using rbind

奇怪的是,基本R比我尝试过的任何其他解决方案都要快得多。

这是我的CSV文件的样子:

> head(PT)
  Line          Timestamp       Lane.01 Lane.02 Lane.03 Lane.04 Lane.05 Lane.06 Lane.07 Lane.08
1    PL1    05-Jan-16 07:17:36      NA      NA      NA      NA      NA      NA      NA      NA
2    PL1    05-Jan-16 07:22:38      NA      NA      NA      NA      NA      NA      NA      NA
3    PL1    05-Jan-16 07:27:41      NA      NA      NA      NA      NA      NA      NA      NA
4    PL1    05-Jan-16 07:32:43    9.98   10.36   10.41   10.16   10.10    9.97   10.07    9.59
5    PL1    05-Jan-16 07:37:45    9.65    8.87    9.88    9.86    8.85    8.75    9.19    8.51
6    PL1    05-Jan-16 07:42:47    9.14    8.98    9.29    9.04    9.01    9.06    9.12    9.08

我已经创建了三种读取和绑定数据的方法。这些文件位于单独的目录中,我将其定义为:

dataPath <- "data"
PTfiles <- list.files(path=dataPath, full.names = TRUE)

方法1:基础R

classes <- c("factor", "character", rep("numeric",8))

# build function to load data
load_data <- function(dataPath, classes) { 
   tables <- lapply(PTfiles, read.csv, colClasses=classes, na.strings=c("NA", ""))
   do.call(rbind, tables)
}

#clock
method1 <- system.time(
   PT <- load_data(path, classes)
)

方法2:read_csv 在这种情况下,我创建了一个包装函数来使用read_csv。

#create wrapper function for read_csv
read_csv.wrap <- function(x) { read_csv(x, skip = 1, na=c("NA", ""),
                      col_names = c("tool", "timestamp", paste("lane", 1:8, sep="")),
                      col_types = 
                         cols(
                            tool = col_character(),
                            timestamp = col_character(),
                            lane1 = col_double(),
                            lane2 = col_double(),
                            lane3 = col_double(),
                            lane4 = col_double(),
                            lane5 = col_double(),
                            lane6 = col_double(),
                            lane7 = col_double(),
                            lane8 = col_double()
                           )
                     )
}

##
# Same as method 1, just uses read_csv instead of read.csv

load_data2 <- function(dataPath) { 
   tables <- lapply(PTfiles, read_csv.wrap)
   do.call(rbind, tables)
}

#clock
method2 <- system.time(
   PT2 <- load_data2(path)
)

方法三:read_csv + dplyr::bind_rows

load_data3 <- function(dataPath) { 
   tables <- lapply(PTfiles, read_csv.wrap)
   dplyr::bind_rows(tables)
}

#clock
method3 <- system.time(
   PT3 <- load_data3(path)
)

我想不通的是,为什么read_csvdplyr方法在理应更快的情况下,运行时间却更长了。尽管CPU时间有所减少,但为什么经过系统文件的经过时间会增加呢?这是怎么回事呢?

编辑 - 根据评论中的建议,我添加了data.table方法。

方法4 data.table

library(data.table)

load_data4 <- function(dataPath){
   tables <- lapply(PTfiles, fread)
   rbindlist(tables)
}

method4 <- system.time(
   PT4 <- load_data4(path)
)

data.table方法从CPU方面来看是最快的。但问题仍然存在,即read_csv方法在何处表现出如此之慢。

data.table方法是最快的,因为它使用了更高效的算法和内存管理技巧。而read_csv方法则需要进行额外的数据类型推断和内存分配,这可能会导致速度变慢。

总体而言,如果你对速度要求很高并且有大量数据处理需求,那么使用data.table方法可能会更加适合。

> rbind(method1, method2, method3, method4)
        user.self sys.self elapsed
method1      0.56     0.39    1.35
method2      0.42     1.98   13.96
method3      0.36     2.25   14.69
method4      0.34     0.67    1.74

5
我认为 data.table::fread() 是最快的。 - cirofdo
2
接下来使用 data.table::rbindlist() 连接它们。 - Dirk Eddelbuettel
如果您能将此问题转化为一个非常具体、可重现的示例,并提供一个单一的文件,那么在GitHub上提出这个问题可能会更有成效。如果可以重现,Hadley可能会对基于R的性能差距非常感兴趣。 - joran
但是如果没有你的文件等资料,其他人很难为你做到这一点或提供帮助,如果你知道我的意思... - joran
1
你尝试过指定列类型吗?我最近看到的另一种有用的方法是通过 sergeant 查询本地 Apache Drill(请参见此处)。安装它比上面的更费力,但一旦安装成功,它就非常强大。 - alistaire
显示剩余7条评论
2个回答

1

我会在终端(Unix)中执行此操作。我会将所有文件放入同一个文件夹中,然后在终端中导航到该文件夹,并使用以下命令仅创建一个CSV文件:

cat *.csv > merged_csv_file.csv

这种方法的一个观察结果是每个文件的标题会出现在观察结果的中间。为了解决这个问题,我建议你执行以下操作:

只从第一个文件获取标题

head -2 file1.csv > merged_csv_file.csv

然后使用以下命令从其他文件中跳过前面的“X”行,其中“X”是要跳过的行数。

tail -n +3 -q file*.csv >> merged_csv_file.csv

-n +3表示从第三行开始打印到结尾,-q告诉它不要打印带有文件名的头部(参见man手册),>>将内容添加到文件中,而不是覆盖它像 >。


如果不考虑头文件就合并文件,那么我会非常小心,因为你永远不知道奇怪的文件是否有一个不同构成的头文件。一个更安全的命令行工具是 xsv cat,它会考虑头文件: https://github.com/BurntSushi/xsv - dlaehnemann

0

我可能找到了一个相关的问题。我正在从一些模拟输出中读取嵌套的CSV数据,其中多个列具有CSV格式的数据作为元素,我需要将其解压并重塑以进行分析。

对于我有多次运行的模拟,这导致了需要解析成千上万个元素。使用map(。,read_csv),这需要数小时才能转换完成。当我重写我的脚本以应用lambda函数中的read.csv时,操作会在几秒钟内完成。

我很好奇是否存在某种中间系统I / O操作或错误处理,它会创建瓶颈,而您不必面对单个输入文件。


1
目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community
这并没有回答问题。一旦您拥有足够的声望,您将能够评论任何帖子;相反,提供不需要询问者澄清的答案。- 来自审核 - xilliam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接