Упростить огромное условное утверждение - PullRequest
0 голосов
/ 01 февраля 2019

Аннотация

В жизни бывают случаи, когда ваши сигналы шумят, и вы хотите перейти от этого

enter image description here

к этому

enter image description here

Существует множество существующих подходов, позволяющих сгладить данные: локальные линейные и полиномиальные регрессии, различные виды скользящих средних:

https://en.wikipedia.org/wiki/Kernel_smoother

https://homepages.inf.ed.ac.uk/rbf/HIPR2/gsmooth.htm

https://www.stat.wisc.edu/~mchung/softwares/hk/hk.html

https://matthew -brett.github.io / training / smoothing_intro.html

Как эффективно вычислить матрицу гауссова ядра в numpy?

Как сгладить блоки трехмерного воксельного мира?

Однако я обнаружил, что для моего случая лучше всего подходит сглаживание ядра Гаусса.На настольном компьютере мне это показалось очаровательным, но при переключении на мобильное устройство алгоритм остановил работу устройства из-за высоких вычислительных требований.

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

Это также привело к тому, что размер ядра Гаусса должен был составлять десятки тысяч компонентовкоторый напрямую повлиял на сложность алгоритма для моего наивного подхода, он требует, чтобы десятки тысяч элементов в фильтре были умножены и суммированы с десятками тысяч компонентов из сигнала, чтобы получить только один компонент сглаженных данных.

Существует эффективный способ сглаживания ядра Гаусса, который требует использования преобразования Фурье, которое очень сложно реализовать эффективно.

Поэтому вместо того, чтобы идти по этому пути, я подумал, что вы могли бы действоватьДелайте сглаживание намного эффективнее, не вдаваясь в сложности преобразований Фурье.

По сути, любой сигнал - это просто набор пар [time, value], упорядоченных по времени.При интерполяции с использованием линейной интерполяции она всегда может быть представлена ​​с помощью Полигональная цепочка , и вы фактически можете сглаживать один компонент Полигональная цепочка (то есть Сегмент линии ) аналитически.Я не буду углубляться в то, как вы на самом деле вычисляете сверточный интеграл аналитически определенного ядра по аналитически определенному отрезку.Здесь просто стоит сказать, что результатом является огромная кусочная функция:

enter image description here

, которая дает ряд if операторов:

public static Double Solution(Double x, Double w, Double x1, Double y1, Double x2, Double y2)
{
    Double result;

    if (x + w < x2 && w + x1 <= x)
    {
        result = (x * y1 - x2 * y1 - x * y2 + x1 * y2) / (x1 - x2);
    }
    else if (x1 < x + w && x < x1 && x + w < x2)
    {
        result = Math.Pow(w + x - x1, 2) * ((w + x + 2 * x1 - (3 * x2)) * y1 - (w + x - x1) * y2) * Math.Pow(w, -2) / (x1 - x2) / 6;
    }
    else if (x == x1 && x + w < x2)
    {
        result = y1 / 2 + w * (y1 - y2) / (x1 - x2) / 6;
    }
    else if (x + w < x2 && x1 < x && x < w + x1)
    {
        result = Math.Pow(w, -2) / (x1 - x2) * (Math.Pow(w, 3) * (y1 - y2) + 3 * w * w * (-x2 * y1 + x * (y1 - y2) + x1 * y2) - Math.Pow(x - x1, 2) * ((x + 2 * x1 - (3 * x2)) * y1 + (-x + x1) * y2) + 3 * w * (x - x1) * ((x + x1 - (2 * x2)) * y1 + (-x + x1) * y2)) / 6;
    }
    else if (x2 <= x + w && x < x1)
    {
        result = (x1 - x2) * (2 * y1 * x1 + x2 * y1 + x1 * y2 + 2 * x2 * y2 - 3 * w * (y1 + y2) - 3 * x * (y1 + y2)) * Math.Pow(w, -2) / 6;
    }
    else if (x2 <= x && w + x1 <= x && x < w + x2)
    {
        result = -Math.Pow(w - x + x2, 2) * ((w - x + x2) * y1 + (-w + x - 3 * x1 + (2 * x2)) * y2) * Math.Pow(w, -2) / (x1 - x2) / 6;
    }
    else if (x2 <= x && x < w + x1)
    {
        result = -(x1 - x2) * (2 * y1 * x1 + x2 * y1 + x1 * y2 + 2 * x2 * y2 + 3 * w * (y1 + y2) - 3 * x * (y1 + y2)) * Math.Pow(w, -2) / 6;
    }
    else if (x == x1 && (x + w == x2 && (x2 <= x && x + w < x1 && w + x2 <= x || x < w + x1) || x < w + x1 && x2 <= x + w && x < x2))
    {
        result = (-x1 + x2) * (3 * w * (y1 + y2) + (x1 - x2) * (y1 + 2 * y2)) * Math.Pow(w, -2) / 6;
    }
    else if (x < x2 && x2 <= x + w && w + x1 <= x)
    {
        result = Math.Pow(w, -2) / (x1 - x2) * (Math.Pow(w, 3) * (-y1 + y2) + 3 * w * w * (-x2 * y1 + x * (y1 - y2) + x1 * y2) + Math.Pow(x - x2, 2) * ((-x + x2) * y1 + (x - 3 * x1 + (2 * x2)) * y2) + 3 * w * (x - x2) * (-2 * x1 * y2 + x * (-y1 + y2) + x2 * (y1 + y2))) / 6;
    }
    else if (x < x2 && x2 <= x + w && x1 < x && x < w + x1)
    {
        result = Math.Pow(w, -2) / (x1 - x2) * (-2 * Math.Pow(x1, 3) * y1 + 3 * x1 * x1 * x2 * y1 + Math.Pow(x2, 3) * y1 - Math.Pow(x1, 3) * y2 - 3 * x1 * (x2 * x2) * y2 + 2 * Math.Pow(x2, 3) * y2 + 2 * Math.Pow(x, 3) * (-y1 + y2) - 3 * w * Math.Pow(x1 - x2, 2) * (y1 + y2) + 6 * x * x * (x2 * y1 - x1 * y2) + 3 * x * (2 * x1 * x2 * (-y1 + y2) + x1 * x1 * (y1 + y2) - (x2 * x2) * (y1 + y2))) / 6;
    }
    else
    {
        result = 0.0e0;
    }

    return result;
}

А вот как это выглядит визуально: enter image description here

Вы также можете объединить несколько линейных сегментов с помощью суммы функций сглаживания для этих сегментов

enter image description here

Таким образом, уменьшая сложность вычислений до количества имеющихся у вас точек:

Проблема

Если приведенное выше утверждение выглядит неэффективным и существуетпроблемы численной устойчивости с ним.Когда отрезки, определяемые x1,y1,x2,y2, получают большие числа, результат взрывается.Я пытаюсь справиться с этими проблемами, но это сложнее, чем я ожидал.Даже тщательное расширение уравнений вносит некоторые странные ошибки в вычисления.Поэтому я подумал, что было бы неплохо проконсультироваться со StackOverflow, чтобы, вероятно, получить больше идей о том, как оптимизировать это утверждение и сделать его более устойчивым в численном выражении.

Заранее спасибо,

...