; Use this structure if you want simple results from data with discrete dependent variables like defect data. (defstruct result target (a 0) (b 0) (c 0) (d 0) acc pf prec pd f g) ; Use this structure if you just want simple results from data with numeric dependent variables like effort. (defstruct effort-result mres) ; Use this structure if you want to use (display-effort-results) to perform ranking tests and draw quartile charts. ; (display-effort-results) takes a list of these: one of these per treatment you want to rank. ; treatment is a text label displayed with the quartile charts. You can get this to whatever you want. (See examples.lisp) (defstruct effort-results treatment mres MDMRES max med min) ; Magnitude of Relative Error - standard measurement of error for numerics. (defun MRE (predicted actual) (abs (/ (- predicted actual) actual))) ; median (defun median (list) (nth (floor (/ (length list) 2)) list)) ; Functions to simplify pushing new MREs to the effort-result(s) data structures. (defun effort-result+ (result predicted actual) (push (MRE predicted actual) (effort-result-mres result))) (defun effort-results+ (result predicted actual) (push (MRE predicted actual) (effort-results-mres result))) ; Computes the MDMRES for n runs, min, max, and med for use in computing quartile charts. (defun effort-results! (res) (setf (effort-results-MDMRES res) (map 'list (lambda (result) (median result)) (effort-results-mres res)) (effort-results-min res) (reduce #'min (effort-results-MDMRES res)) (effort-results-max res) (reduce #'max (effort-results-MDMRES res)) (effort-results-med res) (median (effort-results-MDMRES res)))) (defun abccdddd (list-of-results &optional (crit 97) (ztest #'mann-whitney) (performance #'effort-results-MDMRES)) (let* (( rank 1) (binary-chop-results (divide (sort (doitems (item i list-of-results list-of-results) (effort-results! item)) #'< :key (lambda (item) (effort-results-med item)))))) (do ((chop 0 (incf chop))) ((= (length binary-chop-results) chop)) (if (= chop 0) (doitems (result i (first binary-chop-results)) (format t "~a ~a min: ~4,2f med: ~4,2f max: ~4,2f ~a~%" rank (quartile-line (effort-results-min result) (- (effort-results-med result) (effort-results-min result)) (effort-results-med result) (- (effort-results-max result) (effort-results-med result)) (effort-results-max result) 75 1) (effort-results-min result) (effort-results-med result) (effort-results-max result) (effort-results-treatment result))) (progn (let* ((same-rank t)) (doitems (lower i (nth chop binary-chop-results)) (doitems (upper m (nth (- chop 1) binary-chop-results)) (if (= (funcall ztest (funcall performance lower) (funcall performance upper) crit) 1) (setf same-rank nil)))) (if (equal same-rank nil) (progn (setf same-rank t) (incf rank)))) (doitems (lower i (nth chop binary-chop-results)) (format t "~a ~a min: ~4,2f med: ~4,2f max: ~4,2f ~a~%" rank (quartile-line (effort-results-min lower) (- (effort-results-med lower) (effort-results-min lower)) (effort-results-med lower) (- (effort-results-max lower) (effort-results-med lower)) (effort-results-max lower) 75 1) (effort-results-min lower) (effort-results-med lower) (effort-results-max lower) (effort-results-treatment lower)))))))) (defun divide (list) (if (= (length list) 1) (list list) (concatenate 'list (divide (subseq list 0 (round (/ (length list) 2)))) (list (subseq list (round (/ (length list) 2))))))) ; Compute quartile charts and rank. ; ; list-of-results: a list of effort-results structures for each treatment you performed. Don't forget to set (effort-results-treatment x) ; crit: confidence ranges. 90, 95, 99 currently. More can be added easily. ; ztest: A symbol representing a stat test for ranking. Stat tests need to accept two populations as lists and a critical range. (defun display-effort-results(list-of-results &optional (crit 95) (ztest #'mann-whitney) (performance #'effort-results-MDMRES)) (let* (current-rank-items (rank 1) ; We sort the structures from lowest to greatest medians. (sorted-results (sort (doitems (item i list-of-results list-of-results) (effort-results! item)) #'< :key (lambda (item) (effort-results-med item))))) ; We only check to see if our current result is different from the rank we're currently testing. If it is statistically different, we ; increment the rank and continue. (doitems (result i sorted-results) (let ((same-rank t) (q1 (- (effort-results-med result) (effort-results-min result))) (q3 (- (effort-results-max result) (effort-results-med result)))) (doitems (indice j current-rank-items) (if (= (funcall ztest (funcall performance result) (funcall performance (nth j sorted-results)) crit) 1) (setf same-rank nil) (setf same-rank t))) (if (equal same-rank nil) (progn (setf current-rank-items (make-list 0) same-rank t) (incf rank)) (push i current-rank-items)) (format t "~a ~a min: ~4,2f med: ~4,2f max: ~4,2f ~a~%" rank (quartile-line (effort-results-min result) q1 (effort-results-med result) q3 (effort-results-max result) 50 1) (effort-results-min result) (effort-results-med result) (effort-results-max result) (effort-results-treatment result)))))) ; print a single set of results from an (effort-result) structure (defun effort-stats (results) (let* ((MDMREs (map 'list (lambda (result) (median (effort-result-mres result))) results)) (minimum (reduce #'min MDMREs)) (maximum (reduce #'max MDMREs)) (med (median MDMREs)) (q1 (- med minimum)) (q3 (- maximum med))) (format t "~a min: ~4,2f med: ~4,2f max: ~4,2f" (quartile-line minimum q1 med q3 maximum 50 1) minimum med maximum))) ; Draws the quartile chart line. Feel free to call this directly for custom reporting functions. (defun quartile-line(min q1 median q3 max width scale) (let ((scaledWidth (round (/ width scale))) (scaledMin (round (/ min scale))) (scaledQ1 (round (/ q1 scale))) (scaledMedian (min (round (/ median scale)) width)) (scaledQ3 (min (round (/ q3 scale)) width)) (scaledMax (min (round (/ max scale)) width)) (l (make-string width :initial-element #\Space))) (dotimes (i width) (if (and (>= i scaledMin) (<= i scaledQ1)) (setf (char l i) #\-)) (if (and (>= i scaledQ3) (<= i scaledMax)) (setf (char l i) #\+))) (setf (char l scaledMedian) #\|) (concatenate 'string "[" l "]"))) (defmethod print-object ((r result) str) (with-slots (target pf prec pd f g n c d acc) r (format str "target: ~a n: ~a pf: ~a prec: ~a pd: ~a f: ~a g: ~a acc: ~a" target (+ c d) (round (* 100 pf)) (round (* 100 prec)) (round (* 100 pd)) (round (* 100 f)) (round (* 100 g)) (round (* 100 acc))))) (defun results! (results) "Update all fields in a hash of results" (dohash (klass result results results) (result! result))) (defun result! (result) "Update all fields in one result" (with-slots ( a b c d acc pf prec pd f g n) result (let (notpf (zip (float (expt 10 -16)))) (setf acc (/ (+ a d) (+ zip a b c d)) pf (/ c (+ zip a c )) prec (/ d (+ zip c d )) pd (/ d (+ zip b d )) notpf (- 1 pf) f (/ (* 2 prec pd) (+ zip prec pd)) g (/ (* 2 notpf pd) (+ zip notpf pd))))) result) (defun results0 (tbl) "For each class in a table, generate one place to store results" (let ((results (make-hash-table))) (dolist (klass (table-sym-klass-names tbl) results) (setf (gethash klass results) (make-result :target klass))))) (defun results+ (results actual predicted) (dohash (target result results results) (with-slots (a b c d) result (if (eql actual target) (if (eql predicted actual) (incf d) (incf b)) (if (eql predicted target) (incf c) (incf a))))))