2

I want to fit my data in gnuplot, with a fitting relation that has the modified Bessel function of the 2nd kind in it. So let's say it kind of looks like this:

f(x)= A*x + b*besselk(1,b)

(I wrote it in terms of matlab or octave and the coefficients, I want to find with the fit, are A and b, so one of them is IN the bessel). But the problem is gnuplot doesn't have modified bessel function of 2nd kind.

Anyone have any idea how I can do that?

Cœur
  • 37,241
  • 25
  • 195
  • 267
  • You could use a suitable numeric substitute for the modified Bessel function. There are a number of math "cook books" that compile linear algebraic approximations for all kinds of functions. One example is *Numerical Recipies* by Willial Press et al. It also has a [website](http://www.nr.com/) with a free online version of the first edition. There is a chapter with all the variations of Bessel functions in it. Gnuplot internally has implemented the Bessel function (and others) in the same way. – Karl Aug 16 '15 at 09:47

1 Answers1

3

The best fitting strategy is usually to reduce your non-linear or non-polynomial problem to a linear or polynomial one. In particular, the linear problem always has one and only one solution. So we would ideally fit f(x) = A*x + B where B = b * besy1(b) - this is for Bessel functions of the second kind, see edit below for modified Bessel functions of the second kind, which are not available in Gnuplot. You do that like this:

fit A*x + B "datafile" via A, B

Once you have B you can locate the b that corresponds to the intersection of y = x * besy1(x) with B at x = b. Because besy1(x) is oscillatory you might have several results, but depending on the range where your data is given you can choose the correct one. Say you got B = 1.2 from the fit, then the intersections in the [0:10] interval are as follows:

plot [0:10] x*besy1(x), 1.2

enter image description here

If your region of interest is around x = 4.65, where there is the approximate location of one of the intersections, then look for the exact intersect. The distance between x * besy1(x) and B will approach zero in this region and so the distance squared can be approximated by a parabola with a well defined minimum:

plot [4.6:4.7] (x*besy1(x)-1.2)**2

enter image description here

Your optimum x = b is the position of this minimum. You can export this as data and fit to a parabola f(x) = a2*x**2 + b2*x + c2 with the minimum given by f'(x) = 0, that is, x = -b2 / (2.*a2):

set table "data_minimum"
plot [4.6:4.7] (x*besy1(x)-1.2)**2
unset table
fit [4.6:4.7] a2*x**2 + b2*x + c2 "data_minimum" via a2,b2,c2
print -b2/2./a2

This gives x = 4.65447163370989 for the minimum's location, which corresponds to the optimum b in B = b*besy1(b).

The accuracy of this will depend on the goodness of the quadratic fit, which will in turn depend on how narrow about the minimum your range of x values is. In this case, the range [4.6:4.7] led to the quadratic fit being quite good but not perfect (you could narrow it down even further):

plot [4.6:4.7] "data_minimum" t "data", a*x**2+b*x+c t "quadratic fit"

enter image description here

Edit

For modified Bessel functions of the second kind, or other complicated functions not available in Gnuplot, you could use an external parser. For example see my answer on how to use an external python code to parse functions: Passing Python functions to Gnuplot.

You can use scipy to access your function modifying the Python script from my other answer (file name test.py):

import sys
from scipy.special import kn as kn

n=float(sys.argv[1])
x=float(sys.argv[2])

print kn(n,x)

and within Gnuplot use this as

kn(n,x) = real(system(sprintf("python test.py %g %g", n, x)))

then all the above procedure works just replacing besy1(x) by kn(1,x).

Community
  • 1
  • 1
Miguel
  • 7,497
  • 2
  • 27
  • 46
  • Thanks for you elaborate reply! It's an interesting approach however I think that the "besy1(x)" you wrote is NOT the modified Bessel of the 2nd kind but just the Bessel of the 2nd kind. And I checked that because I fitted both with gnuplot and matlab as well (matlab has the besselk in it) and didn't get results that are comparably close. So I'm guessing that the besselly you wrote is not in fact the modified one? – Poisonedhoney Aug 15 '15 at 16:18
  • As of verson 5.0, gnuplot can import a function from an binary shared library (.so or .dll). Check `help import`. That'll probably be a lot faster if your datasets are large. – Karl Aug 16 '15 at 08:54
  • @Miguel, your scheme for inverting the bessel function is a bit complicated. This here does essentially the same, and directly gives the exact (up to gnuplots fit accuracy) solution: If `bl` is `bessel(b)`, simply set b to it's approximate value and do `set sample 2; fit bessel(b)"+" using 1:(bl) via b`. – Karl Aug 16 '15 at 09:27
  • @KarlRatzsch I know it's a bit complicated, but I wanted to avoid relying on using non-polynomic expressions for the fitting, which rely on having good initial values, in particular for oscillatory functions. But of course your solution would also work in this case. – Miguel Aug 16 '15 at 09:37
  • @Miguel: On the contrary. Your solution needs an accurate choice of the region for the fit. Mine (i.e. gnuplots fit algorithm) automatically does that, up to machine precision. It only needs a starting value that doesn't let it go for *another* solution (as can happen for an oscillating function). – Karl Aug 16 '15 at 09:59
  • @KarlRatzsch I hope this settles the debate. As you probably know, gnuplot relies internaly on an approach that is very similar to my solution: a xi^2 minimization. Your giving a starting guess is equivalent to my giving a range where there is a minimum of the distance squared, only that you should make sure that you minimum is unique in the domain of the fit, e.g. by doing `fit [xmin:xman] ...` making sure there is only one minimum between `xmin` and `xmax`. I agree that your solution is more accurate, but you need to make sure the latter statement applies. – Miguel Aug 16 '15 at 10:28
  • No, you misunderstand my approach. I give no range for the fit. The x-values supplied by "+" are not even used. What it does is not even a fit actually, it uses the fit routine to search for the minimum of `(bessel(b) - bl)²`. – Karl Aug 16 '15 at 11:05
  • I realized that after writing it. In this case you should make sure the step size of the search algorithm is small enough compared to the size of the region where there is only one minimum. – Miguel Aug 16 '15 at 11:34