Реализация экспоненциальной скользящей средней в Java

Я, по сути, есть массив значений, как это:

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 перед он.

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

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

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

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

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

5 ответов


вычислить экспоненциальная скользящая средняя, вам нужно сохранить некоторое состояние вокруг, и вам нужен параметр настройки. Это требует небольшого класса (предполагая, что вы используете 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;
    }
}

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


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


мне трудно понять ваши вопросы, но я постараюсь ответить в любом случае.

1) Если ваш алгоритм нашел 0.25 вместо 0.36, то это неправильно. Это неправильно, потому что оно предполагает монотонное увеличение или уменьшение (то есть "всегда идет вверх" или "всегда идет вниз"). Если вы не усредняете все свои данные, ваши точки данных-как вы их представляете-нелинейны. Если вы действительно хотите найти максимальное значение между двумя точками времени, то срежьте свой массив из t_min to 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 (четвертый и третье) и так далее. Я мог бы сделать это "период три", или" четыре", или"Н". Обратите внимание, что данные намного более гладкие. Хороший способ "увидеть скользящие средние на работе" - перейти в Google Finance, выбрать акцию (попробуйте Tesla Motors; pretty volatile (TSLA)) и нажать" technicals " в нижней части графика. Выберите " скользящая средняя "с заданным периодом и" экспоненциальная скользящая средняя", чтобы сравнить их различия.

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

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


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


в подвижной форме.... я также использую общин.Apache math library

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

Если у вас возникли проблемы с математикой, вы можете пойти с простой скользящей средней вместо экспоненциальной. Таким образом, выход, который вы получите, будет последним 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 терминов, когда вы находитесь на своей второй точке данных. Кроме того, существуют более эффективные способы вычисления этой скользящей средней (sum = sum-oldest + newest), но это чтобы понять, что происходит.