Matlot++-C++绘图工具

2020-08-30 13:43:18

数据可视化可以帮助程序员和科学家识别他们数据中的趋势,并有效地将这些结果与他们的同行交流。现代C++正被用于各种科学应用程序,该环境可以从满足科学数据可视化典型设计目标的图形库中获益良多。除了将结果导出到其他环境的选项外,C++中常用的替代方案要么是依赖于现有用户界面的非专用库,要么是绑定到其他语言。Matlot++是一个用于数据可视化的图形库,它提供交互式绘图、为科学出版物以高质量格式导出绘图的方法、与类似库一致的紧凑语法、具有专门算法的数十个绘图类别、多种编码样式,并支持通用后端。

这些示例假设我们位于matlot名称空间中。使用这些示例了解如何快速使用库进行数据可视化。如果您有兴趣了解库是如何工作的,可以稍后阅读完整文章中的详细信息。

这些示例使用独立函数来创建打印。还可以将面向对象的样式用于打印。我们将在章节编码样式中讨论这些编码样式。

Stair对象渲染数据点之间的带楼梯的线,以表示离散数据。

误差栏对象包括额外的线来表示数据点周围的错误。对数曲线图是将x或y轴调整到对数刻度的实用函数。

对象函数行和字符串函数将函数存储为lambda函数或带有表达式的字符串,而不是存储数据点。这些对象使用惰性求值来生成绝对数据点。只有在调用Draw函数时才会生成数据。

第一次使用惰性计算调用绘制函数时,直方图对象会创建直方图边缘和柱状图。如果用户在调用Draw之前更改了对象参数,惰性求值可以避免计算不必要的边。该对象包括几种用于自动定界直方图的边缘和柱状图的算法。

散点图还取决于线对象。由于LINE对象可以表示带标记的线,因此散布函数只需创建不带线的标记即可。

入库散点图使用上一节直方图算法的变体作为额外步骤,将所有数据放入可以用不同颜色或大小表示的二维箱中。当数据点太多,以至于散点图不适合可视化数据时,这很有用。

Plot Matrix子类别是直方图和散点图的组合。它在图上创建轴对象矩阵,并为每对数据集创建散点图。

函数parallellot使用平行坐标创建绘图。在这种类型的打印中,平行线对象存储一组表示多维数据的任意轴对象。

词云是从文本或词对及其频率生成的。在赋予与每个单词频率成比例的大小之后,定位标签的算法从最大到最小迭代单词。对于每个单词,它会将该单词在转换为笛卡尔坐标的极坐标中旋转,直到它不与任何其他单词重叠。

默认情况下,颜色和大小取决于词频。我们可以通过向wordcloud函数传递第三个参数来自定义颜色。

帕累托图表是一种既使用$y$轴又使用$y$轴的图表。$y_1$轴用于表示数据值按降序排列的条形图。$y_2$轴用于表示数据在$y_1$轴上的累积分布函数。默认情况下,帕累托图最多包括10个项目,或者表示95%的累积分布所需的任意多个项目。

如果您需要帕累托阵面而不是帕累托图表,我们指的是二维阵面的散点图、三维阵面的曲线图或多目标阵面的平行坐标图。横断面数据分布中介绍了这些打印子类别。如果您还需要一个工具来有效地计算这些前沿,我们可以参考帕累托前沿图书馆(Pareto Front Library)。

对于第一个地理绘图,Matlot++调用Geolot(),它使用世界地图创建一个填充多边形。第一个地块接收标签MAP,这样后续的地理地块就可以识别出不需要重新创建此世界地图。

世界地图的数据来自自然地球。它们以1:10米、1:50米和1:110米的比例提供数据。GeoPlot功能最初将使用1:110m比例尺的数据。Geolimits功能可用于更新地理地块的轴限制。用于调整轴限制(xlim和ylim)的常用函数与地理限制之间的区别在于,后者还将根据$x$和$y$轴的新限制更新地图分辨率。

Geolimits功能将查询地物大小,并根据轴的新限制,根据需要将地图更新为1:10m或1:50m比例。由于以1:10m或1:50m的比例渲染整个世界地图以仅显示该地图的一个区域是非常低效的,因此地理限制功能还会裁剪与正在显示的新区域相关的数据。

请注意,这不仅涉及删除新限制之外的数据点,还需要在正确的边界上创建新的数据点,以创建与区域中的地图入口点一致的新多边形。因此,算法需要跟踪原始世界地图中表示为闭合多边形的所有子地图。如果子地图完全在新范围之内或之外,我们可以分别包括或取消数据点。但是,如果子地图只有一部分在新的限制内,要为多边形生成正确的边界,我们需要跟踪限制外的所有点,以便对这些点在限制外的方向进行分类。为此,我们只包含在新限制周围更改象限的点,以便贴图入口点创建的多边形看起来与完整的世界地图仍在这些新限制之外渲染时一样。

如果您对地理绘图不感兴趣,构建脚本将包含一个选项,用于从库中删除1:10m和1:50m比例的高分辨率地图。在这种情况下,无论轴的限制如何,库都将始终使用比例为1:110米的地图。

函数WORLD_CITIES返回世界主要城市的列表。它的参数定义了城市在$x$和$y$轴上的最小距离。GREEDY_TSP函数是一种天真的贪婪算法,用于寻找这些城市之间的路线,作为旅行商问题(TSP)。我们使用GeoPlot函数来绘制这条路线。请注意,我们使用方法链接来定义一些进一步的绘图属性。最后,文本函数包括地图中的城市名称。

通过在轴上放置极坐标,轴移动到极模式,在这种模式下,我们使用$r$和$t$轴,而不是$x$和$y$轴。

从后端的角度来看,这些轴是对用户的抽象。通过将位置从极坐标$r$和$t$转换到笛卡尔坐标$x$和$y$来绘制$r$和$t$轴上的数据点,关系为$x=r\cos{t}$和$y=r\sin{t}$。

除了此转换之外,这些绘图子类别类似于直线图、散点图、直方图、箭头图和直线函数。

函数极化直方图将数据分配到作为其第二个参数提供的多个条柱中。

所有这些子类别都取决于等高线类型。它们还依赖于懒惰的评估来生成轮廓线。在Contours类中调用函数Draw时,它会预处理三维函数的所有轮廓线。

虽然显示带有$z$轴的值的热图相对简单,但是计算相对于$z$轴的等高线比乍看起来要复杂得多。我们提供了计算等高线的函数contourc。此函数使用Matplotlib采用的算法的改编。

该算法创建由$x$和$y$值定义的四边形网格。它使用此栅格推断通过具有相同$z$值的位置的等高线。该算法扫描网格两次以生成这些线。第一个扫掠查找从边界开始的线。第二个扫描查找内部闭环。

填充的等高线是一对等高线高程的闭合多边形。填充等高线的某些多边形可能是其他多边形内部的孔。该算法需要跟踪这些关系,以便我们能够以准确的顺序呈现多边形。为了避免标识多边形之间的这种关系的额外步骤,扫掠算法已经标识了每个标高的哪些多边形是洞。

一旦我们找到了具有轮廓线的四边形,就会通过对该四边形周围的$z$值进行插值来生成该线。

默认情况下,函数fconfiguration将从lambda函数生成9条轮廓线。另一方面,函数configuration和contourf绘制等高线,并从$x$、$y$和$z$的数据点网格填充等高线。

所有这些子类别都取决于矢量对象类型。在二维绘图中,对于具有向量位置的$x$和$y$的每个值,还需要指示其方向和大小的$u$和$v$的值。在三维绘图中,方向和大小由$u$、$v$和$w$定义。

箭图(或速度图)显示矢量栅格,其方向和大小会进行缩放,以防止后续四边形中的矢量之间重叠。

所有这些子类别都依赖于NETWORK类。图形是表示对象以及这些对象之间的关系的抽象结构。对象被表示为顶点,关系被描述为边。

在抽象图中,顶点在空间中没有特定位置。从数学上讲,图形不依赖于它的布局。然而,图形布局对其可理解性有很大影响。网络类可以通过几种算法为图的顶点计算合适的位置:Kamada Kawai算法、Fruchterman-Reingold算法、圆布局、随机布局和自动布局。

Kamada Kawai和Fruchterman-Reingold算法的实现依赖于NodeSoup库。自动布局使用用于小图形的Kamada Kawai算法和用于较大图形的Fruchterman-Reingold算法。

这些子类别取决于矩阵类。矩阵类最多可以有四个矩阵。如果它只有一个矩阵,则用色彩映射表表示。如果它有三个矩阵,则它们表示红色、绿色和蓝色通道。如果它有四个矩阵,则第四个矩阵表示一个Alpha通道,用于控制每个像素的透明度。

我们使用CIMG库来加载和保存图像。只要CIMG有权访问适当的库,它就可以处理许多常见的图像格式。Matlot++构建脚本将查看以下可选库的编译时间:JPEG、TIFF、ZLIB、PNG、LAPACK、BLAS、OpenCV、X11、fftw3、OpenEXR和Magick++。构建脚本将尝试将此列表中的所有库链接到Matlot++。

Matlot++包括几个方便的函数来操作带有图像的矩阵:imread、rgb2grey、gray2rgb、imresize和imwrite。所有这些函数都适用于矩阵列表。

注释类别用于在绘图上创建单个对象,而不是数据集的表示。批注类别与其他类别之间的一个重要区别是,默认情况下,即使用户不调用Hold函数,批注也不会替换Aaxes对象中已经存在的绘图。

Rectangle对象的边框曲率可以在$0$到$1$之间。我们还可以使用文本、箭头、多边形和线条进行注释。

为了方便起见,Colors.h头包含许多函数,可以从字符串生成颜色,反之亦然。

我们的平铺函数是子绘图函数的便捷快捷方式。如果没有空间容纳下一个平铺,我们会自动重新排列轴,并增加子图的行数或列数以适合下一个平铺。使用子图可以更好地控制子图。

交互式绘图窗口有一个用于保存当前图形的小部件。因为此小部件使用的后端与用于生成交互式图像的后端相同,所以最终图像与用户在窗口中看到的内容非常匹配。

您可以使用SAVE功能以编程方式将图形保存为多种格式:

第一个选项(save(Filename))从文件扩展名推断适当的文件格式。在这两种情况下(save(Filename)和save(filename,fileformat)),此函数将后端临时更改为适合绘制图形的非交互式后端。每种格式使用不同的后端,根据格式的不同,最终图像不一定与交互式绘图窗口上的内容匹配。原因是有些文件格式故意不包含相同的功能。

30+---+|*+||**|25|-+**+-||**||**|20|-+**+-||**|*。*|15|-+*+-||*|10|-+*。|*||*|5|-+*+||*。**+**|0+---+1 2 3 4 5 6 7。

将图像保存为适合嵌入LaTeX文档的格式,例如

这将以LaTeX文本替换标签的格式导出图像,以便绘图适合文档的其余部分。

全局当前轴对象是全局地物注册表中当前地物中的当前轴对象。

例如,可以使用PLOT(Y);在当前轴上创建线条图(如果需要,也可以创建新的AXES对象)。

此外,还可以使用PLOT(ax,y);在轴对象AX上创建线条图。

我们可以通过AUTO AX=gca();AX->;Plot(Y);在当前轴上创建相同的线状图。

这不那么冗长,并且在需要传递这些对象的大型项目中提供了更好的控制。

假设用户显式管理轴以在另一个函数中创建绘图,则这些样式的一个更完整的示例可能是。

这两个示例将生成相同的情节。所有独立函数都是模板化函数,它们使用元编程来调用当前AXES对象上的主函数。如果第一个参数不是AXES_HANDLE,它将使用GCA(截面图形和轴)从地物注册表获取AXES_HANDLE,并将所有参数转发给此AXES对象中的函数。如果第一个参数是AXES_HANDLE,则模板函数会将除第一个参数以外的所有参数转发到此AXES对象。这种独立函数模板的使用使开发人员可以维护这两种编码样式。

请注意,因为示例需要函数my_function的Axes对象,所以我们还需要获取对具有独立函数的Axes对象的引用。在这种情况下,独立函数的冗长程度不亚于面向对象的接口。

要坚持独立函数,我们可以创建my_function的两个版本:一个版本接收axes_handle,第二个版本从图形注册表获取axes_handle并调用第一个版本。如果my_function将作为库向其他用户公开,这可能会给这些用户带来便利。但是,请注意,这只是将详细信息从main函数移动到my_function。事实上,这就是Matlot++中独立函数的工作方式。

图形也有两种模式:反应性(或交互性)模式和静默模式。处于反应模式的地物在其任意子对象更改时都会更新。这是通过touch函数实现的,当任何子对象更改其外观时,都会调用该函数。这将创建一种交互模式,在该模式中,只要我们调整地物的属性,就会立即更新地物。如果我们将交互式图形与独立函数相结合,我们就有了类似Matlab的绘图样式。这是一种编码模式,其中图形注册表作为绘图的流工作。这种编码风格的问题是,用户可能会不必要地创建无用的中间绘图。

静默模式下的图形通过调用函数raw()或show()(剖面图形和轴)进行更新。除非调用这些函数,否则图形中不会发生任何变化。面向对象的编码风格和静默模式的结合就是绘图的类似于OO-Matplotlib的风格(";OO-Matplotlib";)。这是用户明确决定何时显示或更新绘图的编码样式。对于不能将计算资源浪费在可能对应用程序没有价值的中间数字上的应用程序来说,这是有益的。

我们通常使用反应式的独立函数和静默式的面向对象接口。默认情况下,新图形处于反应式模式,除非它使用非交互式后端。用户可以通过以下方式打开和关闭此反应模式:

//反应模式auto f=gcf(False);auto ax=f->;gca();auto p=ax->;Plot(ax,x,y);//绘制一个cep->;color(";red";).line_width(2);//再绘制两次等待();//暂停控制台。

为方便起见,剖面图类别中的示例使用反应式模式。等待功能暂停控制台,直到用户与绘图窗口交互。如果后端基于流程管道,因为这些管道是单向的,关闭窗口不足以恢复。用户需要使用控制台解锁执行。一个类似的示例是静默模式,即。

//安静模式auto f=gcf(True);auto ax=f->;gca();auto p=ax->;Plot(x,y);//不绘制->;color(";red&34;).line_width(2);//不绘制->;show();//仅绘制一次并暂停控制台

在本例中,地物仅更新一次。用户可以用DRAW函数替换SHOW函数,但是一旦执行完成,窗口就会关闭。谨慎使用wait()和show()非常重要。这些函数用于某些特定的可执行文件,因此交互式绘图在用户可以看到之前不会关闭。在库中调用这些函数可能是不合理的,因为用户必须手动干预执行才能继续。

为了支持更紧凑的语法,该库允许对Plot对象进行方法链接。例如,我们可以创建一个简单的线条图,并通过以下方式修改其外观。

第一个代码片断之所以起作用,是因为Plot向轴上的对象返回一个line_handle。我们可以使用此线条手柄修改线条图。每当我们修改属性时,setter函数都会调用touch,如果它处于反应模式,它将再次绘制图形。第二个o。

.