有比fread()更快的读取大数据的方式吗?

9

首先,我已经在Stack和Google上搜索过,并找到了像这样的帖子:Quickly reading very large tables as dataframes。虽然这些帖子很有帮助并且回答得很好,但我正在寻找更多信息。

我正在寻找读取/导入可以达到50-60GB的“大”数据的最佳方法。 我目前正在使用来自data.tablefread()函数,它是我目前知道的最快的函数。我所用的电脑/服务器配备了良好的CPU(工作站)和32 GB RAM,但仍需要花费很长时间才能读取超过10GB的数据,有时甚至会接近数十亿个观测值。

我们已经拥有SQL数据库,但由于某些原因,我们必须在R中处理大数据。 在处理此类巨大文件时,是否有一种方法可以加速R或比fread()更好的选择?

谢谢。

编辑:fread(“data.txt”,verbose = TRUE)

omp_get_max_threads() = 2
omp_get_thread_limit() = 2147483647
DTthreads = 0
RestoreAfterFork = true
Input contains no \n. Taking this to be a filename to open
[01] Check arguments
  Using 2 threads (omp_get_max_threads()=2, nth=2)
  NAstrings = [<<NA>>]
  None of the NAstrings look like numbers.
  show progress = 1
  0/1 column will be read as integer
[02] Opening the file
  Opening file C://somefolder/data.txt
  File opened, size = 1.083GB (1163081280 bytes).
  Memory mapped ok
[03] Detect and skip BOM
[04] Arrange mmap to be \0 terminated
  \n has been found in the input and different lines can end with different line endings (e.g. mixed \n and \r\n in one file). This is common and ideal.
[05] Skipping initial rows if needed
  Positioned on line 1 starting: <<ID,Dat,No,MX,NOM_TX>>
[06] Detect separator, quoting rule, and ncolumns
  Detecting sep automatically ...
  sep=','  with 100 lines of 5 fields using quote rule 0
  Detected 5 columns on line 1. This line is either column names or first data row. Line starts as: <<ID,Dat,No,MX,NOM_TX>>
  Quote rule picked = 0
  fill=false and the most number of columns found is 5
[07] Detect column types, good nrow estimate and whether first row is column names
  Number of sampling jump points = 100 because (1163081278 bytes from row 1 to eof) / (2 * 5778 jump0size) == 100647
  Type codes (jump 000)    : 5A5AA  Quote rule 0
  Type codes (jump 100)    : 5A5AA  Quote rule 0
  'header' determined to be true due to column 1 containing a string on row 1 and a lower type (int32) in the rest of the 10054 sample rows
  =====
  Sampled 10054 rows (handled \n inside quoted fields) at 101 jump points
  Bytes from first data row on line 2 to the end of last row: 1163081249
  Line length: mean=56.72 sd=20.65 min=25 max=128
  Estimated number of rows: 1163081249 / 56.72 = 20506811
  Initial alloc = 41013622 rows (20506811 + 100%) using bytes/max(mean-2*sd,min) clamped between [1.1*estn, 2.0*estn]
  =====
[08] Assign column names
[09] Apply user overrides on column types
  After 0 type and 0 drop user overrides : 5A5AA
[10] Allocate memory for the datatable
  Allocating 5 column slots (5 - 0 dropped) with 41013622 rows
[11] Read the data
  jumps=[0..1110), chunk_size=1047820, total_size=1163081249
|--------------------------------------------------|
|==================================================|
Read 20935277 rows x 5 columns from 1.083GB (1163081280 bytes) file in 00:31.484 wall clock time
[12] Finalizing the datatable
  Type counts:
         2 : int32     '5'
         3 : string    'A'
=============================
   0.007s (  0%) Memory map 1.083GB file
   0.739s (  2%) sep=',' ncol=5 and header detection
   0.001s (  0%) Column type detection using 10054 sample rows
   1.809s (  6%) Allocation of 41013622 rows x 5 cols (1.222GB) of which 20935277 ( 51%) rows used
  28.928s ( 92%) Reading 1110 chunks (0 swept) of 0.999MB (each chunk 18860 rows) using 2 threads
   +   26.253s ( 83%) Parse to row-major thread buffers (grown 0 times)
   +    2.639s (  8%) Transpose
   +    0.035s (  0%) Waiting
   0.000s (  0%) Rereading 0 columns due to out-of-sample type exceptions
  31.484s        Total

1
你真的需要在R中使用所有数据吗?我建议事先使用Unix环境下的awksed和/或cat进行转换、过滤或创建子集。另一种方法是使用furrr:future_map读取数据块以实现并行化。 - Roman
1
或者,由于您已经将数据存储在 SQL 数据库中,只需连接到该数据库并提取子样本以进行处理。 - joran
1
如果您提前知道数据集的维度,可以预先分配所需的空间并自己编写Rccp函数(用于导入),这样速度会稍微快一些(但不要期望有很大的改进)。 - JacobJacox
1
@Jimbou 谢谢,我会看一下 furrr:future_map。 @joran 这不太实际,但我无法直接连接到 sql 数据库,这就是为什么我在这里问这个问题的原因。@JacobJacox 谢谢你,我已经尝试过了,但速度并没有提升太多! - Gainz
1
你提到你的工作站有一个好的CPU和32GB的内存,但你没有说明存储子系统是SSD还是HDD。当然,SSD比HDD要好得多。甚至比大多数SSD更快的是使用英特尔Optane内存。考虑到你正在处理的数据集的大小,我建议将系统内存升级到64GB。 - Bruce Schardt
显示剩余2条评论
2个回答

5
假设您想要将文件完全读入R,使用数据库或选择子集列/行并不会有太大的帮助。在这种情况下,以下操作可能会有所帮助:
- 确保您正在使用较新版本的data.table
- 确保设置了最优线程数
使用setDTthreads(0L)来使用所有可用线程;默认情况下,data.table使用50%的可用线程。
- 检查fread(..., verbose=TRUE)的输出,并可能将其添加到您的问题中
- 将您的文件放在快速磁盘或RAM磁盘上,并从那里读取
如果您的数据具有许多不同的字符变量,则可能无法获得很好的速度,因为填充R的内部全局字符缓存是单线程的,因此解析可能会很快,但创建字符向量将成为瓶颈。

谢谢,我会研究一下! - Gainz
1
@Gainz 我会说是磁盘读取速度,尝试使用一些外部工具测量驱动器的读取速度,并将其与fread的速度进行比较。如果有许多不同的字符,CPU可能肯定是一个问题,如答案中所提到的。在问题中添加详细输出。 - jangorecki
1
@Gainz 看起来加速最简单的方法是使用更多的核心。在工作站上应该有超过2个线程。有关核心的更多详细信息可以从 getDTthreads(verbose=TRUE) 获取。 - jangorecki
1
你如何访问机器?使用ssh吗?只需从命令行检查应该有多少个线程。getDTthreads仅报告2个线程。服务器上可能有一些配置,将最多分配2个线程给每个用户。 - jangorecki
1
是的,ssh,我认为你关于分配线程的想法是正确的,似乎我的同事也只能访问2个线程。我会尝试与TI谈论这个问题。感谢您的帮助,jangorecki,我们非常感激! - Gainz
显示剩余3条评论

4
您可以使用select = columns来仅加载相关列,而不会使内存饱和。例如:
dt <- fread("./file.csv", select = c("column1", "column2", "column3"))

我使用了read.delim()来读取一个文件,而无法完整加载。因此,您可以将数据转换为.txt格式并使用read.delim()来读取。
不过,为什么不打开与您的数据源SQL服务器的连接呢?您可以使用library(odbc)打开SQL服务器的连接,并像平常一样编写查询语句。这样可以优化您的内存使用。
请参考odbc的简短介绍

1
是的,我已经使用了 odbc,实际上我没有使用 select = columns,我应该考虑到这一点。就像我在评论中说的那样,我无法直接连接到 SQL 服务器(这就是为什么我要问这个问题的原因)。我知道这真的很不实用,但我必须在 R 中这样做。如果我无法直接连接到 SQL 服务器,由于似乎 fread() 仍然是 R 中最快的选项,所以我会接受您的答案。谢谢! - Gainz
1
很高兴能够帮到你。或者,你可以尝试在Python中使用pandas加载它。个人认为,就语法而言,data.table是最好的包,但是pandas可以非常快速地读取文件。它还有一个usecols参数。例如:pd.read_csv("./file.csv", usecols=["column1", "column2"]) - Arturo Sbr
1
好主意,我对 pandas 不是很了解,但值得一试。我会尝试在这两者之间运行基准测试。再次感谢! - Gainz
我经常使用fread处理50GB的数据,它的表现非常出色。你为什么认为它在这个大小上表现不佳?它是为这样的大型数据集设计的,甚至更大。Pandas无法帮助,因为它无法在128GB机器上加载50GB的CSV文件,由于Pandas内存使用量过高。最终,Python datatable可能会有所帮助。 - jangorecki
@Jangorecki 我不知道datatable是为如此大的数据集而设计的。我这么说是因为我最近加载文件时遇到了问题。data.table无法加载所有行。 - Arturo Sbr

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