Orthogonal Distance Regression (ODR) not converging when I flip x and y input
See original GitHub issueI’m having some trouble getting scipy’s Orthogonal Distance Regression (ODR) to converge on the same line if I flip the x input and y input (by same line, I mean I take the coefficients estimated by ODR, set y=0
and solve for x
(or x=0
and solve for y
when flipped) : y = mx + b
x = my + b
- the value of x
is different when I flip x
and y
input). Because ODR should be calculating total least squares, the line should be the same no matter input order.
I’ve found that if I play with maxit and sstol I can get close to a convergence, but the values aren’t necessarily appropriate for every x
/y
pair.
Am I missing a step, or misunderstanding this? As you can see from the two figures below, when I flip x
and y
input I get different lines (you can visually tell they’re different by the number of points each intersects).
Reproducing code example:
import scipy.odr as odr # orthogonal distance regression
import scipy
import seaborn as sns
from scipy.stats import linregress
import matplotlib.pyplot as plt
import pandas as pd
def linear_fit(coefs, x):
'''Linear function y = m*x + b'''
m,b = coefs
return m*x + b
def perform_odr(x, y):
OLS_m,OLS_b = linregress(x, y)[:2] # estimate coefficients from OLS
linmod = scipy.odr.Model(linear_fit)
mydata = scipy.odr.Data(x, y)
myodr = scipy.odr.ODR(mydata, linmod, beta0=[OLS_m, OLS_b]) #, maxit=75, sstol=0.000000001)
return myodr.run()
def plot_odr(x, y, ax=None):
"""Plot Orthogonal Distance Regression line of best fit."""
if ax is None:
fig, ax = plt.subplots()
fitted = perform_odr(x, y)
fitted.pprint()
print('y = %sx + %s' % tuple(round(coef, 5) for coef in fitted.beta))
# extend line to boundaries of figure by adding points corresponding to xmin/xmax
xlims = plt.xlim()
ylims = plt.ylim()
xnew = pd.Series(xlims[0]).append(x)
xnew = xnew.append(pd.Series(xlims[1]))
# plot
plt.plot(xnew, linear_fit(fitted.beta, xnew), 'k-', linewidth=1.5, label='ODR regression', zorder=1)
plt.xlim(xlims)
plt.ylim(ylims)
return ax, fitted
x = [25.88, 33.77, 26.55, 36.17, 36.57, 32.43, 38.65, 37.2, 33.57, 32.06, 25.48, 33.12, 26.24, 38.0, 37.25, 23.4, 34.28, 30.8, 34.22, 36.76, 30.46, 33.61, 35.32, 24.99, 39.23, 29.22, 32.54, 29.28, 31.31, 33.46, 28.06, 39.59, 28.98, 28.12, 27.21, 36.54, 25.35]
y = [0.05959377810359001, 0.06366157531738281, 0.03445526957511902, 0.021249201148748398, 0.013540455140173435, 0.033232174813747406, 0.02101573720574379, 0.021147532388567924, 0.014944841153919697, 0.02261320687830448, 0.04238538816571236, 0.02475816197693348, 0.04893920198082924, 0.01641816273331642, 0.02175772748887539, 0.029113013297319412, 0.019221415743231773, 0.019603392109274864, 0.01673588529229164, 0.020033005625009537, 0.02505655400454998, 0.03193040192127228, 0.02318655699491501, 0.032157305628061295, 0.017262479290366173, 0.02122090384364128, 0.04239807650446892, 0.028340162709355354, 0.02409830316901207, 0.029440563172101974, 0.026672156527638435, 0.010255633853375912, 0.02117013931274414, 0.027912333607673645, 0.020978346467018127, 0.015505819581449032, 0.02792999893426895]
fig, ax = plt.subplots()
plt.scatter(x,y)
ax,fit = plot_odr(x, y, ax)
fig, ax = plt.subplots()
plt.scatter(y,x)
ax,fit = plot_odr(y, x, ax)
Scipy/Numpy/Python version information:
import sys, scipy, numpy; print(scipy.__version__, numpy.__version__, sys.version_info)
1.6.2 1.20.2 sys.version_info(major=3, minor=8, micro=5, releaselevel='final', serial=0)
Issue Analytics
- State:
- Created 2 years ago
- Comments:11 (8 by maintainers)
Yes, the
maxit
argument.I don’t think there is a bug.
scipy.odr
is a nonlinear solver, and it might not necessarily converge in the same way when representing what is conceptually the same problem in different ways.