5

R 似乎每个整数都需要四个字节的存储空间,即使是小的整数:

> object.size(rep(1L, 10000))
40040 bytes

而且,更重要的是,即使是因素:

> object.size(factor(rep(1L, 10000)))
40456 bytes

我认为,尤其是在后一种情况下,这可以处理得更好。有没有一种解决方案可以帮助我将这种情况下的存储要求降低到每行 8 位甚至 2 位?也许是一种在内部使用该raw类型进行存储但在其他方面表现得像正常因素的解决方案。该bit软件包为位提供了此功能,但我没有发现任何类似的因素。

我只有几百万行的数据框消耗了千兆字节,这是对内存和运行时间的巨大浪费(!)。压缩将减少所需的磁盘空间,但同样会以运行时间为代价。

有关的:

4

3 回答 3

6

既然您提到raw(并假设您的因子级别少于 256 个) -如果内存是您的瓶颈而 CPU 时间不是,您可以执行先决条件转换操作。例如:

f = factor(rep(1L, 1e5))
object.size(f)
# 400456 bytes

f.raw = as.raw(f)
object.size(f.raw)
#100040 bytes

# to go back:
identical(as.factor(as.integer(f.raw)), f)
#[1] TRUE

您也可以单独保存因子水平并恢复它们,如果这是您感兴趣的事情,但就分组和所有事情而言,您可以只使用它来完成所有操作,raw并且永远不会回到因子(除了演示文稿)。

如果您有特定用例对此方法有疑问,请发布,否则我认为这应该可以正常工作。


这是您的课程的起点byte.factor

byte.factor = function(f) {
  res = as.raw(f)
  attr(res, "levels") <- levels(f)
  attr(res, "class") <- "byte.factor"
  res
}

as.factor.byte.factor = function(b) {
  factor(attributes(b)$levels[as.integer(b)], attributes(b)$levels)
}

因此,您可以执行以下操作:

f = factor(c('a','b'), letters)
f
#[1] a b
#Levels: a b c d e f g h i j k l m n o p q r s t u v w x y z

b = byte.factor(f)
b
#[1] 01 02
#attr(,"levels")
# [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
#[20] "t" "u" "v" "w" "x" "y" "z"
#attr(,"class")
#[1] "byte.factor"

as.factor.byte.factor(b)
#[1] a b
#Levels: a b c d e f g h i j k l m n o p q r s t u v w x y z

如果您想制作通用并添加您想要添加的任何功能,请查看如何data.table覆盖。一切都应该很简单。rbind.data.frameas.factor

于 2013-11-14T21:50:37.827 回答
3

另一种解决方案是使用ff. ff支持以下 vmode/类型(请参阅 参考资料?vmode):

 ‘boolean’    ‘as.boolean’    1 bit logical without NA           
 ‘logical’    ‘as.logical’    2 bit logical with NA              
 ‘quad’       ‘as.quad’       2 bit unsigned integer without NA  
 ‘nibble’     ‘as.nibble’     4 bit unsigned integer without NA  
 ‘byte’       ‘as.byte’       8 bit signed integer with NA       
 ‘ubyte’      ‘as.ubyte’      8 bit unsigned integer without NA  
 ‘short’      ‘as.short’      16 bit signed integer with NA      
 ‘ushort’     ‘as.ushort’     16 bit unsigned integer without NA 
 ‘integer’    ‘as.integer’    32 bit signed integer with NA      
 ‘single’     ‘as.single’     32 bit float                       
 ‘double’     ‘as.double’     64 bit float                       
 ‘complex’    ‘as.complex’    2x64 bit float                     
 ‘raw’        ‘as.raw’        8 bit unsigned char                
 ‘character’  ‘as.character’  character

例如:

library(ff)
v <- ff(as.factor(sample(letters[1:4], 10000, replace=TRUE)), vmode="byte", 
    levels=letters[1:4])

这将只使用每个元素一个字节。另一个优点/缺点是,当数据变得太大而无法存储到内存中时,它会自动存储在磁盘上(这当然会影响性能)。

但是,无论您使用什么解决方案,您都可能会遇到性能下降的问题。R 在内部使用整数作为因子,因此在调用任何 R 方法之前,必须将数据从紧凑存储转换为 R 的整数,这将花费成本。除非,您只使用专门为紧凑存储类型编写的方法(这些可能必须用 c/c++/... 编写)。

于 2013-11-20T15:37:15.370 回答
2

有点开箱即用,但如果元素在某种程度上是有序的,游程编码可能适用于几级的长因子;这可以由Bioconductor中的IRanges包支持

rle = Rle(factor("A"), 1000000)
df = DataFrame(rle=rle)

> object.size(rle)
1528 bytes

DataFrameRle支持所有标准操作,例如,子集,Rle 的添加。当然,节省大小主要取决于维护排序顺序。

于 2013-07-18T12:06:08.897 回答