函数的批量定义,或如何绘制光谱

时间:2018-06-05 14:03:19

标签: loops gnuplot

我有一个存储在包含两列的文件中的计算光谱:第一个是x位置(波数),第二个是高度(强度)。为了模拟真实的光谱,我想为每条线(大约60个)放置一个高斯,并绘制所有线的总和。正如here所解释的那样,我从数据文件中读取系数位置和高度:

g(x,x0,s,I) = I/sqrt(2*pi*s**2)*exp(-(x-x0)**2/(2*s**2))
stats "file.dat" nooutput
nlines=int(STATS_records-1)
paramstr(N) = sprintf('stats "file.dat" every ::%i::%i u (x%i=$1,I%i=$2) nooutput',N,N,N,N)
do for [i=0:nlines] {eval(paramstr(i))}
spectrum(x,s) = sum [i=0:nlines] g(x,value(sprintf('x%i',i)'),s,value(sprintf('I%i',i)))

这很有效,可以产生预期的效果。但是,就我而言,它不是一个文件,而是大约30个文件,每个文件包含不同的光谱。这些文件具有系统名称,由“t”,“g +”和“g-”构成的5符号代码组成,即可能的文件名为ttttt.dat,g + g + ttg-.dat等。

我想批量转换所有这些,但之后只能以交互方式绘制特定选择的那些。因此我的想法是为这些文件中的每一个创建一个单独命名的函数,即ttttt(x​​,s),g + g + ttg-(x,s)等。因为+和 - 不允许在函数名中替换这些分别与¯和_:

a = '"very/long/path/to/dir/with/files/'
filelist = system('ls '.@a")
conflist = system('ls '.@a"." | sed 's/.dat//' | sed 's/g+/g¯/g' | sed 's/g-/g_/g'")

现在,我将上面显示的代码包含在文件列表中的单词的循环中。第一次尝试的结果是所有函数看起来都相同并且包含按字母顺序排列的最后一个文件的参数,因为每个循环都覆盖了x%i和I%i的值。因此,我必须通过包含文件名(例如x3替换为xttttt3)来确保每个函数引用其自己的参数。通过这样做,定义了大约3600个变量。

do for [c = 1:words(titlelist)] {
    paramstr(N) = sprintf('stats @a/%s" every ::%i::%i u (x%s%i=$1,I%s%i=$2) nooutput',word(titlelist,c),N,N,word(conflist,c),N,word(conflist,c),N)
    do for [i=0:nlines] {eval(paramstr(i))}
    eval(sprintf("%s(x,s) = sum [i=0:nlines] g(x,value(sprintf('x%s%i',word(conflist,c),i)),s,value(sprintf('I%s%i',word(conflist,c),i)))",word(conflist,c)))
}

但是,在调用plot ttttt(x,s=5)时,我收到错误消息undefined variable: c。将c设置为1到30之间的某个值后,每个函数显示相同的频谱,即对应于filelist的第30个条目的频谱。在这一点上,我已经没有想法了。有没有人建议如何从plot tttg¯t(x,s), tg_g¯ttt(x,s), ttttt(x,s)等命令中获得正确的结果?

编辑:问题是由sprintf的错误嵌套引起的。嵌套sprintf的重新洗牌和字符串的仔细连接导致正确的工作调用:

eval(sprintf("%s(x,s) = sum [i=0:nlines] g(x,value('x%s'.i),s,value('I%s'.i))", word(conflist,c),word(conflist,c),word(conflist,c)))

显然,这是非常糟糕的写作,繁琐,几乎无法理解,而且绘图速度很慢。我将很高兴地欣赏任何更优雅的解决方案!

1 个答案:

答案 0 :(得分:0)

与此同时,我自己想出了一个解决方案。 Gnuplot在迭代绘图方面相当慢,这就是为什么用显式和代替高斯的迭代求和是有益的。这是通过将迭代“外包”到bash来完成的,后者为gnuplot创建了一个输入文件Spectrum.gp

#!/bin/bash
for FILE in $(ls *.dat); do
NAME=$(echo $FILE | sed 's/.dat//')
echo "${NAME}(x,s) = \\" >> Spectrum.gp
awk -v str1="g(x," -v str2=",s," -v str3=") + \\" '{print str1 $1 str2 $2 str3}' $FILE | sed '$ s/ + \\//g' >> Spectrum.gp
done

sed -i "1i s = 0.8" Spectrum.gp
sed -i "1i g(x,x0,s,I) = expo(x,x0,s)**2>50 ? 0 : I/sqrt(2*pi*s**2)*exp(-2*expo(x,x0,s)**2)" Spectrum.gp
sed -i "1i expo(x,x0,s) = (x-x0)/(2*s)" Spectrum.gp

结果文件如下所示(假设我有两个文件molecule1.datmolecule2.dat,每个文件有5行:

expo(x,x0,s) = (x-x0)/(2*s)
g(x,x0,s,I) = expo(x,x0,s)**2>50 ? 0 : I/sqrt(2*pi*s**2)*exp(-2*expo(x,x0,s)**2)
s = 0.8
molecule1(x,s) = \
g(x,73.9,s,7.2) + \
g(x,137.37,s,6.2) + \
g(x,218.33,s,3.3) + \
g(x,292.3,s,2.0) + \
g(x,407.94,s,1.0)
molecule2(x,s) = \
g(x,85.3,s,1.2) + \
g(x,150.2,s,5.2) + \
g(x,151.3,s,3.7) + \
g(x,370.3,s,2.3) + \
g(x,401.0,s,9.0)

然后通过load Spectrum.gp,然后是plot molecule1(x,s)将文件加载到gnuplot中。通过这种方法,Spectrum.gp的生成以及绘图只需不到一秒钟的时间。

万一有人对高斯的定义感到疑惑:高斯离中心很远,就接近零,因此,只要指数内的项大于合理猜测的阈值,总值就会精确地设为零,从而节省了一些计算成本。

我希望这会帮助那里的任何人。