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

发布于 2024-10-06 08:06:24 字数 573 浏览 9 评论 0原文

我正在测量一些系统性能数据并将其存储在数据库中。根据这些数据点,我随时间绘制折线图。从本质上讲,这些数据点有点嘈杂,即。每个点都至少偏离局部平均值一点点。当从一个点直接绘制到下一个点的折线图时,会产生锯齿状的图形。在大的时间尺度上,如>每像素 10 个数据点,此噪声被压缩为宽锯齿线区域,即 20 像素高,而不是较小比例中的 1 像素。

我读过有关线条平滑、抗锯齿、简化以及所有这些内容的内容。但我发现的一切似乎都与其他事情有关。

我不需要抗锯齿,.NET 在屏幕上绘制线条时已经为我做到了这一点。

我不想要简化。我需要让极端值保持可见,至少是其中的大多数。

我认为它朝着样条曲线的方向发展,但我找不到太多示例图像来评估所描述的东西是否是我想要的。不过,我确实在 Google Books 上找到了一本高度科学的书,里面充满了半页长的公式,我现在不想读它……

举个例子,看看 Linux/Gnome 的系统监视器应用程序。我用平滑线绘制了最近的 CPU/内存/网络使用情况。这可能有点过于简单化,但我会尝试一下,看看是否可以调整它。

我更喜欢 C# 代码,但其他语言的算法或代码也可以,只要我可以将其移植到 C# 而无需外部引用。

I'm measuring some system performance data to store it in a database. From those data points I'm drawing line graphs over time. In their nature, those data points are a bit noisy, ie. every single point deviates at least a bit from the local mean value. When drawing the line graph straight from one point to the next, it produces jagged graphs. At a large time scale like > 10 data points per pixel, this noise is compressed into a wide jagged line area that is, say, 20px high instead of 1px as in smaller scales.

I've read about line smoothing, anti-aliasing, simplifying and all these things. But everything I've found seems to be about something else.

I don't need anti-aliasing, .NET already does that for me when drawing the line on the screen.

I don't want simplification. I need the extreme values to remain visible, at least most of them.

I think it goes in the direction of spline curves but I couldn't find much example images to evaluate whether the described thing is what I want. I did find a highly scientific book at Google Books though, full of half-page long formulas, which I wasn't like reading through now...

To give you an example, just look at Linux/Gnome's system monitor application. I draws the recent CPU/memory/network usage with a smoothed line. This may be a bit oversimplified, but I'd give it a try and see if I can tweak it.

I'd prefer C# code but algorithms or code in other languages is fine, too, as long as I can port it to C# without external references.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

雄赳赳气昂昂 2024-10-13 08:06:24

您可以进行一些数据平滑。不使用真实数据,而是应用简单的平滑算法来保留峰值,如 Savitzky-Golayfilter。

您可以在此处获取系数

最简单的方法是:

从我链接到的网站中获取最高系数:

// 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 = 点数越大,数据就越平滑。但是,您也会失去峰值准确度,但当简单地将一些点平均在一起时,损失的程度不会那么严重。

You can do some data-smoothing. Instead of using the real data, apply a simple smoothing algorithm that keeps the peaks like a Savitzky-Golayfilter.

You can get the coefficients here.

The easiest to do is:

Take the top coefficients from the website I linked to:

// 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

// now for every point from your data you calculate a smoothed point:

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;

The first 2 and last 2 points you cannoth smooth when using 5 points.

If you want your data to be more "smoothed" you can experiment with coefficents with larger data points.

Now you can draw a line through your "smoothed" data. The larger your np = number of points, the smoother your data. But you also loose peak accuracy, but not as much when simply averaging some points together.

樱娆 2024-10-13 08:06:24

您无法在图形代码中修复此问题。如果您的数据有噪声,那么无论您使用哪种线平滑算法,图表也会有噪声。您需要先过滤数据。使用从原始数据插值的点创建第二个数据集。最小二乘拟合是一种常用技术。平均法实施起来很简单,但往往会隐藏极端值。

You cannot fix this in the graphics code. If your data is noisy then the graph is going to be noisy as well, no matter what kind of line smoothing algorithm you use. You'll need to filter the data first. Create a second data set with points that are interpolated from the original data. A Least Squares fit is a common technique. Averaging is simple to implement but tends to hide extremes.

佼人 2024-10-13 08:06:24

我认为您正在寻找的是提供“样条线”的例程。这是描述样条线的链接:

http://en.wikipedia.org/wiki/Spline_(数学)

如果是这种情况,我对样条库没有任何建议,但最初的谷歌搜索发现了很多。

抱歉没有代码,但希望了解术语会对您的搜索有所帮助。

鲍勃

I think what you are looking for is a routine to provide 'splines'. Here is a link describing splines:

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

If that is the case I don't have any recommendations for a spline library, but an initial google search turned up a bunch.

Sorry for no code, but hopefully knowing the terminology will aid you in your search.

Bob

隐诗 2024-10-13 08:06:24

在显示数据点之前使用 MIN/MAX/AVG 减少数据点的数量。它看起来会更好而且速度会更快

Reduce the number of data points, using MIN/MAX/AVG before you display them. It'll look nicer and it'll be faster

东风软 2024-10-13 08:06:24

网络流量图通常使用加权平均值。您可以每秒采样一次到长度为 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 比率以实际反映每个样本的时间周期,因为您的计时器不会每秒准确触发一次。

Graphs of network traffic often use a weighted average. You can sample once per second into a circular list of length 10 and for the graph, at each sample, graph the average of the samples.

If 10 isn't enough you can store many more. You don't need to recalculate the average from scratch, either:

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

If you don't want to store all 10, however, you can approximate with this:

new_average = old_average*9/10 + new_sample/10

Lots of routers use this to save on storage. This ramps toward the current traffic rate exponentially.

If you do implement this, do something like this:

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

to avoid the initial ramp-up. You should also adjust the 9/10, 1/10 ratio to actually reflect the time preiod of each sample because your timer won't fire exactly once per second.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文