Как я могу рисовать сглаженные/округленные / изогнутые линейные графики? (С#)

Я измеряю некоторые данные о производительности системы, чтобы сохранить его в базе данных. Из этих точек данных я рисую линейные графики с течением времени. По своей природе эти точки данных немного шумные, т. е. каждая отдельная точка отклоняется хотя бы немного от локального среднего значения. При рисовании линейного графика прямо из одной точки в другую он создает зубчатые графики. В большом масштабе времени, например > 10 точек данных на пиксель, этот шум сжимается в широкую зубчатую область линии, которая, скажем, 20px высока 1px как в меньших масштабах.

Я читал о сглаживании линий, сглаживании, сглаживании, упрощении и всех этих вещах. Но все, что я нашел, похоже, связано с чем-то другим.

Мне не нужно сглаживание, .NET уже делает это для меня при рисовании линии на экране.

Я не хочу упрощения. Мне нужно, чтобы экстремальные значения оставались видимыми, по крайней мере, большинство из них.

Я думаю, что он идет в направлении сплайновых кривых, но я не мог найдите много примеров изображений, чтобы оценить, является ли описанная вещь тем, что я хочу. Однако я нашел в Google Books высоконаучную книгу, полную формул длиной в полстраницы, которые я не хотел читать сейчас...

чтобы дать вам пример, просто посмотрите на приложение системного монитора Linux / Gnome. Я рисую недавнее использование CPU/памяти / сети с сглаженной линией. Это может быть немного упрощено, но я бы попробовал и посмотрел, смогу ли я его настроить.

Я бы предпочел C# код но алгоритмы или код на других языках тоже хороши, если я могу перенести его на C# без внешних ссылок.

5 ответов


вы можете сделать некоторое сглаживание данных. Вместо использования реальных данных примените простой алгоритм сглаживания, который сохраняет пики, как Савицкий-Голайфильтр.

вы можете получить коэффициенты здесь.

проще всего сделать это:

Берем верхнюю коэффициентов с сайта я связан:

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

первые 2 и последние 2 пункта вы cannoth гладкий при использовании 5 очков.

Если вы хотите, чтобы ваши данные были более "сглаженными", вы можете экспериментировать с коэффициентами с большими точками данных.

теперь вы можете нарисовать линию через "сглаженных" данных. Чем больше np = количество точек,тем более плавные данные. Но вы также теряете пиковую точность, но не так много, когда просто усредняете некоторые точки вместе.


вы не можете исправить это в коде. Если ваши данные шумны, график также будет шумным, независимо от того, какой алгоритм сглаживания линий вы используете. Сначала нужно отфильтровать данные. Создайте второй набор данных с точками, интерполированными из исходных данных. Подгонка наименьших квадратов-обычная техника. Усреднение просто реализовать, но имеет тенденцию скрывать крайности.


Я думаю, что вы ищете рутину для обеспечения "сплайнов". Вот ссылка, описывающая сплайны:

http://en.wikipedia.org/wiki/Spline_ (математика)

Если это так, у меня нет никаких рекомендаций для библиотеки сплайнов, но первоначальный поиск google оказался кучей.

извините за отсутствие кода, но, надеюсь, знание терминологии поможет вам в вашем поиске.

Боб


уменьшите количество точек данных, используя MIN/MAX/AVG перед их отображением. Это будет выглядеть лучше, и это будет быстрее


графики сетевого трафика часто используют средневзвешенное значение. Вы можете выполнять выборку один раз в секунду в круговой список длиной 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 реально отражать срок каждого образца, потому что таймер не срабатывает ровно раз в секунду.