本文(以及系列中将要发布的其他文章)的目标是使用完全相同的数据重现[SPJ02]中的可视化效果,但每次当然会使用另一个绘图包,以便对所有包进行1:1的比较。
《Towards Data Science》的出版指南不允许重复这些可视化的描述。因此,请参阅[SPJ02]以获取更多信息,或在Julia Forem上阅读本文的更自包含版本。
VegaLite.jl与Gadfly.jl一样,也是图形语法(GoG)的一个非常完整的实现,我们将在下面的示例中看到。它是由大卫·安索夫教授(加州大学伯克利分校)领导的一个由20多名贡献者组成的团队编写的。VegaLite是数据科学包(称为Queryverse)的一部分,其中包括查询语言(Query.jl)、文件IO和UI工具(ElectronDisplay.jl)等。
从技术上讲,VegaLite采取了完全不同的方法:虽然Gadfly完全是用Julia编写的,但VegaLite更像是Vega-Lite图形包的语言接口(注意其名称中的破折号,与Julia包VegaLite相对应)。Vega-Lite以JSON格式的可视化规范作为输入,Vega-Lite编译器将其转换为相应的可视化效果。
Vega-Lite完全独立于Julia生态系统,除了VegaLite外,还存在其他语言(如JavaScript、Python、R或Scala)的接口(完整列表请参见“Vega-Lite生态系统”)。
由于Vega-Lite使用JSON作为其输入格式,这些规范具有相当声明性质。VegaLite试图通过@vlplot宏来模仿这种格式,正如我们将在下面的示例中看到的,该宏是所有可视化的基础。这使其不太像Julia,例如Gadfly,但另一方面,熟悉Vega-Lite的人很容易学会如何使用VegaLite。如果VegaLite文档中有遗漏的内容,通常很容易在Vega-Lite文档中找到相应的部分。
Vega-Lite(以及VegaLite)的一个区别性特征是其互动性。其规范不仅描述了可视化效果,还描述了事件、兴趣点以及如何对这些事件作出反应的规则。但这个特性超出了本文的范围。对于对此感兴趣的读者,我建议查看Vega-Lite主页或论文“Vega-Lite: A Grammar of Interactive Graphics”。
与前一篇文章中一样,我将使用以下相同的图表类型(或者按照GoG的说法称之为几何图形)进行比较:
VegaLite提供的类型的完整列表可以在此图库中找到。
与[SPJ02]一样,我们假设示例数据在DataFrames结构countries、subregions_cum和regions_cum中可用。
并且与[SPJ02]一样,大多数图表首先以基本版本呈现,使用图形包的默认设置,然后使用自定义属性进行优化。
第一个图表是柱状图,显示了按地区划分的人口规模(2019年)。在VegaLite中,所有图表都是使用@vlplot命令创建的。在下面的代码中,使用了Julia的流水线语法(|>),将regions_cum-DataFrame指定为@vlplot的输入。
regions_cum |>
@vlplot(
width = 600, height = 300,
:bar,
x = :Region, y = :Pop2019, color = :Region
)
这将产生以下柱状图:
现在我们手动设置坐标轴标签、标题和背景颜色,并将x轴上的柱状标签更改为水平方向,以提高可读性。在VegaLite中,标题属性用于标签以及图表标题,轴属性用于更改柱状标签的方向,配置用于一般属性,如背景颜色(与Gadfly中的主题相对应)。
regions_cum |>
@vlplot(
width = 600, height = 300,
title = "Population by Region, 2019",
:bar,
x = {:Region, title = "Region", axis = {labelAngle = 0}},
y = {:Pop2019, title = "Population [millions]"},
color = :Region,
config = {background = "ghostwhite"}
)
… 创建以下柱状图:
下一个柱状图描述了按子地区划分的人口(同样使用@vlplot):
subregions_cum |>
@vlplot(
width = 600, height = 300,
:bar,
x = :Subregion, y = :Pop2019, color = :Region
)
在下一步中,我们切换到了水平柱状图,并再次手动调整了标签、标题和背景颜色。在VegaLite中,通过将x轴和y轴的数据属性翻转,我们可以获得水平布局:
subregions_cum |>
@vlplot(
title = "Population by Subregion, 2019",
width = 600, height = 300,
:bar,
x = {:Pop2019, title = "Population [millions]"},
y = {:Subregion, title = "Subregion"},
color = :Region,
config = {background = "ghostwhite"}
)
现在我们希望在绘制图表之前按人口大小对子地区进行排序。为此,我们可以使用Julia对subregions_cum-DataFrame进行排序(与在Gadfly示例中所做的一样),但VegaLite提供了使用sort属性在图形引擎中对数据进行排序的可能性。
subregions_cum |>
@vlplot(
title = "Population by Subregion, 2019",
width = 600, height = 300,
:bar,
x = {:Pop2019, title = "Population [millions]"},
y = {:Subregion, sort = "-x", title = "Subregion"},
color = :Region,
config = {background = "ghostwhite"}
)
在这一点上需要注意:虽然可以在图形引擎内部对数据进行排序,但我不建议在数据集较大时这样做,因为它比直接使用Julia要慢得多。
下一个图表是一个散点图(使用点几何图形),用于描述国家层面的人口与增长率的关系:
countries |>
@vlplot(
width = 600, height = 300,
:point,
x = :Pop2019, y = :PopChangePct, color = :Region
)
现在我们将对x轴应用对数尺度。同样,我们添加了一些标签、背景颜色等:
countries |>
@vlplot(
title = "Population vs. Growth Rate, 2019",
width = 600, height = 300,
:point,
x = {:Pop2019, title = "Population [millions]",
scale = {type = :log, base = 10}},
y = {:PopChangePct, title = "Growth Rate [%]"},
color = :Region,
config = {background = "ghostwhite"}
)
用于绘制直方图时,VegaLite严格遵循GoG,因为它使用与柱状图相同的几何图形(唯一的区别是x轴上的数据在一个称为binning的过程中映射到人为的类别)。以下代码使用参数bin设置为true的柱状几何图形,通过以下@vlplot命令创建了一个直方图,显示了不同国家之间人均GDP的分布:
countries |>
@vlplot(
width = 600, height = 300,
:bar,
x = {:GDPperCapita, bin = true}, y = “count()”
)
默认情况下选择了合理的bin大小(这在Gadfly中不是这种情况)。
在下一步中,我们再次添加标签等。为了使bin的数量与Gadfly示例中的数量完全相同,我们使用以下代码将其明确设置为20:
countries |>
@vlplot(
title = "Distribution of GDP per Capita, 2019",
width = 600, height = 300,
:bar,
x = {:GDPperCapita, bin = {maxbins = 20},
title = "GDP per Capita [USD]"},
y = {"count()", title = "Number of countries"},
config = {background = "ghostwhite"}
)
下一个图表显示了每个地区的人均GDP分布,首先使用箱线图,然后使用小提琴图。
我们跳过使用默认值的版本,直接进入基于箱线图几何图形的“美化”版本:
countries |>
@vlplot(
title = "GDP per Capita by Region, 2019",
width = 600, height = 300,
:boxplot,
x = {:Region, title = "Region", axis = {labelAngle = 0}},
y = {:GDPperCapita, title = "GDP per Capita [USD]"},
color = :Region,
config = {background = "ghostwhite"}
)
由于VegaLite本身不支持小提琴图作为一种几何图形,因此必须使用密度图(每个地区一个)构建它们,这些密度图在水平上排列。这导致了以下相当复杂的规范:
countries |>
@vlplot(
title = "GDP per Capita by Region, 2019",
height = 400,
mark = {:area, orient = "horizontal"},
transform = [
{density = "GDPperCapita", groupby = ["Region"]}
],
x = {"density:q", stack = "center", impute = nothing, title = nothing,
axis = {labels = false}},
y = {"value:q", title = "GDP per Capita [USD]", axis = {labelAngle = 0}},
column = {"Region",
header = {titleOrient = "bottom", labelOrient = "bottom"}},
color = :Region,
config = {background = "ghostwhite"},
width = 120, spacing = 0
)
用于创建密度图的基本几何图形是面积几何图形。然后,数据按地区分组,并为每个组计算密度。这是通过变换操作完成的。将密度分配给x轴会得到垂直密度图。在下一步中,所有五个密度图使用column属性水平排列。
最后一行中的width和spacing属性定义了每列(即每个密度图)在水平方向上具有120像素的宽度,并且在这些图之间没有空间。
因此,我们最终得到了以下小提琴图:
与Gadfly示例中一样,我们注意到分布的真正有趣部分位于0到10万美元之间的范围内。因此,我们希望在y轴上限制图表的范围,以实现一种缩放效果。
在Gadfly示例中,我们通过将y轴上的值限制在该范围内来实现所需的效果。在VegaLite中,也可以使用scale = {domain = [0, 100000]}来指定此限制。不幸的是,这并没有给我们想要的结果:图表将在此范围内绘制,但图表本身仍然使用整个范围,直到20万美元,因此部分绘制在图表外部:
在VegaLite中获得大致相似的结果的唯一方法是使用过滤表达式将数据限制在0到10万美元的范围内。但请注意:这在概念上是不同的,不会像在整个数据集上执行的那样给我们完全相同的图表。因此,我们没有这个可视化的真正解决方案。
这可能只是VegaLite文档的问题,我在其中找不到其他解决方案(或者是我没有做足够的研究,例如还可以使用Vega-Lite的广泛文档)。
我认为,上面的示例非常清楚地展示了VegaLite是另一个Julia绘图包,它相当密切地遵循了图形语法的概念(甚至比Gadfly更密切)。因此,对于VegaLite也适用于相同的发现,即绘图规范非常一致,因此易于学习。
但正如我们从小提琴图中可以看到的那样,如果事先没有定义,规范可能变得相当复杂。再加上相对非Julia的语法,需要一些时间来学习和适应,我不建议VegaLite用于偶尔的用户。它需要一些学习和训练。但是,如果你投入了时间和精力,你将获得一个非常强大(且互动性强)的可视化工具。
一个有趣的VegaLite附加组件是交互式数据探索工具Voyager(见:DataVoyager.jl)。这是一个应用程序,可以加载数据并创建各种可视化效果,无需任何编程。
如果你想自己尝试上面的示例,可以从我的GitHub存储库中获取Pluto笔记本,这是一种可以执行的这篇文章的变体。