2011-11-04 100 views
10

我想了解如何使用包mmap 訪問大型csv文件。更確切地說,我想mmap和csv文件

  1. 創建從csv文件mmap物體mmap.csv();
  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-結構 record.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

+0

我對mmap一無所知,但我只是想確保你知道有一個小插曲:http://cran.r-project.org/web/packages/mmap/vignettes/mmap.pdf –

+0

@徐旺:謝謝,我知道這個小插曲。這是讓我通過1和2。 – Ryogi

回答

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是模式你需要。

您可以更進一步,並使用您的設計的某些命名約定將模式存儲在文件中。您也可以使用lenoff參數指定mmap來創建自定義二進制對象。

+0

謝謝傑夫。我沒有意識到'm $ storage.mode'可以直接使用 – Ryogi

+0

這太過分了。你有可能是同一個Jeff Ryan創作'mmap'軟件包嗎? :) – Iterator

+2

回答自己:是的 - 並且[你的網站]上有一些不錯的文章(http://www.lemnica.com/esotericR/)。你的大部分軟件包如果不是全部都在我的清單上試用。我很高興看到你在這裏。 :) – Iterator

3

這應該工作:

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

這裏就是我得到:

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

(這是由於cars被存儲在我的版本R.結果的雙打比賽中你當類型更改爲整數 - 請參閱下面的Update 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產生"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型。

+0

謝謝迭代器。由於直接使用'storage.mode',我接受了Jeff的回答。 – Ryogi

+0

感謝您提出一個好問題。我確信必須有更好的方式,而不是像我一樣爭吵類型。 :)儘管如此,嘗試查看可以獲取數據類型的位置看起來很有趣。 – Iterator

4

不過,我給另一個答案,因爲第一個答案是對的首要問題(?怎麼能夠直接從MMAP對象m構建record.struct),我認爲這也可以解決謂:「推斷對於「寬」csv文件(即具有數十或數百列的文件),右C結構可能是非常不切實際的。「我的動機是消除對於CSV文件很難獲取類型信息的想法。 :)

假設數據是有規律的(即原子每列,它必須是,如果它要得到內存映射),那麼你可以簡單地這樣做:

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

因此,你只讀入少量信息,讓R爲你確定課程。您可能需要解決stringsAsFactors問題,即通過read.csv(..., stringsAsFactors = FALSE)