Nota : publiqué una versión ampliada de esta respuesta en mi sitio web .
¿Podría considerar publicar una respuesta similar con el motor R real expuesto?
¡Seguro! Bajamos por la madriguera del conejo.
La primera capa es lm
la interfaz expuesta al programador R. Puede ver la fuente de esto simplemente escribiendo lm
en la consola R. La mayor parte (como la mayoría de la mayoría del código de nivel de producción) está ocupado revisando entradas, configurando atributos de objeto y arrojando errores; pero esta línea sobresale
lm.fit(x, y, offset = offset, singular.ok = singular.ok,
...)
lm.fit
es otra función R, puedes llamarla tú mismo. Si lm
bien funciona convenientemente con fórmulas y marcos de datos, lm.fit
desea matrices, por lo que ese es un nivel de abstracción eliminado. Verificando la fuente lm.fit
, más trabajo ocupado y la siguiente línea realmente interesante
z <- .Call(C_Cdqrls, x, y, tol, FALSE)
Ahora estamos llegando a alguna parte. .Call
es la forma en que R llama al código C. Hay una función C, C_Cdqrls en la fuente R en alguna parte, y necesitamos encontrarla. Aqui esta .
Mirando la función C, de nuevo, encontramos en su mayoría comprobación de límites, limpieza de errores y trabajo ocupado. Pero esta linea es diferente
F77_CALL(dqrls)(REAL(qr), &n, &p, REAL(y), &ny, &rtol,
REAL(coefficients), REAL(residuals), REAL(effects),
&rank, INTEGER(pivot), REAL(qraux), work);
Así que ahora estamos en nuestro tercer idioma, R ha llamado C, que está llamando a fortran. Aquí está el código fortran .
El primer comentario lo dice todo
c dqrfit is a subroutine to compute least squares solutions
c to the system
c
c (1) x * b = y
(Curiosamente, parece que el nombre de esta rutina se cambió en algún momento, pero alguien olvidó actualizar el comentario). Así que finalmente estamos en el punto donde podemos hacer algo de álgebra lineal, y en realidad resolver el sistema de ecuaciones. Este es el tipo de cosas en las que fortran es realmente bueno, lo que explica por qué pasamos por tantas capas para llegar aquí.
El comentario también explica qué va a hacer el código
c on return
c
c x contains the output array from dqrdc2.
c namely the qr decomposition of x stored in
c compact form.
Entonces fortran va a resolver el sistema encontrando la descomposición .Q R
Lo primero que sucede, y con mucho lo más importante, es
call dqrdc2(x,n,n,p,tol,k,qraux,jpvt,work)
Esto llama a la función fortran dqrdc2
en nuestra matriz de entrada x
. ¿Qué es esto?
c dqrfit uses the linpack routines dqrdc and dqrsl.
Así que finalmente hemos llegado a linpack . Linpack es una biblioteca de álgebra lineal fortran que existe desde los años 70. El álgebra lineal más grave eventualmente encuentra su camino hacia linpack. En nuestro caso, estamos utilizando la función dqrdc2
c dqrdc2 uses householder transformations to compute the qr
c factorization of an n by p matrix x.
Aquí es donde se realiza el trabajo real. Me llevaría un buen día completo descubrir qué está haciendo este código, es tan bajo como es posible. Pero genéricamente, tenemos una matriz y queremos factorizarla en un producto X = Q R donde Q es una matriz ortogonal y R es una matriz triangular superior. Es algo inteligente, porque una vez que tienes Q y R puedes resolver las ecuaciones lineales para la regresiónXX= Q RQRQR
XtXβ= XtY
muy facilmente. En efecto
XtX= RtQtQ R = RtR
entonces todo el sistema se convierte
RtR β= RtQty
pero es triangular superior y tiene el mismo rango que X t X , por lo que mientras nuestro problema esté bien planteado, es un rango completo, y también podríamos resolver el sistema reducidoRXtX
R β= Qty
Pero aquí está lo asombroso. es triangular superior, por lo que la última ecuación lineal aquí es justa , por lo que la resolución de β n es trivial. Luego puede subir las filas, una por una, y sustituirlas en las β que ya conoce, obteniendo cada vez una ecuación lineal variable simple para resolver. Entonces, una vez que tienes Q y R , todo se derrumba a lo que se llama sustitución hacia atrás , lo cual es fácil. Puede leer sobre esto con más detalle aquí , donde se elabora un pequeño ejemplo explícito.Rconstant * beta_n = constant
βnorteβQR