Подтвердить что ты не робот

Как быстро загрузить данные в R?

У меня есть некоторые R-скрипты, где мне нужно как можно быстрее загрузить несколько кадров данных в R. Это очень важно, так как чтение данных является самой медленной частью процедуры. Например: нанесение рисунков из разных фреймов данных. Я получаю данные в формате sav (SPSS), но я могу преобразовать его в любой формат, как было предложено. К сожалению, слияние данных не является вариантом.

Что может быть самым быстрым способом загрузки данных? Я думал о следующем:

  • преобразуется из sav в двоичный объект R (Rdata) в первый раз, а позже всегда загружает это, поскольку это кажется намного быстрее, чем read.spss.
  • преобразуется из файлов sav в csv и считывает данные из данных с заданными параметрами, обсуждаемыми в этой теме,
  • или стоит ли настраивать базу данных MySQL на localhost и загружать данные из этого? Это может быть быстрее? Если да, могу ли я сохранить любые пользовательские значения attr переменных (например, переменные .labels из импортированных файлов Spss)? Или это должно быть сделано в отдельной таблице?

Любые другие мысли приветствуются. Спасибо за каждое предложение заранее!


Я сделал небольшой эксперимент ниже на основе полученных вами ответов, а также добавил (24/01/2011) довольно "хакерское", но очень быстрое решение загружая только несколько переменных/столбцов из специального двоичного файла. Последний, кажется, самый быстрый метод, который я могу себе представить сейчас, поэтому я составил (05/03/2011: вер. 0.3) небольшой пакет с именем saves, чтобы справиться с этой функцией. Пакет находится под "тяжелой" разработкой, любые рекомендации приветствуются!

Я скоро опубликую виньетку с точными результатами тестов с помощью microbenchmark.

4b9b3361

Ответ 1

Это зависит от того, что вы хотите сделать и как вы обрабатываете данные дальше. В любом случае загрузка из двоичного объекта R всегда будет быстрее, если вам всегда нужен один и тот же набор данных. Здесь предельная скорость - это скорость вашего жесткого диска, а не R. Бинарная форма - это внутреннее представление кадра данных в рабочей области, поэтому больше не требуется никакого преобразования.

Любой текстовый файл - это другая история, так как вы постоянно включаете накладные расходы: каждый раз, когда вы читаете в текстовом файле, данные должны быть преобразованы в двоичный объект R. Я бы забыл о них. Они полезны только для переноса наборов данных из одного приложения в другое.

Настройка бэкэнда MySQL очень полезна, если вам нужны разные части данных или разные подмножества в разных комбинациях. Особенно при работе с огромными наборами данных тот факт, что вам не нужно загружать весь набор данных, прежде чем вы сможете начать выбирать строки/столбцы, может получить вам довольно много времени. Но это работает только с огромными наборами данных, так как чтение двоичного файла происходит намного быстрее, чем поиск в базе данных.

Если данные не слишком велики, вы можете сохранить разные фреймы данных в одном файле RData, что даст вам возможность немного упростить процесс. У меня часто есть набор данных в списке или в отдельной среде (см. Также ?environment для некоторых простых примеров). Это позволяет решениям lapply/eapply обрабатывать сразу несколько кадров данных.

Ответ 2

Спасибо всем за советы и ответы, я сделал несколько резюме и эксперимент на основе этого.

См. небольшой тест с открытой базой данных (ESS 2008 в Венгрии) ниже. В базе данных имеется 1508 случаев и 508 переменных, поэтому это могут быть данные среднего размера. Это может быть хорошим примером для проведения теста (для меня), но, конечно, для особых потребностей потребуется эксперимент с адекватными данными.

Чтение данных из файла SPSS sav без каких-либо изменений:

> system.time(data <- read.spss('ESS_HUN_4.sav'))
   user  system elapsed 
  2.214   0.030   2.376 

Загрузка с преобразованным двоичным объектом:

> save('data',file='ESS_HUN_4.Rdata')
> system.time(data.Rdata <- load('ESS_HUN_4.Rdata'))
   user  system elapsed 
   0.28    0.00    0.28 

Попытка с помощью csv:

> write.table(data, file="ESS_HUN_4.csv")
> system.time(data.csv <- read.csv('ESS_HUN_4.csv'))
   user  system elapsed 
  1.730   0.010   1.824 

Попытка с "тонкой настройкой" загрузки csv:

> system.time(data.csv <- read.table('ESS_HUN_4.csv', comment.char="", stringsAsFactors=FALSE, sep=","))
   user  system elapsed 
  1.296   0.014   1.362 

Также с пакетом sqldf, который, как представляется, загружает файлы csv намного быстрее:

> library(sqldf)
> f <- file("ESS_HUN_4.csv")
>  system.time(bigdf <- sqldf("select * from f", dbname = tempfile(), file.format = list(header = T, row.names = F, sep="\t")))
   user  system elapsed 
  0.939   0.106   1.071 

А также загрузка данных из базы данных MySQL, работающей на localhost:

> library(RMySQL) 
> con <- dbConnect(MySQL(), user='root', dbname='test', host='localhost', password='')
> dbWriteTable(con, "data", as.data.frame(data), overwrite = TRUE)
> system.time(data <- dbReadTable(con, 'data'))
   user  system elapsed 
  0.583   0.026   1.055 
> query <-('SELECT * FROM data')
> system.time(data.sql <- dbGetQuery(con, query))
   user  system elapsed 
  0.270   0.020   0.473 

Здесь, я думаю, мы должны добавить два сообщенных system.time, поскольку подключение к данным также учитывается в нашем случае. Прошу прокомментировать, если я что-то не понял.

Но посмотрим, запрашивает ли только некоторые переменные, например. при построении графика в большинстве случаев нам не нужен весь фреймворк данных, и запросить только две переменные достаточно, чтобы создать хороший график из них:

> query <-('SELECT c1, c19 FROM data')
> system.time(data.sql <- dbGetQuery(con, query))
   user  system elapsed 
  0.030   0.000   0.112 

Что кажется действительно замечательным! Конечно, сразу после загрузки таблицы dbReadTable

Резюме: нечего бить, читая все данные из двоичного файла, но чтение некоторых столбцов (или других фильтрованных данных) из одной и той же таблицы базы данных может быть также взвешено в некоторых особых случаях.

Условия тестирования: ноутбук HP 6715b (AMD X2 2 ГГц, 4 ГБ DDR2) с низким уровнем SSD.


ОБНОВЛЕНИЕ (24/01/2011). Я добавил довольно хакерский, но довольно "творческий" способ загрузки только нескольких столбцов двоичного объекта - который выглядит намного быстрее, чем любой метод рассмотрен выше.

Знайте: код будет выглядеть очень плохо, но все же очень эффективно:)

Сначала я сохраняю все столбцы data.frame в разные двоичные объекты через следующий цикл:

attach(data)
for (i in 1:length(data)) {
    save(list=names(data)[i],file=paste('ESS_HUN_4-', names(data)[i], '.Rdata', sep=''))
}
detach(data)

И затем я загружаю два столбца данных:

> system.time(load('ESS_HUN_4-c19.Rdata')) + 
>     system.time(load('ESS_HUN_4-c1.Rdata')) + 
>     system.time(data.c1_c19 <- cbind(c1, c19))
    user  system elapsed 
    0.003   0.000   0.002 

Как выглядит "сверхбыстрый" метод!:) Примечание: он был загружен в 100 раз быстрее, чем самый быстрый (погрузка всего бинарного объекта) выше.

Я составил очень маленький пакет (названный: saves), посмотрите github для получения более подробной информации, если вы заинтересованы.


UPDATE (06/03/2011): новая версия моего маленького пакета (saves) был загружен в CRAN, в котором можно сохранять и загружать переменные еще быстрее - если только пользователю нужно только подмножество доступных переменных в кадре данных или списке. Подробнее см. vignette в источниках пакета или на странице моя домашняя страница, и позвольте мне представить также хороший блок-план некоторых эталонных тестов:

Comparison of different data frame/list loading mechanism by speed

Этот блокнот показывает преимущество использования пакета сохранения для загрузки только поднабора переменных из load и read.table или read.csv из базы, read.spss из иностранных или sqldf или RMySQL пакетов.

Ответ 3

Я очень доволен RMySQL. Я не уверен, правильно ли я получил ваш вопрос, но метки не должны быть проблемой. Существует несколько удобных функций, которые используют только таблицы SQL и имена строк по умолчанию, но, конечно, вы можете использовать некоторые SQL-инструкции.

Я бы сказал (кроме больших наборов данных, которые оправдывают хастл), одна из основных причин использования RMySQL знакома с синтаксисом SQL, чем с функциями Juggling данных. Лично я предпочитаю GROUP BY по совокупности. Обратите внимание, что использование хранимых процедур изнутри R не работает особенно хорошо.

Нижняя строка... настройка локального хоста MySQL - это не слишком много усилий - попробуйте! Я не могу точно сказать о скорости, но у меня есть ощущение, что это быстрее. Однако я попытаюсь вернуться сюда.

EDIT: здесь тест... и победитель: spacedman

# SQL connection
source("lib/connect.R")

dbQuery <- "SELECT * FROM mytable"
mydata <- dbGetQuery(con,dbQuery)
system.time(dbGetQuery(con,dbQuery))
# returns
#user  system elapsed 
# 0.999   0.213   1.715 

save.image(file="speedtest.Rdata")
system.time(load("speedtest.Rdata"))
#user  system elapsed 
#0.348   0.006   0.358 

Размер файла был всего около 1 МБ. MacBook Pro 4 GB Ram 2.4 GHZ Intel Core Duo, Mac OSX 10.6.4, MySQL 5.0.41 Просто никогда не пробовал это, потому что я обычно работаю с большим набором данных, и загрузка - это не проблема, а обработка... если есть проблемы времени. +1 для Q!

Ответ 4

Если это вообще возможно, данные преобразуются в формат csv или другой "простой", чтобы сделать чтение как можно быстрее (см. ответ Joris). Я импортирую файлы csv в массе с помощью функции apply, что-то вроде строк:

list.of.files <- as.list(list.files("your dir"))
lapply(list.of.files, FUN = function(x) {
    my.object <- read.table(...) # or some other function, like read.spss
})