median() and mean()

3 views
Skip to first unread message

Beau Hartshorne

unread,
Jun 21, 2006, 7:32:13 PM6/21/06
to MochiKit
I needed median and mean functions for something I'm working on.
Should they go into MochiKit somewhere? Are they correct? Fast enough?

/* http://www.nist.gov/dads/HTML/mean.html */
var mean = function() {
var data = flattenArguments(arguments);
return sum(data) / data.length;
};

/* http://www.nist.gov/dads/HTML/median.html */
var median = function() {
var data = flattenArguments(arguments);
data.sort(compare);
if (data.length % 2 == 0) {
var upper = data.length / 2;
return (data[upper] + data[upper - 1]) / 2;
} else {
return data[(data.length - 1) / 2];
}
};

>>> mean(1, [2, [3]]) === 2
true

>>> mean(1, 2, 3) === 2
true

>>> median(3, 1, 2) === 2
true

>>> median(4,2,3,1) === 2.5
true

Moe Aboulkheir

unread,
Jun 21, 2006, 8:18:58 PM6/21/06
to MochiKit

On Wed, 21 Jun 2006 16:32:13 -0700, Beau Hartshorne <harts...@gmail.com>

> >>> mean(1, [2, [3]]) === 2
>true

that's a bit weird. maybe mean/median should expect a single, flat list argument? wanting to know the mean of a bunch of nested lists is such a bizarre request that i don't think it's unreasonable to require that the caller flattens the arguments.

Bob Ippolito

unread,
Jun 21, 2006, 8:30:58 PM6/21/06
to Beau Hartshorne, MochiKit

On Jun 21, 2006, at 4:32 PM, Beau Hartshorne wrote:

>
> I needed median and mean functions for something I'm working on.
> Should they go into MochiKit somewhere? Are they correct? Fast enough?
>
> /* http://www.nist.gov/dads/HTML/mean.html */
> var mean = function() {
> var data = flattenArguments(arguments);
> return sum(data) / data.length;
> };
>
> /* http://www.nist.gov/dads/HTML/median.html */
> var median = function() {
> var data = flattenArguments(arguments);
> data.sort(compare);
> if (data.length % 2 == 0) {
> var upper = data.length / 2;
> return (data[upper] + data[upper - 1]) / 2;
> } else {
> return data[(data.length - 1) / 2];
> }
> };

These look correct and fast enough. I might've written mean such that
it inlined more or less what flattenArguments does and calculated the
sum and length at the same time without building an intermediate
Array at all. median could be similarly special cased but that would
be a pain in the ass to do faster and it isn't really very commonly
used anyway. Note that using sum() currently introduces a dependency
on MochiKit.Iter, but I'd be open to moving it to MochiKit.Base and
specialize based upon the input type and iter availability like map()
does.

The empty list cases should be handled somehow.. mean obviously
returns NaN because it's doing divide by zero, but median much less
obviously returns NaN: (undefined + undefined)/2. I'm not sure if
people would expect 0.0 or an error to be thrown, I doubt NaN is
desired though.

Renaming mean to average or avg might make sense. I think more people
are familiar with the term average.

-bob

Bob Ippolito

unread,
Jun 21, 2006, 8:55:14 PM6/21/06
to Moe Aboulkheir, MochiKit

That's basically a side-effect of allowing both mean([1,2,3]) and mean
(1,2,3) via flattenArguments.

-bob

Moe Aboulkheir

unread,
Jun 21, 2006, 9:23:30 PM6/21/06
to MochiKit
On Wed, 21 Jun 2006 17:55:14 -0700, Bob Ippolito <b...@redivi.com> wrote:
>
>On Jun 21, 2006, at 5:18 PM, Moe Aboulkheir wrote:
>>...wanting to know the mean of a bunch of nested lists is such a
>>bizarre request that i don't think it's unreasonable to require that the
>>caller flattens the arguments.
>
>That's basically a side-effect of allowing both mean([1,2,3]) and mean
>(1,2,3) via flattenArguments.

i'm not so sure that the two character win of mean(1, 2, 3) over mean([1, 2, 3]) is worth it. debugging javascript is troublesome as it is, and returning a result for ambiguous/nonsense inputs like 1, [2, [3]] can't help, especially when it's inconsistent with the behaviour of similar functions in the same library - e.g. MochiKit.Iter.sum(1, [2, [3]]) -> ValueError: "1 is not iterable" (and confusingly, MochiKit.Iter.sum([1, [2, [3]]]) -> "12,3" in MK 1.4)

Bob Ippolito

unread,
Jun 21, 2006, 10:24:52 PM6/21/06
to Moe Aboulkheir, MochiKit

It's pretty much consistent with what Python does:

>>> min(1,2,3)
1
>>> min([1,2,3])
1
>>> sum(1,2,3)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: sum expected at most 2 arguments, got 3
>>> sum([1,2,3])
6

I don't think the collapsing of nested array is a big deal. I
wouldn't document it as a feature, it's just an implementation detail.

The fact that adding a number and an array gives you a nonsense
string is just something you have to live with in JavaScript.

-bob

Beau Hartshorne

unread,
Jun 21, 2006, 11:24:38 PM6/21/06
to Bob Ippolito, MochiKit

I'll work on mean. Moving sum seems like way more work than just
writing a loop. Instead of renaming mean, what about aliasing it (and
note that in the docs)? Here's what JavaScript does with similar
functions that expect arguments:

>>> Math.min()
Infinity
>>> Math.max()
-Infinity
>>> Math.ceil()
NaN
>>> Math.cos()
NaN

And MochiKit:

>>> listMin()
TypeError: Undefined value [...]
>>> listMax()
TypeError: Undefined value [...]
>>> listMin([])
null
>>> listMax([])
null

I think listMin and listMax follow Math.min and .max. mean and median
should probably return NaN.

Reply all
Reply to author
Forward
0 new messages