Update:
@Roland делает несколько хороших точек в разделе комментариев, и пост лучше для них. Хотя я изначально сосредоточился на проблемах переполнения памяти, он отметил, что даже если этого не происходит, управление памятью разных копий занимает значительное время, что является более распространенной повседневной заботой. В настоящее время также добавлены примеры обеих проблем.
Мне нравится этот вопрос в stackoverflow, потому что я думаю, что это действительно об избежании переполнения стека в R при работе с большими наборами данных. Те, кто не знаком с data.table
семейством set
, могут извлечь пользу из этой дискуссии!
Следует использовать setDT()
при работе с большими наборами данных, которые занимают значительное количество ОЗУ, поскольку операция будет модифицировать каждый объект на месте, сохраняя память. Для данных, которые являются очень небольшим процентом ОЗУ, использование копирования и модификации data.table в порядке.
Создание функции setDT
было фактически вдохновлено следующим потоком переполнения стека, который заключается в работе с большим набором данных (несколько ГБ). Вы увидите мелодию Мэтта Доула, предложив имя 'setDT'.
Convert a data frame to a data.table without copy
Немного больше глубина:
С R, данные хранятся в памяти. Это значительно ускоряет работу, поскольку оперативная память намного быстрее, чем устройства хранения данных. Однако проблема может возникнуть, когда один из наборов данных является большой частью ОЗУ. Зачем? Поскольку база R имеет тенденцию делать копии каждого data.frame
, когда к ним применяются некоторые операции. Это улучшилось после версии 3.1, но адресация выходит за рамки этой публикации. Если вы вытягиваете несколько data.frame
или list
s в один data.frame
или data.table
, использование вашей памяти будет расширяться довольно быстро, потому что в какой-то момент во время работы в ОЗУ имеется несколько копий ваших данных. Если набор данных достаточно велик, у вас может закончиться нехватка памяти, когда будут созданы все копии, и ваш стек будет переполняться. См. Пример ниже. Мы получаем ошибку, а исходный адрес памяти и класс объекта не изменяются.
> N <- 1e8
> P <- 1e2
> data <- as.data.frame(rep(data.frame(rnorm(N)), P))
>
> pryr::object_size(data)
800 MB
>
> tracemem(data)
[1] "<0000000006D2DF18>"
>
> data <- data.table(data)
Error: cannot allocate vector of size 762.9 Mb
>
> tracemem(data)
[1] "<0000000006D2DF18>"
> class(data)
[1] "data.frame"
>
Возможность просто изменить объект на месте без копирования - это большое дело. Это то, что setDT
делает, когда он принимает list
или data.frame
и возвращает data.table
. Тот же пример, что и выше, с использованием setDT
, теперь работает нормально и без ошибок. Меняются как адрес класса, так и адрес памяти, и никаких копий не происходит.
> tracemem(data)
[1] "<0000000006D2DF18>"
> class(data)
[1] "data.frame"
>
> setDT(data)
>
> tracemem(data)
[1] "<0000000006A8C758>"
> class(data)
[1] "data.table" "data.frame"
@Roland указывает на то, что для большинства людей, тем больше беспокойство скорость, которая страдает как побочный эффект такого интенсивного использования управления памятью. Вот пример с меньшими данными, которые не приводят к сбою процессора, и иллюстрирует, насколько быстрее выполняется setDT
для этого задания. Обратите внимание на результаты «tracemem» после data <- data.table(data)
, делая копии data
. Сравните это с setDT(data)
, который не печатает одну копию. Затем мы должны позвонить tracemem(data)
, чтобы увидеть новый адрес памяти.
> N <- 1e5
> P <- 1e2
> data <- as.data.frame(rep(data.frame(rnorm(N)), P))
> pryr::object_size(data)
808 kB
> # data.table method
> tracemem(data)
[1] "<0000000019098438>"
> data <- data.table(data)
tracemem[0x0000000019098438 -> 0x0000000007aad7d8]: data.table
tracemem[0x0000000007aad7d8 -> 0x0000000007c518b8]: copy as.data.table.data.frame as.data.table data.table
tracemem[0x0000000007aad7d8 -> 0x0000000018e454c8]: as.list.data.frame as.list vapply copy as.data.table.data.frame as.data.table data.table
> class(data)
[1] "data.table" "data.frame"
>
> # setDT method
> # back to data.frame
> data <- as.data.frame(data)
> class(data)
[1] "data.frame"
> tracemem(data)
[1] "<00000000125BE1A0>"
> setDT(data)
> tracemem(data)
[1] "<00000000125C2840>"
> class(data)
[1] "data.table" "data.frame"
>
Как это влияет на синхронизацию? Как видим, setDT
намного быстрее для него.
> # timing example
> data <- as.data.frame(rep(data.frame(rnorm(N)), P))
> microbenchmark(setDT(data), data <- data.table(data))
Unit: microseconds
expr min lq mean median max neval uq
setDT(data) 49.948 55.7635 69.66017 73.553 100.238 100 79.198
data <- data.table(data) 54594.289 61238.8830 81545.64432 64179.131 611632.427 100 68647.917
Устанавливаемые функции могут использоваться во многих областях, а не только при преобразовании объектов в data.tables. Вы можете найти более подробную информацию о ссылочной семантике и о том, как применять ее в другом месте, вызывая виньетку на эту тему.
library(data.table)
vignette("datatable-reference-semantics")
Это большой вопрос, и тех, кто думает о использовании R с большими наборами данных, или кто просто хочет ускорить работы с данными активами, могут извлечь выгоду от того, знакомы со значительными улучшениями производительности data.table
ссылочных семантики.
Если данные уже существуют в data.frame, и вы с удовольствием преобразуете этот файл data.frame (или список) в таблицу data.table, затем используйте setDT(). Если вы хотите построить data.table из векторов или чего-то еще, используйте data.table(). Вы никогда не должны делать 'x <- setDT (y)' - Я думаю, вы неправильно понимаете, что это означает, что объект должен быть изменен ссылкой. Возможно, вам нужно пройти через виньетки: http://r-datatable.com/Getting-Started – Frank
И посмотрите, что происходит с таблицей 'ab' data.table после присвоения новых столбцов' d' и 'e': 'd [, newCol: = 1]; абы; e [, newCol: = 1]; ab'. – SymbolixAU
@Frank Я вернусь в литературе. Как вы, возможно, знаете, требуется много итераций для вещей, которые нужно щелкнуть для некоторых людей - как и я. Спасибо, что указал мне в правильном направлении. –