-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlinefit.py
331 lines (300 loc) · 9.74 KB
/
linefit.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
import math
import scipy
#import pylab
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.dates as dtm
#import numpy.fft as nft
import scipy.optimize as spo
#from matplotlib import pyplot as plt
#import pylab as plt
#from matplotlib import rc
import numpy
#import string
#import sys
#from matplotlib import *
#from pylab import *
#import os
import random
#
# gamma function lives here:
#import scipy.special
#from scipy.special import gamma
#from scipy.optimize import leastsq
from matplotlib import axis as aa
#from threading import Thread
#
import datetime as dtm
#import time
import pytz
import calendar
import operator
import urllib.request, urllib.parse, urllib.error
import rbIntervals as rbi
#from eqcatalog import *
# 20120719 yoder: (this might have impacts on apps. that auto-fetch from ANSS)
from ANSStools import *
# maping bits:
#import matplotlib # note that we've tome from ... import *. we should probably eventually get rid of that and use the matplotlib namespace.
#matplotlib.use('Agg')
#from matplotlib.toolkits.basemap import Basemap
#from mpl_toolkits.basemap import Basemap as Basemap
#from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
#
class linefit:
# a simple line-fit class. we'll need X,Y,wt,
# call like: lf1=m.linefit(dataSet)
#
#
datas=[] # [X, Y, Wt]
totalVar=0 # sum( (x_i - f(x_i)**2 )
errs=[]
Ndof=0
a=None
b=None
activeRes=None
AIC=None
nPrams=None # len(fitData)=Ndof+nPrams
#
def __init__(self, inData=[]):
self.initialize(inData)
def initialize(self, inData=[]):
# what does the data look like?
self.activeRes=self.linRes
dLen=len(inData) # if dLen==2,3, then we probably have [[X], [Y], [wt]]
#
if dLen<=3:
# we should probably sort the arrays...
self.datas=inData
if dLen>3:
# data is probably like [[x,y,w], [x,y,w]...]
if len(inData[0])==1:
# 1D array; assume a plain ole sequence for X
self.datas=[]
self.datas+=[list(range(len(inData)))]
self.datas+=[list(map(operator.itemgetter(0), inData))]
if len(inData[0])>=2:
# assume the data are ordered pairs, so we can sort them on the x coordinate.
inData.sort(key=operator.itemgetter(0))
self.datas=[]
self.datas+=[list(map(operator.itemgetter(0), inData))]
self.datas+=[list(map(operator.itemgetter(1), inData))]
if len(inData[0])>=3: self.datas+=[list(map(operator.itemgetter(2), inData))]
if len(self.datas)==2:
# add even weight.
self.datas+=[[]]
for x in self.datas[0]:
self.datas[2]+=[1.0]
#
#
def meanVar(self):
# aka, rChiSqr
return self.totalVar/self.Ndof
#def doOmoriFit(self, p0=[None, None, None], xmin=None, xmax=None, r0=0):
def doOmoriFit(self, p0=[None, None, None], xmin=None, xmax=None, r0=0, fitres=None):
# the pram r0 is for the OmoriIntRes2() residual, which incorporates backtround seismicity.
# do a linear fit:
# a,b parameters are first guesses.
# if they are None, guess starting prams:
if fitres==None: fitres=self.omoriIntRes
if p0==None: p0=[None, None, None]
if p0[1]==None:
p0[1]=(float(self.datas[1][-1])-float(self.datas[1][0]))/(float(self.datas[0][-1])-float(self.datas[0][0]))
if p0[0]==None:
p0[0]=self.datas[1][0]-p0[1]*self.datas[0][0]
if p0[2]==None: p0[2]=1.0
#self.a=a
#self.b=b
p=scipy.array(p0)
#
if xmin==None: xmin=self.datas[0][0]
if xmax==None: xmax=self.datas[0][-1]
#
# get X,Y,W:
X=[]
Y=[]
W=[]
for i in range(len(self.datas[0])):
if self.datas[0][i]<xmin: continue
#
X+=[self.datas[0][i]]
Y+=[self.datas[1][i]]
W+=[self.datas[2][i]]
if X[-1]>=xmax: break
#
# now, fit data:
#print "do the fit..."
# note: args are (y, x, wt)
#plsq=spo.leastsq(self.linRes, p, args=(scipy.array(self.datas[1]), scipy.array(self.datas[0]), scipy.array(self.datas[2])), full_output=1)
print(("prams: %s" % str(p)))
#plsq=spo.leastsq(fitres, p, args=(scipy.array(Y), scipy.array(X), scipy.array(W), r0), full_output=1)
plsq=spo.leastsq(fitres, p, args=(scipy.array(Y), scipy.array(X), scipy.array(W)), full_output=1)
#print "fit done. sum error..."
for sig in self.errs:
self.totalVar+=sig*sig
#self.Ndof=len(self.datas[0])-len(p)
self.Ndof=len(X)-len(p)
self.nPrams=len(p)
self.AIC=self.totalVar+2*nPrams
self.a=plsq[0][0]
self.b=plsq[0][1]
'''
plsq=spo.leastsq(linRes, p, args=(ymax,x), full_output=0, maxfev=200000)
amax=plsq[0][0]
bmax=plsq[0][1]
'''
return plsq
def doLogFit(self, lbase=10.0, a=None, b=None, xmin=None, xmax=None, thisdatas=None):
# fit the log-log representation.
if thisdatas==None: thisdatas=self.datas
#print "datalen: %d" % len(thisdatas)
logdatas=[[], [], []]
#
# get logarithms of data:
for i in range(len(thisdatas[0])):
logdatas[0]+=[math.log(thisdatas[0][i], lbase)]
logdatas[1]+=[math.log(thisdatas[1][i], lbase)]
wt=1
if len(thisdatas)>=3: wt=thisdatas[2][i]
logdatas[2]+=[wt]
#
#return logdatas
return self.doFit(a, b, xmin, xmax, logdatas)
def doFit(self, a=None, b=None, xmin=None, xmax=None, thisdatas=None, fop=1):
if thisdatas==None: thisdatas=self.datas
# do a linear fit:
# a,b parameters are first guesses.
# if they are None, guess starting prams:
self.errs=[]
self.totalVar=0
if b==None:
b=(float(thisdatas[1][-1])-float(thisdatas[1][0]))/(float(thisdatas[0][-1])-float(thisdatas[0][0]))
if a==None:
a=thisdatas[1][0]-b*thisdatas[0][0]
self.a=a
self.b=b
p=scipy.array([a,b])
if xmin==None:
# xmin=thisdatas[0][0]
xmin = min(thisdatas[0])
if xmax==None:
# xmax=thisdatas[0][-1]
xmax = max(thisdatas[0])
#
# get X,Y,W:
X=[]
Y=[]
W=[]
#
for i in range(len(thisdatas[0])):
#if thisdatas[0][i]<xmin: continue
#
X+=[thisdatas[0][i]]
Y+=[thisdatas[1][i]]
W+=[thisdatas[2][i]]
if X[-1]>=xmax: continue
#
# now, fit data:
#print scipy.array(Y), scipy.array(X), scipy.array(W)
#print len(scipy.array(Y)), len(scipy.array(X)), len(scipy.array(W))
#print self.activeRes
#print "do the fit..."
# note: args are (y, x, wt)
#plsq=spo.leastsq(self.linRes, p, args=(scipy.array(self.datas[1]), scipy.array(self.datas[0]), scipy.array(self.datas[2])), full_output=1)
plsq=spo.leastsq(self.activeRes, p, args=(scipy.array(Y), scipy.array(X), scipy.array(W)), full_output=fop)
#print "fit done. sum error..."
for sig in self.errs:
self.totalVar+=sig*sig
#self.Ndof=len(thisdatas[0])-len(p)
self.AIC=self.totalVar+2.*len(p)
self.Ndof=len(X)-len(p)
self.dataLen=len(X)
self.a=plsq[0][0]
self.b=plsq[0][1]
'''
plsq=spo.leastsq(linRes, p, args=(ymax,x), full_output=0, maxfev=200000)
amax=plsq[0][0]
bmax=plsq[0][1]
'''
#
# now, get error for a,b.
# start by calculating Delta and DeltaPrime (error-partition for w!=1, w=1):
self.delta=0
self.deltaPrime=0
aX=scipy.array(X)
aXX=aX**2
aY=scipy.array(Y)
aYY=aY**2
aW=scipy.array(W)
aWW=aW**2
#sigsSq=scipy.array(self.errs)**2 # i think this does not get squared (we're using linRes() )... it would not be a bad idea to just calc them here...
#self.delta=sum
# delta first (this might not be quite right except when w=1/sig^2, so be careful...)
# note: we assume W=1/sigma**2
#self.delta=sum(aW)*sum(aXX*aW) - (sum(aX*aW))**2 # check this formulation for order of operations...
self.delta=sum(aWW)*sum(aXX*aWW) - (sum(aX*aWW))**2. # check this formulation for order of operations...
self.deltaPrime=float(len(X))*sum(aXX) - sum(aX)**2.
#
# weighted pram errors (variance):
#thisSig=self.totalVar**.5
#
self.vara=(1.0/self.delta)*sum(aXX*aWW)
self.varb=(1.0/self.delta)*sum(aWW) # note that w=1/sig^2 in most texts, as per gaussian stats.
# this is more general than that, and it might make a big fat
# # mess when applied outside gaussian distributions.
#
# w_i=1 case (note, this can be generallized to w_i=w; 1/delta -> 1/(w*delta):
self.varaprime=(self.meanVar()/self.deltaPrime)*sum(aXX)
self.varbprime=float(len(X))*self.meanVar()/self.deltaPrime
return plsq
def tofile(self, fname='data/lfdata.dat', lfheader='#data from linefit object\n'):
fout=open(fname, 'w')
fout.write(lfheader)
for i in range(len(self.datas[0])):
fout.write('%f\t%f\t%f\n' % (self.datas[0][i], self.datas[1][i], self.datas[2][i]))
fout.close()
def fLin(self, x,p):
return p[0] + x*p[1]
def fPL(self, x,p):
return (10**p[0])*(x**p[1])
def linRes(self, p,y,x,w):
err=y-(p[0]+x*p[1])
self.errs=w*err
#return w*err
return self.errs
def omoriRateRes(self, p, y, x, w):
err=y-(1.0/(p[0]*(1+(x/p[1]))**p[2]))
self.errs=w*err
#return w*err
return self.errs
def omoriIntRes(self, p, y, x, w):
err=y-(p[0]*(1+(x/p[1]))**p[2])
werr=w*err
self.errs=w*err
#return werr
return self.errs
def omoriIntRes2(self, p, y, x, w, r0):
# this is an omori like function that includes a "background rate" r0.
#err=w*(y-(p[0]*(1+(x/p[1]))**p[2]))
#r0=0.2222
#err=w*(y- (1/(r0 + 1/(p[0]*(1+(x/p[1]))**p[2]) )) )
err=w*(y- 1/(r0 + 1/(p[0]*(1+(x/p[1]))**p[2])) )
self.errs=err
return err
def getFitPlotAry(self):
print(("from getFitPlotAry(): %s, %s, %s, %s" % (self.datas[0][0], self.datas[0][-1], self.a, self.b)))
Fx=[self.datas[0][0], self.datas[0][-1]]
Fy=[self.datas[0][0]*self.b + self.a, self.datas[0][-1]*self.b + self.a]
return [Fx, Fy]
def quickPlot(self, toFig=True, colorNum=0):
fitXY=self.getFitPlotAry()
# toFig: do a figure here or assume that there is an active figure in the ether. this way, we can put many quickPlots onto a single canvas.
if toFig:
plt.figure(0)
plt.clf()
plt.plot(self.datas[0], self.datas[1], color=pycolor(colorNum))
plt.plot(fitXY[0], fitXY[1], color=pycolor(colorNum), label='a=%f, b=%f, rChi^2=%f' % (self.a, self.b, self.meanVar()**.5))
if toFig:
plt.legend(loc='upper right')
plt.show()
return None