Nota : ho pubblicato una versione estesa di questa risposta sul mio sito Web .
Considereresti gentilmente di pubblicare una risposta simile con l'attuale motore R esposto?
Sicuro! Scendiamo nella tana del coniglio.
Il primo livello è lm
, l'interfaccia esposta al programmatore R. Puoi vedere la fonte per questo semplicemente digitando lm
sulla console R. La maggior parte (come la maggior parte della maggior parte del codice del livello di produzione) è impegnata nel controllo degli input, nell'impostazione degli attributi degli oggetti e nel lancio di errori; ma questa linea sporge
lm.fit(x, y, offset = offset, singular.ok = singular.ok,
...)
lm.fit
è un'altra funzione R, puoi chiamarla tu. Mentre lm
funziona comodamente con formule e frame di dati, lm.fit
desidera matrici, quindi questo è un livello di astrazione rimosso. Verifica della fonte per lm.fit
, più lavoro occupato e la seguente riga davvero interessante
z <- .Call(C_Cdqrls, x, y, tol, FALSE)
Adesso stiamo andando da qualche parte. .Call
è il modo di R di chiamare nel codice C. C'è una funzione C, C_Cdqrls nell'origine R da qualche parte, e dobbiamo trovarla. Eccolo .
Osservando la funzione C, troviamo ancora il controllo dei limiti, la pulizia degli errori e il lavoro occupato. Ma questa linea è diversa
F77_CALL(dqrls)(REAL(qr), &n, &p, REAL(y), &ny, &rtol,
REAL(coefficients), REAL(residuals), REAL(effects),
&rank, INTEGER(pivot), REAL(qraux), work);
Quindi ora siamo nella nostra terza lingua, R ha chiamato C che sta chiamando fortran. Ecco il codice fortran .
Il primo commento dice tutto
c dqrfit is a subroutine to compute least squares solutions
c to the system
c
c (1) x * b = y
(interessante, sembra che il nome di questa routine sia stato cambiato ad un certo punto, ma qualcuno ha dimenticato di aggiornare il commento). Quindi siamo finalmente nel punto in cui possiamo fare un po 'di algebra lineare e risolvere effettivamente il sistema di equazioni. Questo è il genere di cose in cui Fortran è davvero bravo, il che spiega perché siamo passati attraverso così tanti livelli per arrivare qui.
Il commento spiega anche cosa farà il codice
c on return
c
c x contains the output array from dqrdc2.
c namely the qr decomposition of x stored in
c compact form.
Così fortran sta per risolvere il sistema trovando il decomposizione.Q R
La prima cosa che succede, e di gran lunga la più importante, è
call dqrdc2(x,n,n,p,tol,k,qraux,jpvt,work)
Questo chiama la funzione fortran dqrdc2
sulla nostra matrice di input x
. Che cos'è questo?
c dqrfit uses the linpack routines dqrdc and dqrsl.
Quindi finalmente ce l'abbiamo fatta a Linpack . Linpack è una libreria di algebra lineare fortran che esiste dagli anni '70. L'algebra lineare più seria alla fine trova la sua strada per linpack. Nel nostro caso, stiamo usando la funzione dqrdc2
c dqrdc2 uses householder transformations to compute the qr
c factorization of an n by p matrix x.
Qui è dove viene svolto il lavoro effettivo. Ci vorrebbe un bel giorno intero per capire cosa sta facendo questo codice, è di livello basso come arrivano. Ma genericamente, abbiamo una matrice e vogliamo fattorizzarla in un prodotto X = Q R dove Q è una matrice ortogonale e R è una matrice triangolare superiore. Questa è una cosa intelligente da fare, perché una volta che hai Q e R puoi risolvere le equazioni lineari per la regressioneXX= Q RQRQR
XtXβ= XtY
molto facilmente. Infatti
XtX= RtQtQ R = RtR
così l'intero sistema diventa
RtR β= RtQty
ma è triangolare superiore e ha lo stesso rango di X t X.RXtX , quindi fintanto che il nostro problema è ben posto, è di rango massimo e potremmo anche risolvere il sistema ridotto
R β= Qty
Ma ecco la cosa fantastica. è triangolare superiore, quindi l'ultima equazione lineare qui è giusta , quindi risolvere per β n è banale. Puoi quindi risalire le righe, una ad una, e sostituire le β che già conosci, ottenendo ogni volta una semplice equazione lineare a una variabile da risolvere. Quindi, una volta che hai Q e R , tutto crolla in quella che viene chiamata sostituzione all'indietro , il che è facile. Puoi leggere questo in modo più dettagliato qui , dove un piccolo esempio esplicito è completamente elaborato.Rconstant * beta_n = constant
βnβQR