Home Download Tutorial 1 Tutorial 2 Tutorial 3 Tutorial 4 Tutorial 5
;+
; NAME:
;              MREGRESS
;
; PURPOSE:
;              Perform a multiple linear regression fit
;
; USAGE:
;              Result = MREGRESS(X, Y,                           $ 
;                                 MEASURE_ERRORS=measure_errors, $
;                                 SIGMA=sigma,                   $
;                                 INVERSE=inv,                   $
;                                 STATUS=status)
;
; ARGUMENTS:
;       X:     (Input) The array of independent variable data.  X must
;              be dimensioned as an array of Npoints by Nterms, where
;              there are Nterms coefficients (independent variables) to be
;              found and Npoints of samples.
;              Note that this array is transposed with respect to the
;              input of the IDL routine REGRESS.
;
;       Y:     (Input) The vector of dependent variable points.  Y must have 
;              Npoints elements.
;
;
; KEYWORDS:
;   MEASURE_ERRORS : Set this keyword to a vector containing standard
;       measurement errors for each point Y[i].  This vector must be the same
;       length as X and Y.
;
;       For Gaussian errors (e.g. instrumental uncertainties), MEASURE_ERRORS 
;       should be set to the standard deviations of each point in Y. 
;       For Poisson or statistical weighting MEASURE_ERRORS should be
;       set to sqrt(Y).
;
;   SIGMA : Set this keyword to a named variable to recieve the errors
;       on the returned coefficients
;
;   INVERSE : Set this keyword to a named value to receive the structure
;       containing the covariance matrix and other terms that can be re-used
;       to solve the system with the same X and other Y.
;
;   STATUS : Set this keyword to a named variable to receive the status
;       of the operation. Possible status values are:
;         - 0 for successful completion, 
;         - 1 for a singular array (which indicates that the inversion
;           is invalid), and 
;         - 2 which is a warning that a small pivot element was used
;           and that significant accuracy was probably lost.
;       If STATUS is not specified then any error messages will be output
;       to the screen.
;
; RETURN:
;       MREGRESS returns a column vector of coefficients that has Nterms
;       elements.
;
; DESCRIPTION:
;       Adapted from the IDL program REGRESS
;
;       Perform a multiple linear regression fit:
;               Y[i] = A[0]*X[0,i] + A[1]*X[1,i] + ... +
;                      A[Nterms-1]*X[Nterms-1,i]
;
;       This is a variant of the IDL funtion REGRESS, where the
;       covariance matrix and other intermediate arrays can be saved
;       to solve faster the system with successive values of Y and
;       the same X.
;       An noticeable difference is also that MREGRESS does not
;       include a constant term (if necessary, the constant may be
;       included as one of the terms of X).
;
; HISTORY:
;       Written, Ph. Prugniel 2008/01/17 (from the IDL REGRESS)
;-
; CATEGORY:    ULY_UTIL
;------------------------------------------------------------------------------
FUNCTION mregress,x,y, $
                  MEASURE_ERRORS=measure_errors, $
                  SIGMA=sigma, $
                  INVERSE=inv, $
                  STATUS=status

COMPILE_OPT idl2

ON_ERROR,2              ;Return to caller if an error occurs
sy = SIZE(Y)            ;Get dimensions of x and y.
sx = SIZE(X)

ndimX = sx[0]
nterm = (ndimX EQ 1) ? 1 : sx[ndimX]   ; # of terms (coefficients)
nptsX = sx[1]       ;# of observations (samples)
npts = sy[1]            ;# of observations (samples)

IF (nptsX NE npts) THEN MESSAGE, $
        'X and Y have incompatible dimensions.'
IF (ndimX EQ 1) THEN x = REFORM(x, npts, 1, /OVER)   ; change X to a 2D array

nfree = npts-1          ;degs of freedom

invert = 0b
if n_tags(inv) gt 0 then if (size(inv.wx))[1] ne (size(y))[1] then invert = 1b

statarith = check_math(MASK=32) ; mregress may cause an underflow: harmless, handle/ignore

if n_tags(inv) le 0 or invert then begin
    weights = 1/measure_errors^2
    sw = TOTAL(weights)/npts
    weights = weights/sw
    wgt = REBIN(weights,npts,nterm)

; This commented block is the version with a constant term
;    xmean = TOTAL(wgt*x,1, DOUBLE=double)
;    if (nterm gt 1) then $
;      xx = x - REBIN(transpose(xmean),npts,nterm) $ ;x(j,i) - xmean(i)
;    else xx = x - xmean
;    wx = TEMPORARY(wgt) * xx    ;weights(i)*(x(j,i)-xmean(i))
;    sigmax = SQRT(TOTAL(xx*wx,1)/nfree) ;weights(i)*(x(j,i)-xm)*(x(k,i)-xm)
;    ar = matrix_multiply(wx, xx, /ATRANSPOSE)/(nfree * sigmax #sigmax)

    wx = TEMPORARY(wgt) * x    ;weights(i)*x(j,i)
    
    sigmax = SQRT(TOTAL(x*wx,1)/nfree) ;weights(i)*x(j,i)*(x(k,i)-xm)

    ar = matrix_multiply(wx, x, /ATRANSPOSE)/(nfree * sigmax #sigmax)

    ar = INVERT(ar, status)
    IF (status EQ 1L && ~ARG_PRESENT(status)) THEN BEGIN
        IF (ndimX EQ 1) THEN x = REFORM(x, /OVER) ; change X back to a vector
        MESSAGE, "Inversion failed due to singular array."
    END
    IF (status EQ 2L && ~ARG_PRESENT(status)) THEN BEGIN
        MESSAGE, /INFO, $
          "Inversion used a small pivot element. Results may be inaccurate."
    endif

    ; error terms
    sigma = ar[LINDGEN(nterm)*(nterm+1)]/(sw*nfree*sigmax^2)
    neg  = where(sigma lt 0, cnt)  ; may have sigma^1<0 if small pivot
    if cnt gt 0 then sigma[neg] = 0
    sigma = sqrt(sigma)

    inv = {a:ar, ww:weights, wx:wx, sx:sigmax, sigma:sigma}
endif

; this commented block is the version with constant term
;ymean = TOTAL(y*inv.ww)   ;y mean
;yy = y-ymean
;sigmay = SQRT(TOTAL(inv.ww * yy^2)*npts/nfree) ;weights*(y(i)-ymean)
;correlation = matrix_multiply(inv.wx, yy, /ATRANSPOSE) / (inv.sx * sigmay * nfree)

sigmay = SQRT(TOTAL(inv.ww * y^2)/nfree)    ;weights*y(i)

correlation = matrix_multiply(inv.wx, y, /ATRANSPOSE) / (inv.sx*sigmay*nfree)
a = (correlation # inv.a)*(sigmay/inv.sx)   ;get coefficients

;yfit = matrix_multiply(a,x, /BTRANSPOSE)      ;compute fit
;const = ymean - TOTAL(a*xmean)                ;constant term
;yfit = yfit + const                           ;add it in

IF (ndimX EQ 1) THEN x = REFORM(x, /OVER)     ; change X back to a vector

if statarith eq 0 then statarith = check_math(MASK=32)

return, a
END
;--- end ---

Part of ULySS package