我基本上有一个像这样的值数组:
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;
}
答案 0 :(得分:29)
要计算exponential moving average,您需要保持一些状态,并且需要一个调整参数。这需要一个小类(假设您使用的是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 :(得分:4)
我很难理解你的问题,但无论如何我都会尽力回答。
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财经部,选择股票(尝试特斯拉汽车;非常不稳定(TSLA))并点击图表底部的“技术”。选择具有给定时段的“移动平均线”和“指数移动平均线”以比较它们的差异。
指数移动平均线只是对此的另一种阐述,但是对“旧”数据的权重小于“新”数据;这是一种将平滑“偏向”背面的方法。请阅读维基百科条目。
所以,这更像是一个评论而不是答案,但是小评论框只是微不足道。祝你好运。
答案 2 :(得分:0)
看看this。 如果您的噪音平均为零,请考虑使用Kalman filter。
答案 3 :(得分:0)
以滚动的方式......我也使用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;
}
答案 4 :(得分:0)
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;
}
}
答案 5 :(得分:-4)
如果你在数学方面遇到麻烦,你可以使用简单的移动平均线而不是指数。所以你得到的输出将是最后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个术语。此外,有更有效的方法来计算这个移动平均线(总和=总和 - 最旧+最新),但这是为了获得正在发生的事情的概念。