【问题标题】:Trend analysis using iterative value increments使用迭代值增量进行趋势分析
【发布时间】:2011-01-29 05:28:11
【问题描述】:

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

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

【问题讨论】:

    标签: java algorithm ireport data-analysis


    【解决方案1】:

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

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

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

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

    如果您正在测量的项目以某种基础方程可以预测的方式波动,那么您可以使用Kalman filter 根据之前(最近)的测量值和方程提供对下一个点的最佳估计对预测的行为进行建模。

    作为起点,Bayesian estimators 和卡尔曼滤波器上的维基百科条目会有所帮助。

    【讨论】:

      【解决方案2】:

      结果图像

      结果仍然不完整,但它清楚地显示出比问题中更好的趋势线。

      计算

      缺少两个关键组件:

      • 滑动窗口。 ListDouble 值不能超过给定大小。
      • 计算。接受答案的变体(少调用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;
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2018-03-27
        • 1970-01-01
        • 1970-01-01
        • 2013-09-11
        • 1970-01-01
        • 1970-01-01
        • 2012-09-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多