3 ;;; Time-stamp: <2009-04-02 15:51:12 tony>
4 ;;; Creation: <2008-09-08 08:06:30 tony>
6 ;;; Author: AJ Rossini <blindglobe@gmail.com>
7 ;;; Copyright: (c) 2007-2008, AJ Rossini <blindglobe@gmail.com>. BSD.
8 ;;; Purpose: Stuff that needs to be made working sits inside the progns...
10 ;;; What is this talk of 'release'? Klingons do not make software
11 ;;; 'releases'. Our software 'escapes', leaving a bloody trail of
12 ;;; designers and quality assurance people in its wake.
14 ;;; This file contains the current challenges to solve, including a
15 ;;; description of the setup and the work to solve....
20 ;;(asdf:oos 'asdf:compile-op 'lispstat)
21 ;;(asdf:oos 'asdf:load-op 'lispstat)
23 (in-package :lisp-stat-unittests
)
25 ;; tests = 79, failures = 7, errors = 17
27 (describe (run-tests :suite
'lisp-stat-ut
))
28 (run-tests :suite
'lisp-stat-ut
)
31 ;; FIXME: Example: currently not relevant, yet
34 :test-case
'lisp-stat-unittests
::create-proto
35 :suite
'lisp-stat-unittests
::lisp-stat-ut-proto
))
38 (describe 'lisp-stat-ut
)
44 (describe (lift::run-tests
:suite
'lisp-stat-ut-dataframe
))
45 (lift::run-tests
:suite
'lisp-stat-ut-dataframe
)
49 :test-case
'lisp-stat-unittests
::create-proto
50 :suite
'lisp-stat-unittests
::lisp-stat-ut-proto
))
52 (defparameter *my-df-1
*
53 (make-instance 'dataframe-array
54 :storage
#2A
((1 2 3 4 5)
56 :doc
"This is an interesting dataframe-array"
57 :case-labels
(list "x" "y")
58 :var-labels
(list "a" "b" "c" "d" "e")))
60 (setf (dfref *my-df-1
* 0 0) -
1d0
)
64 (make-dataframe #2A
((1 2 3 4 5)
67 (make-dataframe (rand 4 3))
71 (make-instance 'dataframe-array
78 (make-instance 'dataframe-array
85 (make-instance 'dataframe-array
86 :storage
#2A
((1d0 2d0
)
92 (defparameter *my-df-1
*
93 (make-dataframe #2A
((1 2 3 4 5)
95 :caselabels
(list "x" "y")
96 :varlabels
(list "a" "b" "c" "d" "e")
97 :doc
"This is an interesting dataframe-array"))
99 (caselabels *my-df-1
*)
100 (varlabels *my-df-1
*)
103 (defparameter *my-df-2
*
104 (make-instance 'dataframe-array
106 (make-array-from-listoflists
107 (cybertiggyr-dsv::load-escaped
108 "/media/disk/Desktop/sandbox/CLS.git/Data/example-mixed.csv"))
109 :doc
"This is an interesting dataframe-array"))
110 #|
:case-labels
(list "x" "y")
111 :var-labels
(list "a" "b" "c" "d" "e")
119 (describe 'make-matrix
)
121 (defparameter *indep-vars-2-matrix
*
122 (make-matrix (length iron
) 2
124 (mapcar #'(lambda (x y
)
125 (list (coerce x
'double-float
)
126 (coerce y
'double-float
)))
130 (defparameter *dep-var
*
131 (make-vector (length absorbtion
)
135 (mapcar #'(lambda (x) (coerce x
'double-float
))
138 (make-dataframe *dep-var
*)
139 (make-dataframe (transpose *dep-var
*))
141 (defparameter *dep-var-int
*
142 (make-vector (length absorbtion
)
144 :element-type
'integer
145 :initial-contents
(list absorbtion
)))
148 (defparameter *xv
+1a
*
151 :initial-contents
#2A
((1d0 1d0
)
160 (defparameter *xv
+1b
*
165 :initial-contents
'((1d0)
175 (m= *xv
+1a
* *xv
+1b
*) ; => T
177 (princ "Data Set up"))
183 ;; REVIEW: general Lisp use guidance
185 (fdefinition 'make-matrix
)
186 (documentation 'make-matrix
'function
)
188 #| Examples from CLHS
, a bit of guidance.
190 ;; This function assumes its callers have checked the types of the
191 ;; arguments, and authorizes the compiler to build in that assumption.
192 (defun discriminant (a b c
)
193 (declare (number a b c
))
194 "Compute the discriminant for a quadratic equation."
195 (- (* b b
) (* 4 a c
))) => DISCRIMINANT
196 (discriminant 1 2/3 -
2) => 76/9
198 ;; This function assumes its callers have not checked the types of the
199 ;; arguments, and performs explicit type checks before making any assumptions.
200 (defun careful-discriminant (a b c
)
201 "Compute the discriminant for a quadratic equation."
202 (check-type a number
)
203 (check-type b number
)
204 (check-type c number
)
205 (locally (declare (number a b c
))
206 (- (* b b
) (* 4 a c
)))) => CAREFUL-DISCRIMINANT
207 (careful-discriminant 1 2/3 -
2) => 76/9
213 (progn ;; FIXME: Regression modeling
215 ;; data setup in previous FIXME
216 (defparameter *m
* nil
218 ;; need to make vectors and matrices from the lists...
221 (def *m
* (regression-model (list->vector-like iron
)
222 (list->vector-like absorbtion
)))
224 (def m
(regression-model (list->vector-like iron
)
225 (list->vector-like absorbtion
) :print nil
))
229 (send m
:own-methods
)
230 ;; (lsos::ls-objects-methods m) ; bogus?
233 (def m
(regression-model (list->vector-like iron
)
234 (list->vector-like absorbtion
)))
236 (def m
(regression-model (listoflists->matrix-like
(list iron aluminum
))
237 (list->vector-like absorbtion
) :print nil
))
241 (send m
:sweep-matrix
)
242 (format t
"~%~A~%" (send m
:sweep-matrix
))
244 ;; need to get multiple-linear regression working (simple linear regr
245 ;; works)... to do this, we need to redo the whole numeric structure,
246 ;; I'm keeping these in as example of brokenness...
248 (send m
:basis
) ;; this should be positive?
249 (send m
:coef-estimates
) )
252 (progn ;; FIXME: Need to clean up data examples, licenses, attributions, etc.
253 ;; The following breaks because we should use a package to hold
254 ;; configuration details, and this would be the only package outside
255 ;; of packages.lisp, as it holds the overall defsystem structure.
256 (load-data "iris.lsp") ;; (the above partially fixed).
263 (progn ;; FIXME: read data from CSV file. To do.
266 ;; challenge is to ensure that we get mixed arrays when we want them,
267 ;; and single-type (simple) arrays in other cases.
270 (defparameter *csv-num
*
271 (cybertiggyr-dsv::load-escaped
272 #p
"/media/disk/Desktop/sandbox/CLS.git/Data/example-numeric.csv"
276 (nth 0 (nth 0 *csv-num
*))
278 (defparameter *csv-num
*
279 (cybertiggyr-dsv::load-escaped
280 #p
"/media/disk/Desktop/sandbox/CLS.git/Data/example-numeric2.dsv"
281 :field-separator
#\
:))
283 (nth 0 (nth 0 *csv-num
*))
286 ;; The handling of these types should be compariable to what we do for
287 ;; matrices, but without the numerical processing. i.e. mref, bind2,
288 ;; make-dataframe, and the class structure should be similar.
290 ;; With numerical data, there should be a straightforward mapping from
291 ;; the data.frame to a matrix. With categorical data (including
292 ;; dense categories such as doc-strings, as well as sparse categories
293 ;; such as binary data), we need to include metadata about ordering,
294 ;; coding, and such. So the structures should probably consider
296 ;; Using the CSV file:
298 (defun parse-number (s)
299 (let* ((*read-eval
* nil
)
300 (n (read-from-string s
)))
306 (parse-number " 34 ")
308 (+ (parse-number "3.4") 3)
309 (parse-number "3.4 ")
310 (parse-number " 3.4")
311 (+ (parse-number " 3.4 ") 3)
315 ;; (coerce "2.3" 'number) => ERROR
316 ;; (coerce "2" 'float) => ERROR
318 (defparameter *csv-num
*
319 (cybertiggyr-dsv::load-escaped
320 #p
"/media/disk/Desktop/sandbox/CLS.git/Data/example-numeric.csv"
322 :filter
#'parse-number
325 (nth 0 (nth 0 *csv-num
*))
327 (defparameter *csv-num
*
328 (cybertiggyr-dsv::load-escaped
329 #p
"/media/disk/Desktop/sandbox/CLS.git/Data/example-numeric2.dsv"
331 :filter
#'parse-number
))
333 (nth 0 (nth 0 *csv-num
*))
335 ;; now we've got the DSV code in the codebase, auto-loaded I hope:
336 cybertiggyr-dsv
:*field-separator
*
337 (defparameter *example-numeric.csv
*
338 (cybertiggyr-dsv:load-escaped
"Data/example-numeric.csv"
339 :field-separator
#\
,))
340 *example-numeric.csv
*
342 ;; the following fails because we've got a bit of string conversion
343 ;; to do. 2 thoughts: #1 modify dsv package, but mucking with
344 ;; encapsulation. #2 add a coercion tool (better, but potentially
346 #+nil
(coerce (nth 3 (nth 3 *example-numeric.csv
*)) 'double-float
)
348 ;; cases, simple to not so
349 (defparameter *test-string1
* "1.2")
350 (defparameter *test-string2
* " 1.2")
351 (defparameter *test-string3
* " 1.2 ")
356 (progn ;; experiments with GSL and the Lisp interface.
357 (asdf:oos
'asdf
:load-op
'gsll
)
358 (asdf:oos
'asdf
:load-op
'gsll-tests
)
360 ;; the following should be equivalent
361 (setf *t1
* (LIST 6.18d0
6.647777777777779d0
6.18d0
))
362 (setf *t2
* (MULTIPLE-VALUE-LIST
364 (gsll:make-marray
'DOUBLE-FLOAT
365 :INITIAL-CONTENTS
'(-3.21d0
1.0d0
12.8d0
)))
367 (gsll:MAKE-MARRAY
'DOUBLE-FLOAT
368 :INITIAL-CONTENTS
'(3.0d0
1.0d0
2.0d0
))))
369 (LET ((MEAN (gsll:MEAN VEC
)))
370 (LIST (gsll:ABSOLUTE-DEVIATION VEC
)
371 (gsll:WEIGHTED-ABSOLUTE-DEVIATION VEC WEIGHTS
)
372 (gsll:ABSOLUTE-DEVIATION VEC MEAN
))))))
375 ;; from (gsll:examples 'gsll::numerical-integration) ...
376 (gsll:integration-qng gsll
::one-sine
0.0d0 PI
)
378 (gsll:defun-single axpb
(x) (+ (* 2 x
) 3)) ;; a<-2, b<-3
379 (gsll:integration-qng axpb
1d0
2d0
)
383 (defun-single axpb2
(x) (+ (* a x
) b
)))
384 (gsll:integration-qng axpb2
1d0
2d0
)
387 ;; (gsll:integration-qng
390 ;; (defun-single axpb2 (x) (+ (* a x) b)))
393 ;; right, but weird expansion...
394 (gsll:integration-qng
397 (defun axpb2 (x) (+ (* a x
) b
))
398 (gsll:def-single-function axpb2
)
402 ;; Linear least squares
404 (gsll:gsl-lookup
"gsl_linalg_LU_decomp") ; => gsll:lu-decomposition
405 (gsll:gsl-lookup
"gsl_linalg_LU_solve") ; => gsll:lu-solve
411 (progn ;; philosophy time
413 (setf my-model
(model :name
"ex1"
414 :data-slots
(list w x y z
)
415 :param-slots
(list alpha beta gamma
)
416 :math-form
(regression-model :formula
'(= w
(+ (* beta x
)
420 :centrality
'median
; 'mean
427 (setf my-dataset
(statistical-table :table data-frame-contents
428 :metadata
(list (:case-names
(list ))
430 (:documentation
"string of doc"))))
432 (setf my-analysis
(analysis
435 :parameter-map
(pairing (model-param-slots my-model
)
436 (data-var-names my-dataset
))))
438 ;; ontological implications -- the analysis is an abstract class of
439 ;; data, model, and mapping between the model and data. The fit is
440 ;; the instantiation of such. This provides a statistical object
441 ;; computation theory which can be realized as "executable
442 ;; statistics" or "computable statistics".
443 (setf my-analysis
(analyze my-fit
444 :estimation-method
'linear-least-squares-regression
))
446 ;; one of the tricks here is that one needs to provide the structure
447 ;; from which to consider estimation, and more importantly, the
448 ;; validity of the estimation.
451 (setf linear-least-squares-regression
452 (estimation-method-definition
453 :variable-defintions
((list
454 ;; from MachLearn: supervised,
456 :data-response-vars list-drv
; nil if unsup
459 :data-predictor-vars list-dpv
460 ;; nil in this case. these
461 ;; describe "out-of-box" specs
462 :hyper-vars list-hv
))
463 :form
'(regression-additive-error
464 :central-form
(linear-form drv pv dpv
)
465 :error-form
'normal-error
)
466 :resulting-decision
'(point-estimation interval-estimation
)
467 :philosophy
'frequentist
468 :documentation
"use least squares to fit a linear regression
471 (defparameter *statistical-philosophies
*
472 '(frequentist bayesian fiducial decision-analysis
)
473 "can be combined to build decision-making approaches and
476 (defparameter *decisions
*
477 '(estimation selection testing
)
478 "possible results from a...")
479 ;; is this really true? One can embedded hypothesis testing within
480 ;; estimation, as the hypothesis estimated to select. And
481 ;; categorical/continuous rear their ugly heads, but not really in
484 (defparameter *ontology-of-decision-procedures
*
488 (list :maximum-likelihood
493 (list :maximum-likelihood
499 :bioequivalence-inversion
)
504 :partially-parametric
))
505 "start of ontology"))
516 :initial-contents
'((1d0 2d0
3d0
4d0
5d0
6d0
7d0
8d0
))))
522 :initial-contents
'((1d0 1d0
)
532 ;; so something like (NOTE: matrices are transposed to begin with, hence the incongruety)
533 (defparameter *xtx-2
* (m* (transpose *xv
+1*) *xv
+1*))
534 ;; #<LA-SIMPLE-MATRIX-DOUBLE 2 x 2
538 (defparameter *xty-2
* (m* (transpose *xv
+1*) (transpose *y
*)))
539 ;; #<LA-SIMPLE-VECTOR-DOUBLE (2 x 1)
543 (defparameter *rcond-2
* 0.000001)
544 (defparameter *betahat-2
* (gelsy *xtx-2
* *xty-2
* *rcond-2
*))
545 ;; *xtx-2* => "details of complete orthogonal factorization"
546 ;; according to man page:
547 ;; #<LA-SIMPLE-MATRIX-DOUBLE 2 x 2
548 ;; -119.33147112141039d0 -29.095426104883202d0
549 ;; 0.7873402682880205d0 -1.20672274167718d0>
551 ;; *xty-2* => output becomes solution:
552 ;; #<LA-SIMPLE-VECTOR-DOUBLE (2 x 1)
553 ;; -0.16666666666668312d0
554 ;; 1.333333333333337d0>
556 *betahat-2
* ; which matches R, see below
558 (documentation 'gelsy
'function
)
561 ;; (#<LA-SIMPLE-VECTOR-DOUBLE (2 x 1)
562 ;; -0.16666666666668312 1.333333333333337>
565 ;; ## Test case in R:
566 ;; x <- c( 1.0, 3.0, 2.0, 4.0, 3.0, 5.0, 4.0, 6.0)
567 ;; y <- c( 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0)
569 ;; ## => Call: lm(formula = y ~ x)
571 ;; Coefficients: (Intercept) x
578 ;; lm(formula = y ~ x)
581 ;; Min 1Q Median 3Q Max
582 ;; -1.833e+00 -6.667e-01 -3.886e-16 6.667e-01 1.833e+00
585 ;; Estimate Std. Error t value Pr(>|t|)
586 ;; (Intercept) -0.1667 1.1587 -0.144 0.89034
587 ;; x 1.3333 0.3043 4.382 0.00466 **
589 ;; Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
591 ;; Residual standard error: 1.291 on 6 degrees of freedom
592 ;; Multiple R-squared: 0.7619, Adjusted R-squared: 0.7222
593 ;; F-statistic: 19.2 on 1 and 6 DF, p-value: 0.004659
597 ;; which suggests one might do (modulo ensuring correct
598 ;; orientations). When this is finalized, it should migrate to
603 (defparameter *n
* 20) ; # rows = # obsns
604 (defparameter *p
* 10) ; # cols = # vars
605 (defparameter *x-temp
* (rand *n
* *p
*))
606 (defparameter *b-temp
* (rand *p
* 1))
607 (defparameter *y-temp
* (m* *x-temp
* *b-temp
*))
609 (defparameter *rcond
* (* (coerce (expt 2 -
52) 'double-float
)
610 (max (nrows *x-temp
*) (ncols *y-temp
*))))
611 (defparameter *orig-x
* (copy *x-temp
*))
612 (defparameter *orig-b
* (copy *b-temp
*))
613 (defparameter *orig-y
* (copy *y-temp
*))
615 (defparameter *lm-result
* (lm *x-temp
* *y-temp
*))
616 (princ (first *lm-result
*))
617 (princ (second *lm-result
*))
618 (princ (third *lm-result
*))
619 (v= (third *lm-result
*)
620 (v- (first (first *lm-result
*))
621 (first (second *lm-result
*))))
626 ;; Some issues exist in the LAPACK vs. LINPACK variants, hence R
627 ;; uses LINPACK primarily, rather than LAPACK. See comments in R
628 ;; source for issues.
631 ;; Goal is to start from X, Y and then realize that if
632 ;; Y = X \beta, then, i.e. 8x1 = 8xp px1 + 8x1
633 ;; XtX \hat\beta = Xt Y
634 ;; so that we can solve the equation W \beta = Z where W and Z
635 ;; are known, to estimate \beta.
637 ;; the above is known to be numerically instable -- some processing
638 ;; of X is preferred and should be done prior. And most of the
639 ;; transformation-based work does precisely that.
641 ;; recall: Var[Y] = E[(Y - E[Y])(Y-E[Y])t]
642 ;; = E[Y Yt] - 2 \mu \mut + \mu \mut
643 ;; = E[Y Yt] - \mu \mut
645 ;; Var Y = E[Y^2] - \mu^2
648 ;; For initial estimates of covariance of \hat\beta:
650 ;; \hat\beta = (Xt X)^-1 Xt Y
651 ;; with E[ \hat\beta ]
652 ;; = E[ (Xt X)^-1 Xt Y ]
653 ;; = E[(Xt X)^-1 Xt (X\beta)]
656 ;; So Var[\hat\beta] = ...
658 ;; and this gives SE(\beta_i) = (* (sqrt (mref Var i i)) adjustment)
664 (let ((*default-implementation
* :foreign-array
))
670 (rcond (* (coerce (expt 2 -
52) 'double-float
)
671 (max (nrows a
) (ncols a
))))
675 (list x
(gelsy a b rcond
))
676 ;; no applicable conversion?
677 ;; (m- (#<FA-SIMPLE-VECTOR-DOUBLE (10 x 1))
678 ;; (#<FA-SIMPLE-VECTOR-DOUBLE (10 x 1)) )
679 (v- x
(first (gelsy a b rcond
))))))
682 (princ *temp-result
*)
685 (let ((*default-implementation
* :lisp-array
))
691 (rcond (* (coerce (expt 2 -
52) 'double-float
)
692 (max (nrows a
) (ncols a
))))
696 (list x
(gelsy a b rcond
))
697 (m- x
(first (gelsy a b rcond
)))
699 (princ *temp-result
*)
705 :type
:row
;; default, not usually needed!
706 :initial-contents
'((1d0 3d0
2d0
4d0
3d0
5d0
4d0
6d0
))))
712 :initial-contents
'((1d0 2d0
3d0
4d0
5d0
6d0
7d0
8d0
))))
714 ;; so something like (NOTE: matrices are transposed to begin with, hence the incongruety)
715 (defparameter *xtx-1
* (m* *xv
* (transpose *xv
*)))
716 (defparameter *xty-1
* (m* *xv
* (transpose *y
*)))
717 (defparameter *rcond-in
* (* (coerce (expt 2 -
52) 'double-float
)
721 (defparameter *betahat
* (gelsy *xtx-1
* *xty-1
* *rcond-in
*))
723 ;; (#<LA-SIMPLE-VECTOR-DOUBLE (1 x 1)
724 ;; 1.293103448275862>
727 ;; ## Test case in R:
728 ;; x <- c( 1.0, 3.0, 2.0, 4.0, 3.0, 5.0, 4.0, 6.0)
729 ;; y <- c( 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0)
733 ;; lm(formula = y ~ x - 1)
746 (asdf:oos
'asdf
:load-op
'cl-plplot
)
752 (type-of #2A
((1 2 3 4 5)
755 (type-of (rand 10 20))
757 (typep #2A
((1 2 3 4 5)
761 (typep (rand 10 20) 'matrix-like
)
763 (typep #2A
((1 2 3 4 5)
767 (typep (rand 10 20) 'array
)