我的 data.table 加入超过了 64 位 Windows 机器上 32GB RAM 的内存限制

My data.table join exceeds memory limit on 64bit Windows machine with 32gb RAM

背景

我需要对非常大的 data.table 进行一些数据操作,我们将调用 d1(约 1.25 亿行 x 10 列)我从 .csv 读入 R表格使用 fread。数据是关于汽车保险运行ce -- t运行与变速箱相关和与发动机相关的索赔。这是读物:

d1 <- fread("c:/analysis.csv", 
               header = TRUE, 
               na.strings = c("", "NA"),
               data.table = TRUE,
               stringsAsFactors = TRUE)

我需要在 d1 上做的工作相当简单 dplyr 东西 -- 一些连接(在小得多的 tables 上),一对 mutate的等。让我们将那些更小的 tables 称为 d2d3 以供参考;假设这些也以与上述 d1 相同的方式 data.tables 读入 R 。我已经在 d1 的更小的“测试”子集上计划了这些操作(连接和变异),所以我知道它们会起作用。

我 运行 正在 R 使用的机器有 32gb 内存、最新的 Comet Lake Intel Core i5 和 1TB NVMe SSD。我的 Windows 和 R 版本是 64 位。

作为参考,这里是 space 每个数据集占用的数量:

> format(object.size(d1), "Gb")
[1] "4 Gb"
> format(object.size(d2), "Mb")
[1] "3.2 Mb"

这里是str(d1)所以你可以看到数据类型:

Classes ‘data.table’ and 'data.frame':  125640181 obs. of  10 variables:
 $ id                   : int  1551444  ...
 $ service_dt           : IDate, format: "2020-11-22"  ...
 $ service_code         : Factor w/ 41817 levels "316",..
 $ problem_code         : Factor w/ 39531 levels "0",
 $ problem_flag         : int  0 0 0 0 0 0 0 1 1 0 ...
 $ problem_type         : Factor w/ 2 levels "transmission","engine": 1 1 ...
 $ customer_dob         : IDate, format: "1976-04-14" "1980-04-25" ...
 $ customer_gender_cd   : Factor w/ 3 levels "F","M","U": 1 2 ...
 $ customer_zip_cd      : Factor w/ 8354 levels "00000","00003"
 $ old_id               : int  13449983 ...

还有str(d2)

'data.frame':   37323 obs. of  4 variables:
 $ service_code     : Factor w/ 36281 levels "00002081702",..: 1 2 3 ...
 $ parts_used       : Factor w/ 215 levels "Disc Brake Rotor (Front)",..: 136 ...
 $ category         : Factor w/ 5 levels "Brakes",..: 1 1 ...
 $ subcategory_1    : Factor w/ 24 levels "Rotors",..: 22 20 ...

问题

我去运行这个加入...

d1 <- left_join(d1, d2, by = c("service_code" = "service_code"))

... 我收到此错误:Error: cannot allocate vector of size 277.7 Gb。请注意,这里的 d2 看起来像这样:

> dim(d2)
[1] 37323     4

我试过的

所以我在 Whosebug 上阅读了几篇“R 内存不足”的帖子,试图弄清楚我能做些什么。使用 data.tables 是一个建议。在 运行 加入之前,我尝试使用 gc() 到 运行 垃圾收集;那也不管用。如果我查看内存限制,我会得到:

> memory.limit()
[1] 32502

我认为这意味着 R 可能已经分配了我系统的所有 RAM,我不确定这是否可以更高。

在其他帖子中,我阅读了有关大型数据集的包 ff,但我 运行 在尝试使用它时遇到了其他麻烦。我已经成功地将我的 data.tables 变成了 ffdf 格式,如下所示:

d1 <- as.ffdf(d1)
d2 <- as.ffdf(d2)

然后尝试运行加入,但是得到这个错误:Error in UseMethod("left_join") : no applicable method for 'left_join' applied to an object of class "ffdf".

我担心的是,即使我能以某种方式将这个特定的 ff 代码转换为 运行,我必须做的其余工作非常依赖于 dplyr。我担心 ff 宇宙中不存在我需要做的等效函数,如果这有意义的话。有没有办法继续使用 ff,但将 d1d2(和 d3,此处未显示)保留为 data.tables?

编辑

在与@serkan 进行一些互动后——请参阅下面他们非常有用的答案——我发现 d2 具有我的连接键 d2$service_code 的重复值。事实上,其中大约有 1100 个。事后看来,这可能是 R 在简单的左连接上被 vector of size 277.7 Gb 绊倒的主要原因。所以,我今天学到了一些东西:左连接时,确保你的RHS table的关键变量是唯一的!

我建议 dtplyr 以这种方式,

large_data <- tibble(
        id = 1:125640181,
        value1 = 1:125640181,
        value2 = 1:125640181,
        value3 = 1:125640181,
        value4 = 1:125640181,
        value5 = 1:125640181,
        value6 = 1:125640181,
        value7 = 1:125640181,
        value8 = 1:125640181,
        value9 = 1:125640181
) %>% lazy_dt() 


small_data <- tibble(
        id = 1:37323,
        value1 = 1:37323,
        value2 = 1:37323,
        value3 = 1:37323
        
) %>% lazy_dt() 

然后 join 通过,

joined_data <- left_join(
        large_data,
        small_data, by = "id"
) %>% as_tibble()

这给出了,

# A tibble: 6 x 13
     id value1.x value2.x value3.x value4 value5 value6 value7 value8 value9 value1.y value2.y value3.y
  <int>    <int>    <int>    <int>  <int>  <int>  <int>  <int>  <int>  <int>    <int>    <int>    <int>
1     1        1        1        1      1      1      1      1      1      1        1        1        1
2     2        2        2        2      2      2      2      2      2      2        2        2        2
3     3        3        3        3      3      3      3      3      3      3        3        3        3
4     4        4        4        4      4      4      4      4      4      4        4        4        4
5     5        5        5        5      5      5      5      5      5      5        5        5        5
6     6        6        6        6      6      6      6      6      6      6        6        6        6

在我的机器上也有 32 Gb 内存。您可以保留所有 dplyr 函数和 syntax,同时滥用 data.table!

的内存效率

您可以在 https://github.com/tidyverse/dtplyr

阅读更多相关信息