Yo vi esta lista de aquí y no podía creer que había muchas formas de resolver los mínimos cuadrados. El "normal ecuaciones" en la Wikipedia que parecía ser una bastante sencilla: $$ {\displaystyle {\begin{aligned}{\hat {\alpha }}&={\bar {y}}-{\hat {\beta }}\,{\bar {x}},\\{\hat {\beta }}&={\frac {\sum _{i=1}^{n}(x_{i}-{\bar {x}})(y_{i}-{\bar {y}})}{\sum _{i=1}^{n}(x_{i}-{\bar {x}})^{2}}}\end{aligned}}} $$
Así que ¿por qué no usarlos? Supuse que no debe ser un computacional o precisión de la cuestión, dado que en el primer enlace sobre Mark L. Piedra, menciona que el SVD o QR son métodos populares en software estadístico y de que las ecuaciones normales son "TERRIBLES de la fiabilidad y la precisión numérica punto de vista". Sin embargo, en el código siguiente, la normal de ecuaciones me están dando la precisión de ~12 decimales cuando se compara con tres populares python funciones: numpy del polyfit; scipy del linregress; y scikit-learn LinearRegression.
Lo más interesante es que el normal de la ecuación es el método más rápido cuando n = 100000000. El tiempo de computación para mí son: 2.5 s para linregress; 12.9 s para polyfit; 4.2 s para LinearRegression; y 1.8 s para el normal de la ecuación.
Código:
import numpy as np
from sklearn.linear_model import LinearRegression
from scipy.stats import linregress
import timeit
b0 = 0
b1 = 1
n = 100000000
x = np.linspace(-5, 5, n)
np.random.seed(42)
e = np.random.randn(n)
y = b0 + b1*x + e
# scipy
start = timeit.default_timer()
print(str.format('{0:.30f}', linregress(x, y)[0]))
stop = timeit.default_timer()
print(stop - start)
# numpy
start = timeit.default_timer()
print(str.format('{0:.30f}', np.polyfit(x, y, 1)[0]))
stop = timeit.default_timer()
print(stop - start)
# sklearn
clf = LinearRegression()
start = timeit.default_timer()
clf.fit(x.reshape(-1, 1), y.reshape(-1, 1))
stop = timeit.default_timer()
print(str.format('{0:.30f}', clf.coef_[0, 0]))
print(stop - start)
# normal equation
start = timeit.default_timer()
slope = np.sum((x-x.mean())*(y-y.mean()))/np.sum((x-x.mean())**2)
stop = timeit.default_timer()
print(str.format('{0:.30f}', slope))
print(stop - start)