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

Java 使用迭代值增量的趋势分析,java,algorithm,ireport,data-analysis,Java,Algorithm,Ireport,Data Analysis,我们已将iReport配置为生成以下图表: 实际数据点为蓝色,趋势线为绿色。这些问题包括: 趋势线的数据点太多 趋势线不遵循贝塞尔曲线(样条曲线) 问题的根源在于incrementer类。递增器以迭代方式提供数据点。似乎没有办法获得这组数据。计算趋势线的代码如下所示: import java.math.BigDecimal; import net.sf.jasperreports.engine.fill.*; /** * Used by an iReport variable to i

我们已将iReport配置为生成以下图表:

实际数据点为蓝色,趋势线为绿色。这些问题包括:

  • 趋势线的数据点太多
  • 趋势线不遵循贝塞尔曲线(样条曲线)
问题的根源在于incrementer类。递增器以迭代方式提供数据点。似乎没有办法获得这组数据。计算趋势线的代码如下所示:

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

如何创建更平滑、更准确的趋势线表示?

这取决于所测量项目的行为。这是以可以建模的方式移动(或改变)的东西吗

如果项目预计不会改变,那么您的趋势应该是整个样本集的基本平均值,而不仅仅是过去两次测量。你可以用贝叶斯定理得到这个。运行平均值可以使用简单的公式递增计算

Mtn1=(Mtn*N+x)/(N+1)

其中x是时间t+1的测量值,Mtn1是时间t+1的平均值,Mtn是时间t的平均值,N是时间t的测量次数

如果您正在测量的项目以某种基本方程可以预测的方式波动,那么您可以使用a根据以前(最近)的测量结果和建模预测行为的方程提供下一点的最佳估计


作为一个起点,Wikipedia上的条目和Kalman Filters将很有帮助。

结果图像

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;
  }
}
结果仍然不完整,但它清楚地显示了比问题中更好的趋势线

计算

缺少两个关键组件:

  • 滑动窗。由
    双倍
    值组成的
    列表,不能超过给定的大小
  • 计算。接受答案的变体(少调用一次
    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;
  }
}
import java.math.BigDecimal;
导入java.util.ArrayList;
导入java.util.List;
导入net.sf.jasperreports.engine.fill.AbstractValueProvider;
导入net.sf.jasperreports.engine.fill.JRFillVariable;
导入net.sf.jasperreports.engine.fill.jrrincrementer;
/**
*iReport变量用于增加其平均值。
*/
公共类RunningAverageIncrementer
实现JR增量器{
/**默认计数数*/
私有静态最终整数默认计数=128;
/**滑动窗口内的计数数*/
私有静态最终整数默认值滑动窗口大小=30;
/**存储值的滑动窗口*/
私有列表值=新的ArrayList(默认值);
/**
*由RunningAverageIncrementerFactory类实例化。
*/
公共运行平均增量器(){
}
/**
*计算以前已知值的平均值。
*@return getValues()返回的值列表的平均值。
*/
私有双计算{
双结果=0.0;
列表值=getValues();
for(双d:getValues()){
结果+=d.双值();
}
返回结果/值。size();
}
/**
*每次接收到要平均的新值时调用。
*@param value要包含在平均值中的新值。
*/
私有无效记录值(双倍值){
列表值=getValues();
//抛弃那些不应该再影响趋势的旧价值观。
//
如果(values.size()>GetSlidingWindowsSize()){
值。删除(0);
}
这个.values.add(值);
}
私有列表getValues(){
返回值;
}
私有int getIterations(){
返回getValues().size();
}
/**
*返回新增加的值,该值通过平均值计算
*此方法的上一次调用的上一个值。
* 
*@param jrFillVariable未使用。
*@param将新数据点计数为平均值。
*@param abstractValueProvider未使用。
*@返回新增加的值。
*/
公共对象增量(JRFillVariable、JRFillVariable、对象计数、,
AbstractValueProvider(AbstractValueProvider){
double value=((数字)计数).double value();
记录值(value);
双倍上一次平均值=calculateAverage();
双新平均=
((value-previousAverage)/(getIterations()+1))+previousAverage;
返回新的BigDecimal(newAverage);
}
受保护的int GetSlidingWindowsSize(){
返回默认的滑动窗口大小;
}
}