Отчетная линия тренда - PullRequest
4 голосов
/ 16 марта 2010

Как бы вы создали в JasperReports линию, которая следит за трендом для данных, помимо отображения точек данных? Вот снимки до и после:

До image

* После 1009 * image

В отчете о временных рядах нет такой возможности нарисовать оранжевую линию. (Оранжевая линия должна быть плавной и тонкой, но это общая идея.)

Есть идеи, как создать такой отчет в iReport 3.7.1?

1 Ответ

5 голосов
/ 22 марта 2010

Для одного решения требуются следующие предметы:

  • BezierLineCustomizer, чтобы сделать линии изогнутыми.
  • RunningAverageIncrementer для расчета скользящего среднего на основе переменной.
  • Переменная iReport, которая использует RunningAverageIncremeter.

BezierLineClassizer Class

public class BezierLineCustomizer
  implements JRChartCustomizer {
  public BezierLineCustomizer() {
  }

  public void customize( JFreeChart jFreeChart, JRChart jrChart ) {
    XYPlot xyPlot = ( XYPlot )jFreeChart.getPlot();

    XYSplineRenderer splineRenderer = new XYSplineRenderer();

    // Make the spline line thick and orange.
    //
    splineRenderer.setSeriesShapesVisible( 0, false );
    splineRenderer.setSeriesShapesVisible( 1, false );
    splineRenderer.setSeriesLinesVisible( 1, false );
    splineRenderer.setSeriesStroke(
        0, new BasicStroke(
            4.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
            1.0f, null, 0.0f
        )
    );

    splineRenderer.setSeriesPaint( 0, new Color( 255, 140, 0 ) );
    splineRenderer.setSeriesVisibleInLegend( 1, Boolean.FALSE );

    // Duplicate the data into a new dataset to control its line independently.
    //
    xyPlot.setDataset( 1, xyPlot.getDataset(0) );

    XYItemRenderer defaultRenderer = new XYLineAndShapeRenderer();
    defaultRenderer.setSeriesVisible( 0, Boolean.FALSE );
    defaultRenderer.setSeriesVisibleInLegend( 0, Boolean.FALSE );

    xyPlot.setRenderer( 1, defaultRenderer );
    xyPlot.setRenderer( 0, splineRenderer );
  }
}

RunningAverageIncrementer Class

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

Переменная iReport

Создайте переменную, которая использует класс RunningAverageIncrementerFactory (упражнение, оставленное читателю). Установите его переменное выражение на график значения. Установите начальное значение выражения в ноль.

сплайн

Установите для свойства Classizer Class диаграммы TimeSeries использование класса BezierLineCustomizer.

Результат

После этих модификаций хорошо видно скользящее среднее:

image

...