Что такое идиоматический стиль Джулии для операций с колонками или строками?
Извиняется, если этот довольно общий - хотя и еще вопрос кодирования.
С небольшим количеством времени на моих руках я пытался узнать немного Julia
. Я подумал, что хорошим началом было бы скопировать функцию R
microbenchmark
, чтобы я мог легко сравнивать функции R и Julia.
например. это microbenchmark
вывод для 2 R-функций, которые я пытаюсь подражать:
Unit: seconds
expr min lq median uq max neval
vectorised(x, y) 0.2058464 0.2165744 0.2610062 0.2612965 0.2805144 5
devectorised(x, y) 9.7923054 9.8095265 9.8097871 9.8606076 10.0144012 5
Итак, до сих пор в Джулии я пытаюсь написать идиоматический и, надеюсь, понятный/сложный код. Поэтому я заменил двойной цикл со списком, чтобы создать массив таймингов, например:
function timer(fs::Vector{Function}, reps::Integer)
# funs=length(fs)
# times = Array(Float64, reps, funs)
# for funsitr in 1:funs
# for repsitr in 1:reps
# times[reps, funs] = @elapsed fs[funs]()
# end
# end
times= [@elapsed fs[funs]() for x=1:reps, funs=1:length(fs)]
return times
end
Это дает массив таймингов для каждой из двух функций:
julia> test=timer([vec, devec], 10)
10x2 Array{Float64,2}:
0.231621 0.173984
0.237173 0.210059
0.26722 0.174007
0.265869 0.208332
0.266447 0.174051
0.266637 0.208457
0.267824 0.174044
0.26576 0.208687
0.267089 0.174014
0.266926 0.208741
Мой вопрос (наконец): как я идиоматически применяю такую функцию, как min
, max
, median
для столбцов (или строк) массива без использования цикла?
Я могу, конечно, сделать это легко для этого простого случая с циклом (sim, который я перечеркнул выше), но я не могу найти что-либо в документах, что эквивалентно словам apply(array,1, fun)
или даже colMeans
.
Ближайшая родовая функция, о которой я могу думать,
julia> [mean(test[:,col]) for col=1:size(test)[2]]
2-element Array{Any,1}:
0.231621
0.237173
.., но синтаксис действительно действительно не привлекателен. Есть ли более естественный способ для apply
функций по столбцам или строкам многомерного массива в Julia?
Ответы
Ответ 1
Анонимные функции были в настоящее время медленны в julia, поэтому я не буду использовать их для бенчмаркинга, если вы не будете тестировать анонимные функции. Это даст неправильное предсказание производительности для кода, который не использует анонимные функции в критичных для производительности частях кода.
Я думаю, вы хотите, чтобы две аргументные функции редукции, такие как sum (arr, 1), суммировали по первому размеру. Если функции библиотеки нет, вы можете использовать reducedim
Ответ 2
Требуемая функция mapslices
.
Ответ 3
Я думаю, что @ivarne имеет правильный ответ (и пометил его), но я просто добавлю, что создал функцию apply
как:
function aaply(fun::Function, dim::Integer, ar::Array)
if !(1 <= dim <= 2)
error("rows is 1, columns is 2")
end
if(dim==1)
res= [fun(ar[row, :]) for row=1:size(ar)[dim]]
end
if(dim==2)
res= [fun(ar[:,col]) for col=1:size(ar)[dim]]
end
return res
end
тогда получается то, что я хочу так:
julia> aaply(quantile, 2, test)
2-element Array{Any,1}:
[0.231621,0.265787,0.266542,0.267048,0.267824]
[0.173984,0.174021,0.191191,0.20863,0.210059]
где quantile
является встроенным, который дает min, lq, медианный, uq и max.. точно так же, как microbenchmark.
EDIT Следуя рекомендациям здесь, я протестировал новую функцию mapslice
, которая очень похожа на R apply
и сравнила ее с вышеприведенной функцией. Обратите внимание, что mapslice
имеет dim=1
как срез столбца, тогда как test[:,1]
- это первый столбец... так что противоположность R, хотя он имеет одинаковую индексацию?
# nonsense test data big columns
julia> ar=ones(Int64,1000000,4)
1000000x4 Array{Int64,2}:
# built in function
julia> ms()=mapslices(quantile,ar,1)
ms (generic function with 1 method)
# my apply function
julia> aa()=aaply(quantile, 2, ar)
aa (generic function with 1 method)
# compare both functions
julia> aaply(quantile, 2, timer1([ms, aa], 40))
2-element Array{Any,1}:
[0.23566,0.236108,0.236348,0.236735,0.243008]
[0.235401,0.236058,0.236257,0.236686,0.238958]
Итак, забавы примерно так же быстро, как и друг друга. Из чтения битов списка рассылки Julia они, похоже, намерены выполнить некоторую работу над этим кусочком Julialang, так что создание срезов является ссылкой, а не созданием новых копий каждого фрагмента (строка столбца и т.д.)...