mmap和csv文件

时间:2011-11-04 05:59:15

标签: r mmap

我正在尝试了解如何使用包mmap 访问大型csv文件。更确切地说,我想

  1. 使用mmapcsv文件创建mmap.csv()个对象;
  2. 保存由mmap.csv()创建的文件,其中包含二进制格式的数据;
  3. 能够使用函数mmap()“将二进制数据映射回R”。
  4. 实现1.和2.很简单:只需使用mmap.cv()并保存tempfile() 包含二进制数据,或修改mmap.cv()以接受额外参数 作为输出文件(并相应地修改行tmpstruct <- tempfile())。 我遇到的麻烦是3.特别是,我需要构建一个 C-struct用于来自mmap对象的二进制数据中的记录。 这是一个简单的可重现的例子:

    # create mmap object with its file
    library(mmap)
    data(cars)
    
    m <- as.mmap(cars, file="cars.Rmap")
    colnames(m) <- colnames(cars)
    str(m) 
    munmap(m)
    

    来自str()的信息可用于构建C结构 record.struct允许映射二进制文件cars.Rmap 通过函数mmap。

    > str(m)
    <mmap:temp.Rmap>  (struct) struct [1:50, 1:2] 4 ...
      data         :<externalptr> 
      bytes        : num 400
      filedesc     : Named int 27
     - attr(*, "names")= chr "temp.Rmap"
      storage.mode :List of 2
     $ speed:Classes 'Ctype', 'int'  atomic (0) 
      .. ..- attr(*, "bytes")= int 4
      .. ..- attr(*, "signed")= int 1
     $ dist :Classes 'Ctype', 'int'  atomic (0) 
      .. ..- attr(*, "bytes")= int 4
      .. ..- attr(*, "signed")= int 1
     - attr(*, "bytes")= int 8
     - attr(*, "offset")= int [1:2] 0 4
     - attr(*, "signed")= logi NA
     - attr(*, "class")= chr [1:2] "Ctype" "struct"
      pagesize     : num 4096
      dim          :NULL
    

    在这种情况下,我们需要两个4字节整数:

    # load from disk
    record.struct <- struct(speed = integer(),  # int32(), 4 byte int
                            dist  = integer()   # int32(), 4 byte int
                            )
    m <- mmap("temp.Rmap", mode=record.struct)
    

    推断正确的C-struct对于“宽”csv文件(即具有数十或数百列的文件)非常不切实际。这是我的问题: 如何直接构建record.struct 来自mmap对象m

3 个答案:

答案 0 :(得分:8)

使用mmap和mmap.csv

或多或少完整的例子
data(mtcars)
tmp <- tempfile()
write.csv(mtcars, tmp)
m <- mmap.csv(tmp)   # mmap in the csv
head(m)
                    X  mpg cyl disp  hp drat    wt  qsec vs am gear carb
1 Mazda RX4           21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
2 Mazda RX4 Wag       21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
3 Datsun 710          22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
4 Hornet 4 Drive      21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
5 Hornet Sportabout   18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
6 Valiant             18.1   6  225 105 2.76 3.460 20.22  1  0    3    1


st <- m$storage.mode

## since m is already mmap'd as a binary, we'll use that here - but you'd store this
m1 <- mmap(attr(m$filedesc, "names"), mode=st, extractFUN=as.data.frame)

head(m1)
                    X  mpg cyl disp  hp drat    wt  qsec vs am gear carb
1 Mazda RX4           21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
2 Mazda RX4 Wag       21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
3 Datsun 710          22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
4 Hornet 4 Drive      21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
5 Hornet Sportabout   18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
6 Valiant             18.1   6  225 105 2.76 3.460 20.22  1  0    3    1

如前所述,m $ storage.mode是您需要的模式

您可以更进一步,使用您设计的一些命名约定将模式存储在文件中。您还可以使用 len 关闭 args创建自定义二进制对象以进行mmap。

答案 1 :(得分:4)

我给出了另一个答案,因为第一个答案是关于主要问题(如何直接从mmap对象record.struct构建m),但是,我认为也可以解决谓词:“推断正确的C-struct对于”宽“的csv文件(即具有数十或数百列的文件)是非常不切实际的。”我的动机是消除CSV文件很难获得类型信息的想法。 :)

假设数据是常规的(即每列的原子数,如果它要获得内存映射,则必须是这样),那么你可以简单地执行此操作:

tmpDF <- read.csv(myFile, nrow = 10)
myClasses <- rapply(tmpDF, typeof)

因此,您只阅读少量信息,让R为您确定课程。您可能需要解决stringsAsFactors问题,即通过read.csv(..., stringsAsFactors = FALSE)

答案 2 :(得分:3)

这应该有效:

varClasses <- rapply(m$storage.mode, typeof)

这是我得到的:

> rapply(m$storage.mode, typeof)
    speed     dist
 "double" "double" 

(这是因为cars在我的R版本中存储为双精度。当类型更改为整数时,结果与您匹配 - 请参阅下面的更新1.)

使用它来创建struct对象只需要用适当的C类型替换这些类型(例如将int更改为integer),这可以通过列表完成查找,然后您可以使用paste创建适当的参数列表。


这是m对我来说的样子,使用与你给出的相同的命令:

> str(m)
<mmap:/tmp/Rtmpz...>  (struct) struct [1:50, 1:2] 4 ...
  data         :<externalptr> 
  bytes        : num 800
  filedesc     : Named int 3
 - attr(*, "names")= chr "/tmp/RtmpzGwIDT/file77aa9d47"
  storage.mode :List of 2
 $ speed:Classes 'Ctype', 'double'  atomic (0) 
  .. ..- attr(*, "bytes")= int 8
  .. ..- attr(*, "signed")= int 1
 $ dist :Classes 'Ctype', 'double'  atomic (0) 
  .. ..- attr(*, "bytes")= int 8
  .. ..- attr(*, "signed")= int 1
 - attr(*, "bytes")= int 16
 - attr(*, "offset")= int [1:2] 0 8
 - attr(*, "signed")= logi NA
 - attr(*, "class")= chr [1:2] "Ctype" "struct"
  pagesize     : num 4096
  dim          :NULL

更新1:当我明确地将cars转换为整数,并确保对象是一个数据框(即cars2 <- as.data.frame(apply(cars, 2, as.integer)); colnames(cars2) = colnames(cars))时,一切正常,rapply生成{{1正如预期的那样。

更新2:这是创建传递给"integer"的内部参数的黑客行为:

struct()

这是近似值,因为我怀疑oTypes = rapply(m$storage.mode, typeof) lNames = names(oTypes) lTypes = as.character(oTypes) lTypes = paste(lTypes,'()', sep = "") lArgs = paste(lNames, lTypes, sep = "=", collapse = ",") 需要从R转换为C类型。