ページ

2012年7月12日木曜日

平均にも色々あるようだ

統計の教科書を読みながら久しぶりに Common Lispに触れてみる。

平均と言って、普段使っているのは算術平均。

(1 2 3 4 5)

というリストがあるとして、全要素を合計した値を要素数で割れば良い。

> (+ 1 2 3 4 5)
15

> (/ (+ 1 2 3 4 5) 5)
3

リスト (1 2 3 4 5) の算術平均は 3 と出た。

しかし、成長率とか増加率のような比率を平均したい場合には算術平均は使えないので代わりに幾何平均を使う。

(0.1 0.2 0.3 0.4 0.5)

というリストがある場合。めんどうなので各要素に 1 を足しておく。

mapcar が便利。

> (mapcar #'(lambda (x) (+ x 1)) '(0.1 0.2 0.3 0.4 0.5))
(1.1 1.2 1.3 1.4 1.5)

それで要素全部を掛け算する。

> (* 1.1 1.2 1.3 1.4 1.5)
3.6036

このリストが各年の成長率だとしたら 5 年間で約 3.6 倍になるらしい。

それでは平均して 1 年あたり何パーセントずつ成長しているのかを計算するには

算術平均なら

> (/ (+ 1.1 1.2 1.3 1.4 1.5)  5)
1.3

1.3 という数になる。つまり平均30パーセント増。
要素数が 5 で、それぞれ 1.3倍ずつ増えていくのだから 1.3 の 5 乗になるはず。
実際に計算してみると

> (expt 1.3 5)
3.712929

となるので、さっきの 3.6036 とは違う。だから算術平均では駄目。
そこで全要素の積を計算し、要素数 n 個なら n 乗根を出せば良い。

> (expt (* 1.1 1.2 1.3 1.4 1.5) (/ 1 5))
1.2922523

平均して29パーセントの成長率と言える。しかし、各要素に 1 を足さないで (0.1 0.2 0.3 0.4 0.5) のままで同じ計算 (expt (* 0.1 0.2 0.3 0.4 0.5) 0.2) をすると違う結果 0.26 になる。これで平均26パーセントの成長率だと思ってしまうと間違いになるから注意しないといけない。

それから或る区間を往復した場合の平均速度のような場合は調和平均を使う。

60kmの距離を行きは10 [km/h]、帰りは6[km/h]で往復した場合の平均速度は

> (/ (+ 10 6) 2)
8

算術平均で 8 [km/h] としたら間違い。
60kmの距離を10[km/h] の速さで移動したら所要時間は 60/10 = 6 時間、60kmの距離を 6[km/h] の速さで移動したら所要時間は 60/6 = 10 時間、所要時間の合計は 6 + 10 = 16 時間、距離は 60×2 = 120[km] なので、平均速度は 120/16 = 15/2 = 7.5[km/h] となる。

この場合のリストは (10 6) で、要素数が2個しかないけど、要素数が多い場合はやっぱり mapcar が便利

> (setf a '(10 6))
(10 6)
> (list '/ 1 (list '* (list '/ 1 (length a)) (cons '+ (mapcar #'(lambda (x) (/ 1 x)) a))))
(/ 1 (* (/ 1 2) (+ 1/10 1/6)))
> (eval (list '/ 1 (list '* (list '/ 1 (length a)) (cons '+ (mapcar #'(lambda (x) (/ 1 x)) a)))))
15/2
> (/ 15 2.0)
7.5

ちゃんと 7.5 になってる。

与えられた数字のリストを用いて算術平均、幾何平均、調和平均を出力する clisp のプログラムを作ってみようと考え本を読みながら試行錯誤。

とりあえずこんなのができた。

$ cat sample.lisp
#!/usr/bin/clisp
(defun readlist (&rest args)
        (values (read-from-string
                (concatenate 'string "("
                        (apply #'read-line args)
                        ")"))))

(setf a (readlist))
(setf len (length a))
(format t "list:~A, length:~A~%" a len)

(format t "~%arithmetic average:~%" )
(setf b (cons '+ a))
(format t "~A=~F~%" b (eval b))
(setf c (list '/ b len))
(format t "~A=~F~%" c (eval c))

(format t "~%geometric average:~%")
(setf d (cons '* a))
(format t "~A=~F~%" d (eval d))
(setf e (list 'expt d (/ 1 len)))
(format t "~A=~F~%" e (eval e))

(format t "~%harmonic average:~%")
(setf f (cons '+ (mapcar #'(lambda (x) (/ 1 x)) a)))
(format t "~A=~F~%" f (eval f))
(setf g (list '/ 1 (list '* '(/ 1 len) f)))
(format t "~A=~F~%" g (eval g))


$ echo 1 2 3 4 5 | ./sample.lisp
list:(1 2 3 4 5), length:5

arithmetic average:
(+ 1 2 3 4 5)=15.0
(/ (+ 1 2 3 4 5) 5)=3.0

geometric average:
(* 1 2 3 4 5)=120.0
(EXPT (* 1 2 3 4 5) 1/5)=2.6051712

harmonic average:
(+ 1 1/2 1/3 1/4 1/5)=2.2833333
(/ 1 (* (/ 1 LEN) (+ 1 1/2 1/3 1/4 1/5)))=2.189781