将Python函数传递给Gnuplot

4
在Gnuplot中绘制Python函数并不直观,尽管有一些解决方案。例如,可以将其值转换为数组,或者手动将其表达式翻译成Gnuplot的语法。下面是一个使用模块Gnuplot.py作为接口的示例:
#!/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)

第一种方法在处理相当数量的点时效果良好,但是当缩放过多或将窗口更改为超出数组限制时会失败,而第二种方法在任何缩放级别下都有效。为了说明这一点,让我们放大上一个脚本的输出:

function sampling vs expression translation

因此,找到一种将Python函数(lambda或常规函数)绘制为Gnuplot函数的方法是很有趣的。我可以想到两种解决方案:自动翻译表达式(仅适用于“简单”的lambda函数),或者让Gnuplot直接使用Python函数。

第一种解决方案:表达式翻译(仅适用于简单的lambda函数)

这种方法不仅难以自动化,而且对于复杂的函数来说也无法实现。但是,我们仍然可以使用这种方法来处理简单的lambda函数。为了勾画实现的行为:

>>> 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 )'

有没有办法在Python中实现这个lambda_to_gnuplot函数?

第二种解决方案:直接将Python函数传递给Gnuplot

“完美”的解决方案是让Gnuplot使用Python函数。在我最大胆的梦想中,它应该是这样的:

>>> 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)

这是最容易使用的解决方案,但如果不是不可能的话,它的实现将非常困难。您有关于如何实现此解决方案的任何提示吗?当然,答案可能会绕过Gnuplot.py


非常有趣的想法! - Alfe
@Alfe:你觉得这个地方特别有趣在哪里? - Arcturus B
到目前为止,我知道我必须将Python函数转换为Gnuplot语法才能绘制它,或者在打开Gnuplot之前选择样本,从而失去所有(或大部分)Gnuplot的交互功能(例如缩放)。现在你的问题让我意识到可以用其他方式实现。这就是我觉得有趣的地方。 - Alfe
1个回答

3

我不确定我的答案是否完全回答了你的问题,但你可以尝试在gnuplot中将你的python脚本作为系统调用来执行,并传递参数。

比如,假设有一个简单的python脚本test.py

import sys

x=float(sys.argv[1])

print x**2

当从shell中像这样调用时,它会返回参数的平方:

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

现在,在gnuplot中将此转换为一个函数:
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

我加入了real()函数,以便将系统调用的字符串输出转换为浮点数。这样就可以像使用普通的gnuplot函数一样使用它了。不需要说明的是,这将比仅执行plot x**2花费更长的时间:

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

enter image description here


这确实是一个非常好的开始,谢谢!我将尝试编写一个Python函数来实现这种方法,并使其可以通过单个调用使用。 - Arcturus B
1
@Alfe 谢谢你的建议,我已经进行了修改。 - Miguel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接