файлы mmap и csv - PullRequest
       6

файлы mmap и csv

10 голосов
/ 04 ноября 2011

Я пытаюсь понять, как использовать пакет mmap для доступа к большим файлам CSV. Точнее, я бы хотел

  1. Создание объекта mmap из файла csv с mmap.csv();
  2. Сохранить файл, созданный mmap.csv(), содержащий данные в двоичном формате;
  3. Уметь «отображать двоичные данные обратно в R» с помощью функции mmap().

Достичь 1. и 2. легко: просто используйте mmap.cv() и сохраните tempfile() который содержит двоичные данные, или измените mmap.cv(), чтобы принять дополнительный параметр в качестве выходного файла (и измените строку tmpstruct <- tempfile() соответственно). У меня проблемы с 3. В частности, мне нужно построить C-структура для записей в двоичных данных из объекта 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-структуры может быть очень непрактичным для "широких" CSV-файлов (то есть файлов с десятками или сотнями столбцов). Вот мой вопрос: Как можно построить record.struct напрямую из объекта mmap m?

Ответы [ 3 ]

8 голосов
/ 04 ноября 2011

Более или менее полный пример того, что вы спрашиваете - используя 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 args to mmap.

4 голосов
/ 04 ноября 2011

Я даю другой ответ, потому что первый ответ для основного вопроса ( Как можно построить record.struct непосредственно из объекта mmap m? ), однако, я думаю, что также возможнообратитесь к предикату: «Вывод правильной C-структуры может быть очень непрактичным для« широких »CSV-файлов (то есть файлов с десятками или сотнями столбцов)».Моя мотивация состоит в том, чтобы развеять идею о том, что информацию о типе трудно получить для файлов CSV.:)

Предполагая, что данные являются регулярными (то есть атомарными для каждого столбца, что должно быть, если он собирался отображать память), тогда вы могли бы просто сделать это:* Таким образом, вы читаете лишь небольшое количество информации и позволяете R определять классы для вас.Возможно, вам придется решить проблему stringsAsFactors, например, read.csv(..., stringsAsFactors = FALSE).

3 голосов
/ 04 ноября 2011

Это должно работать:

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 выдает "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.

...