content: /*
   * Copyright (c) 2010 Pentaho Corporation.  All rights reserved. 
   * This software was developed by Pentaho Corporation and is provided under the terms 
   * of the GNU Lesser General Public License, Version 2.1. You may not use 
   * this file except in compliance with the license. If you need a copy of the license, 
   * please go to http://www.gnu.org/licenses/lgpl-2.1.txt. The Original Code is Time Series 
   * Forecasting.  The Initial Developer is Pentaho Corporation.
   *
   * Software distributed under the GNU Lesser Public License is distributed on an "AS IS" 
   * basis, WITHOUT WARRANTY OF ANY KIND, either express or  implied. Please refer to 
   * the license for the specific language governing your rights and limitations.
   */
  
  /*
   *    TSEvalModule.java
   *    Copyright (C) 2010 Pentaho Corporation
   */
  
  package weka.classifiers.timeseries.eval;
  
  import java.util.ArrayList;
  import java.util.List;
  
  import weka.classifiers.evaluation.NumericPrediction;
  import weka.core.Instance;
  import weka.core.Utils;
  
  /**
   * Abstract superclass of all evaluation modules.
   * 
   * @author Mark Hall (mhall{[at]}pentaho{[dot]}com)
   * @version $Revision: 49983 $
   *
   */
  public abstract class TSEvalModule {
    
    /** the target fields that evaluation is computed for */
    protected List<String> m_targetFieldNames;  
    
    /**
     * Gets a list of known evaluation modules.
     * 
     * @return a list of known evaluation modules
     */
    public static List<TSEvalModule> getModuleList() {
      List<TSEvalModule> result = new ArrayList<TSEvalModule>();
      result.add(new ErrorModule()); result.add(new MAEModule());
      result.add(new MSEModule()); result.add(new RMSEModule());
      result.add(new MAPEModule()); result.add(new DACModule());
      result.add(new RAEModule()); result.add(new RRSEModule());
      
      return result;
    }
    
    /**
     * Factory method for obtaining a named evaluation module. If the
     * name does not match any of the known modules, then this method
     * will assume it is fully qualified and try to instantiate it.
     * 
     * @param moduleName the name of the module to obtain
     * @return the named module
     * @throws IllegalArgumentException if the supplied module
     * name is unknown and can't be instantiated.
     */
    public static TSEvalModule getModule(String moduleName) 
      throws IllegalArgumentException {
      
      if (moduleName.equalsIgnoreCase("Error")) {
        return new ErrorModule();
      } else if (moduleName.equalsIgnoreCase("MAE")) {
        return new MAEModule();
      } else if (moduleName.equalsIgnoreCase("MSE")) {
        return new MSEModule();
      } else if (moduleName.equalsIgnoreCase("RMSE")) {
        return new RMSEModule();
      } else if (moduleName.equalsIgnoreCase("MAPE")) {
        return new MAPEModule();
      } else if (moduleName.equalsIgnoreCase("DAC")) {
        return new DACModule();
      } else if (moduleName.equalsIgnoreCase("RAE")) {
        return new RAEModule();
      } else if (moduleName.equalsIgnoreCase("RRSE")) {
        return new RRSEModule();
      } else {
        // assume a fully qualified class name and try to instantiate
        try {
          Object candidateModule = Class.forName(moduleName).newInstance();
          if (candidateModule instanceof TSEvalModule) {
            return (TSEvalModule)candidateModule;
          }
        } catch (InstantiationException e) {
          throw new IllegalArgumentException("Unable to instantiate " + moduleName);
        } catch (IllegalAccessException e) {
          throw new IllegalArgumentException("Unknown evaluation moduel " + moduleName);
        } catch (ClassNotFoundException e) {
          throw new IllegalArgumentException("Unable to instantiate " + moduleName);
        }        
      }
      
      throw new IllegalArgumentException("Unknown evaluation moduel " + moduleName);
    }
    
    /**
     * Reset the module
     */
    public abstract void reset();
    
    /**
     * Return the short identifying name of this evaluation module
     * 
     * @return the short identifying name of this evaluation module
     */
    public abstract String getEvalName();
    
    /**
     * Return the longer (single sentence) description
     * of this evaluation module
     * 
     * @return the longer description of this module
     */
    public abstract String getDescription();
    
    /**
     * Return the mathematical formula that this
     * evaluation module computes.
     * 
     * @return the mathematical formula that this module
     * computes.
     */
    public abstract String getDefinition();
    
    /**
     * Evaluate the given forecast(s) with respect to the given
     * test instance. Targets with missing values are ignored.
     * 
     * @param forecasts a List of forecasted values. Each element 
     * corresponds to one of the targets and is assumed to be in the same
     * order as the list of targets supplied to the setTargetFields() method.
     * @throws Exception if the evaluation can't be completed for some
     * reason. 
     */
    public abstract void evaluateForInstance(List<NumericPrediction> forecasts, Instance inst) 
      throws Exception;
    
    /**
     * Calculate the measure that this module represents.
     * 
     * @return the value of the measure for this module for each
     * of the target(s).
     * @throws Exception if the measure can't be computed for some reason.
     */
    public abstract double[] calculateMeasure() throws Exception;
    
    /**
     * Return the summary description of the computed measure for
     * each target.
     * 
     * @return the summary string description of the computed measure.
     * @throws Exception if the measure can't be computed for some reason.
     */
    public abstract String toSummaryString() throws Exception;
    
    /**
     * Set a list of target field names. This list must
     * be the same as that provided to the TSForecaster
     * via TSForecast.setFieldsToForecast()
     * 
     * @param targets a List of target field names
     */
    public void setTargetFields(List<String> targets) {
      m_targetFieldNames = targets;
      reset();
    }
    
    /**
     * Get the list of target field names.
     * 
     * @return the list of target field names.
     */
    public List<String> getTargetFields() {
      return m_targetFieldNames;
    }
  
    /**
     * Get the value of the named target attribute from
     * the supplied instance.
     * 
     * @param targetName the name of the target attribute to get the
     * value of
     * @param inst the instance to retrieve the target value from
     * @return
     */
    protected double getTargetValue(String targetName, Instance inst) {
      if (inst == null) {
        return Utils.missingValue();
      }
      
      int targetIndex = inst.dataset().attribute(targetName).index();
      
      return inst.value(targetIndex);
    }
  }
  