使用拼图图类型监控您的数据
作者:社区 / 产品,用例,开发者
2020年8月26日
导航到
本文由 InfluxData 实习生 Rashi Bose 和 Rose Parker 撰写。
InfluxDB 提供了多种图类型可视化来使用户能够轻松监控其数据。然而,大多数这些图类型仅在你能够以数值表示数据时才有用。如果你想要跟踪应用程序的健康状况,或者可视化在 Kubernetes 中部署的 pod 的状态随时间的变化,怎么办?在这两种情况下,状态都是通过使用几个离散值随时间跟踪的,不能画在 x/y 图上。
在这篇文章中,我们将向您展示我们在夏季实习期间创建拼图图类型的过程。这种图非常适合跟踪随时间的变化状态。
问题是什么?
该项目始于我们的队友 Mark Rushakoff 的一个请求,他需要一个可以随时间绘制给定类别数据值变化的图类型。x 轴代表时间,y 轴代表不同数据系列的名称。最后,该图将会有彩色方块,每个颜色代表一个特定的值,方块的长度代表该值持续的时间。Mark 提供了我们(如下所示)的大致图形,图中有两个数据集系列 1 和系列 2,以及可能的值列表“foo”、“bar”和“quux”。在图中,每个值都被分配了一个颜色,在我们的图中,这些方块会用这种颜色填充。与 Mark 面对面交流后,我们确保项目仍有价值并明确了一些具体细节,然后我们开始了。
为什么选择拼图图?
我们必须问自己的第一个问题是这个可视化应该叫什么名字。原始请求没有提到这种图类型的名称,所以我们做了一些研究。我们考虑了甘特图、时间轴和状态图等图类型,但最终决定采用拼图图。拼图图可以可视化两个或更多定性变量的数据——在我们的情况下,是系列和值。我们选择这种图类型是因为尽管我们的图比拼图图稍微专业一些,但我们想确保它符合我们选择的任何图的要求。此外,与其他可能性不同,拼图图是一种常见的图表类型,人们应该能够识别这个名称并对其用途有所了解。
我们的第一次尝试
第一次尝试将我们的可视化类型添加到Giraffe(使用ReactJS构建的图形可视化库)时,我们遇到了理解所有不同变量和文件的问题,所以我们决定采取最简单的方法,尽可能准确地模拟现有的最相似图形类型(直方图和热图)。正如您所想象的,这并不成功。我们无法将任何内容可视化,并且不理解我们遇到的错误。我们很快意识到,使用这种策略我们无法取得任何进展,因此我们退一步,在添加代码之前试图弄清楚所有这些是如何协同工作的。
从头开始
我们首先使用Canvas元素创建了一个简单的马赛克图,以确定绘制图形所需的数据形状。当我们查看Giraffe中其他图形显示的数据时,我们发现我们接收到的数据将包含多个列,包括时间、值、系列名称,以及可能的其他数据。每一行数据都将有一个时间戳和该时间点的系列值。对于我们的图形,我们需要根据间隔组织这些数据。由于我们想显示一个值(例如“foo”)持续了多长时间,我们需要知道“foo”成为值的时刻以及值再次改变的时刻。因此,我们需要创建一个表格,存储每个间隔的开始和结束时间,以及值和系列名称。
在我们对我们要达到的目标有了更好的想法后,我们开始添加一些代码,确保我们在需要的地方,并将马赛克图构建得类似于其他图形类型。
在我们的第一次迭代中,我们在转换函数中硬编码了列名,以与我们的样本数据集相对应。因此,我们的转换函数只能与我们的样本数据集一起工作。然后,当我们确定图形看起来像我们的目标图形时,我们删除了硬编码的值,以便图形可以与任何传入的数据集一起工作。
创建一个虚拟数据集
我们遇到的第一问题之一是我们没有一组适用于我们图形的原始数据。正如我们之前提到的,所有其他图形类型都使用数值。因此,当时Giraffe中存在的虚拟数据集不符合我们的需求。为了解决这个问题,我们编写了一个脚本,将现有虚拟数据集的值转换为具有字符串值的虚拟数据集,将数值放入具有字符串名称的范围内。这产生了一个数据集,其中每个系列代表一个CPU,每个值间隔映射到一个随时间变化的颜色。
可视化图形
在将数据调整为正确形状后,我们终于可以开始绘制它了。我们首先创建了绘制图形本身的函数。我们设法在不太麻烦的情况下使可视化出现,但图形看起来与预期不同。每个系列的边界点不一致,某些颜色变淡。
为了更好地了解问题所在,我们创建了一个第二,更小的原始数据集。使用这个数据集,我们发现我们的初始转换函数错误地计算了给定系列的结束时间。我们最初将一个系列的结束时间分配为下一个系列的开始时间。然而,这种方法是不正确的,因为每个系列是独立运行的,所以一个系列的结束时间发生在下一个系列的开始时间之前。为了解决这个问题,我们将每个系列的最后一个间隔的结束时间设置为图形中的最后一个时间戳。
我们接下来要做的事情是绘制坐标轴,因为我们认为这应该是相对简单的。但我们错了。我们希望将x轴设置为时间,y轴用来显示系列名称。我们能够比较容易地设置x轴,但设置y轴则证明更加复杂,因为之前没有图表使用字符串作为它们的y轴。我们最终通过在名为yTicks
的变量中存储系列名称来实现这一点,该变量存储非马赛克层的数字数组。为了实现这一点,我们必须为所有y轴函数创建一个特定的马赛克案例,包括查找适当边距宽度的函数,因为它们都依赖于yTicks
是一个数字数组。
一旦我们正确地完成了可视化,我们就可以开始添加工具提示。在Giraffe的所有图表类型中,马赛克图表的工具提示充当图表图例。因此,我们首先需要决定在工具提示中包含哪些信息。在我们的初次尝试中,我们假设用户最希望看到的是他们当前悬停的方框的信息(时间范围、系列、值)。结果发现,显示当前时间戳的所有信息更简单,但我们过滤了这些信息,只显示指定时间戳和系列的信息。
在那次大约两周的第一轮迭代之后,我们与Mark会面,以获取我们对图表的反馈。我们收到了积极的反馈,唯一的建议是在工具提示中添加当前方框的持续时间。在那次快速修复之后,我们就可以开始下一步了。所有图表类型首先在Giraffe中实现,然后导入到InfluxDB中。现在,我们已经有了工作图表类型,我们可以告别Giraffe(暂时)并开始将马赛克图表添加到InfluxDB中。
将图表类型添加到InfluxDB
从与Giraffe的错误中吸取教训,我们第一天在InfluxDB中探索了如何实现其他图表类型,特别是散点图。我们努力理解我们真正需要引用马赛克图表的地方,因为每个图表的实现略有不同。因此,我们决定记录散点图被引用的函数及其原因,然后逐渐将这些地方也添加了马赛克图表。在这个过程中,我们添加了很多console.log
语句,错误地添加和删除了我们认为需要的文件,但我们最终弄明白了。经过一周的工作和构建代码库的心理模型,我们只创建了两个新文件:一个用于可视化的马赛克图文件和一个用于可视化的马赛克图定制选项文件。
回到起跑线
当我们最终在InfluxDB中渲染了马赛克图后,我们发现我们的图表没有达到预期的效果。我们决定查看查询返回的数据的小样本,并使用该数据遍历我们的转换函数以验证函数是否返回了正确的输出。在遍历代码的过程中,我们发现从查询中接收到的数据与我们测试Giraffe中数据的顺序不同。在我们的Giraffe测试数据集中,我们按系列名称对所有数据进行排序,所以series1
的所有值都出现,然后才是series2
的所有值。在查询返回的数据中,所有不同系列的值都穿插在一起,尽管它们仍然按时间顺序排列。为了解决这个问题,我们重写了转换函数,以稍微不同的方式组织数据。当我们按区间收集数据时,我们还根据系列名称对数据进行排序。这避免了我们更改数据处理方式的问题。
解决了这个问题后,我们发现又出现了另一个问题。图表现在看起来有点模糊,就像我们在Giraffe中把一个系列的结束时间作为下一个系列的开始时间时一样。仔细检查后,我们发现正在使用的数据集为同一系列分配了多个值到每个时间戳,因此图表使用这些不同值集重新绘制。为了解决这个问题,我们在转换函数中添加了对同一系列重复时间戳的检查。如果发生这种情况,我们将停止收集数据,并只渲染到那时收集到的数据。
为了确保我们理解更改后传入的数据将是什么样子,我们向Mark询问了他想要使用马赛克图渲染的数据集。我们成功使用该数据创建了一个马赛克图,并安排与Mark会面以展示给他。不幸的是,图表没有包括他期望的一些功能,比如能够向工具提示或y轴添加更多数据。会议后,我们探索了添加这些功能的可能性,但再次与Mark和我们的导师咨询后,我们得出结论,我们没有足够的时间在截止日期前添加它们。
总结
一旦我们在UI中有了可以工作的图表,我们就开始设置马赛克图的后端。我们两个之前都没有接触过后端代码,所以我们与导师配对以更好地理解后端基础设施。我们定义了一个图表应该如何保存的模式,并遵循其他图表的例子来设置保存和导出马赛克图的其余代码。虽然我们没有在UI中包括Mark请求的更改,但我们更新了后端,以便将来能够进行这些更改。
我们随后进行了一些临时的清理工作。我们对前端代码进行了重构,包括马赛克转换函数,以使用户体验更加直观,并禁用了任何运行不正常的功能。在一切达到我们的预期之后,我们将马赛克图置于功能开关之后,以便在不影响当前用户体验的情况下进行适当测试。由于我们在实习的最后一周完成了这个功能,所以它不会在我们实习结束前发布,但我们希望看到马赛克图类型在下一个InfluxDB更新中发布!
下一步
由于时间限制,有些功能我们没有机会添加。例如,正如我们之前简短提到的,Mark对同时查看更多数据维度的能力感兴趣,所以我们很想添加选择多个字段用于y轴的能力,并为每个系列创建分组,同时添加更多字段到提示框中,并探索在每段时间的颜色中添加更多维度的方法。
我们还希望将更多功能添加到马赛克定制选项中,例如,允许用户添加轴的前缀和后缀。我们还希望允许用户选择提示框是否仅显示悬停的间隔信息,还是显示当前时间戳的所有间隔信息。
此外,每个系列的最后时间目前默认设置为整个数据集的最后时间戳。我们希望探讨将每个系列的最后时间设置为每个系列收集的最后时间戳的可能性,以确定哪种行为更受欢迎。
结论
与所有经历一样,如果我们有机会重做这个项目,我们会有一些不同的做法。最重要的是,我们会尝试更频繁地与Mark会面。如果我们这样做,我们相信我们能够更早地确保我们了解应该如何渲染数据以及图应包含哪些功能。
尽管我们希望缩短达到这个阶段所需的时间,但每次遇到障碍,我们都能学到宝贵的经验。一方面,我们没有包括所有希望的功能,这给了我们学习如何做出必要的妥协以按时发布软件的机会。
我们遇到的另一个问题是缺乏与复杂代码库合作的经验。在Giraffe项目中,代码仅针对数值,我们必须认识到这种隐含的期望,并努力创建一个可以渲染字符串的图表。这种经验帮助我们改进了识别复杂代码库中潜在假设的方法。
在我们整个项目中所面临的所有问题中,一个问题反复出现:我们如何量化我们所做的工作?有时候我们会花几个小时仅仅试图理解现有代码的工作方式,而不添加我们自己的代码。这让我们感觉没有取得任何进展。然而,我们逐渐意识到,我们花在构建心理模型上的所有时间实际上是解决整体问题的关键步骤。在整个实习期间,我们已经清楚地认识到,阅读代码比编写代码更困难。因此,理解他人代码的时间与编写自己代码的时间一样有价值。
如果没有在这里遇到的InfluxData每个人的支持,我们这个夏天就不会学到如此宝贵的经验。因此,我们想感谢所有帮助使这个项目和实习成为可能的人。我们特别想感谢我们的导师Bucky和Deniz,以及剩下的监控团队。我们很高兴有机会认识并和他们一起工作;我们非常感激他们所有的建议和指导。没有他们,我们可能就会失败。
您可以在Storybook中试用这个可视化,并可以在亲自尝试Giraffe。一如既往,如果您有任何问题,请在我们InfluxDB社区Slack和论坛网站上发帖。