我们已将iReport配置为生成以下图表:
实际数据点为蓝色,趋势线为绿色。问题包括:
问题的根源在于增量类。递增器迭代地提供有数据点。似乎没有办法获取数据集。计算趋势线的代码如下所示:
import java.math.BigDecimal;
import net.sf.jasperreports.engine.fill.*;
/**
* Used by an iReport variable to increment its average.
*/
public class MovingAverageIncrementer
implements JRIncrementer {
private BigDecimal average;
private int incr = 0;
/**
* Instantiated by the MovingAverageIncrementerFactory class.
*/
public MovingAverageIncrementer() {
}
/**
* Returns the newly incremented value, which is calculated by averaging
* the previous value from the previous call to this method.
*
* @param jrFillVariable Unused.
* @param object New data point to average.
* @param abstractValueProvider Unused.
* @return The newly incremented value.
*/
public Object increment( JRFillVariable jrFillVariable, Object object,
AbstractValueProvider abstractValueProvider ) {
BigDecimal value = new BigDecimal( ( ( Number )object ).doubleValue() );
// Average every 10 data points
//
if( incr % 10 == 0 ) {
setAverage( ( value.add( getAverage() ).doubleValue() / 2.0 ) );
}
incr++;
return getAverage();
}
/**
* Changes the value that is the moving average.
* @param average The new moving average value.
*/
private void setAverage( BigDecimal average ) {
this.average = average;
}
/**
* Returns the current moving average average.
* @return Value used for plotting on a report.
*/
protected BigDecimal getAverage() {
if( this.average == null ) {
this.average = new BigDecimal( 0 );
}
return this.average;
}
/** Helper method. */
private void setAverage( double d ) {
setAverage( new BigDecimal( d ) );
}
}
您如何创建更流畅,更准确的趋势线表示?
答案 0 :(得分:4)
这取决于您正在测量的项目的行为。这是以可以建模的方式移动(或改变)的东西吗?
如果预计项目不会改变,那么您的趋势应该是整个样本集的基础平均值,而不仅仅是过去的两个测量值。你可以用贝叶斯定理得到这个。可以使用简单公式
逐步计算运行平均值Mtn1 =(Mtn * N + x)/(N + 1)
其中x是时间t + 1的测量值,Mtn1是时间t + 1的平均值,Mtn是时间t的平均值,N是时间t的测量值。
如果您测量的项目以某种基本方程可预测的方式波动,那么您可以使用Kalman filter根据之前(最近的)测量结果提供下一个点的最佳估计值。模拟预测行为的方程式。
作为起点,Bayesian estimators和卡尔曼过滤器上的维基百科条目将有所帮助。
答案 1 :(得分:1)
结果图片
结果仍然不完整,但它清楚地显示了比问题更好的趋势线。
<强>计算强>
缺少两个关键组件:
List
Double
个getIterations()
值不能超过给定大小。计算。接受答案的变体(少调用((value - previousAverage) / (getIterations() + 1)) + previousAverage
):
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import net.sf.jasperreports.engine.fill.AbstractValueProvider;
import net.sf.jasperreports.engine.fill.JRFillVariable;
import net.sf.jasperreports.engine.fill.JRIncrementer;
/**
* Used by an iReport variable to increment its average.
*/
public class RunningAverageIncrementer
implements JRIncrementer {
/** Default number of tallies. */
private static final int DEFAULT_TALLIES = 128;
/** Number of tallies within the sliding window. */
private static final int DEFAULT_SLIDING_WINDOW_SIZE = 30;
/** Stores a sliding window of values. */
private List<Double> values = new ArrayList<Double>( DEFAULT_TALLIES );
/**
* Instantiated by the RunningAverageIncrementerFactory class.
*/
public RunningAverageIncrementer() {
}
/**
* Calculates the average of previously known values.
* @return The average of the list of values returned by getValues().
*/
private double calculateAverage() {
double result = 0.0;
List<Double> values = getValues();
for( Double d: getValues() ) {
result += d.doubleValue();
}
return result / values.size();
}
/**
* Called each time a new value to be averaged is received.
* @param value The new value to include for the average.
*/
private void recordValue( Double value ) {
List<Double> values = getValues();
// Throw out old values that should no longer influence the trend.
//
if( values.size() > getSlidingWindowSize() ) {
values.remove( 0 );
}
this.values.add( value );
}
private List<Double> getValues() {
return values;
}
private int getIterations() {
return getValues().size();
}
/**
* Returns the newly incremented value, which is calculated by averaging
* the previous value from the previous call to this method.
*
* @param jrFillVariable Unused.
* @param tally New data point to average.
* @param abstractValueProvider Unused.
* @return The newly incremented value.
*/
public Object increment( JRFillVariable jrFillVariable, Object tally,
AbstractValueProvider abstractValueProvider ) {
double value = ((Number)tally).doubleValue();
recordValue( value );
double previousAverage = calculateAverage();
double newAverage =
((value - previousAverage) / (getIterations() + 1)) + previousAverage;
return new BigDecimal( newAverage );
}
protected int getSlidingWindowSize() {
return DEFAULT_SLIDING_WINDOW_SIZE;
}
}
源代码
{{1}}