Gri:科学绘图语言

作者:Dan E. Kelley

与其他计算机用户一样,科学家们有时也会发现自己在简单工具和复杂工具之间,在易用性和强大功能之间左右为难。

以写作为例。最简单的写作工具是办公套件类的工具,因为它们是基于GUI的。如果您会点击和指向,就可以快速生成结果,而无需经历陡峭的学习曲线。

然而,您可能也要为这种浅显的学习曲线付出代价。许多大量使用数学符号的技术作家发现,基于GUI的产品在用于除一页纸以外的任何用途时,都既有限制又令人恼火。例如,我们研究领域(物理海洋学)的研究生经常在他们的论文中撰写数百个方程。几乎无一例外,这些学生都使用标记语言(主要是 TeX 和 LaTeX)来完成这项工作。必须承认,基于标记的写作工具比基于GUI的工具更难学习,但学习的努力会得到回报,即对输出的精确控制,这种控制在美学上令人愉悦,并且足够灵活以满足所有合理的需求。

适用于文字的也适用于图片。对于快速完成的工作,使用基于GUI的绘图软件包或电子表格非常方便。然而,许多用户更喜欢基于标记的系统来制作复杂的插图,原因与他们喜欢基于标记的系统来处理复杂文本的原因相同。另一个因素是,基于GUI的系统无法帮助生成必须在无人干预的情况下自动生成的插图。

加拿大新斯科舍省达尔豪西大学的海洋学家准备的风暴潮预报提供了一个有趣的例子。风暴潮是由与风暴相关的异常风应力和低气压驱动的海平面异常升高。这些风暴潮可能会对低洼地区造成相当大的破坏。由于如果风暴潮恰好发生在涨潮时,破坏会加剧,因此准确预测风暴潮到达时间非常重要。如果人们得到充分的风暴潮预警,则可以大大减少风暴潮造成的损失。达尔豪西大学的研究人员开发了数值海洋模型,类似于数值天气模型,用于预测西北大西洋风暴潮的发生。目标是提供提前预警系统,在网络上以图形方式显示风暴潮预报。Gri 用于该系统中的图形,因为它可以在无人干预的情况下运行。它非常灵活,研究人员可以定制插图,以便非技术用户快速理解。

我们提到风暴潮的例子主要是因为我们觉得它很有趣。许多人使用 Gri,因此我们可以选择 Gri 在科学和工程不同学科中的其他应用示例。为了帮助您决定 Gri 是否可以在您自己的工作中为您提供帮助,我们将解释如何使用 Gri 制作一些基本的科学插图(折线图、等高线图和图像图)。这足以让您在几分钟内入门。之后,我们将概述我们自己和同事工作中的一些示例。在第二部分中,我们将毫不犹豫地解释这项工作的科学背景,因为我们很喜欢在本杂志上阅读这类内容。

入门

科学家们喜欢 Gri 的一个特点是,它可以对输出外观的几乎所有方面进行精细控制。这一点很重要,因为科学家的需求多种多样,从复杂的工作图到用于提案、会议演示和出版物的简化而优雅的图表。许多 Gri 用户报告说,它足够灵活,可以满足所有应用范围,无需学习一个用于工作图的工具和另一个用于“出版质量”插图的工具。

Gri 通过高度可配置来实现其灵活性;它有很多旋钮可以转动。仅仅因为旋钮在那里并不意味着您需要触摸它们,因为 Gri 默认设置对于许多应用程序来说是合理的。默认设置的合理性很可能源于 Gri 是由一位科学家为他的研究工作而设计的,而不是由一个委员会或一个调查(或想象)市场的公司结构设计的。

折线图

说明 Gri 的默认设置和简单性的最佳方法或许是展示如何生成最常见的科学插图形式,即描述 x,y 数据的折线图。具体来说,假设我们有一个名为 linegraph.dat 的 ASCII 文件,其中包含以下 x,y 对

0.05 12.5
0.25 19.0
0.50 15.0
0.75 15.0
0.95 13.0

然后,创建折线图只需三个 Gri 命令

open linegraph.dat
read columns x y
draw curve
如果这些命令存储在名为 linegraph.gri 的文件中,并且 Gri 在 UNIX shell 中以 gri linegraph.gri 的形式调用,则结果将是一个名为 linegraph.ps 的 PostScript 文件,如图 1 所示。Gri 不会绘制到屏幕上,因为屏幕绘图库比 PostScript 更有限,并且所有平台上都免费提供高质量的 PostScript 查看器。

Gri: A Language for Scientific Illustration

图 1. Gri 生成的简单折线图

该示例说明了一些问题。首先,Gri 语法很简单,以英语为模式,并有意使用最少的标点符号。例如,open 命令将包含数据的文件名作为参数。此文件名未像在基于子例程的语言中那样用括号括起来。我们认为,缺少括号使 Gri 比某些语言更易于阅读。文件名也不需要用引号括起来,尽管对于文件名中带有空格的文件名(以及由 UNIX 管道命令创建的伪文件名,Perl 风格支持)则需要引号。

现在让我们继续讨论 read 行。Gri 知道四种数据类型:标量、列、网格和图像。这些类型正如您所期望的那样:标量保存数值或字符串,列保存数字列表,网格保存二维矩阵,图像保存像素。每种数据类型都有一系列相关的 read 命令和数据处理命令。习惯于编程语言的人应该注意到,Gri 是一种面向插图的语言,而不是面向编程的语言,因此它对列赋予了特殊的含义。因此,上面示例中名为 x 和 y 的列对应于笛卡尔空间中的 x,y 值。Gri 还有一个名为 z 的列用于网格化,一个名为 w 的列用于统计操作中的权重,名为 u 和 v 的列用于矢量场,等等。

draw curve 命令告诉 Gri 绘制一条由连接这些点的线段组成的曲线。考虑到对绘图的关注,您可能不会感到惊讶地得知 Gri 有许多 draw 选项——实际上超过 40 个。例如,要在数据点处绘制符号,您可以在 read 命令之后的某个位置添加行 draw symbol。这将在 x,y 点处绘制项目符号。项目符号是默认值,但 Gri 提供了十几个其他符号选择。这些符号是根据科学期刊和技术制图文本的建议设计的。例如,已努力确保任何两个符号的叠加不会导致符号被误认为是第三种符号类型。默认符号大小为直径 0.1 厘米(以项目符号的预期方式定义,并以合理的方式定义非圆形符号)。如果这不适合您的应用,您可以发出诸如 set symbol size 0.2 之类的命令来创建更大的符号(在本例中为 0.2 厘米)。这绝不是唯一的 set 命令。它有很多选项,因为 Gri 被设计为可配置的。实际上,每个 draw 命令平均有近两个 set 命令。

除了这些基本命令和接下来讨论的其他一些命令外,Gri 还提供编程功能,例如“if”语句、循环和系统调用。它还提供了一种简单的方案来创建新命令以补充现有命令;例如,

Draw Logo
{
... Gri commands to draw a logo
}

创建一个新命令来绘制徽标。此命令通过 Draw Logo 调用,就像它是内置命令一样。许多用户将此类定义存储在名为 ~/.grirc 的文件中,从而为他们所做的工作自定义 Gri。

Gri 在处理命令时逐步更新其 PostScript 输出文件。当 Gri 执行任何绘图命令时,项目将使用当时已设置的设置(字体、颜色、线宽等)绘制。用艺术类比来说,draw 将使用最近的 set 命令存储在画笔上的任何颜色进行绘制。许多其他脚本图形语言遵循不同的方法,某些命令能够更改已绘制材料的形式(例如,matlab 中的轴)。这些语言旨在以探索性方式交互使用,因此进行这种乱序处理是有意义的。但是,Gri 按顺序处理命令,因为它被设计为以计划方式非交互使用。

Gri 脚本通常不是一次性编写一大块。相反,Gri 用户通常一次编写一点脚本,频繁运行 Gri 以查看结果。有些人喜欢始终打开 PostScript 查看器,每次运行 Gri 时都单击更新按钮。大多数 Gri 用户从 Emacs 内部运行它,Emacs 有一个按钮可以运行 Gri 并显示输出。这只是 Gri Emacs 模式的一个有用的功能。它还提供语法着色、缩进、与在线 Gri 文档的链接、特定于命令的帮助、命令完成以及列出包含给定单词的所有 Gri 命令的搜索工具。

等高线图

折线图只需三个命令即可完成,而等高线图并没有复杂多少。假设我们有一个名为 temp.dat 的文件,其中包含从 1950 年到 2000 年,每十年对海洋温度进行一次测量的数据,深度分别为 500 米、400 米、300 米等等,一直到地表 0 米。存储此类数据集的自然方法是 Gri 中的矩阵或“网格”。在 Gri 中绘制此图分三个步骤。

# First, set up the x,y vertices of
# the grid ...
set x grid 1950 2000  10
set y grid  0    500 100
# then read the grid data ...
open temp.dat
read grid data
# and plot contours:
draw contour

如您所见,Gri 中的注释以井号开头,就像许多其他脚本语言一样。在本例中,使用了均匀网格(即,样本之间的时间间隔为 10 年),但非均匀网格也很容易处理。对于许多应用,网格是从数据文件中读取的,而不是在命令文件中指定的。至于等高线的绘制,这相当容易,因为 Gri 可以通过扫描网格来确定合理的默认等高线范围。

图像图

图像处理起来也不难多少。世界上有很多图像格式,而 Gri 只处理少数几种。这几乎不会引起问题,因为有很好的转换程序可用(例如,ImageMagick)。假设我们有一个 8 位分辨率的原始(无头)图像文件。8 位像素有 256 个可能的值,范围为 0 到 255。卫星衍生的海洋温度测量值通常采用这种格式。数据的常用比例是 4.9 摄氏度获得像素值 0,而 30.4 摄氏度获得 255,两者之间呈线性变化。假设图像为 512 像素乘 512 像素,地理覆盖范围跨越一个框,左下角坐标为 x=0 和 y=0,右上角坐标为 x=20 和 y=20。我们希望图像以蓝色表示冷水,红色表示暖水。列表 1 显示了如何在 Gri 中处理此问题。如您所见,让 Gri 创建折线图、等高线图和图像图非常容易。

列表 1。

关于文件名和其他属性的快速说明。在到目前为止给出的示例中,我们始终在脚本中指定数据文件的名称,但这意味着用户必须修改文件才能用于其他数据。因此,许多脚本使用名为 query 的命令来询问用户输入文件的名称。此命令将用户的答案存储到变量中,这些变量稍后可以在 open 命令中使用。query 命令有一个选项可以提供允许的答案列表(例如,“yes”和“no”),以便用户不会犯错。在许多实验室中,query 被广泛使用,以便新手可以使用复杂的 Gri 脚本,而无需编辑他们尚不理解的 Gri 脚本。这使学生可以投入到他们的研究中,而不会因工作工具而分心。

放射性核素揭示海洋混合

让我们转到一个更复杂的例子,该例子基于我们一篇研究论文中的插图。在此处以及本文的其余部分中,我们将仅显示 Gri 代码的片段,以说明尚未涵盖的主题。

理解气候系统的关键问题是海洋与大气之间的相互作用。海洋顶部三米的热容量等于整个大气层的热容量,但每个人都知道海洋的深度不止三米。事实上,平均深度更像三公里。鉴于此,海洋对地球的热平衡以及气候系统至关重要,这并不令人惊讶。除了吸收太阳热量(或重新释放热量)的能力外,海洋还具有将热量从一个位置输送到另一个位置的洋流。这种输送的确切路径尚未完全绘制出来,我们仍然不确定该系统的一些基本动力学方面。大量证据表明,海洋中的垂直混合对于这些热流模式非常重要。因此,海洋混合不仅对海洋本身很重要,而且对整个气候系统也很重要。这是研究海洋混合的主要动机,海洋混合是我们的研究专长。

观察混合的一个好方法是插入示踪剂并观察其去向。这并不像听起来那么容易,因此海洋学家也大量使用自然发生的或在没有特定研究混合意图的情况下引入的示踪剂。后者的一个例子是 20 世纪 60 年代美国和苏联进行的大气核弹试验引入大气的一组放射性核素。这些放射性核素已经进入海洋。一种名为氚的氢同位素放射性核素对于研究海洋混合特别有用。图 2 显示了一篇论文(参见“资源”)中的图,其中我们中的一位作者与一位名叫 Kim Van Scoy 的地球化学家同事一起,试图通过追踪氚在几十年中的运动来计算海洋混合速率。

Gri: A Language for Scientific Illustration

图 2. 等高线插图,显示海洋中氚最大浓度的深度

我们的方法是对特定地理区域内空间平均的属性进行时间导数。该区域由平均海洋环流模式的流线定义。现在,平均网格化数据很容易:只需沿网格的行和列求和即可。然而,不幸的是,我们的观测不是在网格上进行的。如图 2 所示,观测位置明显不均匀,这些位置对应于为实现其他目标而设计的航次中的船舶航迹。因此,我们的第一步是将这些 x,y,z 数据投影到均匀的 x,y 网格上。在读入列并设置 x,y 网格(与上面讨论的完全相同)之后,我们使用以下命令创建网格:

convert columns to grid barnes

这是我们的第一个 convert 命令示例。此类别中的命令执行从一种数据类型到另一种数据类型的转换。由于等高线处理在网格上进行,因此我们必须将列数据转换为网格。在本例中,我们选择使用所谓的“barnes”网格化方法,这只是 Gri 中的几种网格化方法之一(参见“资源”)。该方法应用高斯加权低通滤波(平均)方案,该方案迭代运行。初始迭代描绘出场中的大规模变化,而随后的迭代则填充数据采样足够密集的区域中的细节,以证明这样做是合理的。

在前面的示例中,我们让 Gri 选择等高线;事实上,这就是我们开始使用工作图的方式,这些工作图最终生成了图 2 中所示的图表。然而,对于出版物,我们最终决定通过用更粗的线条绘制某些等高线来突出显示它们

set line width rapidograph 00
draw contour 50 unlabelled
set line width rapidograph 2
draw contour 100

其中使用了通过用于 Rapidograph 技术绘图笔的方案命名线宽的“rapidograph”方法。线宽也可以以打印机点为单位给出。

在这个阶段,我们得到了我们想要的科学结果。我们使用 Gri 命令 write grid 将网格化数据写入文件,然后我们使用 Perl 脚本(从 Gri 内部使用 system 命令调用)集成这些值,然后我们就完成了部分工作。为了演示,我们想以地图格式绘制它,因此我们首先稍微自定义了轴

set x name ""
set y name ""
set x format %.0lf$\circ$
set y format %.0lf$\circ$

前两个命令通过将名称设置为空字符串来删除轴上的名称 x 和 y。后两个命令使用 C 编程语言的表示法设置轴数字的格式。我们还在格式中添加了一个类似 LaTeX 的部分(用美元符号括起来),以使 Gri 绘制经度和纬度的度数符号。Gri 可以通过这种方式绘制希腊字母和数学符号,尽管 TeX 的模拟绝非完整,因为它是一项艰巨的任务。

没有海岸线的地图没有太大帮助。由于海岸线文件非常大,我们的系统以二进制格式保留数据以进行快速读取。我们使用称为 netCDF 的格式,该格式在地球科学中非常流行(参见 http://www.unidata.ucar.edu/packages/netcdf/)。这种格式的众多优点中,有一个与 Linux 用户非常相关:它在大小端计算机以及具有不同字长的计算机之间完全二进制兼容。它还允许命名数据实体,因此您不必记住第一列是纬度,第二列是经度,反之亦然。Gri 可以轻松处理 netCDF 格式

open map_land.nc netCDF
read columns x="lon" y="lat"

只需这些即可读取海岸线数据。我们将用浅棕色填充海岸线,然后绘制黑色海岸线

read colornames from RGB "/usr/lib/X11/rgb.txt"
set color burlywood
draw curve filled
set color black
draw curve
除了基于 X11 的颜色和上面黑色等十几种熟悉的颜色外,Gri 还允许您在 RGB 或 HSV 框架中指定颜色。
用卫星和船舶观测海洋

说到颜色,Gri 的一个常见应用是生成彩色图像的插图。在海洋学中,此类图像通常分为两大类:数值模型生成的场和卫星观测生成的场。在这两种情况下,Gri 相对于更多基于图像的工具的优势在于,Gri 鼓励用户绘制其他图形元素以及图像。

图 3 提供了一个很好的例子,展示了巴西里约格兰德大学的海洋学家牵头的 ECOLAP 计划中的一些工作(参见 http://www.peld.furg.br/)。该研究小组有一个为期十年的计划,旨在测量和了解巴西里约格兰德河口和邻近海域的物理和生物变异性。图 3 显示了海洋温度的卫星图像,以及 2000 年 2 月 22 日进行的船载观测的位置。图中陆地被涂成红棕色,调色板指示卫星测量的海面温度。卫星图像的处理与本文开头附近给出的更假设的示例中描述的完全相同。这两个面板是通过简单地更改轴并重新绘制来绘制的。调色板是用名为 draw palette 的命令绘制的,引导线是用命令 draw boxdraw line 绘制的,标签是用 draw label 绘制的。到现在为止,您可能已经感觉到很容易猜出 Gri 命令的名称。这种猜测不是必需的;只需在 Emacs 模式下键入“draw”,然后按 ESC 键,然后按 TAB 键,该模式将显示以单词“draw”开头的所有命令,即所有绘图选项。

Gri: A Language for Scientific Illustration

图 3. 显示巴西东部、乌拉圭和阿根廷东部的卫星衍生的海面温度的图像

图 3 中一个值得注意的特征是使用符号来指示进行船舶海洋属性测量的位置。这些船载观测通常从海底延伸到海面,通常涉及水体的物理属性以及生物属性(例如,不同物种在不同深度中的出现)的测量。在过去的几十年中,海洋学家在解释船载观测中的模式方面面临着巨大的挑战,而图 3 说明了原因。考虑一下较大图像中间附近的船舶样本。它位于一股细细的冷水流(绿色)中,而其他样本则位于较温暖的水域中。在某种程度上,生物学只是“随波逐流”,因为洋流将水从一个地方移动到另一个地方,因此如果这个中间样本与附近的站点具有不同的生物学特征(例如,冷水特有的物种),也就不足为奇了。卫星数据和船舶数据在一个图上的叠加(在 Gri 中很容易实现)为研究中的系统提供了深刻的见解。

几乎不用说,Gri 的基于脚本的性质对于构建此类图表非常重要。此图表的任何内容都不是用鼠标准备的,Gri 脚本中的任何内容都不需要为船舶的另一次航行进行修改(因为 query 命令用于设置所有文件名)。一旦船舶进行观测并将经纬度对写入数据文件,Gri 脚本就可以重新运行并准备新的图表。

测量海洋混合的新方法

理解湍流是物理学中的一项重大挑战,而海洋混合提供了一个很好的例子。最终目标是能够根据易于测量的大尺度属性来预测混合,混合是一种难以测量的小尺度现象。一种提出的技术是检查中间尺度上水密度的垂直变化。如果可以做到这一点,它将极大地扩展我们的海洋混合知识数据库,因为密度测量很常见。但这能做到吗?第二个作者的博士论文解决了这个问题。图 4 显示了一个模仿有关这项工作的论文的图表。该插图显示了两件事。红色图像显示了混合应该发生在哪里的理论预测,包括时间和深度。蓝色填充曲线显示了实际发生混合的位置。

Gri: A Language for Scientific Illustration

图 4. 海洋表面区域的混合指标

更具体地说,红色图像显示了所谓的理查森数。理论表明,只有当理查森数(稳定和不稳定效应之间竞争的度量)降至 1/4 的临界值以下时,才能发生称为开尔文-亥姆霍兹翻转的混合类型。我们测量了理查森数在网格上的深度和时间变化。我们的第一个想法是对其进行等高线处理,但由于我们想叠加其他东西,因此我们决定使用图像来获得清晰度。Gri 代码的要点可以从您目前为止阅读的内容中猜到,唯一的新功能是使用命令 convert grid to image 将我们的网格数据转换为可以着色的图像。我们通过在红色色调的强度范围内运行颜色标度(而不是像上一个示例那样跨色调运行颜色标度)来绘制红色阴影中的图像。我们这样做是因为我们想叠加另一条特定颜色的曲线,并且在下面的光谱中很难辨别出来。图像表明,混合应该发生在深度大约为 17 米的带状区域中,并且该混合带应该在观测时间内上下浮动。

理论用红色描绘,我们将转向观测。蓝色看起来很漂亮,因此我们首先绘制蓝色垂直线,这些垂直线对应于从船舷放下密度测量探针的时间。我们将密度随深度的变化称为密度“剖面”。海洋中的海水密度通常随深度增加,因为重水下沉,而浮力水上升。然而,涡流混合运动会颠覆这种密度剖面,暂时将重水抬升到轻水之上。借助足够精确的密度探针,可以通过绘制观测到的密度剖面与通过重新排序密度数据以使密度随深度单调增加而创建的人工剖面之间的差异来以图形方式揭示这种混合。我们跟踪各个点在重新排序过程中必须移动的距离,并将此称为“索普位移剖面”。此剖面指示混合斑块的强度和范围。我们使用填充曲线绘制这些索普剖面,如命令

draw curve filled to x 0.0

在首先重新定义轴以使 x=0 对应于船舶观测时间之后,我们为每个剖面执行一次此操作。

您可能会同意,观测结果与理论大致一致,因为观测到的高混合率的深度和时间(蓝色曲线)似乎与理论(红色图像)相匹配。这的主要含义不是低理查森数会产生混合;我们已经从现场和实验室的实验中知道了这一点。相反,主要含义是我们的密度探针能够拾取这种特定强度的混合信号。这很重要,因为我们使用的仪器比通常用于测量混合的仪器更常见。有关该技术的工作原理和原因的更多信息,我们鼓励您查阅我们的论文,我们可能要补充一点,该论文的每一张图都使用了 Gri。

请注意,此图表中的轴位于绘制数据的框之外。第二作者更喜欢这种风格,而第一作者更喜欢传统风格。在这方面,与大多数事情一样,Gri 为您提供了选择。

资源

Gri: A Language for Scientific Illustration
Dan Kelley (Dan.Kelley@Dal.Ca) 是加拿大新斯科舍省达尔豪西大学的海洋学副教授。

Peter Galbraith (psg@debian.org) 是加拿大渔业和海洋部的研究科学家。

Dan 编写了 Gri,Peter 编写了 Emacs 模式。两位作者都不是专业程序员这一事实可以解释这些工具的有限实用性。

加载 Disqus 评论