【问题标题】:mmap and csv filesmmap 和 csv 文件
【发布时间】:2011-11-04 05:59:14
【问题描述】:

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

  1. 从带有mmap.csv()csv 文件创建mmap 对象;
  2. 保存mmap.csv()创建的包含二进制格式数据的文件;
  3. 能够使用函数mmap()“将二进制数据映射回R”。

实现 1. 和 2. 很容易:只需使用 mmap.cv() 并保存 tempfile() 包含二进制数据,或修改 mmap.cv() 以接受额外的参数 作为输出文件(并相应地修改tmpstruct <- tempfile() 行)。 我遇到的问题是3。特别是,我需要构建一个 来自 mmap 对象的二进制数据中记录的 C 结构。 这是一个简单的可重现示例:

# 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-struct 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)

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

【问题讨论】:

标签: r mmap


【解决方案1】:

您所问的或多或少完整的示例 - 使用 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 是您需要的 mode

您可以更进一步,使用您设计的一些命名约定将模式存储在文件中。您还可以使用 lenoff 参数创建自定义二进制对象来映射。

【讨论】:

  • 谢谢杰夫。没想到m$storage.mode可以直接使用
  • 这更漂亮。有没有可能你是编写 mmap 包的 Jeff Ryan? :)
  • 回答我自己:是的 - 在your site 上有一些不错的文章。如果不是全部的话,你的大部分包裹都在我的名单上,可以试用。我很高兴看到你在 SO。 :)
【解决方案2】:

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

假设数据是规则的(即每列原子,如果要进行内存映射,则必须如此),那么您可以简单地这样做:

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

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

【讨论】:

    【解决方案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 &lt;- as.data.frame(apply(cars, 2, as.integer)); colnames(cars2) = colnames(cars))时,一切正常,rapply 产生 "integer",正如预期的那样。

    更新 2:这是创建内部参数以传递给 struct() 的技巧:

    oTypes  = rapply(m$storage.mode, typeof)
    lNames  = names(oTypes)
    lTypes  = as.character(oTypes)
    lTypes  = paste(lTypes,'()', sep = "")
    lArgs   = paste(lNames, lTypes, sep = "=", collapse = ",")
    

    这是一个近似值,因为我怀疑 lTypes 需要从 R 类型转换为 C 类型。

    【讨论】:

    • 感谢迭代器。由于直接使用了storage.mode,我接受了 Jeff 的回答。
    • 感谢您提出一个好问题。我确信必须有比像我那样争吵类型更好的方法。 :) 尽管如此,尝试查看可以从何处获取数据类型看起来很有趣。
    猜你喜欢
    • 2018-12-17
    • 1970-01-01
    • 2012-10-24
    • 2011-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-11
    相关资源
    最近更新 更多