Как быстро загрузить данные в R? - PullRequest
36 голосов
/ 21 января 2011

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

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

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

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


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

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

Ответы [ 4 ]

39 голосов
/ 21 января 2011

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

См. Небольшой тест с общедоступной базой данных ( 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 

Попытка с "штрафом-tuned " 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) с недорогим твердотельным накопителем.


ОБНОВЛЕНИЕ (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 для получения дополнительной информации, если вы заинтересованы.


ОБНОВЛЕНИЕ (03/03/2011) : новая версия моего маленького пакета ( сохраняет ) была загружена в CRAN, в которой можно сохранять и загружать переменные еще быстрее - еслитолько пользователю требуется только подмножество доступных переменных во фрейме или списке данных.См. виньетка в источниках пакета для деталей или тот, что на моей домашней странице , и позвольте мне также представить хороший блокпост с некоторыми сделанными тестами:

Comparison of different data frame/list loading mechanism by speed

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

19 голосов
/ 21 января 2011

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

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

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

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

1 голос
/ 21 января 2011

Если это вообще возможно, преобразуйте данные в csv или другой «простой» формат, чтобы сделать чтение максимально быстрым (см. Ответ Джориса). Я импортирую 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
})
1 голос
/ 21 января 2011

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

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

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

РЕДАКТИРОВАТЬ: вот тест ... и победитель: 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 ГБ, оперативная память 2,4 ГГц, Intel Core Duo, Mac OSX 10.6.4, MySQL 5.0.41. Никогда не пытался, потому что я обычно работаю с большим набором данных, и загрузка - это не проблема, скорее обработка ... если есть проблемы со временемсовсем.+1 за Q!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...