Arcturus B
Arcturus B

Reputation: 5621

Passing Python functions to Gnuplot

Plotting a Python function in Gnuplot is not straightforward although there are some solutions. For example, one could either cast its values into an array or manually translate its expression into Gnuplot’s syntax. Here is an example that uses the module Gnuplot.py as an interface:

#!/usr/bin/env python

import Gnuplot
import numpy as np

## define function ##
func = lambda x, x0, y0, w: y0 * np.exp( -4*np.log(2) * ( (x-x0) / w )**2 )
# also works with a regular function:
# def func(x, x0, y0, w):
    # return y0 * np.exp( -4*np.log(2) * ( (x-x0) / w )**2 )
popt = (10.1, 5, 2)

## linspace ##
x = np.linspace(0, 20, num=1000) # (x min, x max, number of points)
y = func(x, *popt)
func_linspace = Gnuplot.Data(x, y, with_='lines', title='linspace')

## expression “translation” (lambda only) ##
func_translation = Gnuplot.Func(
    '{y0} * exp( -4*log(2) * ( (x-{x0}) / {w} )**2 )'.format(
        x0=popt[0],
        y0=popt[1],
        w=popt[2],
        ),
    title='expression translation')

## plot ##
g = Gnuplot.Gnuplot()
g.plot(func_linspace, func_translation)

The first method works fine with a decent number of points but fails when zooming-in too much or changing the window out of the array’s limits, while the second one works at any zoom level. To illustrate this point, let’s zoom-in the output of the previous script:

function sampling vs expression translation

For this reason, it would be interesting to find a way to plot Python functions (lambda or regular functions) as Gnuplot functions. I can think of two solution: automatically translating the expression (works only for “simple” lambda functions”), or having Gnuplot directly use the Python function.

First solution: expression translation (simple lambda functions only)

This method would not only be tricky to automate, it would also be impossible to implement with elaborate functions. However we could still use this method for simple lambda functions. To sketch the behaviour of an implementation:

>>> def lambda_to_gnuplot(func, popt):
...     # determine if translation is possible
...     # extract function expression and replace parameters with values
...     return func_expression # str
>>> lambda_to_gnuplot(
...     lambda x, x0, y0, w: y0 * np.exp( -4*np.log(2) * ( (x-x0) / w )**2),
...     (10.1, 5, 2))
'5 * exp( -4*log(2) * ( (x-10.1) / 2 )**2 )'

Would there be a way to implement this lambda_to_gnuplot function in python?

Second solution: directly passing the Python function to Gnuplot

The “perfect” solution would be having Gnuplot use the Python function. In my most daring dreams, it is something like:

>>> def func(x, x0, y0, w):
...     if x < x0:
...         return 0
...     else:
...         return y0 * np.exp( -4*np.log(2) * ( (x-x0) / w )**2)
>>> func_direct = Gnuplot.PyFunction(lambda x: func(x, 10.1, 5, 2))
>>> g.plot(func_direct)

This is the easiest solution to use, but its implementation would be very tough, if not impossible. Any hints on how this solution might be implemented? The answer may of course bypass Gnuplot.py.

Upvotes: 4

Views: 1451

Answers (1)

Miguel
Miguel

Reputation: 7627

I am not sure if I'm fully answering your question, but you could try executing your python script as a system call within gnuplot passing the argument(s).

For instance, imagine the simple python script test.py:

import sys

x=float(sys.argv[1])

print x**2

which will return the square of the argument when called like this from a shell:

:~$ python test.py 2
4.0
:~$ python test.py 3
9.0
:~$ python test.py 4
16.0

Now, within gnuplot, turn this into a function:

gnuplot> f(x) = real(system(sprintf("python test.py %g", x)))
gnuplot> print f(1)
1.0
gnuplot> print f(2)
4.0
gnuplot> print f(3)
9.0
gnuplot> print f(4)
16.0

I added the real() so that the string output from the system call is converted to float. This now allows you to use it as a regular gnuplot function. I don't need to mention this will take a lot longer to execute than just plot x**2:

f(x) = real(system(sprintf("python test.py %g", x)))
plot f(x)

enter image description here

Upvotes: 3

Related Questions