与使用因子相比,是否有可能拥有更小的数据集?

时间:2018-12-07 18:50:48

标签: r dataframe

我正在尝试减少某些数据集的内存占用量,在这些数据集中我每列具有少量因素(重复多次)。有更好的方法将其最小化吗?为了进行比较,这是我仅使用因素得出的结果:

library(pryr)

N <- 10 * 8
M <- 10

初始数据:

test <- data.frame(A = c(rep(strrep("A", M), N), rep(strrep("B", N), N)))
object_size(test)
# 1.95 kB

使用因素:

test2 <- as.factor(test$A)
object_size(test2)
# 1.33 kB

在旁边:我很天真地以为他们用数字替换了字符串,并且很高兴看到test2小于test3。谁能指出一些有关如何优化因子表示的材料?

test3 <- data.frame(A = c(rep("1", N), rep("2", N)))
object_size(test3)
# 1.82 kB

3 个答案:

答案 0 :(得分:8)

恐怕差异很小。

原理很简单:您将只存储2和160个整数(仅4个字节),而不是(在您的示例中)160个字符串。
除了R种内部存储字符的方式相同。

每种现代语言都支持(实际上)无限长度的字符串。这就带来了一个问题,您不能将字符串的向量(或数组)存储为一个连续的块,因为任何元素都可以重置为任意长度。因此,如果为一个元素分配了另一个值,而这个值恰好更长一些,这意味着必须对数组的其余部分进行移位。否则操作系统/语言应为每个字符串保留大量空间。
因此,将字符串存储在内存中方便的任何位置,并将数组(或R中的向量)作为 pointers 的块存储到值实际所在的位置。
在R的早期,即使实际值相同,每个指针也指向内存中的另一个位置。因此,在您的示例中,有160个指针指向160个存储位置。但这已经改变了,如今已实现为指向2个内存位置的160个指针。 可能会有一些细微的差异,主要是因为一个因子通常只能支持2 ^ 31-1级别,这意味着32位整数足以存储它,而字符大多使用64位指针。再说一次,因素的开销更大。
通常,如果您确实有很大比例的重复项,那么使用 在使用因素方面可能会有一些优势,但是如果不是这样,甚至可能会损害您的内存使用率。

您提供的示例不起作用,因为您正在将data.frame与一个因子(而不是裸字符)进行比较。
甚至更强大:当重现您的示例时,只有将stringsAsFactors设置为FALSE时,您才能得到结果,因此您正在将一个因数与data.frame中的一个因数进行比较。 否则,比较结果将得出较小的不同:字符1568,因数1328。
而且只有当您具有很多相同的值时,这才起作用,如果您看一下,您会发现该系数可能更大:

> object.size(factor(sample(letters)))
2224 bytes
> object.size(sample(letters))
1712 bytes

因此,通常来说,除了在您实际要存储的内容中使用常识之外,没有任何真正的方法可以压缩数据同时保持易于使用。

答案 1 :(得分:2)

对于您的问题,我没有直接答案,但这是Hadley Wickham的"Advanced R"书中的一些信息:

  

因素

     

属性的一个重要用途是定义因素。一个因素   是一个只能包含预定义值的向量,用于   存储分类数据。因素建立在整数向量之上   使用两个属性:类“ factor”,使它们的行为   与常规整数向量和水平不同   定义允许值的集合。

也:

  

“尽管因素看起来(并且经常表现得像字符向量,但它们   实际上是整数。将它们像字符串一样对待时要小心。   一些字符串方法(例如gsub()和grepl())会强制转换为   字符串,而其他字符串(例如nchar())将抛出错误,并且仍然   其他(如c())将使用基础整数值。为了这   原因,通常最好是将因子明确转换为字符   向量,如果您需要类似字符串的行为。在早期的R版本中,   使用因素代替性格具有记忆优势   向量,但情况已不再如此。”

答案 2 :(得分:1)

R中有一个名为fstLightning Fast Serialization of Data Frames for R) 的软件包,您可以在其中为数据帧创建压缩的fst对象。详细说明请参见{{3 }},但我将简要说明如何使用它以及fst对象占用多少空间。首先,让您的test数据框更大一些,如下所示:

library(pryr)
N <- 1000 * 8
M <- 100
test <- data.frame(A = c(rep(strrep("A", M), N), rep(strrep("B", N), N)))
object_size(test)
# 73.3 kB

现在,让我们将该数据帧转换为fst对象,如下所示:

install.packages("fst") #install the package
library(fst) #load the package
path <- paste0(tempfile(), ".fst") #create a temporary '.fst' file
write_fst(test, path) #write the dataframe into the '.fst' file
test2 <- fst(path) #load the data as an fst object
object_size(test2)
# 2.14 kB

创建的.fst文件的磁盘空间为434 bytes。您可以将test2作为普通数据帧处理(据我尝试)。

希望这会有所帮助。