65.9K
CodeProject 正在变化。 阅读更多。
Home

数据可视化和贝塞尔曲线

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.30/5 (7投票s)

2018年8月7日

CPOL

8分钟阅读

viewsIcon

21511

downloadIcon

819

使用纵向贝塞尔曲线进行数据建模和可视化。

引言

我喜欢处理纵向数据。这种数据是动态的;描绘的是变化,而不仅仅是静态的横截面差异。但总会存在边界、噪声、插值、连续性和可视化等方面的问题。这些似乎正是贝塞尔曲线擅长的地方。贝塞尔曲线的轨迹保持在边界内,位于排序时间序列的数据点之间。它们也通过第一个和最后一个数据点。在这些端点之间,曲线是连续的,并且可以微分以及在任何中间点进行求值。因此,贝塞尔曲线可以平滑大量数据点的波动,并提高未展开模式的可视性。

背景

贝塞尔曲线被广泛用于各种目的。我对感兴趣的读者推荐维基百科页面(Wikipedia page),Mike Kamermans(GitHub上的Pomax)撰写的关于该主题的电子书“bible/primer”,以及Paul Bourke提供的快速代码解决方案,我已将其改编为C#并发现其非常有用。

数据特征

这个数据可视化系列的第一篇文章是关于用贝塞尔曲线对数据进行建模。我们将研究曲线拟合、时域点求值和绘图以及微分。需要强调的是,这里我们讨论的是从起始点(沿着时间或X轴)移动到结束点,没有循环、尖点或回溯的纵向数据。例如,我们将使用学生从6年级到12年级随时间变化的学术表现。一旦我们为数据集中的所有学生案例都得到了一组贝塞尔曲线轨迹,在未来的文章中,我们将着手构建机器学习分类模型,并利用这些模型来识别各种模式,例如能够识别表现良好或可能处于风险中的学生的轨迹。

我们将使用一小部分(N=50)模拟的学校评分周期平均绩点(MPgpa)学生表现历史数据,这些数据涵盖核心课程(数学、科学、语言艺术和社会研究内容区域的平均值),从进入初中的起始时间点到高中毕业,或因转学或辍学而结束的时间点。MPgpa的范围限定在0.004.00之间。这是一个模拟样本,来源于一个大型多州、多学校 district 的蒙特卡洛版本,经过彻底去标识化的研究数据库。这些数据代表了各种学校评分周期策略(每年4或6个评分周期,季度、学期、三学期制等)。此数据注入了相当多的随机高斯噪声,使其有些不真实(幸运的是,真实的学校表现数据通常不会有如此大的波动),但也说明了贝塞尔曲线的数据平滑能力。

Using the Code

在这个使用Visual Studio 2017和.NET 4.7编写的C#项目BezierCurveDemo中,我们首先在DataPoint类中定义了一个DataPoint元组(time, MPgpa)类型。学生历史被定义为List<DataPoint>类型,在本例中是硬编码的,首先是strings,然后在一个DataList类中转换为List<List<DataPoint> StudentHistories以便快速访问。使用字典也可以达到同样的效果。另一个有用的对象是List<double> Xframe,它在这里对我们来说包含从6.0到13.0(包括)之间141个等距的时间点,例如,从6年级入学开始到高中毕业的最终点。这将成为可视化的X轴时间轴。

下载项目,在Visual Studio中打开解决方案文件,然后单击“启动”来构建和运行应用程序。该解决方案需要3个包(MSTest.TestAdapter.1.2.1MSTest.TestFramework.1.2.1System.ValueTuple.4.3.1),这些包应该会自动下载和恢复。

一个WinForm窗口将打开,包含两个列表框、一个图表和几个按钮。左上角的listbox以表格格式显示student的实际表现历史。左下角的listbox显示一系列在时间点间隔处的贝塞尔曲线值,以及在该间隔处的曲线的1阶和2阶导数。右侧的图表显示了该student在初中和高中课程时间轴上的实际轨迹和光滑的贝塞尔曲线轨迹。窗体上的其他控件允许在数据集中向前或向后移动、保存窗体图像以及关闭应用程序。

关注点

该应用程序提供了一个“演示”菜单,包含两个选项:一个选项绘制实际历史数据和该历史的贝塞尔曲线模型,用户可以逐个案例浏览。另一个选项说明了一个“意大利面条图”可视化,其中包含所有案例的贝塞尔曲线模型,并通过一个简单的线性分类器方法进行“分类”。

演示代码的几个特性值得注意。首先,为了更通用的可用性,DataPoint元组是(X, Y)而不是(time, MPgpa),涉及这类元组列表的方法也是如此。其次,在Bezier.cs模块中,有两个类可以用于相似的目的。其中,BezierFit类由独立的有用static方法组成,便于快速处理大型List<List<DataPoint>>集合。

第二个类Bezier定义了一个具有属性和方法的贝塞尔曲线对象。演示主要使用这个类。这两个类都依赖于一个外部的Xframe数组,其中包含等距的X轴或时间值。这对于可视化和聚合很有用。有一个Xframe(start, end, NofPoints)方法,可以返回一个双精度数的列表作为水平框架,以及各种方法来返回类似的Y轴贝塞尔曲线值的列表。还有一个方便的XPointIndex(X, Xframe)方法,它返回Xframe数组中最接近给定X轴值的索引。

使用纵向贝塞尔曲线时,有一个严重的问题需要考虑。一条曲线(无循环、尖点或回溯)由一组有界的点定义,并通过已知、给定的起点和终点。中间的曲线值是平滑的轨迹估计。演示中使用的函数:XYBezierOrig(Mu),其中Mu是距离起点沿曲线的相对距离(从0.01.0);速度非常快。然而,贝塞尔曲线通常……嗯……“弯曲”,所以路径距离比沿X轴或时间轴的线性距离要长。根据上面引用的贝塞尔曲线摘要,没有正式的解决方案可以将Mu值映射到X值。相反,需要数值方法。

演示包含一个MuForX方法,它采用二分逼近法,保证收敛……但执行速度有些慢。演示方法XYBezier(X)首先调用MuForX(X)方法来获得Mu的估计值。然后调用XYBezierOrig(Mu)来获得曲线在X轴上的点X的值。这对演示来说没问题,但它可能会减慢聚合100或1000条贝塞尔曲线的分析速度。在本系列未来的部分,当我们使用训练好的机器学习模型来分类贝塞尔曲线(并聚合结果)时,使用并行并发方法可以大大加快分析速度。

这个演示包含一个自定义函数,用于使用有限差分方法计算贝塞尔曲线沿点的1阶和2阶导数。它还包含许多针对各种贝塞尔方法的“单元测试”。

结论

我希望从这个演示中得出的主要结论是,贝塞尔曲线是收集于不同时间或X轴点的噪声纵向数据的非常有用的模型。这些曲线允许在框架内的特定兴趣点进行合理的横截面和/或队列比较,例如市场周期或评分期,或其他点,在这些点上,否则大量数据可能包含缺失值。

第二个推论是,如果模型是好的,那么关于这些模型的推断也可能是好的。这个演示说明了学生通过各自公立学校课程的许多不同表现轨迹。可以合理地询问这些是否是相对成功的指标,在终点或在“当前”或中间兴趣点,例如从8年级初中到9年级和高中的过渡点9.0。

也许会认为,通过查看实际数据或曲线数据就可以做出关于特定学生是否“处于风险”(并可能受益于额外的支持服务)的决定。然而,假设您需要实时对100或1000名学生做出类似的决定,因为每个学生都在其课程中进步。学校辅导员每天都在做这类事情,但大多没有此类数据的可用性或使用。一个能够以各种方式对表现轨迹进行分类的机器学习模型无疑将成为数据驱动决策的有益工具。这或许是本系列未来文章的主题。

除此之外,本项目还提供了一系列有用的技术和方法来处理贝塞尔曲线,这些技术和方法可以改编到其他类似的项目中。

历史

  • 2018年8月6日:版本 1.0
  • 2018年8月7日:版本 1.1,添加了图像和代码链接
  • 2018年8月9日:版本 1.2,代码小幅改进
© . All rights reserved.