Файлы mmap и csv
Я пытаюсь понять, как использовать пакет mmap
для доступа к большим файлам csv. Точнее, я хотел бы
- Создайте объект
mmap
из файла csv
с помощью mmap.csv()
;
- Сохраните файл, созданный
mmap.csv()
, содержащий данные в двоичном формате;
- Уметь "сопоставить двоичные данные с R" с помощью функции
mmap()
.
Достижение 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-структуры может быть очень непрактичным для "широких" csv файлов (т.е. файлов с десятками или сотнями столбцов). Вот мой вопрос:
Как можно построить record.struct
напрямую
из объекта mmap m
?
Ответы
Ответ 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 - это режим, который вам нужен.
Вы можете пойти еще дальше и сохранить режим в файле, используя какое-то соглашение об именах вашего проекта. Вы также можете создать собственный двоичный объект, используя аргументы len и off для mmap.
Ответ 2
Я даю другой ответ, потому что первый ответ для основного вопроса (как можно построить record.struct
непосредственно из объекта mmap m
?), однако, я думаю, что также можно обратиться к предикату: "Inferring правая 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 <- 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.