1 """
2 Evaluate model functions.
3
4 Most functions accept a domain of interest and a set of parameters, and return
5 a model function (e.g., a sine, gaussian etc...). Parameters are usually a
6 record array containing named fields for the parameters.
7
8 Some nonlinear optimizers expect and return parameters as a 1D array of the
9 independent parameters. Therefore, all functions also accept these 1D numpy
10 arrays, and they are converted by L{check_input} to record arrays. The latter
11 function actually accepts both record arrays and 1D arrays, and acts as a
12 translator between the two parameter representations.
13 """
14 import logging
15 import functools
16
17 import numpy as np
18 from numpy import pi,cos,sin,sqrt,tan,arctan
19 from scipy.interpolate import splev
20 from scipy.special import erf,jn
21 from scipy.stats import distributions
22
23
24 logger = logging.getLogger('SIGPROC.EVAL')
42 return check
43
47 """
48 Residual sum of squares.
49
50 @param data: data
51 @type data: numpy array
52 @param model: model
53 @type model: numpy array
54 @rtype: float
55 @return: residual sum of squares
56 """
57 return np.sum( (model-data)**2 )
58
61 """
62 Calculate variance reduction.
63
64 @param data: data
65 @type data: numpy array
66 @param model: model
67 @type model: numpy array
68 @rtype: percentage
69 @return: variance reduction (percentage)
70 """
71 residus = data-model
72 variance_reduction = 1.-np.sum(residus**2.)/sum((data-data.mean())**2.)
73 return variance_reduction*100.
74
75 -def bic(data,model,k=0,n=None):
76 """
77 BIC for time regression
78
79 This one is based on the MLE of the variance,
80
81 Sigma_k^2 = RSS/n
82
83 Where our maximum likelihood estimator is then (RSS/n)^n
84
85 @param data: data
86 @type data: numpy array
87 @param model: model
88 @type model: numpy array
89 @param k: number of free, independent parameters
90 @type k: integer
91 @param n: number of effective observations
92 @type n: integer or None
93 @rtype: float
94 @return: BIC
95 """
96 if n is None:
97 n = len(data)
98
99 RSS_ = RSS(data,model)
100
101 BIC = n * np.log(RSS_/n) + k * np.log(n)
102 return BIC
103
104 -def aic(data,model,k=0,n=None):
105 """
106 AIC for time regression
107
108 This one is based on the MLE of the variance,
109
110 Sigma_k^2 = RSS/n
111
112 Where our maximum likelihood estimator is then (RSS/n)^n
113
114 @param data: data
115 @type data: numpy array
116 @param model: model
117 @type model: numpy array
118 @param k: number of free, independent parameters
119 @type k: integer
120 @param n: number of effective observations
121 @type n: integer or None
122 @rtype: float
123 @return: AIC
124 """
125 if n is None:
126 n = len(data)
127
128 RSS_ = RSS(data,model)
129
130 AIC = n * np.log(RSS_/n) + 2*k + n
131 return AIC
132
134 """
135 Use Fisher's test to combine probabilities.
136
137 http://en.wikipedia.org/wiki/Fisher's_method
138
139 >>> rvs1 = distributions.norm().rvs(10000)
140 >>> rvs2 = distributions.norm().rvs(10000)
141 >>> pvals1 = scipy.stats.distributions.norm.sf(rvs1)
142 >>> pvals2 = scipy.stats.distributions.norm.sf(rvs2)
143 >>> fpval = fishers_method([pvals1,pvals2])
144
145 And make a plot (compare with the wiki page).
146
147 >>> p = pl.figure()
148 >>> p = pl.scatter(pvals1,pvals2,c=fpval,edgecolors='none',cmap=pl.cm.spectral)
149 >>> p = pl.colorbar()
150
151
152
153
154 @param pvalues: array of pvalues, the first axis will be combined
155 @type pvalues: ndarray (shape Nprob times n)
156 @return: combined pvalues
157 @rtype: ndarray (shape n)
158 """
159 pvalues = np.asarray(pvalues)
160 X2 = -2*np.sum(np.log(pvalues),axis=0)
161 df = 2*pvalues.shape[0]
162 return distributions.chi2.sf(X2,df)
163
164
165
166
167
168
169 -def phasediagram(time,signal,nu0,D=0,t0=None,forb=None,asini=None,
170 return_indices=False,chronological=False):
171 """
172 Construct a phasediagram, using frequency nu0.
173
174 Possibility to include a linear frequency shift D.
175
176 If needed, the indices can be returned. To 'unphase', just do:
177
178 Example usage:
179
180 >>> original = np.random.normal(size=10)
181 >>> indices = np.argsort(original)
182 >>> inv_indices = np.argsort(indices)
183 >>> all(original == np.take(np.take(original,indices),inv_indices))
184 True
185
186 Optionally, the zero time point can be given, it will be subtracted from all
187 time points
188
189 Joris De Ridder, Pieter Degroote
190
191 @param time: time points [0..Ntime-1]
192 @type time: ndarray
193 @param signal: observed data points [0..Ntime-1]
194 @type signal: ndarray
195 @param nu0: frequency to compute the phasediagram with (inverse unit of time)
196 @type nu0: float
197 @param t0: zero time point, if None: t0 = time[0]
198 @type t0: float
199 @param D: frequency shift
200 @type D: float
201 @param chronological: set to True if you want to have a list of every phase
202 separately
203 @type chronological: boolean
204 @param return_indices: flag to return indices
205 @type return_indices: boolean
206 @return: phase points (sorted), corresponding observations
207 @rtype: ndarray,ndarray(, ndarray)
208 """
209 cc = 173.144632674
210 if (t0 is None): t0 = 0.
211 if forb is None:
212 phase = np.fmod(nu0 * (time-t0) + D/2. * (time-t0)**2,1.0)
213 else:
214 alpha = nu0*asini/cc
215 phase = np.fmod(nu0 * (time-t0) + alpha*(sin(2*pi*forb*time) - sin(2*pi*forb*t0)),1.0)
216
217 phase = np.where(phase<0,phase+1,phase)
218
219
220 if chronological:
221 chainnr = np.floor((time-t0)*nu0)
222 myphase = [[]]
223 mysignl = [[]]
224 currentphase = chainnr[0]
225
226 for i in xrange(len(signal)):
227 if chainnr[i]==currentphase:
228 myphase[-1].append(phase[i])
229 mysignl[-1].append(signal[i])
230 else:
231 myphase[-1] = np.array(myphase[-1])
232 mysignl[-1] = np.array(mysignl[-1])
233 currentphase = chainnr[i]
234 myphase.append([phase[i]])
235 mysignl.append([signal[i]])
236 myphase[-1] = np.array(myphase[-1])
237 mysignl[-1] = np.array(mysignl[-1])
238
239 else:
240 indices = np.argsort(phase)
241
242
243 if not return_indices and not chronological:
244 return phase[indices], signal[indices]
245
246 elif not chronological:
247 return phase[indices], signal[indices], indices
248
249 else:
250 return myphase,mysignl
251
252
253
254 @check_input
255 -def sine(times, parameters):
256 """
257 Creates a harmonic function based on given parameters.
258
259 Parameter fields: C{const, ampl, freq, phase}.
260
261 This harmonic function is of the form
262
263 f(p1,...,pm;x1,...,x_n) = S{sum} c_i + a_i sin(2 S{pi} (f_i+ S{phi}_i))
264
265 where p_i are parameters and x_i are observation times.
266
267 Parameters can be given as a record array containing columns 'ampl', 'freq'
268 and 'phase', and optionally also 'const' (the latter array is summed up so
269 to reduce it to one number).
270
271 C{pars = np.rec.fromarrays([consts,ampls,freqs,phases],names=('const','ampl','freq','phase'))}
272
273 Parameters can also be given as normal 1D numpy array. In that case, it will
274 be transformed into a record array by the C{sine_preppars} function. The
275 array you give must be 1D, optionally contain a constant as the first
276 parameter, followed by 3 three numbers per frequency:
277
278 C{pars = [const, ampl1, freq1, phase1, ampl2, freq2, phase2...]}
279
280 Example usage: We construct a synthetic signal with two frequencies.
281
282 >>> times = np.linspace(0,100,1000)
283 >>> const = 4.
284 >>> ampls = [0.01,0.004]
285 >>> freqs = [0.1,0.234]
286 >>> phases = [0.123,0.234]
287
288 The signal can be made via a record array
289
290 >>> parameters = np.rec.fromarrays([[const,0],ampls,freqs,phases],names = ('const','ampl','freq','phase'))
291 >>> mysine = sine(times,parameters)
292
293 or a normal 1D numpy array
294
295 >>> parameters = np.hstack([const,np.ravel(np.column_stack([ampls,freqs,phases]))])
296 >>> mysine = sine(times,parameters)
297
298 Of course, the latter is easier if you have your parameters in a list:
299
300 >>> freq1 = [0.01,0.1,0.123]
301 >>> freq2 = [0.004,0.234,0.234]
302 >>> parameters = np.hstack([freq1,freq2])
303 >>> mysine = sine(times,parameters)
304
305 @param times: observation times
306 @type times: numpy array
307 @param parameters: record array containing amplitudes ('ampl'), frequencies ('freq')
308 phases ('phase') and optionally constants ('const'), or 1D numpy array (see above).
309 B{Warning:} record array should have shape (n,)!
310 @type parameters: record array or 1D array
311 @return: sine signal (same shape as C{times})
312 @rtype: array
313 """
314 if 'const' in parameters.dtype.names:
315 total_fit = parameters['const'].sum()
316 else:
317 total_fit = 0.
318 for i in xrange(len(parameters)):
319 total_fit += parameters['ampl'][i]*sin(2*pi*(parameters['freq'][i]*times+parameters['phase'][i]))
320 return total_fit
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417 -def periodic_spline(times,parameters,t0=None):
418 """
419 Evaluate a periodic spline function
420
421 Parameter fields: C{freq, knots, coeffs, degree}, as returned from the
422 corresponding fitting function L{fit.periodic_spline}.
423
424 @param times: observation times
425 @type times: numpy array
426 @param parameters: [frequency,"cj" spline coefficients]
427 @return: sine signal with frequency shift (same shape as C{times})
428 @rtype: array
429 """
430
431 if t0 is None:
432 t0 = times[0]
433
434 mytrend = 0.
435
436 for i in range(len(parameters)):
437 frequency = parameters[i]['freq']
438
439 coefficients = (parameters[i]['knots'],parameters[i]['coeffs'],parameters[i]['degree'])
440 phases = np.fmod((times-t0) * frequency,1.0)
441 mytrend += splev(phases,coefficients)
442
443 return mytrend
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535 @check_input
536 -def box(times,parameters,t0=None):
537 """
538 Evaluate a box transit model.
539
540 Parameter fields: C{cont, freq, ingress, egress, depth}
541
542 Parameters [[frequency,depth, fractional start, fraction end, continuum]]
543 @rtype: array
544 """
545 if t0 is None: t0 = 0.
546
547
548 parameters = np.asarray(parameters)
549 model = np.ones(len(times))*sum(parameters['cont'])
550
551 for parameter in parameters:
552
553
554 phase = np.fmod((times - t0) * parameter['freq'], 1.0)
555 phase = np.where(phase<0,phase+1,phase)
556 transit_place = (parameter['ingress']<=phase) & (phase<=parameter['egress'])
557 model = np.where(transit_place,model-parameter['depth'],model)
558 return model
559
560
561
562 -def spots(times,spots,t0=None,**kwargs):
563 """
564 Spotted model from Lanza (2003).
565 Extract from Carrington Map.
566
567 Included:
568 - presence of faculae through Q-factor (set constant to 5 or see
569 Chapman et al. 1997 or Solank & Unruh for a scaling law, since this
570 factor decreases strongly with decreasing rotation period.
571 - differential rotation through B-value (B/period = 0.2 for the sun)
572 - limb-darkening through coefficients ap, bp and cp
573 - non-equidistant time series
574 - individual starspot evolution and lifetime following a Gaussian law,
575 (see Hall & Henry (1993) for a relation between stellar radius and
576 sunspot lifetime), through an optional 3rd (time of maximum) and
577 4th parameter (decay time)
578
579 Not included:
580 - distinction between umbra's and penumbra
581 - sunspot structure
582
583 Parameters of global star:
584 - i_sun: inclination angle, i=pi/2 is edge-on, i=0 is pole on.
585 - l0: 'epoch angle', if L0=0, spot with coordinate (0,0)
586 starts in center of disk, if L0=0, spot is in center of
587 disk after half a rotation period
588 - period: equatorial period
589 - b: differential rotation b = Omega_pole - Omega_eq where Omega is in radians per time unit (angular velocity)
590 - ap,bp,cp: limb darkening
591
592 Parameters of spots:
593 - lambda: 'greenwich' coordinate (longitude) (pi means push to back of star if l0=0)
594 - theta: latitude (latitude=0 means at equator, latitude=pi/2 means on pole (B{not
595 colatitude})
596 - A: size
597 - maxt0 (optional): time of maximum size
598 - decay (optional): exponential decay speed
599
600
601 Note: alpha = (Omega_eq - Omega_p) / Omega_eq
602 alpha = -b /2*pi * period
603
604 Use for fitting!
605 """
606
607 if t0 is None: t0 = 0.
608
609
610 signal = np.zeros(len(times))
611
612
613 this_signal = 0
614 for spot in spots:
615
616 C = 1. / (0.25 * spots[0]['ap'] + 2./3.*spots[0]['bp'] + 1./2.*spots[0]['cp'])
617
618 lambda_i = spot['lambda']
619 A_i = spot['A']
620 if 'decay' in spot.dtype.names:
621
622 A_i *= np.exp(-((spot['maxt0'] - times)/spot['decay'])**2)
623
624 Omega_theta = 2*pi/spots[0]['P_eq'] + spots[0]['b'] * sin(spot['theta'])**2
625
626 lambda_i += Omega_theta*(times-t0)
627
628
629 mu_i = cos(spots[0]['i_sun']) * sin(spot['theta']) + sin(spots[0]['i_sun'])*cos(spot['theta'])*cos(lambda_i - spots[0]['l0'])
630
631 irradiance_spot = C*spots[0]['s0'] * A_i * mu_i * \
632 (spots[0]['ap'] + spots[0]['bp']*mu_i + spots[0]['cp']*mu_i**2) *\
633 ((spots[0]['cs']-1) + spots[0]['q']*(spots[0]['cf'] + spots[0]['cf_']*mu_i - 1))
634
635 this_signal = where(mu_i<0,this_signal,this_signal+irradiance_spot)
636 this_signal += spots[0]['s0'] + spots[0]['sr']
637 return this_signal
638
639
640
641
642
643
644
645
646 @check_input
647 -def gauss(x, parameters):
648 """
649 Evaluate a gaussian fit.
650
651 Parameter fields: C{'ampl','mu','sigma'} and optionally C{'const'}.
652
653 Evaluating one Gaussian:
654
655 >>> x = np.linspace(-20,20,10000)
656 >>> pars = [0.5,0.,3.]
657 >>> y = gauss(x,pars)
658 >>> p = pl.plot(x,y,'k-')
659
660 Evaluating multiple Gaussian, with and without constants:
661
662 >>> pars = [0.5,0.,3.,0.1,-10,1.,.1]
663 >>> y = gauss(x,pars)
664 >>> p = pl.plot(x,y,'r-')
665
666 @parameter x: domain
667 @type x: ndarray
668 @parameter parameters: record array.
669 @type x: numpy record array
670 @return: fitted signal
671 @rtype: array
672 """
673 if 'const' in parameters.dtype.names:
674 C = parameters['const'].sum()
675 else:
676 C = 0.
677
678 y = C
679 for pars in parameters:
680 y += pars['ampl'] * np.exp( -(x-pars['mu'])**2 / (2.*pars['sigma']**2))
681
682 return y
683
684 @check_input
685 -def lorentz(x,parameters):
686 """
687 Evaluate Lorentz profile
688
689 P(f) = A / ( (x-mu)**2 + gamma**2)
690
691 Parameters fields: C{ampl,mu,gamma} and optionally C{const}.
692
693 Evaluating one Lorentzian:
694
695 >>> x = np.linspace(-20,20,10000)
696 >>> pars = [0.5,0.,3.]
697 >>> y = lorentz(x,pars)
698 >>> p = pl.figure()
699 >>> p = pl.plot(x,y,'k-')
700
701 Evaluating multiple Lorentzians, with and without constants:
702
703 >>> pars = [0.5,0.,3.,0.1,-10,1.,.1]
704 >>> y = lorentz(x,pars)
705 >>> p = pl.plot(x,y,'r-')
706 """
707 if 'const' in parameters.dtype.names:
708 y = parameters['const'].sum()
709 else:
710 y = 0.
711 for par in parameters:
712 y += par['ampl'] / ((x-par['mu'])**2 + par['gamma']**2)
713 return y
714
715
716
717 @check_input
718 -def voigt(x,parameters):
719 """
720 Evaluate a Voigt profile.
721
722 Field parameters: C{ampl, mu, gamma, sigma} and optionally C{const}
723
724 Function::
725
726 z = (x + gamma*i) / (sigma*sqrt(2))
727 V = A * Real[cerf(z)] / (sigma*sqrt(2*pi))
728
729 >>> x = np.linspace(-20,20,10000)
730 >>> pars1 = [0.5,0.,2.,2.]
731 >>> pars2 = [0.5,0.,1.,2.]
732 >>> pars3 = [0.5,0.,2.,1.]
733 >>> y1 = voigt(x,pars1)
734 >>> y2 = voigt(x,pars2)
735 >>> y3 = voigt(x,pars3)
736 >>> p = pl.figure()
737 >>> p = pl.plot(x,y1,'b-')
738 >>> p = pl.plot(x,y2,'g-')
739 >>> p = pl.plot(x,y3,'r-')
740
741 Multiple voigt profiles:
742
743 >>> pars4 = [0.5,0.,2.,1.,0.1,-3,0.5,0.5,0.01]
744 >>> y4 = voigt(x,pars4)
745 >>> p = pl.plot(x,y4,'c-')
746
747 @rtype: array
748 @return: Voigt profile
749 """
750 if 'const' in parameters.dtype.names:
751 y = parameters['const'].sum()
752 else:
753 y = 0.
754 for par in parameters:
755 x = x-par['mu']
756 z = (x+1j*par['gamma'])/(par['sigma']*sqrt(2))
757 y += par['ampl']*_complex_error_function(z).real/(par['sigma']*sqrt(2*pi))
758 return y
759
760 @check_input
761 -def power_law(x,parameters):
762 """
763 Evaluate a power law.
764
765 Parameter fields: C{A, B, C}, optionally C{const}
766
767 Function:
768
769 P(f) = A / (1+ Bf)**C + const
770
771 >>> x = np.linspace(0,10,1000)
772 >>> pars1 = [0.5,1.,1.]
773 >>> pars2 = [0.5,1.,2.]
774 >>> pars3 = [0.5,2.,1.]
775 >>> y1 = power_law(x,pars1)
776 >>> y2 = power_law(x,pars2)
777 >>> y3 = power_law(x,pars3)
778 >>> p = pl.figure()
779 >>> p = pl.plot(x,y1,'b-')
780 >>> p = pl.plot(x,y2,'g-')
781 >>> p = pl.plot(x,y3,'r-')
782
783 Multiple power laws:
784 >>> pars4 = pars1+pars2+pars3
785 >>> y4 = power_law(x,pars4)
786 >>> p = pl.plot(x,y4,'c-')
787
788 @parameter x: domain
789 @type x: ndarray
790 @parameter parameters: record array.
791 @type x: numpy record array
792 @return: fitted signal
793 @rtype: array
794 """
795 if 'const' in parameters.dtype.names:
796 y = par['const']
797 else:
798 y = 0.
799 for par in parameters:
800 y += par['A'] / (1+ (par['B']*x)**par['C'])
801 return y
802
811 """
812 Prepare sine function parameters in correct form for evaluating/fitting.
813
814 If you input a record array, this function will output a 1D numpy array
815 containing only the independent parameters for use in nonlinear fitting
816 algorithms.
817
818 If you input a 1D numpy array, it will output a record array.
819
820 @param pars: input parameters in record array or normal array form
821 @type pars: record array or normal numpy array
822 @return: input parameters in normal array form or record array
823 @rtype: normal numpy array or record array
824 """
825
826 if pars.dtype.names:
827
828 if 'const' in pars.dtype.names:
829 converted_pars = np.zeros(3*len(pars)+1)
830 converted_pars[0] = pars['const'].sum()
831 converted_pars[1::3] = pars['ampl']
832 converted_pars[2::3] = pars['freq']
833 converted_pars[3::3] = pars['phase']
834
835 else:
836 converted_pars = np.zeros(3*len(pars))
837 converted_pars[0::3] = pars['ampl']
838 converted_pars[1::3] = pars['freq']
839 converted_pars[2::3] = pars['phase']
840
841 else:
842
843 if len(pars)%3==1:
844 converted_pars = np.zeros((4,(len(pars)-1)/3))
845 converted_pars[0,0] = pars[0]
846 converted_pars[1] = pars[1::3]
847 converted_pars[2] = pars[2::3]
848 converted_pars[3] = pars[3::3]
849 names = ['const','ampl','freq','phase']
850
851 else:
852 converted_pars = np.zeros((3,len(pars)/3))
853 converted_pars[0] = pars[0::3]
854 converted_pars[1] = pars[1::3]
855 converted_pars[2] = pars[2::3]
856 names = ['ampl','freq','phase']
857 converted_pars = np.rec.fromarrays(converted_pars,names=names)
858 return converted_pars
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982 -def box_preppars(pars):
983 """
984 Prepare sine function parameters in correct form for evaluating/fitting.
985
986 If you input a record array, this function will output a 1D numpy array
987 containing only the independent parameters for use in nonlinear fitting
988 algorithms.
989
990 If you input a 1D numpy array, it will output a record array.
991
992 @param pars: input parameters in record array or normal array form
993 @type pars: record array or normal numpy array
994 @return: input parameters in normal array form or record array
995 @rtype: normal numpy array or record array
996 """
997
998 if pars.dtype.names:
999 converted_pars = np.ones(4*len(pars)+1)
1000 converted_pars[0] += sum(pars['cont'])
1001 converted_pars[1::4] = pars['freq']
1002 converted_pars[2::4] = pars['depth']
1003 converted_pars[3::4] = pars['ingress']
1004 converted_pars[4::4] = pars['egress']
1005
1006 else:
1007 converted_pars = np.ones((5,(len(pars)-1)/4))
1008 converted_pars[0,0] = pars[0]
1009 converted_pars[1] = pars[1::4]
1010 converted_pars[2] = pars[2::4]
1011 converted_pars[3] = pars[3::4]
1012 converted_pars[4] = pars[4::4]
1013 names = ['cont','freq','depth','ingress','egress']
1014 converted_pars = np.rec.fromarrays(converted_pars,names=names)
1015 return converted_pars
1016
1019 """
1020 Prepare gauss function parameters in correct form for evaluating/fitting.
1021
1022 If you input a record array, this function will output a 1D numpy array
1023 containing only the independent parameters for use in nonlinear fitting
1024 algorithms.
1025
1026 If you input a 1D numpy array, it will output a record array.
1027
1028 @param pars: input parameters in record array or normal array form
1029 @type pars: record array or normal numpy array
1030 @return: input parameters in normal array form or record array
1031 @rtype: normal numpy array or record array
1032 """
1033
1034 if pars.dtype.names:
1035 if 'const' in pars.dtype.names:
1036 converted_pars = np.zeros(3*len(pars)+1)
1037 converted_pars[-1] = pars['const'].sum()
1038 else:
1039 converted_pars = np.zeros(3*len(pars))
1040 converted_pars[0::3] = pars['ampl']
1041 converted_pars[1::3] = pars['mu']
1042 converted_pars[2::3] = pars['sigma']
1043
1044 else:
1045 if len(pars)%3==0:
1046 converted_pars = np.zeros((3,len(pars)/3))
1047 names = ['ampl','mu','sigma']
1048 else:
1049 converted_pars = np.zeros((4,(len(pars)-1)/3))
1050 converted_pars[3,0] = pars[-1]
1051 names = ['ampl','mu','sigma','const']
1052 converted_pars[0] = pars[0:-1:3]
1053 converted_pars[1] = pars[1::3]
1054 converted_pars[2] = pars[2::3]
1055 converted_pars = np.rec.fromarrays(converted_pars,names=names)
1056 return converted_pars
1057
1059 """
1060 Prepare Lorentz function parameters in correct form for evaluating/fitting.
1061
1062 If you input a record array, this function will output a 1D numpy array
1063 containing only the independent parameters for use in nonlinear fitting
1064 algorithms.
1065
1066 If you input a 1D numpy array, it will output a record array.
1067
1068 @param pars: input parameters in record array or normal array form
1069 @type pars: record array or normal numpy array
1070 @return: input parameters in normal array form or record array
1071 @rtype: normal numpy array or record array
1072 """
1073
1074 if pars.dtype.names:
1075 if 'const' in pars.dtype.names:
1076 converted_pars = np.zeros(3*len(pars)+1)
1077 converted_pars[-1] = pars['const'].sum()
1078 else:
1079 converted_pars = np.zeros(3*len(pars))
1080 converted_pars[0::3] = pars['ampl']
1081 converted_pars[1::3] = pars['mu']
1082 converted_pars[2::3] = pars['gamma']
1083
1084 else:
1085 if len(pars)%3==0:
1086 converted_pars = np.zeros((3,len(pars)/3))
1087 names = ['ampl','mu','gamma']
1088 else:
1089 converted_pars = np.zeros((4,(len(pars)-1)/3))
1090 converted_pars[3,0] = pars[-1]
1091 names = ['ampl','mu','gamma','const']
1092 converted_pars[0] = pars[0:-1:3]
1093 converted_pars[1] = pars[1::3]
1094 converted_pars[2] = pars[2::3]
1095 converted_pars = np.rec.fromarrays(converted_pars,names=names)
1096 return converted_pars
1097
1099 """
1100 Prepare voigt function parameters in correct form for evaluating/fitting.
1101
1102 If you input a record array, this function will output a 1D numpy array
1103 containing only the independent parameters for use in nonlinear fitting
1104 algorithms.
1105
1106 If you input a 1D numpy array, it will output a record array.
1107
1108 @param pars: input parameters in record array or normal array form
1109 @type pars: record array or normal numpy array
1110 @return: input parameters in normal array form or record array
1111 @rtype: normal numpy array or record array
1112 """
1113
1114 if pars.dtype.names:
1115 if 'const' in pars.dtype.names:
1116 converted_pars = np.zeros(4*len(pars)+1)
1117 converted_pars[-1] = pars['const'].sum()
1118 else:
1119 converted_pars = np.zeros(4*len(pars))
1120 converted_pars[0::4] = pars['ampl']
1121 converted_pars[1::4] = pars['mu']
1122 converted_pars[2::4] = pars['sigma']
1123 converted_pars[3::4] = pars['gamma']
1124
1125 else:
1126 if len(pars)%4==0:
1127 converted_pars = np.zeros((4,len(pars)/4))
1128 names = ['ampl','mu','sigma','gamma']
1129 else:
1130 converted_pars = np.zeros((5,(len(pars)-1)/4))
1131 converted_pars[4,0] = pars[-1]
1132 names = ['ampl','mu','sigma','gamma','const']
1133 converted_pars[0] = pars[0:-1:4]
1134 converted_pars[1] = pars[1::4]
1135 converted_pars[2] = pars[2::4]
1136 converted_pars[3] = pars[3::4]
1137 converted_pars = np.rec.fromarrays(converted_pars,names=names)
1138 return converted_pars
1139
1142 """
1143 Prepare gauss function parameters in correct form for evaluating/fitting.
1144
1145 If you input a record array, this function will output a 1D numpy array
1146 containing only the independent parameters for use in nonlinear fitting
1147 algorithms.
1148
1149 If you input a 1D numpy array, it will output a record array.
1150
1151 @param pars: input parameters in record array or normal array form
1152 @type pars: record array or normal numpy array
1153 @return: input parameters in normal array form or record array
1154 @rtype: normal numpy array or record array
1155 """
1156
1157 if pars.dtype.names:
1158 if 'const' in pars.dtype.names:
1159 converted_pars = np.zeros(3*len(pars)+1)
1160 converted_pars[-1] = pars['const'].sum()
1161 else:
1162 converted_pars = np.zeros(3*len(pars))
1163 converted_pars[0::3] = pars['A']
1164 converted_pars[1::3] = pars['B']
1165 converted_pars[2::3] = pars['C']
1166
1167 else:
1168 if len(pars)%3==0:
1169 converted_pars = np.zeros((3,len(pars)/3))
1170 names = ['A','B','C']
1171 else:
1172 converted_pars = np.zeros((4,(len(pars)-1)/3))
1173 converted_pars[3,0] = pars[-1]
1174 names = ['A','B','C','const']
1175 converted_pars[0] = pars[0:-1:3]
1176 converted_pars[1] = pars[1::3]
1177 converted_pars[2] = pars[2::3]
1178 converted_pars = np.rec.fromarrays(converted_pars,names=names)
1179 return converted_pars
1180
1185 """
1186 Complex error function
1187 """
1188 cef_value = np.exp(-x**2)*(1-erf(-1j*x))
1189 if sum(np.isnan(cef_value))>0:
1190 logger.warning("Complex Error function: NAN encountered, results are biased")
1191 noisnans = np.compress(1-np.isnan(cef_value),cef_value)
1192 try:
1193 last_value = noisnans[-1]
1194 except:
1195 last_value = 0
1196 logger.warning("Complex Error function: all values are NAN, results are wrong")
1197 cef_value = np.where(np.isnan(cef_value),last_value,cef_value)
1198
1199 return cef_value
1200
1201
1202
1203
1204 if __name__=="__main__":
1205 import doctest
1206 import pylab as pl
1207 import sys
1208 doctest.testmod()
1209 pl.show()
1210