[Numpy-discussion] Covariance matrix from polyfit
David Schaich
daschaich at gmail.com
Thu Jul 4 20:01:17 EDT 2013
Hi all,
I recently adopted python, and am in the process of replacing my old
analysis tools. For simple (e.g., linear) interpolations and
extrapolations, in the past I used gnuplot. Today I set up the
equivalent with polyfit in numpy v1.7.1, first running a simple test to
reproduce the gnuplot result.
A discussion on this list back in February alerted me that I should use
1/sigma for the weights in polyfit as opposed to 1/sigma**2. Fine --
that's not what I'm used to, but I can make a note.
http://mail.scipy.org/pipermail/numpy-discussion/2013-February/065649.html
Another issue mentioned in that thread is scaling the covariance matrix by
fac = resids / (len(x) - order - 2.0)
This wreaked havoc on the simple test I mentioned above (and include
below), which fit three data points to a straight line. I spent hours
trying to figure out why numpy returned negative variances, before
tracking down this line. And, indeed, if I add a fake fourth data point,
I end up with inf's and nan's.
There is some lengthy justification for subtracting that 2 around line
590 in lib/polynomial.py. Fine -- it's nothing I recall seeing before
(and I removed it from my local installation), but I'm just a new user.
However, I do think it is important to fix polyfit so that it doesn't
produce pathological results like those I encountered today. Here are a
couple of possibilities that would let the subtraction of 2 remain:
* Check whether len(x) > order + 2, and if it is not, either
** Die with an error
** Scale by resids / (len(x) - order) instead of resids / (len(x) -
order - 2.0)
* Don't bother with this scaling at all, leaving it to the users (who
can subtract 2 if they want). This is what scipy.optimize.leastsq does,
after what seems to be a good deal of discussion: "This matrix must be
multiplied by the residual variance to get the covariance of the
parameter estimates – see curve_fit."
http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.leastsq.html
https://github.com/scipy/scipy/pull/448
I leave it to those of you with more numpy experience to decide what
would be the best way to go.
Cheers,
David
http://www-hep.colorado.edu/~schaich/
+++
Here's the simple example:
>>> import numpy as np
>>> m = np.array([0.008, 0.01, 0.015])
>>> dat = np.array([1.0822582, 1.0805417, 1.0766624])
>>> weight = np.array([1/0.000370, 1/0.000355, 1/0.000249])
>>> out, cov = np.polyfit(m, dat, 1, full=False, w=weight, cov=True)
>>> print out, '\n', cov
[-0.79269957 1.08854252]
[[ -2.34965006e-04 2.84428412e-06]
[ 2.84428412e-06 -3.66283662e-08]]
>>> print np.sqrt(-1. * cov[0][0])
0.0153285682895
>>> print np.sqrt(-1. * cov[1][1])
0.000191385386578
+++
Gnuplot gives
+++
Final set of parameters Asymptotic Standard Error
======================= ==========================
A = -0.792719 +/- 0.01533 (1.934%)
B = 1.08854 +/- 0.0001914 (0.01758%)
+++
so up to the negative sign, all is good.
For its part, scipy.optimize.leastsq needs me to do the scaling:
+++
>>> import numpy as np
>>> from scipy import optimize
>>> m = np.array([0.008, 0.01, 0.015])
>>> dat = np.array([1.0822582, 1.0805417, 1.0766624])
>>> err = np.array([0.000370, 0.000355, 0.000249])
>>> linear = lambda p, x: p[0] * x + p[1]
>>> errfunc = lambda p, x, y, err: (linear(p, x) - y) / err
>>> p_in = [-1., 1.]
>>> all_out = optimize.leastsq(errfunc, p_in[:], args=(m, dat, err),
full_output = 1)
>>> out = all_out[0]
>>> cov = all_out[1]
>>> print out, '\n', cov
[-0.79269959 1.08854252]
[[ 3.40800756e-03 -4.12544212e-05]
[ -4.12544212e-05 5.31270007e-07]]
>>> chiSq_dof = ((errfunc(out, m, dat, err))**2).sum() / (len(m) -
len(out))
>>> cov *= chiSq_dof
>>> print cov
[[ 2.34964190e-04 -2.84427528e-06]
[ -2.84427528e-06 3.66282716e-08]]
+++
More information about the NumPy-Discussion
mailing list