使用Common Lisp和Gnuplot从emacs逐个绘制数据

5
假设我有一些数据数组(具体来说是向量)。能否使用Gnuplot逐个元素地绘制它,使其看起来像是通过监视器跟踪的实时信号?
我知道我可以使用Common Lisp将整个数据写入文本文件,然后使用gnuplot以批处理格式绘制它。我需要的是,当数据按顺序到来时,我想在我的绘图上放置一个点。
数据可能是在循环内生成的,因此您可以将x轴视为整数值离散时间轴。因此,在循环中,如果数组的第一个元素生成为5,则我想将一个点放在我的图上(0,5)。然后,如果第二个元素生成为3,则我想再在我的图上放置另一个点(1,7)(保留旧数据点)。因此,随着循环迭代,我逐步绘制数据。
我正在使用emacs和Common Lisp,并希望在这些工具中绘制此数据。如果除Gnuplot之外还有其他选项,我想听听它们。
如果不容易实现,如果我可以通过某个Common Lisp命令运行Gnuplot命令文件,那就太好了。
编辑:
根据人们在此主题下给出的建议,我编写了一个使用cgn(使用ltk)的代码。
现在我在屏幕上预先指定的两个位置打开了两个x11窗口,并进入循环。在循环中,每次我都会打开流并使用:if-exists:append选项将数据(0.25 Hz采样的正弦和余弦波)写入文本文件trial.txt,然后关闭流。然后在每次迭代时,我使用format-gnuplot命令绘制整个数据的gnuplot。这个代码给我两个预先指定的x和y范围的窗口,然后可以在窗口中观察上述正弦和余弦波的演变。
正如我之前所述,我没有坚实的编程背景(我是一名电气工程师,不知何故使用了common lisp),我相当确定我的代码不是最优的且不够优雅。如果您们有其他建议、修正等,我真的想听听。这里是代码:
(setf filename "trial.txt")
(setf path (make-pathname :name filename))
(setf str (open path :direction :output  :if-exists :supersede :if-does-not-exist :create))
(format str "~,4F ~,4F" -1 -1)
(close str)

;start gnuplot process
(start-gnuplot "/Applications/Gnuplot.app/Contents/Resources/bin/gnuplot")

;set 2 x11 windows with the following properties
(format-gnuplot "cd ~S" "Users/yberol/Desktop/lispbox/code")
(format-gnuplot "set terminal x11 0 position 0,0")
(format-gnuplot "set xrange [0:10]")
(format-gnuplot "set yrange [-1:1]")
(format-gnuplot "unset key")
(format-gnuplot "set grid")

(format-gnuplot "plot ~S using 1" filename)
(format-gnuplot "set terminal x11 1 position 800,0")
(format-gnuplot "plot ~S using 2" filename) 

;write data into text 
(loop :for i :from 0 :to 10 :by (/ 1 20) :do
   (setf str (open path :direction :output  :if-exists :append :if-does-not-exist :create))
   (format str "~,4F ~,4F ~,4F ~%" i (sin (* 2 pi (/ 5 20) i)) (cos (* 2 pi (/ 5 20) i)))
   (close str)
   (format-gnuplot "set terminal x11 0")
   (format-gnuplot "plot ~S using 1:2 with lines" filename)
   (format-gnuplot "set terminal x11 1")
   (format-gnuplot "plot ~S using 1:3 with lines" filename)
   (sleep 0.1))
(close-gnuplot)

非常感谢你。

所以你想要一个空窗口,然后逐点显示所有数据,并在显示点n和点n+1之间有一定的延迟,比如一秒钟,我理解你的问题对吗? - Pavel Penev
精确地,1秒钟或取决于我的数据采样率Ts秒。 - jkt
除非我错了,这跟Emacs几乎没有什么关系,对吧?如果您想寻找替代方案,可以考虑R,就像在这个 CV帖子中所示。如果我能想出一个适当的解决方案在CL/gnuplot中,我会发布它的。 - chl
1
@chl 感谢您的回复。数据将在Lisp中生成,我知道在R和Matlab中可以动态绘图,但问题是是否可以将Lisp与任何其他可用的绘图工具进行通信。如果可能的话,我想在Lisp中完成所有操作,但如果不行,我需要在Lisp中生成数据,然后使用R、Matlab、gnuplot等工具进行绘图。 - jkt
@YBE 问题在于更新gnuplot图形设备,而不是CL和gnuplot之间的绑定;我过去使用过cgn,但是除非您想切换到类似xlispstatArc(不是Paul Graham的Arc),否则我不知道CL是否具有动态绘图功能;这两个都基于CLISP的子集。 - chl
@chl 我的编程背景不是很强。我之前用过Matlab。现在我需要使用Lisp,对我来说任何易于实现的解决方案都可以。虽然我问的问题是关于Gnuplot的,但任何工具都可以。 - jkt
5个回答

3

cgn是一个使用LTK的Common Lisp解决方案,用于与gnuplot进行接口交互。


CGN会像我在问题中询问的那样动态工作吗?非常感谢! - jkt
是的,绝对没问题。您只需要运行 start-gnuplot,然后就可以交互式地评估不同的 cgn 函数了。 - Vsevolod Dyomkin
我昨天使用CGN进行了绘图。它有效了。但是,我不确定如何以与MATLAB的drawnow函数类似的方式在gnuplot中实现动态绘制数据的目标。你有什么想法吗?谢谢提前。 - jkt
我不确定drawnow的工作原理,但据我所知,动态地使用cgn可以实现同样的效果。首先,你需要启动start-gnuplot,然后每次评估绘图函数都会实时更改绘制的图形。 - Vsevolod Dyomkin
你的代码看起来还不错。唯一值得注意的是,在CL中有一个特殊的宏(with-open-file (str path :direction :output :if-exists :append :if-does-not-exist :create)) ...)(参见:http://www.lispworks.com/documentation/lw50/CLHS/Body/m_w_open.htm),通常用于代替在文件上显式调用`open`和`close`。 - Vsevolod Dyomkin

2

您可以创建进程来调用gnuplot,并将数据以及绘图命令发送到它的stdin。我不确定如何在Common Lisp中管理这样的进程,但您肯定可以在Emacs中实现:

(setf *gnuplot-proc* (start-process "gnuplot" "*gnuplot-proc*" "gnuplot"))
;;; initiate plotting of data from stdin
(process-send-string *gnuplot-proc*
                     "plot \"-\" with lines\n")
;; send your data
(process-send-string *gnuplot-proc*
                     "5 -1\n4 -3.5\n3 9.5\n")
;; end of data, after this gnuplot would pop up interactive window
(process-send-string *gnuplot-proc* "e\n")

通过这种异步的过程,很容易编写一些程序使其随着新数据的到来而交互式地更新绘图。


2
您可以使用eazy-gnuplot,我也使用它。在这里查看示例:eazy-gnuplot示例。github仓库在这里:github仓库。很抱歉我没有时间在这里提供示例。

为什么选择这一个而不是 awesome-cl/plotting 或 http://quickdocs.org/search?q=plot ?谢谢。 - Ehvince
2
@Ehvince,我不知道你提到的那些。我可能会去了解一下。但是我从另一个角度来看待eazy-gnuplot。该库的作者在他的工作中(机器学习研究)进行了一些相当复杂的绘图,而且界面似乎很容易利用gnuplot的强大功能。结合作者推荐使用的GnuPlot Cookbook书籍,它对我非常有用。 - Joe

2

1

我对Gnuplot不是很熟悉,快速搜索也没有找到太多信息。但也许我可以提出一种方法。比如说你将输入分块,例如'(1 2 3 4 5)将变成'((1) (1 2) (1 2 3) (1 2 3 4) (1 2 3 4 5)),然后你可以为每个块生成一个图表,并使用像lispbuilder-sdl这样的图形库在窗口中显示它,并设置时间延迟。SDL有一个计时器,可以很好地显示图像。


谢谢回复。你的方法看起来可行,虽然如果有更直接的方式对我来说会更好。 - jkt

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