Эффективность операций на структурах данных R

Я задаюсь вопросом, существует ли какая-либо документация об эффективности операций в R, конкретно связанные с манипулированием данными.

Например:

  • Я предполагаю, что эффективно добавить столбцы к кадру данных, потому что я предполагаю, что Вы просто добавляете элемент к связанному списку.
  • Я предполагаю добавлять, что строки медленнее, потому что векторы сохранены в массивах в C level и необходимо выделить новый массив длины n+1 и скопируйте все элементы.

Разработчики, вероятно, не хотят связывать себя с конкретной реализацией, но было бы хорошо иметь что-то более твердое, чем предположения для продолжения.

Кроме того, я знаю основное R подсказка производительности должна использовать векторизованные операции, когда это возможно, в противоположность loops.

  • что относительно различных разновидностей apply?
  • это просто hidden loops?
  • что относительно matrices по сравнению с. data frames?
24
задан Paul 3 March 2016 в 12:59
поделиться

3 ответа

IO данных была одной из особенностей, которую я изучал до того, как посвятил себя изучению R. К лучшему или худшему, вот мои наблюдения и решения/паллиативы по этим вопросам:

1. То, что R не обрабатывает большие данные (>2 ГБ?) Для меня это неправильное название. По умолчанию функции общего ввода данных загружают ваши данные в оперативную память. Не хочу показаться грубым, но для меня это не ошибка - в любой момент, когда мои данные поместятся в доступную мне оперативную память, это то, что мне нужно. Точно так же одной из самых популярных функций SQLite является опция in-memory - у пользователя есть простая возможность загрузить все данные в оперативную память. Если ваши данные не помещаются в память, то R позволяет удивительно легко сохранить их, через подключения к общим системам СУБД (RODBC, RSQLite, RMySQL и т.д.), через опции без запоминания, такие как пакет filehash, и через системы, использующие текущую технологию/практику (например, я могу порекомендовать ff). Другими словами, разработчики R выбрали разумное (и, возможно, оптимальное) значение по умолчанию, от которого очень легко отказаться.

2. Производительность read.table (read.csv, read.delim и др.), наиболее распространенного способа попадания данных в R, может быть улучшена в 5 раз (а по моему опыту часто и намного больше), просто отказавшись от нескольких аргументов read.table по умолчанию - те, которые оказывают наибольшее влияние на производительность, упоминаются в справке R (?read.table). Вкратце, разработчики R сообщают нам, что если вы предоставите значения параметров 'colClasses', 'nrows', 'sep' и 'comment.char' (в частности, передайте в '', если вы знаете, что ваш файл начинается с заголовков или данных в строке 1), то вы увидите значительный прирост производительности. Я обнаружил, что это так.

Вот фрагменты, которые я использую для этих параметров:

Чтобы получить количество строк в вашем файле данных (предоставьте этот сниппет в качестве аргумента к параметру, 'nrows', в вашем вызове read.table):

as.numeric((gsub("[^0-9]+", "", system(paste("wc -l ", file_name, sep=""), intern=T))))

Чтобы получить классы для каждого столбца:

function(fname){sapply(read.table(fname, header=T, nrows=5), class)}  

Примечание: Вы не можете передать этот сниппет в качестве аргумента, вы должны сначала вызвать его, затем передать в возвращаемом значении - другими словами, вызвать функцию, привязать возвращаемое значение к переменной, а затем передать в переменной в качестве значения к параметру 'colClasses' в вашем вызове read.table:

3. Используйте Scan. Имея только немного больше хлопот, вы можете сделать лучше (оптимизируя 'read.table'), используя 'scan' вместо 'read.table' ('read.table' на самом деле просто обертка вокруг 'scan'). И снова, это очень легко сделать. Я использую 'scan' для ввода каждого столбца по отдельности, а затем собираю свой data.frame внутри R, т.е. df = data.frame(cbind(col1, col2,....)).

4. Используйте R's Containers для сохранения вместо обычных файловых форматов (например, 'txt', 'csv'). Родной файл данных R '.RData' - это двоичный формат, который немного меньше сжатого ('.gz') файла данных txt. Вы создаете их, используя save(, ). Вы загружаете их обратно в пространство имен R с помощью load(). Разница во времени загрузки по сравнению с 'read.table' драматична. Например, w/ файл размером 25 MB (несжатый размер)

system.time(read.table("tdata01.txt.gz", sep=","))
=>  user  system elapsed 
    6.173   0.245   **6.450** 

system.time(load("tdata01.RData"))
=> user  system elapsed 
    0.912   0.006   **0.912**   

5. Обращая внимание на типы данных, часто можно получить прирост производительности и уменьшить занимаемую площадь памяти. Этот момент, вероятно, более полезен для получения данных из R. Ключевой момент, который следует помнить здесь, это то, что по умолчанию числа в выражениях R интерпретируются как плавающая запятая с двойной точностью, например, > typeof(5) возвращает "double". Сравните размер объекта с разумным размером массива каждого из них и вы увидите значение (используйте object.size()). Поэтому по возможности принудительно используйте целое число.

Наконец, семейство функций 'apply' (среди прочих) не является "скрытыми циклами" или обертками цикла. Это циклы, реализованные на языке Си с большой разницей в производительности. [edit: AWB правильно указал, что в то время как 'sapply', 'tapply' и 'mapply' реализованы на Си, 'apply' - это просто функция-обёртка.

.
28
ответ дан 28 November 2019 в 23:25
поделиться

Я постараюсь вернуться и предоставить более подробную информацию. Если у вас есть вопросы об эффективности одной операции над другой, то лучше всего сделать профиль своего кода (как предлагает Дирк). Функция system.time() является самым простым способом сделать это, хотя существует много более продвинутых утилит (например, Rprof, как документировано здесь).

Быстрый ответ на вторую часть вашего вопроса:

А как насчет различных вкусов применения? Это просто скрытые петли?

По большей части да, функции apply являются просто циклами и могут быть медленнее, чем для утверждений. Их главное преимущество - более понятный код. Основное исключение, которое я нашел, это lapply, который может быть быстрее, так как кодируется непосредственно на С.

А как насчет матриц против фреймов данных?

Матрицы более эффективны, чем фреймы данных, так как они требуют меньшего объема памяти для хранения. Это связано с тем, что фреймы данных требуют дополнительных атрибутивных данных. Из R Introduction:

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

6
ответ дан 28 November 2019 в 23:25
поделиться

Эти вещи всплывают в списках, в частности, на r-devel. Одним из довольно устоявшихся самородок является то, что, например, операции матрицы, как правило, выполняются быстрее, чем операции data.frame. Затем есть пакеты дополнений, которые хорошо работают -- Пакет Matt's data.table довольно быстр, и Джефф получил xts индексацию, чтобы быть быстрым.

Но "все зависит" -- так что обычно лучше всего советоваться с профилем на вашем конкретном коде. R имеет большую поддержку профилирования, так что Вам следует ее использовать. В моем введении в HPC с помощью R есть несколько примеров профилирования.

11
ответ дан 28 November 2019 в 23:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: