Я запускаю большую симуляцию Монте-Карло и обнаружил, что поднастройка/поиск моих данных — самая медленная часть моего кода. Чтобы протестировать некоторые альтернативы, я сравнил производительность с фреймами данных, data.table и матрицей. Вот эталонный код:
library(data.table)
#install.packages('profvis')
library(profvis)
x.df = data.frame(a=sample(1:10,10000,replace=T), b=sample(1:10,10000,replace=T)) # set up a dataframe
x.dt = as.data.table(x.df) # a data.table
setkey(x.dt,a) # set key for faster searches
x.mat = as.matrix(x.df) # a matrix
profvis({
for (i in 1:10000) {
# test simple subsetting
xsubset.mat = x.mat[100:200,2]
xsubset.df = x.df[100:200,2]
xsubset.dt = x.dt[100:200,2]
# test search preformance
xsearch.mat = x.mat[which(x.df$a==10),2]
xsearch.df = x.df[which(x.df$a==10),2]
xsearch.dt = x.dt[.(10),2]
}
})
Вот мои результаты:
Если серьезно, мне нравится компактный синтаксис data.table, и мне интересно, можно ли что-то сделать, чтобы улучшить его производительность. По словам создателей, он должен быть сверхбыстрым. Я использую его неправильно?
Я мог бы понять более медленные подмножества, учитывая, что [i, j]
может делать так много вещей в зависимости от выражения, что должны быть накладные расходы на начальные проверки. Более медленный поиск кажется мне странным.
Если ваш x.dt одинаков на протяжении этих многих итераций, а поиск/поиск выполняется для целых чисел, таких как a, просто предварительно вычислите подмножества и сохраните их в списке. bs = split(x.dt$b, x.dt$a)
и доступ как bs[[1]]
по мере необходимости.
После еще нескольких тестов я теперь понимаю проблему. Самый быстрый пакет зависит от того, выполняю ли я много маленьких поисков или один большой поиск. Кажется, что data.table имеет много накладных расходов на поиск, что делает его более подходящим для работы с одной огромной таблицей, а не для множества поисков по маленьким.
Рассмотрим следующий код и сравним с оригиналом:
# make a giant table, but search it only once:
x.df = data.frame(a=sample(1:10,100000000,replace=T), b=sample(1:10,100000000,replace=T))
x.dt = as.data.table(x.df)
setkey(x.dt,a)
x.mat = as.matrix(x.df)
profvis({
for (i in 1:1) {
xsubset.mat = x.mat[100:200,2]
xsubset.df = x.df[100:200,2]
xsubset.dt = x.dt[100:200,2]
xsearch.mat = x.mat[which(x.df$a==10),2]
xsearch.df = x.df[which(x.df$a==10),2]
xsearch.dt = x.dt[.(10),2]
}
})
При поиске по небольшим наборам данных «время» не имеет большого значения. Даже если это займет «намного» больше времени, это все еще в наносекундах, так что никого это не волнует. data.table действительно имеет накладные расходы, когда речь идет о наносекундах. Тем не менее, время имеет значение только тогда, когда мы масштабируемся до миллисекунд/секунд/и т. д. — тогда оно становится болезненным, и именно тогда data.table сияет, и в первую очередь это идея, стоящая за ним. Никто не стал бы создавать пакет для повышения производительности за наносекунды в R
Следовательно, ваше утверждение «data.table имеет много накладных расходов на поиск» довольно бессмысленно. Вы должны протестировать большие наборы данных много раз (а не один раз), а затем посмотреть, сколько у них накладных расходов.
подумайте об использовании пакета parallel для моделирования методом Монте-Карло, чтобы запустить код на нескольких ядрах.