3 ;;; Copyright (c) 2005--2007, by A.J. Rossini <blindglobe@gmail.com>
4 ;;; See COPYRIGHT file for any additional restrictions (BSD license).
5 ;;; Since 1991, ANSI was finally finished. Modified to match ANSI
9 ;;;; XLISP-STAT 2.1 Copyright (c) 1990, by Luke Tierney
10 ;;;; Additions to Xlisp 2.1, Copyright (c) 1989 by David Michael Betz
11 ;;;; You may give out copies of this software; for conditions see the file
12 ;;;; COPYING included with this distribution.
14 (defpackage :lisp-stat-regression-nonlin
16 :lisp-stat-object-system
19 :lisp-stat-compound-data
23 :lisp-stat-regression-linear
)
24 (:shadowing-import-from
:lisp-stat-object-system
25 slot-value call-method call-next-method
)
26 (:export nreg-model nreg-model-proto mean-function theta-hat epsilon
29 (in-package :lisp-stat-regression-nonlin
)
32 ;;;; Nonlinear Regression Model Prototype
35 (defproto nreg-model-proto
36 '(mean-function theta-hat epsilon count-limit verbose
)
38 regression-model-proto
)
40 (defun nreg-model (mean-function y theta
51 "Args: (mean-function y theta &key (epsilon .0001) (count-limit 20)
52 (print t) parameter-names response-name case-labels
53 weights included (vetbose print))
54 Fits nonlinear regression model with MEAN-FUNCTION and response Y using initial
55 parameter guess THETA. Returns model object."
56 (let ((m (send nreg-model-proto
:new
)))
57 (send m
:mean-function mean-function
)
59 (send m
:new-initial-guess theta
)
60 (send m
:epsilon epsilon
)
61 (send m
:count-limit count-limit
)
62 (send m
:parameter-names parameter-names
)
63 (send m
:response-name response-name
)
64 (send m
:case-labels case-labels
)
65 (send m
:weights weights
)
66 (send m
:included included
)
67 (send m
:verbose verbose
)
68 (if print
(send m
:display
))
71 (defmeth nreg-model-proto
:save
()
73 Returns an expression that will reconstruct the regression model."
74 `(nreg-model ',(send self
:mean-function
)
76 ',(send self
:coef-estimates
)
77 :epsilon
',(send self
:epsilon
)
78 :count-limit
',(send self
:count-limit
)
79 :predictor-names
',(send self
:predictor-names
)
80 :response-name
',(send self
:response-name
)
81 :case-labels
',(send self
:case-labels
)
82 :weights
',(send self
:weights
)
83 :included
',(send self
:included
)
84 :verbose
',(send self
:verbose
)))
90 (defmeth nreg-model-proto
:compute
()
92 Recomputes the estimates. For internal use by other messages"
93 (let* ((y (send self
:y
))
94 (weights (send self
:weights
))
95 (inc (if-else (send self
:included
) 1 0))
96 (w (if weights
(* inc weights
) inc
)))
97 (setf (slot-value 'theta-hat
)
98 (nlreg (send self
:mean-function
)
100 (slot-value 'theta-hat
)
102 (send self
:count-limit
)
104 (send self
:verbose
)))
105 (setf (slot-value 'x
)
106 (funcall (make-jacobian (slot-value 'mean-function
)
107 (length (slot-value 'theta-hat
)))
108 (slot-value 'theta-hat
)))
109 (setf (slot-value 'intercept
) nil
)
111 (let ((r (send self
:residuals
)))
112 (setf (slot-value 'residual-sum-of-squares
)
113 (sum (* inc r r
))))))
116 ;;; Slot Accessors and Mutators
119 (defmeth nreg-model-proto
:new-initial-guess
(guess)
120 "Message args: (guess)
121 Sets a new initial uess for parmeters."
122 (setf (slot-value 'theta-hat
) guess
)
123 (send self
:needs-computing t
))
125 (defmeth nreg-model-proto
:theta-hat
()
127 Returns current parameter estimate."
128 (if (send self
:needs-computing
) (send self
:compute
))
129 (coerce (slot-value 'theta-hat
) 'list
))
131 (defmeth nreg-model-proto
:mean-function
(&optional f
)
132 "Message args: (&optional f)
133 With no argument returns the mean function as supplied to m. With an
134 argument F sets the mean function of m to F and recomputes the
136 (when (and f
(functionp f
))
137 (setf (slot-value 'mean-function
) f
)
138 (send self
:needs-computing t
))
139 (slot-value 'mean-function
))
141 (defmeth nreg-model-proto
:epsilon
(&optional eps
)
142 "Message args: (&optional eps)
143 With no argument returns the tolerance as supplied to m. With an argument
144 EPS sets the tolerance of m to EPS and recomputes the estimates."
145 (when (and eps
(numberp eps
))
146 (setf (slot-value 'epsilon
) eps
)
147 (send self
:needs-computing t
))
148 (slot-value 'epsilon
))
150 (defmeth nreg-model-proto
:count-limit
(&optional count
)
151 "Message args: (&optional new-count)
152 With no argument returns the iteration count limit as supplied to m. With
153 an argument COUNT sets the limit to COUNT and recomputes the
155 (when (and count
(numberp count
))
156 (setf (slot-value 'count-limit
) count
)
157 (send self
:needs-computing t
))
158 (slot-value 'count-limit
))
160 (defmeth nreg-model-proto
:parameter-names
(&optional
(names nil set
))
161 "Method args: (&optional names)
162 Sets or returns parameter names."
163 (if set
(setf (slot-value 'predictor-names
) names
))
164 (let ((p-names (slot-value 'predictor-names
))
165 (p (length (slot-value 'theta-hat
))))
166 (if (not (and p-names
(= p
(length p-names
))))
167 (setf (slot-value 'predictor-names
)
168 (mapcar #'(lambda (a) (format nil
"Parameter ~a" a
))
170 (slot-value 'predictor-names
))
172 (defmeth nreg-model-proto
:verbose
(&optional
(val nil set
))
173 "Method args: (&optional val)
174 Sets or retrieves verbose setting. If T iteration info is printed during
176 (if set
(setf (slot-value 'verbose
) val
))
177 (slot-value 'verbose
))
180 ;;; Overrides for Linear Regression Methods
183 (defmeth nreg-model-proto
:x
()
185 Returns the Jacobian matrix at theta-hat."
188 (defmeth nreg-model-proto
:intercept
(&rest args
)
190 Always returns nil. (For compatibility with linear regression.)"
193 (defmeth nreg-model-proto
:fit-values
()
195 Returns the fitted values for the model."
196 (coerce (funcall (send self
:mean-function
) (send self
:theta-hat
))
199 (defmeth nreg-model-proto
:coef-estimates
(&optional guess
)
200 "Message args: (&optional guess)
201 With no argument returns the current parameter estimate. With an
202 argument GUESS takes it as a new initial guess and recomputes
204 (if guess
(send self
:new-initial-guess guess
))
205 (send self
:theta-hat
))
207 (defmeth nreg-model-proto
:predictor-names
() (send self
:parameter-names
))
211 ;;;; Linear Regression Coefficients
215 (defun regression-coefficients (x y
&key
(intercept T
) weights
)
216 "Args: (x y &key (intercept T) weights)
217 Returns the coefficients of the regression of the sequence Y on the columns of
219 (let* ((m (if weights
220 (make-sweep-matrix x y weights
)
221 (make-sweep-matrix x y
)))
222 (n (array-dimension x
1)))
223 (coerce (compound-data-seq
225 (select (car (sweep-operator m
(iseq 1 n
)))
228 (select (car (sweep-operator m
(iseq 0 n
)))
235 ;;;; Nonlinear Regression Functions
238 (defun nlreg1 (f j y initial-beta epsilon count-limit weights verbose
)
239 "Args: (mean-function jacobian y initial-beta
240 epsilon count-limit weights verbose)
241 MEAN-FUNCTION returns the mean response vector for a given parameter vector.
242 JACOBIAN returns the jacobian of MEAN-FUNCTION at a given parameter vector.
243 Y is the observed response vector. Returns the estimated parameter vector
244 obtained by a Gauss-Newton algorithm with backtracking that continues until
245 the COUNT-LIMIT is reached or no component of the parameter vector changes
246 by more than EPSILON."
247 (labels ((rss (beta) ; residual sum of squares
248 (let ((res (- y
(funcall f beta
))))
249 (sum (if weights
(* res res weights
) (* res res
)))))
250 (next-beta (beta delta rss
) ; next beta by backtracking
251 (do* ((lambda 1 (/ lambda
2))
252 (new-rss (rss (+ beta delta
))
253 (rss (+ beta
(* lambda delta
)))))
254 ((or (< new-rss rss
) (< lambda
.0001))
255 (if (>= lambda
.0001)
256 (+ beta
(* lambda delta
))
258 (do* ((delbeta (regression-coefficients
259 (funcall j initial-beta
)
260 (- y
(funcall f initial-beta
))
263 (regression-coefficients
265 (- y
(funcall f beta
))
268 (beta initial-beta
(next-beta beta delbeta rss
))
269 (rss (rss beta
) (rss beta
))
270 (count 0 (1+ count
)))
271 ((or (> count count-limit
) (> epsilon
(max (abs delbeta
))))
272 (if (and verbose
(> count count-limit
))
273 (format t
"Iteration limit exceeded.~%"))
275 (if verbose
(format t
"Residual sum of squares: ~10g~%" rss
)))))
277 (defun make-jacobian (f n
)
279 F is a function of an N-vector. Returns a function that approximates the
280 jacobian function iof F by a symmetric difference."
282 (del (* h
(values-list (column-list (identity-matrix n
))))))
284 (let ((b+ (mapcar #'(lambda (x) (+ b x
)) del
))
285 (b- (mapcar #'(lambda (x) (- b x
)) del
)))
286 ;; FIXME:AJR BAD IDIOM FOLLOWS
287 (apply #'bind-columns
(/ (- (mapcar f b
+) (mapcar f b-
)) (* 2 h
)))))))
289 (defun nlreg (f y guess
&optional
290 (epsilon .0001) (count-limit 20) weights verbose
)
291 "Args: (mean-function y guess &optional
292 (epsilon .0001) (count-limit 20) weights verbose)
293 MEAN-FUNCTION returns the mean response vector for a given parameter vector.
294 Y is the observed response vector. Returns the estimated parameter vector
295 obtained by a Gauss-Newton algorithm that continues until the ITERATION-LIMIT
296 is reached or no component of the parameter vector changes by more than
297 EPSILON. The jacobian of MEAN-FUNCTION is approximated by a symmetric difference."
298 (nlreg1 f
(make-jacobian f
(length guess
)) y guess
299 epsilon count-limit weights verbose
))