dev.metalisp.survey/src/stats.lisp
2024-10-19 08:55:21 +02:00

65 lines
1.7 KiB
Common Lisp

;;;; -*- 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)))