9

I have:

(def data [[1 3 4 7 9] [7 6 3 2 7] [1 9 8 6 2]])

I want to average these (element-wise to get):

[3 6 5 5 6]

Like you would in MATLAB:

mean([1 3 4 7 9; 7 6 3 2 7; 1 9 8 6 2])

With Incanter I can do:

(map #(/ % (count m)) (apply plus data))

If data is rather large (and I have lots of them) is there a better way to do this?
Does it help to calculate the (count m) beforehand?
Does it help to defn the #(/ % (count m)) beforehand?

Brad Koch
  • 19,267
  • 19
  • 110
  • 137
Ali
  • 18,665
  • 21
  • 103
  • 138

3 Answers3

8

As of 2013, my recommendation would be to just use core.matrix.stats to import all of this functionality:

(mean [[1 3 4 7 9] [7 6 3 2 7] [1 9 8 6 2]])
=> [3.0 6.0 5.0 5.0 6.0]

core.matrix.stats builds on the core.matrix API, so it will also work on other more optimised implementations of vectors and matrices - this is likely to be a better option if you are doing a lot of heavy matrix processing.

mikera
  • 105,238
  • 25
  • 256
  • 415
8

Here's a pretty clean and simple way to do it:

(def data [[1 3 4 7 9] [7 6 3 2 7] [1 9 8 6 2]])

(defn average [coll] 
  (/ (reduce + coll) (count coll)))

(defn transpose [coll]
   (apply map vector coll))

(map average (transpose data))
=> (3 6 5 5 6)
mikera
  • 105,238
  • 25
  • 256
  • 415
  • Asked myself if (apply + coll) would be more efficient then (reduce + coll). Already answered at http://stackoverflow.com/questions/3153396/clojure-reduce-vs-apply – NielsK Nov 21 '11 at 15:46
  • Reduce is very slightly faster (about 5-10% in informal tests I've just done). But it's down to personal preference really - I tend to think in reduction operations more readily than I do in parameter juggling. – mikera Nov 22 '11 at 01:22
5

Without knowing how to use any of incanter, here's how you could do this "from scratch".

(let [data [[1 3 4 7 9] [7 6 3 2 7] [1 9 8 6 2]]
      num (count data)]
  (apply map (fn [& items]
               (/ (apply + items) num))
         data))

;=> (3 6 5 5 6)
amalloy
  • 89,153
  • 8
  • 140
  • 205
  • Thanks @amalloy it works, but I don't understand how `(apply map (fn ...` would work, shouldn't it be `(apply (map (fn ...` ? – Ali Nov 20 '11 at 23:54
  • 1
    @Ali No, you're applying the `map` function to a sequence of arguments: an anonymous function we construct, and then each element of the `data` sequence. `map` is willing to accept "extra" arguments, by passing them along to the function. For example, `(map + [1 2] [10 20])` ~= `[(+ 1 10) (+ 2 20)]`. – amalloy Nov 21 '11 at 00:00