使用迭代值增量的趋势分析

时间:2010-03-18 21:22:07

标签: java algorithm ireport data-analysis

我们已将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 ) );
  }
}

您如何创建更流畅,更准确的趋势线表示?

2 个答案:

答案 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 DoublegetIterations()值不能超过给定大小。
  • 计算。接受答案的变体(少调用((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}}
相关问题