mmap和csv文件

10

我正在尝试理解如何使用 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结构体,该结构体允许通过mmap函数映射二进制文件cars.Rmap

> 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 结构对于“宽” CSV 文件(即具有数十或数百列的文件)来说可能非常不切实际。这是我的问题:如何可以直接从 mmap 对象 m 构造 record.struct


我对mmap一无所知,但我只是想确保您知道有一个vignette:http://cran.r-project.org/web/packages/mmap/vignettes/mmap.pdf - Xu Wang
@XuWang:谢谢,我知道vignette。这就是让我通过1和2的东西。 - Ryogi
3个回答

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和off参数创建自定义二进制对象,以利用mmap。

谢谢Jeff。我没有意识到m$storage.mode可以直接使用。 - Ryogi
这看起来更加流畅。你是不是编写了mmap包的Jeff Ryan? :) - Iterator
2
回答自己:是的 - 并且您的网站上有一些不错的文章(http://www.lemnica.com/esotericR/)。您的大多数软件包,如果不是全部,都在我的试用列表中。很高兴看到您在SO上。 :) - Iterator

4
我给出另一个答案,因为第一个答案是针对主要问题的(如何直接从mmap对象m构建record.struct?),然而,我认为也有可能解决这个谓词:“推断正确的C-struct对于“宽”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" 

(This is due to cars being stored as doubles in my version of R. Results match yours when the type is changed to integers - see Update 1, below.)
使用这种方法创建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 产生了预期的 "integer" 类型。

更新 2:下面是创建传递给 struct() 的内部参数的一个 hack:

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 类型。


感谢Iterator。我接受了Jeff的答案,因为它直接使用了 storage.mode - Ryogi
感谢您提出了一个好问题。我确信一定有比我所做的类型转换更好的方法。:) 不过,尝试获取数据类型看起来很有趣,可以看看能从哪里获得数据类型。 - Iterator

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接