go-hep.org/x/hep@v0.38.1/fit/README.md (about) 1 # fit 2 3 [](https://godoc.org/go-hep.org/x/hep/fit) 4 5 `fit` is a WIP package to provide easy fitting models and curve fitting functions. 6 7 ## H1D 8 9 ### Fit a gaussian 10 11  12 13 [embedmd]:# (hist_example_test.go go /func ExampleH1D_gaussian/ /\n}/) 14 ```go 15 func ExampleH1D_gaussian() { 16 var ( 17 mean = 2.0 18 sigma = 4.0 19 // Values from gonum/optimize: 20 want = []float64{450.56454241860934, 2.0146898541006277, 3.9613086671267466} 21 // Values from ROOT: 22 // want = []float64{4.53720e+02, 1.93218e+00, 3.93188e+00} 23 ) 24 25 const npoints = 10000 26 27 // Create a normal distribution. 28 dist := distuv.Normal{ 29 Mu: mean, 30 Sigma: sigma, 31 Src: rand.New(rand.NewPCG(0, 0)), 32 } 33 34 // Draw some random values from the standard 35 // normal distribution. 36 hist := hbook.NewH1D(100, -20, +25) 37 for range npoints { 38 v := dist.Rand() 39 hist.Fill(v, 1) 40 } 41 42 gauss := func(x, cst, mu, sigma float64) float64 { 43 v := (x - mu) / sigma 44 return cst * math.Exp(-0.5*v*v) 45 } 46 47 res, err := fit.H1D( 48 hist, 49 fit.Func1D{ 50 F: func(x float64, ps []float64) float64 { 51 return gauss(x, ps[0], ps[1], ps[2]) 52 }, 53 N: len(want), 54 }, 55 nil, &optimize.NelderMead{}, 56 ) 57 if err != nil { 58 log.Fatal(err) 59 } 60 61 if err := res.Status.Err(); err != nil { 62 log.Fatal(err) 63 } 64 if got := res.X; !floats.EqualApprox(got, want, 1e-3) { 65 log.Fatalf("got= %v\nwant=%v\n", got, want) 66 } 67 68 { 69 p := hplot.New() 70 p.X.Label.Text = "f(x) = cst * exp(-0.5 * ((x-mu)/sigma)^2)" 71 p.Y.Label.Text = "y-data" 72 p.Y.Min = 0 73 74 h := hplot.NewH1D(hist) 75 h.Color = color.RGBA{0, 0, 255, 255} 76 p.Add(h) 77 78 f := plotter.NewFunction(func(x float64) float64 { 79 return gauss(x, res.X[0], res.X[1], res.X[2]) 80 }) 81 f.Color = color.RGBA{255, 0, 0, 255} 82 f.Samples = 1000 83 p.Add(f) 84 85 p.Add(plotter.NewGrid()) 86 87 err := p.Save(20*vg.Centimeter, -1, "testdata/h1d-gauss-plot.png") 88 if err != nil { 89 log.Fatal(err) 90 } 91 } 92 } 93 ``` 94 95 ## Curve1D 96 97 ### Fit a gaussian 98 99  100 101 [embedmd]:# (curve1d_example_test.go go /func ExampleCurve1D_gaussian/ /\n}/) 102 ```go 103 func ExampleCurve1D_gaussian() { 104 var ( 105 cst = 3.0 106 mean = 30.0 107 sigma = 20.0 108 want = []float64{cst, mean, sigma} 109 ) 110 111 xdata, ydata, err := readXY("testdata/gauss-data.txt") 112 if err != nil { 113 log.Fatal(err) 114 } 115 116 gauss := func(x, cst, mu, sigma float64) float64 { 117 v := (x - mu) 118 return cst * math.Exp(-v*v/sigma) 119 } 120 121 res, err := fit.Curve1D( 122 fit.Func1D{ 123 F: func(x float64, ps []float64) float64 { 124 return gauss(x, ps[0], ps[1], ps[2]) 125 }, 126 X: xdata, 127 Y: ydata, 128 Ps: []float64{10, 10, 10}, 129 }, 130 nil, &optimize.NelderMead{}, 131 ) 132 if err != nil { 133 log.Fatal(err) 134 } 135 136 if err := res.Status.Err(); err != nil { 137 log.Fatal(err) 138 } 139 if got := res.X; !floats.EqualApprox(got, want, 1e-3) { 140 log.Fatalf("got= %v\nwant=%v\n", got, want) 141 } 142 143 { 144 p := hplot.New() 145 p.X.Label.Text = "Gauss" 146 p.Y.Label.Text = "y-data" 147 148 s := hplot.NewS2D(hplot.ZipXY(xdata, ydata)) 149 s.Color = color.RGBA{0, 0, 255, 255} 150 p.Add(s) 151 152 f := plotter.NewFunction(func(x float64) float64 { 153 return gauss(x, res.X[0], res.X[1], res.X[2]) 154 }) 155 f.Color = color.RGBA{255, 0, 0, 255} 156 f.Samples = 1000 157 p.Add(f) 158 159 p.Add(plotter.NewGrid()) 160 161 err := p.Save(20*vg.Centimeter, -1, "testdata/gauss-plot.png") 162 if err != nil { 163 log.Fatal(err) 164 } 165 } 166 } 167 ``` 168 169 ### Fit a powerlaw (with Y-errors) 170 171  172 173 [embedmd]:# (curve1d_example_test.go go /func ExampleCurve1D_powerlaw/ /\n}/) 174 ```go 175 func ExampleCurve1D_powerlaw() { 176 var ( 177 amp = 11.021171432949746 178 index = -2.027389113217428 179 want = []float64{amp, index} 180 ) 181 182 xdata, ydata, yerrs, err := readXYerr("testdata/powerlaw-data.txt") 183 if err != nil { 184 log.Fatal(err) 185 } 186 187 plaw := func(x, amp, index float64) float64 { 188 return amp * math.Pow(x, index) 189 } 190 191 res, err := fit.Curve1D( 192 fit.Func1D{ 193 F: func(x float64, ps []float64) float64 { 194 return plaw(x, ps[0], ps[1]) 195 }, 196 X: xdata, 197 Y: ydata, 198 Err: yerrs, 199 Ps: []float64{1, 1}, 200 }, 201 nil, &optimize.NelderMead{}, 202 ) 203 if err != nil { 204 log.Fatal(err) 205 } 206 207 if err := res.Status.Err(); err != nil { 208 log.Fatal(err) 209 } 210 if got := res.X; !floats.EqualApprox(got, want, 1e-3) { 211 log.Fatalf("got= %v\nwant=%v\n", got, want) 212 } 213 214 { 215 p := hplot.New() 216 p.X.Label.Text = "f(x) = a * x^b" 217 p.Y.Label.Text = "y-data" 218 p.X.Min = 0 219 p.X.Max = 10 220 p.Y.Min = 0 221 p.Y.Max = 10 222 223 pts := make([]hbook.Point2D, len(xdata)) 224 for i := range pts { 225 pts[i].X = xdata[i] 226 pts[i].Y = ydata[i] 227 pts[i].ErrY.Min = 0.5 * yerrs[i] 228 pts[i].ErrY.Max = 0.5 * yerrs[i] 229 } 230 231 s := hplot.NewS2D(hbook.NewS2D(pts...), hplot.WithYErrBars(true)) 232 s.Color = color.RGBA{0, 0, 255, 255} 233 p.Add(s) 234 235 f := plotter.NewFunction(func(x float64) float64 { 236 return plaw(x, res.X[0], res.X[1]) 237 }) 238 f.Color = color.RGBA{255, 0, 0, 255} 239 f.Samples = 1000 240 p.Add(f) 241 242 p.Add(plotter.NewGrid()) 243 244 err := p.Save(20*vg.Centimeter, -1, "testdata/powerlaw-plot.png") 245 if err != nil { 246 log.Fatal(err) 247 } 248 } 249 } 250 ``` 251 252 ### Fit an exponential 253 254  255 256 [embedmd]:# (curve1d_example_test.go go /func ExampleCurve1D_exponential/ /\n}/) 257 ```go 258 func ExampleCurve1D_exponential() { 259 const ( 260 a = 0.3 261 b = 0.1 262 ndf = 2.0 263 ) 264 265 xdata, ydata, err := readXY("testdata/exp-data.txt") 266 if err != nil { 267 log.Fatal(err) 268 } 269 270 exp := func(x, a, b float64) float64 { 271 return math.Exp(a*x + b) 272 } 273 274 res, err := fit.Curve1D( 275 fit.Func1D{ 276 F: func(x float64, ps []float64) float64 { 277 return exp(x, ps[0], ps[1]) 278 }, 279 X: xdata, 280 Y: ydata, 281 N: 2, 282 }, 283 nil, &optimize.NelderMead{}, 284 ) 285 if err != nil { 286 log.Fatal(err) 287 } 288 289 if err := res.Status.Err(); err != nil { 290 log.Fatal(err) 291 } 292 if got, want := res.X, []float64{a, b}; !floats.EqualApprox(got, want, 0.1) { 293 log.Fatalf("got= %v\nwant=%v\n", got, want) 294 } 295 296 { 297 p := hplot.New() 298 p.X.Label.Text = "exp(a*x+b)" 299 p.Y.Label.Text = "y-data" 300 p.Y.Min = 0 301 p.Y.Max = 5 302 p.X.Min = 0 303 p.X.Max = 5 304 305 s := hplot.NewS2D(hplot.ZipXY(xdata, ydata)) 306 s.Color = color.RGBA{0, 0, 255, 255} 307 p.Add(s) 308 309 f := plotter.NewFunction(func(x float64) float64 { 310 return exp(x, res.X[0], res.X[1]) 311 }) 312 f.Color = color.RGBA{255, 0, 0, 255} 313 f.Samples = 1000 314 p.Add(f) 315 316 p.Add(plotter.NewGrid()) 317 318 err := p.Save(20*vg.Centimeter, -1, "testdata/exp-plot.png") 319 if err != nil { 320 log.Fatal(err) 321 } 322 } 323 } 324 ``` 325 326 ### Fit a polynomial 327 328  329 330 [embedmd]:# (curve1d_example_test.go go /func ExampleCurve1D_poly/ /\n}/) 331 ```go 332 func ExampleCurve1D_poly() { 333 var ( 334 a = 1.0 335 b = 2.0 336 ps = []float64{a, b} 337 want = []float64{1.38592513, 1.98485122} // from scipy.curve_fit 338 ) 339 340 poly := func(x float64, ps []float64) float64 { 341 return ps[0] + ps[1]*x*x 342 } 343 344 xdata, ydata := genXY(100, poly, ps, -10, 10) 345 346 res, err := fit.Curve1D( 347 fit.Func1D{ 348 F: poly, 349 X: xdata, 350 Y: ydata, 351 Ps: []float64{1, 1}, 352 }, 353 nil, &optimize.NelderMead{}, 354 ) 355 if err != nil { 356 log.Fatal(err) 357 } 358 359 if err := res.Status.Err(); err != nil { 360 log.Fatal(err) 361 } 362 363 if got := res.X; !floats.EqualApprox(got, want, 1e-6) { 364 log.Fatalf("got= %v\nwant=%v\n", got, want) 365 } 366 367 { 368 p := hplot.New() 369 p.X.Label.Text = "f(x) = a + b*x*x" 370 p.Y.Label.Text = "y-data" 371 p.X.Min = -10 372 p.X.Max = +10 373 p.Y.Min = 0 374 p.Y.Max = 220 375 376 s := hplot.NewS2D(hplot.ZipXY(xdata, ydata)) 377 s.Color = color.RGBA{0, 0, 255, 255} 378 p.Add(s) 379 380 f := plotter.NewFunction(func(x float64) float64 { 381 return poly(x, res.X) 382 }) 383 f.Color = color.RGBA{255, 0, 0, 255} 384 f.Samples = 1000 385 p.Add(f) 386 387 p.Add(plotter.NewGrid()) 388 389 err := p.Save(20*vg.Centimeter, -1, "testdata/poly-plot.png") 390 if err != nil { 391 log.Fatal(err) 392 } 393 } 394 } 395 ``` 396 ## Fitting with more than one independent variable (x has more than one dimension) 397 ### Fit a flat plane 398 399  400 401 [embedmd]:# (curve_nd_example_test.go go /func ExampleCurveND_plane/ /\n}/) 402 ```go 403 func ExampleCurveND_plane() { 404 var ( 405 m1 = 0.3 406 m2 = 0.1 407 c = 0.2 408 ps = []float64{m1, m2, c} 409 n0 = 10 410 n1 = 10 411 x0min = -1. 412 x0max = 1. 413 x1min = -1. 414 x1max = 1. 415 ) 416 417 plane := func(x, ps []float64) float64 { 418 return ps[0]*x[0] + ps[1]*x[1] + ps[2] 419 } 420 421 xData, yData := genData2D(n0, n1, plane, ps, x0min, x0max, x1min, x1max) 422 423 res, err := fit.CurveND( 424 fit.FuncND{ 425 F: func(x []float64, ps []float64) float64 { 426 return plane(x, ps) 427 }, 428 X: xData, 429 Y: yData, 430 N: 3, 431 }, 432 nil, &optimize.NelderMead{}, 433 ) 434 if err != nil { 435 log.Fatal(err) 436 } 437 438 if err := res.Status.Err(); err != nil { 439 log.Fatal(err) 440 } 441 if got, want := res.X, []float64{m1, m2, c}; !floats.EqualApprox(got, want, 0.1) { 442 log.Fatalf("got= %v\nwant=%v\n", got, want) 443 } 444 445 { 446 // slicing for a particular x0 value to plot y as a function of x1, 447 // to visualise how well the fit is working for a given x0. 448 x0Selection := 8 449 if 0 > x0Selection || x0Selection > n0 { 450 log.Fatalf("x0 slice, %d, is not in valid range [0 - %d]", x0Selection, n0) 451 } 452 x0SlicePos := x0min + ((x0max-x0min)/float64(n0))*float64(x0Selection) 453 454 var x1Slice []float64 455 var ySlice []float64 456 457 for i := range xData { 458 if xData[i][0] == x0SlicePos { 459 x1Slice = append(x1Slice, xData[i][1]) 460 ySlice = append(ySlice, yData[i]) 461 } 462 } 463 464 p := hplot.New() 465 p.Title.Text = fmt.Sprintf("Slice of plane at x0 = %.2f", x0SlicePos) 466 p.X.Label.Text = "x1" 467 p.Y.Label.Text = "y" 468 p.Y.Min = x1min 469 p.Y.Max = x1max 470 p.X.Min = x0min 471 p.X.Max = x0max 472 473 s := hplot.NewS2D(hplot.ZipXY(x1Slice, ySlice)) 474 s.Color = color.RGBA{B: 255, A: 255} 475 p.Add(s) 476 477 shiftLine := func(x, m, c, mxOtherAxis float64) float64 { 478 return m*x + c + mxOtherAxis 479 } 480 481 f := plotter.NewFunction(func(x float64) float64 { 482 return shiftLine(x, res.X[1], res.X[2], res.X[0]*x0SlicePos) 483 }) 484 f.Color = color.RGBA{R: 255, A: 255} 485 f.Samples = 1000 486 p.Add(f) 487 488 p.Add(plotter.NewGrid()) 489 err := p.Save(20*vg.Centimeter, -1, "testdata/2d-plane-plot.png") 490 if err != nil { 491 log.Fatal(err) 492 } 493 } 494 } 495 ```