我该如何绘制平滑/圆角/曲线折线图?(C#)

7
我正在测量一些系统性能数据并将其存储在数据库中。从这些数据点中,我绘制了随时间变化的折线图。由于这些数据点本质上有点嘈杂,即每个点都偏离局部平均值至少一点。当直接从一个点到下一个点绘制折线图时,会产生不规则的图形。在大时间尺度上,如每像素> 10个数据点,此噪声被压缩为宽约20px的不规则线区域,而不是较小比例尺下的1px。
我已经阅读了关于线条平滑、反锯齿、简化等方面的内容。但我找到的所有东西似乎都与我的需求无关。
我不需要反锯齿,.NET在屏幕上绘制线条时已经完成了这项工作。
我不想要简化。我需要极端值保持可见,至少大多数情况下是这样。
我认为这是样条曲线的方向,但我找不到太多示例图像来评估所描述的是否就是我想要的。我确实在谷歌图书中找到了一本高度科学的书,里面充满了半页长的公式,但我现在不想读完它......
举个例子,只需看一下Linux / Gnome的系统监视器应用程序。它使用平滑线绘制最近的CPU / memory / network使用情况。这可能有点过于简化,但我会尝试一下,看看是否可以对其进行调整。
我更喜欢C#代码,但其他语言的算法或代码也可以,只要我可以将其移植到C#中而不需要外部引用即可。

你知道Windows(和.Net)有内置的性能计数器系统吗?我只是想确认你没有重复造轮子(并不是说它们必定适用于你的情况)。 - Klaus Byskov Pedersen
我的数据是在Linux服务器上收集的,包括许多不同的来源,我还有其他的代码。该数据将用于桌面(交互式)或Web应用程序中进行可视化,或者可能在Mono运行时下的电子邮件报告中使用。开发是在Windows上通过Visual Studio完成的。 - ygoe
5个回答

6
你可以进行一些数据平滑处理。不使用真实数据,而是应用简单的平滑算法,保留像Savitzky-Golay滤波器一样的峰值。 你可以在这里获取系数
最简单的方法是:
从我链接到的网站上获取顶部系数:
// For np = 5 = 5 data points
var h = 35.0;
var coeff = new float[] { 17, 12, -3 }; // coefficients from the site
var easyCoeff = new float[] {-3, 12, 17, 12, -3}; // Its symmetrical
var center = 2; // = the center of the easyCoeff array

// 现在,对于数据中的每个点,您都会计算一个平滑点:

smoothed[x] = 
   ((data[x - 2] * easyCoeff[center - 2]) +
    (data[x - 1] * easyCoeff[center - 1]) +
    (data[x - 0] * easyCoeff[center - 0]) +
    (data[x + 1] * easyCoeff[center + 1]) +
    (data[x + 2] * easyCoeff[center + 2])) / h;

使用5个点时,前2个和后2个点无法平滑。

如果您希望数据更加“平滑”,可以尝试使用具有更大数据点的系数进行实验。

现在,您可以通过“平滑”数据绘制一条线。您的np =点数越大,您的数据越平滑。但是,当仅简单地对一些点进行平均时,您也会失去峰值精度,但不会丢失太多。


1
我现在已经实现了这个变体。首先,我将原始源数据点平均到每个像素的约3个值。这样,在呈现从秒到分钟分辨率的数月数据时,可以产生更均匀的数据密度。然后,我通过在另一个网站上找到的最大系数列表运行这些值。它确实在图表中产生了更平滑的线条。但是非常小和极端的峰值会在图表中产生有趣的效果。它在极端处曲线周围的另一个方向上剧烈振荡。我认为这是来自负系数的。 - ygoe

2

在图形代码中无法解决此问题。如果您的数据存在噪点,则无论您使用何种线平滑算法,图形都会出现噪点。您需要先对数据进行过滤。创建第二个数据集,其中包含从原始数据插值得到的点。最小二乘拟合是一种常见技术。平均值很容易实现,但往往会隐藏极端值。


1

我认为你正在寻找一种提供“样条”的例程。这里有一个描述样条的链接:

http://en.wikipedia.org/wiki/Spline_(mathematics)

如果是这样,我没有任何关于样条库的建议,但是初步的谷歌搜索结果很多。

很抱歉没有代码,但是希望了解术语能够帮助您进行搜索。

Bob


0

网络流量的图表通常使用加权平均值。您可以每秒采样一次,将其存储在长度为10的循环列表中,并在每个采样点上绘制样本的平均值。

如果10个不够,您可以存储更多。您也不需要从头开始重新计算平均值:

new_average = (old_average*10 - replaced_sample + new_sample)/10

如果您不想存储全部10个,可以使用以下近似值:

new_average = old_average*9/10 + new_sample/10

许多路由器使用此功能以节省存储空间。这会呈指数级地向当前流量速率递增。

如果您要实现此功能,请执行以下操作:

new_average = old_average*min(9,number_of_samples)/10 + new_sample/10
number_of_samples++

为了避免初始上升,您还应该调整9/10、1/10的比例,以实际反映每个样本的时间段,因为您的计时器不会精确地每秒触发一次。


0

在显示数据之前,使用MIN/MAX/AVG来减少数据点的数量。这样看起来更美观,速度也更快。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接