Реализация экспоненциального скользящего среднего в Java - PullRequest
10 голосов
/ 09 февраля 2012

У меня по существу есть массив значений, подобных этому:

0.25, 0.24, 0.27, 0.26, 0.29, 0.34, 0.32, 0.36, 0.32, 0.28, 0.25, 0.24, 0.25

Приведенный выше массив упрощен, я собираю 1 значение в миллисекунду в своем реальном коде, и мне нужно обработать вывод алгоритма, который я написал, чтобы найти ближайший пик до определенного момента времени. Моя логика не работает, потому что в моем примере выше, 0.36 - это реальный пик, но мой алгоритм будет смотреть назад и видеть самое последнее число 0.25 как пик, так как перед ним наблюдается уменьшение до 0.24.

Цель состоит в том, чтобы взять эти значения и применить к ним алгоритм, который немного "сгладит" их, чтобы у меня было больше линейных значений. (т.е. я хотел бы, чтобы мои результаты были пышными, а не неровными)

Мне сказали применить экспоненциальный фильтр скользящих средних к моим значениям. Как я могу это сделать? Мне очень трудно читать математические уравнения, я гораздо лучше разбираюсь в коде.

Как мне обработать значения в моем массиве, применяя экспоненциальное вычисление скользящего среднего, чтобы выровнять их?

float[] mydata = ...
mySmoothedData = exponentialMovingAverage(mydata, 0.5);

float[] exponentialMovingAverage(float[] input, float alpha) {
    // what do I do here?
    return result;
}

Ответы [ 6 ]

29 голосов
/ 09 февраля 2012

Чтобы вычислить экспоненциальное скользящее среднее , вам нужно сохранить некоторое состояние и вам нужен параметр настройки.Это требует небольшого класса (при условии, что вы используете Java 5 или новее):

class ExponentialMovingAverage {
    private double alpha;
    private Double oldValue;
    public ExponentialMovingAverage(double alpha) {
        this.alpha = alpha;
    }

    public double average(double value) {
        if (oldValue == null) {
            oldValue = value;
            return value;
        }
        double newValue = oldValue + alpha * (value - oldValue);
        oldValue = newValue;
        return newValue;
    }
}

Создайте экземпляр с нужным параметром decay (может потребоваться настройка; должно быть между 0 и 1), а затем используйте average(…) для фильтрации.


При чтении страницы с некоторым математическим повторением все, что вам действительно нужно знать при преобразовании ее в код, - это то, что математики любят записывать индексы в массивы и последовательности с индексами.(У них также есть несколько других обозначений, которые не помогают.) Однако EMA довольно прост, так как вам нужно запомнить только одно старое значение;сложные сложные состояния не требуются.

4 голосов
/ 09 февраля 2012

Мне трудно понять ваши вопросы, но я все равно постараюсь ответить.

1) Если ваш алгоритм нашел 0.25 вместо 0.36, то это неправильно.Это неправильно, потому что предполагает монотонное увеличение или уменьшение (то есть «всегда идет вверх» или «всегда идет вниз»).Если вы не усредняете ВСЕ свои данные, ваши точки данных - как вы их представляете - являются нелинейными.Если вы действительно хотите найти максимальное значение между двумя точками во времени, то разделите ваш массив от t_min до t_max и найдите максимум этого подмассива.

2) Теперь понятие «перемещение»«Средние» очень просто: представьте, что у меня есть следующий список: [1.4, 1.5, 1.4, 1.5, 1.5].Я могу "сгладить", взяв среднее из двух чисел: [1,45, 1,45, 1,45, 1,5].Обратите внимание, что первое число является средним из 1,5 и 1,4 (второе и первое числа);второй (новый список) - среднее значение 1,4 и 1,5 (третий и второй старый список);третий (новый список) в среднем 1,5 и 1,4 (четвертый и третий) и так далее.Я мог бы сделать это «период три» или «четыре», или «n».Обратите внимание, что данные намного более гладкие.Хороший способ «увидеть скользящие средние на работе» - перейти в Google Finance, выбрать акцию (попробуйте Tesla Motors; довольно волатильно (TSLA)) и нажать «технические данные» в нижней части графика.Выберите «Скользящее среднее» с заданным периодом и «Экспоненциальное скользящее среднее», чтобы сравнить их различия.

Экспоненциальная скользящая средняя - это еще одно уточнение этого, но вес «старых» данных меньше, чем «новых»данные;это способ «смещения» сглаживания в сторону спины.Пожалуйста, прочтите статью в Википедии.

Итак, это скорее комментарий, чем ответ, но маленькое поле для комментариев было просто крошечным.Удачи.

0 голосов
/ 03 января 2019
public class MovingAvarage {

public static void main(String[] args) {
    double[] array = {1.2, 3.4, 4.5, 4.5, 4.5};

    double St = 0D;
    for(int i=0; i<array.length; i++) {
        St = movingAvarage(St, array[i]);
    }
    System.out.println(St);

}

private static double movingAvarage(double St, double Yt) {
    double alpha = 0.01, oneMinusAlpha = 0.99;
    if(St <= 0D) {
        St = Yt;
    } else {
        St = alpha*Yt + oneMinusAlpha*St;
    }
    return St;
   }

}
0 голосов
/ 07 марта 2014

По мере поступления .... я также использую математическую библиотеку commons.apache

  public LinkedList EMA(int dperiods, double alpha)
                throws IOException {
            String line;
            int i = 0;
            DescriptiveStatistics stats = new SynchronizedDescriptiveStatistics();
            stats.setWindowSize(dperiods);
            File f = new File("");
            BufferedReader in = new BufferedReader(new FileReader(f));
            LinkedList<Double> ema1 = new LinkedList<Double>();
            // Compute some statistics
            while ((line = in.readLine()) != null) {
                double sum = 0;
                double den = 0;
                System.out.println("line: " + " " + line);
                stats.addValue(Double.parseDouble(line.trim()));
                i++;
                if (i > dperiods)
                    for (int j = 0; j < dperiods; j++) {
                        double var = Math.pow((1 - alpha), j);
                        den += var;
                        sum += stats.getElement(j) * var;
                        System.out.println("elements:"+stats.getElement(j));
                        System.out.println("sum:"+sum);
                    }
                else
                    for (int j = 0; j < i; j++) {
                        double var = Math.pow((1 - alpha), j);
                        den += var;
                        sum += stats.getElement(j) * var;
                    }
                ema1.add(sum / den);
                System.out.println("EMA: " + sum / den);
            }
            return ema1;
        }
0 голосов
/ 09 февраля 2012

Взгляните на это .Если ваш шум имеет нулевое среднее значение, рассмотрите также использование фильтра Калмана .

0 голосов
/ 09 февраля 2012

Если у вас возникли проблемы с математикой, вы можете использовать простую скользящую среднюю вместо экспоненциальной.Таким образом, на выходе вы получите последние x слагаемых, разделенные на x.Не проверенный псевдокод:

int data[] = getFilled();
int outdata[] = initializeme()
for (int y = 0; y < data.length; y++)
    int sum = 0;
    for (int x = y; x < y-5; x++)
        sum+=data[x];
    outdata[y] = sum / 5;

Обратите внимание, что вам нужно будет обрабатывать начальную и конечную части данных, поскольку очевидно, что вы не можете усреднить последние 5 терминов, когда находитесь на второй точке данных.Кроме того, существуют более эффективные способы вычисления этой скользящей средней (сумма = сумма - самая старая + самая новая), но это поможет понять, что происходит.

...