;;;; -*- mode: common-lisp; coding: utf-8; -*- (defpackage ml-survey/stats (:use #:cl) (:export #:preprocess-and-transpose #:calculate-statistics #:stdev #:average #:median #:geomean)) (in-package #:ml-survey/stats) (defun preprocess-and-transpose (data) (apply #'mapcar #'list (mapcar #'cdr data))) (defun calculate-statistics (numbers) (list :median (median numbers) :average (average numbers) :geomean (geomean numbers) :stdev (stdev numbers) :min (reduce #'min numbers) :max (reduce #'max numbers))) (defun average (numbers) (if (null numbers) 0 (* 1.0 (/ (reduce #'+ numbers) (length numbers))))) (defun geomean (numbers) (if (null numbers) 0 (expt (reduce #'* numbers) (/ 1.0 (length numbers))))) (defun median-odd (numbers) (nth (floor (length numbers) 2) numbers)) (defun median-even (numbers) (let* ((mid (floor (length numbers) 2)) (pair (list (nth (1- mid) numbers) (nth mid numbers)))) (average pair))) (defun median (numbers) (let ((sorted (sort (copy-list numbers) #'<))) (if (oddp (length sorted)) (median-odd sorted) (median-even sorted)))) (defun range (numbers) (- (reduce #'max numbers) (reduce #'min numbers))) (defun variance (numbers) (if (< (length numbers) 2) nil ; or we could return nil to indicate undefined variance (let* ((n (length numbers)) (average-value (average numbers)) (squared-diff (mapcar (lambda (x) (expt (- x average-value) 2)) numbers))) (/ (reduce #'+ squared-diff) (1- n))))) (defun stdev (numbers) (sqrt (variance numbers)))