How can I draw smoothed/rounded/curved line graphs? (C#)

情到浓时终转凉″ 提交于 2019-12-09 17:14:34

问题


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.


回答1:


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.




回答2:


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.




回答3:


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




回答4:


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




回答5:


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.



来源:https://stackoverflow.com/questions/4388911/how-can-i-draw-smoothed-rounded-curved-line-graphs-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!