Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I draw smoothed/rounded/curved line graphs? (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.

like image 403
ygoe Avatar asked Dec 08 '10 15:12

ygoe


3 Answers

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.

like image 128
GvS Avatar answered Sep 28 '22 21:09

GvS


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.

like image 21
Hans Passant Avatar answered Sep 28 '22 21:09

Hans Passant


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

like image 31
rcravens Avatar answered Sep 28 '22 19:09

rcravens