首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Clojure / Incanter中的快速向量数学

Clojure / Incanter中的快速向量数学
EN

Stack Overflow用户
提问于 2010-09-28 14:54:52
回答 5查看 5.8K关注 0票数 23

我目前正在研究Clojure和Incanter作为R的替代方法(并不是因为我不喜欢R,而是尝试新语言很有趣)。我喜欢Incanter,并发现语法很有吸引力,但是矢量化操作比R或Python慢得多。

举个例子,我想使用Incanter向量运算、Clojure映射和R得到向量的一阶差。下面是所有版本的代码和时间。正如你所看到的,R显然更快。

Incanter和Clojure:

代码语言:javascript
复制
(use '(incanter core stats)) 
(def x (doall (sample-normal 1e7))) 
(time (def y (doall (minus (rest x) (butlast x))))) 
"Elapsed time: 16481.337 msecs" 
(time (def y (doall (map - (rest x) (butlast x))))) 
"Elapsed time: 16457.850 msecs"

R:

代码语言:javascript
复制
rdiff <- function(x){ 
   n = length(x) 
   x[2:n] - x[1:(n-1)]} 
x = rnorm(1e7) 
system.time(rdiff(x)) 
   user  system elapsed 
  1.504   0.900   2.561

所以我想知道是否有一种方法来加速Incanter/Clojure中的向量运算?此外,还欢迎使用Clojure的循环、Java数组和/或库的解决方案。

我还向Incanter集团发布了这个问题,到目前为止还没有得到任何回应。

更新:我已经将Jouni的答案标记为已被接受,请参阅下面关于我自己的答案,其中我已经稍微清理了他的代码,并添加了一些基准测试。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2010-09-28 20:29:55

这里有一个Java数组实现,它在我的系统上比您的R代码(YMMV)更快。注意,启用反射警告是优化性能时必不可少的,重复的类型提示对y( def上的提示似乎无助于aset),并将所有内容转换为基本的双值(dotime确保我是一个原始int)。

代码语言:javascript
复制
(set! *warn-on-reflection* true)
(use 'incanter.stats)
(def ^"[D" x (double-array (sample-normal 1e7)))

(time
 (do
   (def ^"[D" y (double-array (dec (count x))))
   (dotimes [i (dec (count x))]
     (aset ^"[D" y
       i
       (double (- (double (aget x (inc i)))
                  (double (aget x i))))))))
票数 14
EN

Stack Overflow用户

发布于 2010-09-29 12:15:44

我的最后解决方案

经过所有的测试后,我找到了两种稍微不同的方法来以足够的速度进行计算。

首先,我使用了具有不同类型的返回值的函数diff,下面是返回向量的代码,但我还计时了返回双数组(用y替换(vec y)和用矩阵y替换Incanter.matrix (vec y) )的版本。此函数仅基于java数组。这是基于Jouni的代码,删除了一些额外的类型提示。

另一种方法是使用Java数组进行计算,并将值存储在瞬态向量中。正如您从时间上看到的,如果没有返回和数组的函数,这比方法1稍微快一些。这是在函数difft中实现的。

所以选择真的取决于你不想和数据做什么。我想一个很好的选择是重载函数,以便它返回调用中使用的相同类型。实际上,将java数组传递给diff而不是向量会使~1s更快。

不同函数的时间:

diff返回向量:

代码语言:javascript
复制
(time (def y (diff x)))
"Elapsed time: 4733.259 msecs"

diff返回Incanter.matrix:

代码语言:javascript
复制
(time (def y (diff x)))
"Elapsed time: 2599.728 msecs"

返回双数组的diff:

代码语言:javascript
复制
(time (def y (diff x)))
"Elapsed time: 1638.548 msecs"

difft:

代码语言:javascript
复制
(time (def y (difft x)))
"Elapsed time: 3683.237 msecs"

函数

代码语言:javascript
复制
(use 'incanter.stats)
(def x (vec (sample-normal 1e7)))

(defn diff [x]
  (let [y (double-array (dec (count x)))
        x (double-array x)] 
   (dotimes [i (dec (count x))]
     (aset y i
       (- (aget x (inc i))
                   (aget x i))))
   (vec y)))


(defn difft [x]
  (let [y (vector (range n))
        y (transient y)
        x (double-array x)]
   (dotimes [i (dec (count x))]
     (assoc! y i
       (- (aget x (inc i))
                   (aget x i))))
   (persistent! y))) 
票数 20
EN

Stack Overflow用户

发布于 2010-09-28 19:38:52

布拉德福德·克罗斯的博客有很多关于这方面的文章(他在链接文本上的初创公司使用这些东西)。一般来说,在内部循环中使用瞬变,类型提示(通过*warn-on-reflection*)等都有利于速度的提高。“Clojure的Joy”有一个关于性能调优的很好的章节,您应该读一下。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3814048

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档