On Average
Roger Hui


How do you compute the average in APL? Many authors and speakers say:

      avg←{(+/⍵)÷⍴⍵} 

The function is often used to demonstrate the beauty and power of the language. It is presented in the About the APLs page of Vector. It is the first program in the Dyalog APL Tutorial (page 10). At the recent Dyalog User Conference in Princeton it was presented in at least three sessions, and in one was described as “the very best APL expression”.

Let’s look into it:

      avg 1 2 3 4
2.5

So far so good. What about

      avg¨ (1 2) (3 4 5) (6 5 4 3.14159)
 1.5  4  4.5353975 

The extra blanks hint at the trouble. Application of the monadto the result brings the problem into relief:

      ↑ avg¨ (1 2) (3 4 5) (6 5 4 3.14159)
1.5      
4        
4.5353975

That is, avg should return a scalar rather than a vector result.

Now consider:

      avg 1 2 3 4
2.5
      avg 1 2 3
2
      avg 1 2
1.5
      avg 1

What just happened with the last expression? A few moments of reflection reveal that avg mishandles scalar arguments.

What about matrices and higher-ranked arrays?

      ⎕←data←?3 4⍴10
1 7 4 5
2 0 6 6
9 3 5 8

      avg data
RANK ERROR
avg[0] avg←{(+/⍵)÷⍴⍵}
           ∧

In summary, the problems with avg←{(+/⍵)÷⍴⍵} are:

  It gives a non-scalar result for vector arguments.
  It fails on non-vector arguments; in particular, it gives a puzzling (and wrong) result for scalar arguments.

Fortunately, a better definition readily obtains. It is somewhat longer than the commonly promulgated version, but fixes its defects:

      avg1←{(+⌿⍵)÷⍬⍴(⍴⍵),1}

      avg1 1 2 3 4
2.5

      avg1¨ (1 2) (3 4 5) (6 5 4 3.14159)
1.5 4 4.5353975

      ↑ avg1¨ (1 2) (3 4 5) (6 5 4 3.14159)
1.5 4 4.5353975

      avg1 1
1

      avg1 data
4 3.333333333 5 6.333333333

There is one more point:

      avg ⍳0
1
      avg1 ⍳0
1

I would argue that 0 is a better answer for the average of an empty vector. For example, (+/⍵)÷⍴⍵ ←→ +/⍵÷⍴⍵ and (+⌿⍵)÷⍬⍴(⍴⍵),1 ←→ +⌿⍵÷⍬⍴(⍴⍵),1 for vector , except whenis an empty vector. If instead 0 is that average, then the identity holds for all vectors. Possibly 1 is a reasonable answer, but if so it should be a considered answer and not an unintended consequence of that 0÷0 is 1 .

Finally, the common definition of average in J is arguably “the best expression in J”:

   avgj=: +/ % #

   avgj 1 2 3 4
2.5

   avgj&> 1 2 ; 3 4 5 ; 6 5 4 3.14159
1.5 4 4.5354

   avgj data
4 3.33333 5 6.33333

   avgj 1
1
   avgj i.0
0

Together with the rank operator ("), this definition of the average makes it easy to find the average of various parts of an array.

   avgj"1 data
4.25 3.5 6.25

   ] x=: ? 2 3 4 $ 20
10  7 18  9
 2 12 15  5
15 13 18 10

 0  5  9  6
14  0  6  4
 9  8 15 17

   avgj x
 5    6 13.5  7.5
 8    6 10.5  4.5
12 10.5 16.5 13.5

   avgj"3 x
 5    6 13.5  7.5
 8    6 10.5  4.5
12 10.5 16.5 13.5

   avgj"2 x
      9 10.6667 17 8
7.66667 4.33333 10 9

   avgj"1 x
11 8.5    14
 5   6 12.25

   avgj"0 x
10  7 18  9
 2 12 15  5
15 13 18 10

 0  5  9  6
14  0  6  4
 9  8 15 17


Also appeared in Vector, Volume 24, Number 2&3, 2010-08.

created:  2009-09-17 11:25
updated: 2013-09-28 18:45