All Downloads are FREE. Search and download functionalities are using the official Maven repository.

weka.classifiers.timeseries.gui.AdvancedConfigPanel Maven / Gradle / Ivy

Go to download

Provides a time series forecasting environment for Weka. Includes a wrapper for Weka regression schemes that automates the process of creating lagged variables and date-derived periodic variables and provides the ability to do closed-loop forecasting. New evaluation routines are provided by a special evaluation module and graphing of predictions/forecasts are provided via the JFreeChart library. Includes both command-line and GUI user interfaces. Sample time series data can be found in ${WEKA_HOME}/packages/timeseriesForecasting/sample-data.

The newest version!
/*
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see .
 */


/*
 *    AdvancedConfigPanel.java
 *    Copyright (C) 2010-2016 University of Waikato, Hamilton, New Zealand
 */

package weka.classifiers.timeseries.gui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.table.DefaultTableModel;

import weka.classifiers.Classifier;
import weka.classifiers.timeseries.PrimingDataLearner;
import weka.classifiers.timeseries.WekaForecaster;
import weka.classifiers.timeseries.core.CustomPeriodicTest;
import weka.classifiers.timeseries.core.OverlayForecaster;
import weka.filters.supervised.attribute.TSLagMaker;
import weka.classifiers.timeseries.eval.TSEvalModule;
import weka.classifiers.timeseries.eval.TSEvaluation;
import weka.core.Attribute;
import weka.core.Capabilities;
import weka.core.Capabilities.Capability;
import weka.core.Instances;
import weka.core.Range;
import weka.core.SerializedObject;
import weka.gui.AttributeSelectionPanel;
import weka.gui.ExtensionFileFilter;
import weka.gui.GenericObjectEditor;
import weka.gui.PropertyDialog;
import weka.gui.PropertyPanel;

public class AdvancedConfigPanel extends JPanel {

  protected static final int NUM_PREDEFINED_PERIODICS = 7;
  /**
   * For serialization
   */
  private static final long serialVersionUID = 5465960083615138964L;
  /** The training instances to operate on */
  protected Instances m_instances;
  /** A reference to the simple config panel */
  protected SimpleConfigPanel m_simpleConfig;
  /** Editor for selecting and configuring the base algorithm */
  protected GenericObjectEditor m_baseLearnerEditor = new GenericObjectEditor();

  /** Property panel for editing base algorithm */
  protected PropertyPanel m_baseLearnerPanel = new PropertyPanel(
    m_baseLearnerEditor);

  /** Custom lags checkbox */
  protected JCheckBox m_useCustomLags = new JCheckBox("Use custom lag lengths");

  /** Min lag spinner */
  protected JSpinner m_minLagSpinner;

  /** Max lag spinner */
  protected JSpinner m_maxLagSpinner;

  /** Remove the initial instances where lag values are unknown/missing */
  protected JCheckBox m_removeLeadingInstancesWithMissingLags = new JCheckBox(
    "Remove leading instances with unknown lag values");

  /** Include powers of time attributes */
  protected JCheckBox m_powersOfTime = new JCheckBox("Include powers of time");

  /** Include products of time and lagged variables as attributes */
  protected JCheckBox m_timeLagProducts = new JCheckBox(
    "Included products of time and lagged variables");

  protected JButton m_moreOptsBut = new JButton("More options...");

  /** Adjust for variance check box */
  protected JCheckBox m_adjustForVarianceCheckBox = new JCheckBox(
    "Adjust for variance");

  /** Field for fine tuning the lags that are created by specifying a range */
  protected JTextField m_fineTuneLagsField = new JTextField();

  /** Check box for averaging consecutive long lags */
  protected JCheckBox m_averageLongLags = new JCheckBox(
    "Average consecutive long lags");

  /** Spinner for selecting which lag to start averaging from */
  protected JSpinner m_averageLagsAfter;

  /**
   * Spinner for selecting how many consecutive long lags to average into one
   * new field
   */
  protected JSpinner m_numConsecutiveToAverage;

  /** Data structure for holding the field names for date-derived fields */
  protected Instances m_dateDerivedPeriodicsHeader;

  /** Custom date-derived fields check box */
  protected JCheckBox m_customizeDateDerivedPeriodics = new JCheckBox(
    "Customize");

  /** Edit button for custom date-derived fields */
  protected JButton m_editCustomPeriodicBut = new JButton("Edit");

  /** Add button for custom date-derived fields */
  protected JButton m_addCustomPeriodicBut = new JButton("New");

  /** Delete button for custom date-derived fields */
  protected JButton m_deleteCustomPeriodicBut = new JButton("Delete");

  /** Save button for saving date-derived periodics to a file */
  protected JButton m_savePeriodicBut = new JButton("Save");

  /** Load button for loading pre-defined date-derived periodics from a file */
  protected JButton m_loadPeriodicBut = new JButton("Load");

  /** File chooser for saving/loading date-derived periodics */
  protected JFileChooser m_fileChooser;

  /** Map of custom date-derived fields */
  protected Map> m_customPeriodics =
    new HashMap>();
  /** Panel for holding and selecting date-derived periodic attributes */
  protected AttributeSelectionPanelExtended m_dateDerivedPeriodicSelector =
    new AttributeSelectionPanelExtended(false, false, false, false);
  /** Non date-based primary periodic stuff */
  protected JComboBox m_primaryPeriodicCombo = new JComboBox();
  /** Panel for holding and selecting overlay fields */
  protected AttributeSelectionPanelExtended m_overlaySelector =
    new AttributeSelectionPanelExtended(true, true, true, true);
  /** Check box for overlay fields */
  protected JCheckBox m_useOverlayData = new JCheckBox("Use overlay data");
  /** Holds the structure of overlay fields */
  protected Instances m_overlayHeader;
  /** Holds the names of the currently selected evaluation modules */
  protected Instances m_evaluationModsHeader;
  /** Panel for holding and selecting evaluation modules */
  protected AttributeSelectionPanel m_evaluationMetrics =
    new AttributeSelectionPanel(false, false, false, false);
  /** Perform training set evaluation check box */
  protected JCheckBox m_trainingCheckBox =
    new JCheckBox("Evaluate on training");
  /** Perform hold-out evaluation check box */
  protected JCheckBox m_holdoutCheckBox = new JCheckBox(
    "Evaluate on held out training");
  /** Text field for setting the size of the hold-out set */
  protected JTextField m_holdoutSize = new JTextField();
  /** show the separate test set checkbox and button? */
  protected boolean m_allowSeparateTestSet = true;
  /** Separate test set check box */
  protected JCheckBox m_separateTestSetCheckBox = new JCheckBox(
    "Evaluate on a separate test set");
  /** Button for selecting a separate test set */
  protected JButton m_testSetBut = new JButton("Separate test set");
  /** Check box for outputting predictions */
  protected JCheckBox m_outputPredsCheckBox = new JCheckBox(
    "Output predictions at step");
  /** Combo box for selecting which target to output predictions for */
  protected JComboBox m_outputPredsCombo = new JComboBox();
  protected JLabel m_outputPredsComboLabel = new JLabel("Target to output",
    JLabel.RIGHT);
  /** Spinner for selecting which step to output */
  protected JSpinner m_outputStepSpinner;
  protected JLabel m_outputStepLabel = new JLabel("Step to output",
    JLabel.RIGHT);
  /** Output future predictions check box */
  protected JCheckBox m_outputFutureCheckBox = new JCheckBox(
    "Output future predictions beyond end of series ");
  /** Graph predictions at step check box */
  protected JCheckBox m_graphPredsAtStepCheckBox = new JCheckBox(
    "Graph predictions at step");
  /** Spinner for selecting which step to graph predictions at */
  protected JSpinner m_graphPredsAtStepSpinner;
  protected JLabel m_stepLab = new JLabel("Steps to graph");
  /** Graph targets for specific step */
  protected JCheckBox m_graphTargetForStepsCheckBox = new JCheckBox(
    "Graph target at steps:");
  /** Graph target at steps combo box */
  protected JComboBox m_graphTargetAtStepsCombo = new JComboBox();
  protected JLabel m_targetComboLabel = new JLabel("Target to graph",
    JLabel.RIGHT);
  /**
   * Text field for specifying a range of steps to graph for the selected target
   */
  protected JTextField m_stepRange = new JTextField("1");
  /** Graph future predictions check box */
  protected JCheckBox m_graphFutureCheckBox = new JCheckBox(
    "Graph future predictions beyond end of series");
  static {
    GenericObjectEditor.registerEditors();
  }
  /** A tabbed pane to hold the individual advanced configuration panels */
  JTabbedPane m_configHolder = new JTabbedPane();

  /**
   * Constructor
   *
   * @param s a reference to the simple configuration panel
   * @param allowSeparateTestSet true if the separate test set button is to be
   *          displayed
   */
  public AdvancedConfigPanel(SimpleConfigPanel s, boolean allowSeparateTestSet) {

    m_allowSeparateTestSet = allowSeparateTestSet;
    m_simpleConfig = s;

    setLayout(new BorderLayout());
    layoutLearnerPanel();
    layoutLagPanel();
    layoutDateDerivedPeriodicPanel();
    layoutOverlayPanel();
    layoutEvaluationPanel();
    layoutOutputPanel();

    add(m_configHolder, BorderLayout.CENTER);
  }

  /**
   * Constructor
   *
   * @param s a reference to the simple configuration panel
   */
  public AdvancedConfigPanel(SimpleConfigPanel s) {
    this(s, true);
  }

  /**
   * Tests the Weka advanced config panel from the command line.
   *
   * @param args must contain the name of an arff file to load.
   */
  public static void main(String[] args) {

    try {
      if (args.length == 0) {
        throw new Exception("supply the name of an arff file");
      }
      Instances i =
        new Instances(new java.io.BufferedReader(
          new java.io.FileReader(args[0])));
      SimpleConfigPanel scp = new SimpleConfigPanel(null);
      scp.setInstances(i);
      AdvancedConfigPanel acp = new AdvancedConfigPanel(scp);
      // acp.setInstances(i);

      final javax.swing.JFrame jf = new javax.swing.JFrame("Weka Forecasting");
      jf.getContentPane().setLayout(new BorderLayout());
      jf.getContentPane().add(acp, BorderLayout.CENTER);
      jf.addWindowListener(new java.awt.event.WindowAdapter() {
        @Override
        public void windowClosing(java.awt.event.WindowEvent e) {
          jf.dispose();
          System.exit(0);
        }
      });
      jf.pack();
      jf.setVisible(true);
    } catch (Exception ex) {
      ex.printStackTrace();
      System.err.println(ex.getMessage());
    }
  }

  /**
   * Get a title for displaying in the tab that will hold this panel
   *
   * @return a title for this configuration panel
   */
  public String getTabTitle() {
    return "Advanced configuration";
  }

  /**
   * Get the tool tip for this configuration panel
   *
   * @return the tool tip for this configuration panel
   */
  public String getTabTitleToolTip() {
    return "Advanced configuration";
  }

  /**
   * Get the underlying Weka classifier that will be used to make the
   * predictions
   *
   * @return the underlying Weka classifier
   */
  public Classifier getBaseClassifier() {
    return (Classifier) m_baseLearnerEditor.getValue();
  }

  /**
   * Set the instances that will be used in the training and evaluation of the
   * forecaster
   *
   * @param train the instances to use in the training and evaluation process
   */
  public void setInstances(Instances train) {
    m_instances = train;
    updatePanel();
  }

  /**
   * Set the enabled/disabled status of date-derived periodic panel and its
   * associated widgets.
   *
   * @param s true if date-derived periodicis is to be enabled
   */
  public void enableDateDerivedPeriodics(boolean s) {
    m_customizeDateDerivedPeriodics.setEnabled(s);
    if (!s) {
      m_customizeDateDerivedPeriodics.setSelected(false);
      m_addCustomPeriodicBut.setEnabled(false);
      m_editCustomPeriodicBut.setEnabled(false);
      m_deleteCustomPeriodicBut.setEnabled(false);
      m_savePeriodicBut.setEnabled(false);
      m_loadPeriodicBut.setEnabled(false);
    }
  }

  /**
   * Returns true if date-derived periodics is enabled
   *
   * @return if date-derived periodics is enabled.
   */
  public boolean isEnabledCustomizeDateDerivedPeriodics() {
    return m_customizeDateDerivedPeriodics.isEnabled();
  }

  /**
   * Returns true if the date-derived periodics check box is selected.
   *
   * @return true if the date-derived periodics check box is selected.
   */
  public boolean getCustomizeDateDerivedPeriodics() {
    return m_customizeDateDerivedPeriodics.isSelected();
  }

  /**
   * Returns true if the user has opted to customize the lags.
   *
   * @return true if custom lags are in use.
   */
  public boolean isUsingCustomLags() {
    return m_useCustomLags.isSelected();
  }

  /**
   * Gets the size of the holdout set.
   *
   * @return the size of the holdout set or 0 if no holdout set is being used.
   */
  public double getHoldoutSetSize() {
    double result = 0;

    if (m_holdoutCheckBox.isSelected()) {
      try {
        result = Double.parseDouble(m_holdoutSize.getText());
      } catch (NumberFormatException ex) {
      }
    }

    return result;
  }

  /**
   * Returns true if the user has opted to output future predictions
   *
   * @return if the user has opted to output future predictions
   */
  public boolean getOutputFuturePredictions() {
    return m_outputFutureCheckBox.isSelected();
  }

  /**
   * Returns at which step to output predictions. Returns 0 if the user has not
   * opted to output predictions.
   *
   * @return step at which to output predictions or 0 if no predictions are to
   *         be output.
   */
  public int getOutputPredictionsAtStep() {
    if (!m_outputPredsCheckBox.isSelected()) {
      return 0;
    }

    return ((SpinnerNumberModel) m_outputStepSpinner.getModel()).getNumber()
      .intValue();
  }

  /**
   * Returns true if the user has opted to graph a target at specified steps
   *
   * @return true if the user has opted to graph a target at specified steps
   */
  public boolean getGraphTargetForSteps() {
    return m_graphTargetForStepsCheckBox.isSelected();
  }

  /**
   * Return the target that is to be graphed at various steps
   *
   * @return the target that is to be graphed at specified steps or null if the
   *         user has not opted to graph a targert at specified steps
   */
  public String getGraphTargetForStepsTarget() {
    if (!getGraphTargetForSteps()) {
      return null;
    }

    return m_graphTargetAtStepsCombo.getSelectedItem().toString();
  }

  /**
   * If the user has opted to graph a target a various steps, then this method
   * returns the list of steps that they have selected.
   *
   * @return the list of steps at which to graph a target, or null if the user
   *         has not opted to graph a target at various steps.
   */
  public List getGraphTargetForStepsStepList() {
    String rng = m_stepRange.getText();

    if (rng == null || rng.length() == 0) {
      return null;
    }

    Range range = new Range(rng);
    range.setUpper(m_simpleConfig.getHorizonValue());
    int[] indices = range.getSelection();

    List rangeList = new ArrayList();
    for (int i : indices) {
      rangeList.add((i + 1));
    }

    return rangeList;
  }

  /**
   * Get the selected target to output predictions for. Returns null if the user
   * has not opted to output predictions.
   *
   * @return the name of the target to output predictions for or null if no
   *         predictions are to be output.
   */
  public String getOutputPredictionsTarget() {
    if (!m_outputPredsCheckBox.isSelected()) {
      return null;
    }

    return m_outputPredsCombo.getSelectedItem().toString();
  }

  /**
   * Get the step number to graph all the targets at.
   *
   * @return the step number to graph all the targets at, or 0 if the user has
   *         not opted to graph all the targets.
   */
  public int getGraphPredictionsAtStep() {
    if (!m_graphPredsAtStepCheckBox.isSelected()) {
      return 0;
    }

    return ((SpinnerNumberModel) m_graphPredsAtStepSpinner.getModel())
      .getNumber().intValue();
  }

  /**
   * Returns true if the user has opted to graph future predictions
   *
   * @return true if the user has opted graph future predictions
   */
  public boolean getGraphFuturePredictions() {
    return m_graphFutureCheckBox.isSelected();
  }

  /**
   * Updates various enabled/selected status of widgets based on the current
   * configuration
   */
  public void updatePanel() {
    updateDateDerivedPanel();
    updatePrimaryPeriodic();
    updateOutputPanel();
    updateOverlayPanel();
  }

  /**
   * Updates the status/selection of widgets on the overlay panel
   */
  public void updateOverlayPanel() {
    Instances newI = createAvailableOverlayList();
    if (newI == null) {
      m_useOverlayData.setSelected(false);
      m_useOverlayData.setEnabled(false);
      m_overlaySelector.clearTableModel();
    } else {
      m_useOverlayData.setEnabled(true);
      if (m_useOverlayData.isSelected()) {
        if (m_overlayHeader == null || !newI.equalHeaders(m_overlayHeader)) {
          m_overlayHeader = newI;
          m_overlaySelector.setInstances(newI);
        }
      }
    }
  }

  private Instances createAvailableOverlayList() {

    String ppfn = m_primaryPeriodicCombo.getSelectedItem().toString();
    Instances result = null;
    if (m_simpleConfig.m_targetHeader != null) {
      ArrayList availableAtts = new ArrayList();

      int[] selectedTargets =
        m_simpleConfig.m_targetPanel.getSelectedAttributes();
      for (int i = 0; i < m_instances.numAttributes(); i++) {
        // skip all date attributes
        if (m_instances.attribute(i).isDate()) {
          continue;
        }

        // skip any primary periodic
        if (m_instances.attribute(i).name().equals(ppfn)) {
          continue;
        }

        if (m_simpleConfig.m_targetHeader.attribute(m_instances.attribute(i)
          .name()) != null) {
          // now need to check whether it's been selected as a target
          int indexToCheck =
            m_simpleConfig.m_targetHeader.attribute(
              m_instances.attribute(i).name()).index();

          boolean ok = true;
          for (int selectedTarget : selectedTargets) {
            if (indexToCheck == selectedTarget) {
              ok = false; // can't use this attribute
              break;
            }
          }

          if (ok) {
            availableAtts.add(new Attribute(m_instances.attribute(i).name()));
          }
        } else {
          // this is available
          availableAtts.add(new Attribute(m_instances.attribute(i).name()));
        }
      }
      if (availableAtts.size() > 0) {
        result = new Instances("Overlay", availableAtts, 1);
      }
    }

    return result;
  }

  /**
   * Updates the status/selection of various widgets on the output panel
   */
  public void updateOutputPanel() {
    if (m_simpleConfig.m_targetHeader != null) {
      // configure the target combos
      Vector candidates = new Vector();
      for (int i = 0; i < m_simpleConfig.m_targetHeader.numAttributes(); i++) {
        candidates.add(m_simpleConfig.m_targetHeader.attribute(i).name());
      }

      m_graphTargetAtStepsCombo.setModel(new DefaultComboBoxModel(candidates));
      m_outputPredsCombo.setModel(new DefaultComboBoxModel(candidates));
    }
  }

  /**
   * Updates the status/selection of various widgets on the date-derived
   * periodics panel
   */
  protected void updateDateDerivedPanel() {
    if (m_simpleConfig.m_timeStampCombo.getSelectedItem() == null) {
      return;
    }
    String selectedTimeStamp =
      m_simpleConfig.m_timeStampCombo.getSelectedItem().toString();
    if (selectedTimeStamp.equals("")
      || selectedTimeStamp.equals("")) {
      m_customizeDateDerivedPeriodics.setSelected(false);
      m_customizeDateDerivedPeriodics.setEnabled(false);
      m_editCustomPeriodicBut.setEnabled(false);
      m_addCustomPeriodicBut.setEnabled(false);
      m_deleteCustomPeriodicBut.setEnabled(false);
      m_savePeriodicBut.setEnabled(false);
      m_loadPeriodicBut.setEnabled(false);
    } else if (m_instances != null) {
      Attribute timeStampAtt = m_instances.attribute(selectedTimeStamp);
      if (timeStampAtt != null && timeStampAtt.isDate()) {
        m_customizeDateDerivedPeriodics.setEnabled(true);
      } else {
        m_customizeDateDerivedPeriodics.setSelected(false);
        m_customizeDateDerivedPeriodics.setEnabled(false);
        m_editCustomPeriodicBut.setEnabled(false);
        m_addCustomPeriodicBut.setEnabled(false);
        m_deleteCustomPeriodicBut.setEnabled(false);
        m_savePeriodicBut.setEnabled(false);
        m_loadPeriodicBut.setEnabled(false);
      }
    } else {
      m_customizeDateDerivedPeriodics.setSelected(false);
      m_customizeDateDerivedPeriodics.setEnabled(false);
      m_editCustomPeriodicBut.setEnabled(false);
      m_addCustomPeriodicBut.setEnabled(false);
      m_deleteCustomPeriodicBut.setEnabled(false);
      m_savePeriodicBut.setEnabled(false);
      m_loadPeriodicBut.setEnabled(false);
    }
  }

  /**
   * Sets up the primary periodic drop-down box
   */
  protected void updatePrimaryPeriodic() {
    Vector entries = new Vector();

    entries.add("");
    if (m_instances != null) {
      for (int i = 0; i < m_instances.numAttributes(); i++) {
        if (m_instances.attribute(i).isNominal()) {
          entries.add(m_instances.attribute(i).name());
        }
      }
    }
    m_primaryPeriodicCombo.setModel(new DefaultComboBoxModel(entries));
  }

  /**
   * Updates the status of eval and output widgets
   */
  protected void updateEvalAndOutputEnabledStatus() {
    boolean enable =
      (m_trainingCheckBox.isSelected() || m_holdoutCheckBox.isSelected() || m_separateTestSetCheckBox
        .isSelected());

    // enable/disable and deselect all prediction and graphing widgets
    m_outputPredsCheckBox.setEnabled(enable);

    // m_outputFutureCheckBox.setEnabled(enable);

    m_graphPredsAtStepCheckBox.setEnabled(enable);
    // m_outputStepSpinner.setEnabled(enable);
    // m_graphPredsAtStepSpinner.setEnabled(enable);
    m_graphTargetForStepsCheckBox.setEnabled(enable);

    // m_graphFutureCheckBox.setEnabled(enable);

    if (!enable) {
      m_outputPredsCheckBox.setSelected(false);
      // m_outputFutureCheckBox.setSelected(false);
      m_graphPredsAtStepCheckBox.setSelected(false);
      m_graphTargetForStepsCheckBox.setSelected(false);

      // m_graphFutureCheckBox.setSelected(false);
    }

    boolean enabled = m_graphTargetForStepsCheckBox.isSelected();
    m_stepLab.setEnabled(enabled);
    m_stepRange.setEnabled(enabled);
    m_targetComboLabel.setEnabled(enabled);
    m_graphTargetAtStepsCombo.setEnabled(enabled);
    m_graphPredsAtStepSpinner.setEnabled(enabled);

    enabled = m_outputPredsCheckBox.isSelected();
    m_outputStepLabel.setEnabled(enabled);
    m_outputPredsCombo.setEnabled(enabled);
    m_outputPredsComboLabel.setEnabled(enable);
    m_outputStepSpinner.setEnabled(enabled);

    m_testSetBut.setEnabled(!m_holdoutCheckBox.isSelected());
    m_separateTestSetCheckBox.setEnabled(!m_holdoutCheckBox.isSelected());
    m_holdoutSize.setEnabled(m_holdoutCheckBox.isSelected());
    if (m_holdoutCheckBox.isSelected()) {
      m_separateTestSetCheckBox.setSelected(false);
    }
  }

  /**
   * Layout the evaluation panel
   */
  protected void layoutEvaluationPanel() {
    JPanel basePanel = new JPanel();
    basePanel.setLayout(new BorderLayout());
    /*
     * basePanel.setBorder(BorderFactory. createTitledBorder("Evaluation"));
     */

    JPanel base1 = new JPanel();
    base1.setLayout(new BorderLayout());
    base1.add(m_evaluationMetrics, BorderLayout.CENTER);
    List evalModulesL = TSEvalModule.getModuleList();
    ArrayList atts = new ArrayList();
    int numMods = 0;
    for (TSEvalModule s : evalModulesL) {
      if (!(s.getEvalName().equals("Error"))) {
        atts.add(new Attribute(s.toString()));
        numMods++;
      }
    }
    Instances modInsts = new Instances("Eval modules", atts, 1);
    m_evaluationModsHeader = modInsts;
    m_evaluationMetrics.setInstances(modInsts);
    boolean[] selected = new boolean[numMods];
    selected[0] = true;
    selected[2] = true;
    try {
      m_evaluationMetrics.setSelectedAttributes(selected);
    } catch (Exception ex) {
    }
    m_evaluationMetrics.setPreferredScrollableViewportSize(new Dimension(260,
      80));
    m_evaluationMetrics.setBorder(BorderFactory.createTitledBorder("Metrics"));

    JPanel temp1 = new JPanel();
    temp1.setLayout(new BorderLayout());
    temp1.setBorder(BorderFactory.createTitledBorder("Test options"));
    temp1.add(m_trainingCheckBox, BorderLayout.NORTH);
    // m_trainingCheckBox.setSelected(true);
    m_trainingCheckBox.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        updateEvalAndOutputEnabledStatus();
        if (m_simpleConfig != null) {
          m_simpleConfig.m_performEvaluation.setSelected(m_trainingCheckBox
            .isSelected() || m_holdoutCheckBox.isSelected());
        }
      }
    });

    JPanel temp2 = new JPanel();
    temp2.setLayout(new BorderLayout());
    temp2.add(m_holdoutCheckBox, BorderLayout.WEST);
    temp2.add(m_holdoutSize, BorderLayout.EAST);
    m_holdoutSize.setEnabled(false);

    int width = m_minLagSpinner.getPreferredSize().width;
    int height = m_minLagSpinner.getPreferredSize().height;
    m_holdoutSize.setPreferredSize(new Dimension(width * 1, height));
    m_holdoutSize.setMinimumSize(new Dimension(width * 1, height));
    m_holdoutSize
      .setToolTipText("Number of instances (value >=1) or percentage "
        + "(value < 1) to hold out from the end of the data");
    m_holdoutSize.setText("0.3");
    m_holdoutCheckBox.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        updateEvalAndOutputEnabledStatus();
        if (m_simpleConfig != null) {
          m_simpleConfig.m_performEvaluation.setSelected(m_trainingCheckBox
            .isSelected() || m_holdoutCheckBox.isSelected());
        }
      }
    });
    temp1.add(temp2, BorderLayout.CENTER);

    if (m_allowSeparateTestSet) {
      JPanel checkAndButHolder = new JPanel();
      checkAndButHolder.setLayout(new BorderLayout());
      checkAndButHolder.add(m_separateTestSetCheckBox, BorderLayout.NORTH);
      checkAndButHolder.add(m_testSetBut, BorderLayout.SOUTH);
      temp1.add(checkAndButHolder, BorderLayout.SOUTH);
      m_separateTestSetCheckBox.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
          updateEvalAndOutputEnabledStatus();
        }
      });
    }

    JPanel temp3 = new JPanel();
    temp3.setLayout(new BorderLayout());
    temp3.add(temp1, BorderLayout.NORTH);
    base1.add(temp3, BorderLayout.EAST);
    basePanel.add(base1, BorderLayout.CENTER);
    // basePanel.add(outputOptsHolder, BorderLayout.EAST);

    m_testSetBut.setToolTipText("Evaluate on a separate test set");
    m_testSetBut.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        // TODO
      }
    });

    // holder.add(basePanel, BorderLayout.EAST);
    m_configHolder.addTab("Evaluation", null, basePanel, "Evaluation options");
  }

  /**
   * Layout the output panel
   */
  protected void layoutOutputPanel() {
    JPanel outputOptsHolder = new JPanel();
    outputOptsHolder.setLayout(new GridLayout(1, 2));

    JPanel textOutHolder = new JPanel();
    textOutHolder.setLayout(new BorderLayout());
    textOutHolder.setBorder(BorderFactory.createTitledBorder("Output options"));

    SpinnerNumberModel snm = new SpinnerNumberModel();
    snm.setValue(1);
    snm.setMinimum(1);
    m_outputStepSpinner = new JSpinner(snm);
    Dimension spinD = m_outputStepSpinner.getPreferredSize();
    spinD =
      new Dimension((int) (spinD.getWidth() * 1.5), (int) spinD.getHeight());
    m_outputStepSpinner.setPreferredSize(spinD);
    JPanel checkHolder = new JPanel();
    checkHolder.setLayout(new BorderLayout());
    checkHolder.add(m_outputPredsCheckBox, BorderLayout.WEST);
    // labAndSpinnerHolder.add(m_outputStepSpinner, BorderLayout.EAST);
    JPanel comboAndSpinnerHolder = new JPanel();
    comboAndSpinnerHolder.setLayout(new BorderLayout());

    JPanel combo1Holder = new JPanel();
    combo1Holder.setLayout(new BorderLayout());
    combo1Holder.setBorder(BorderFactory.createEmptyBorder(0, 0, 1, 0));
    combo1Holder.add(m_outputPredsCombo, BorderLayout.EAST);

    combo1Holder.add(m_outputPredsComboLabel, BorderLayout.CENTER);

    comboAndSpinnerHolder.add(combo1Holder, BorderLayout.NORTH);

    JPanel spinnerHolder1 = new JPanel();
    spinnerHolder1.setLayout(new BorderLayout());
    spinnerHolder1.add(m_outputStepSpinner, BorderLayout.EAST);
    spinnerHolder1.add(m_outputStepLabel, BorderLayout.CENTER);

    JPanel spinnerHolder2 = new JPanel();
    spinnerHolder2.setLayout(new BorderLayout());
    spinnerHolder2.setBorder(BorderFactory.createEmptyBorder(1, 0, 0, 0));
    spinnerHolder2.add(spinnerHolder1, BorderLayout.NORTH);

    comboAndSpinnerHolder.add(spinnerHolder2, BorderLayout.CENTER);
    textOutHolder.add(comboAndSpinnerHolder, BorderLayout.CENTER);

    textOutHolder.add(checkHolder, BorderLayout.NORTH);
    m_outputStepSpinner.setEnabled(false);
    m_outputStepLabel.setEnabled(false);
    m_outputPredsCombo.setEnabled(false);
    m_outputPredsCheckBox.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        m_outputStepSpinner.setEnabled(m_outputPredsCheckBox.isSelected());
        m_outputStepLabel.setEnabled(m_outputPredsCheckBox.isSelected());
        m_outputPredsComboLabel.setEnabled(m_outputPredsCheckBox.isSelected());
        m_outputPredsCombo.setEnabled(m_outputPredsCheckBox.isSelected());
      }
    });

    JPanel temp1 = new JPanel();
    temp1.setLayout(new BorderLayout());
    temp1.add(m_outputFutureCheckBox, BorderLayout.NORTH);
    textOutHolder.add(temp1, BorderLayout.SOUTH);
    outputOptsHolder.add(textOutHolder);
    m_outputFutureCheckBox.setSelected(true);

    JPanel graphOutputHolder = new JPanel();
    graphOutputHolder.setLayout(new BorderLayout());
    graphOutputHolder.setBorder(BorderFactory
      .createTitledBorder("Graphing options"));

    outputOptsHolder.add(graphOutputHolder);

    snm = new SpinnerNumberModel();
    snm.setValue(1);
    snm.setMinimum(1);
    m_graphPredsAtStepSpinner = new JSpinner(snm);
    spinD = m_graphPredsAtStepSpinner.getPreferredSize();
    spinD =
      new Dimension((int) (spinD.getWidth() * 1.5), (int) spinD.getHeight());
    m_graphPredsAtStepSpinner.setPreferredSize(spinD);
    JPanel labAndSpinnerHolder = new JPanel();
    labAndSpinnerHolder.setLayout(new BorderLayout());
    labAndSpinnerHolder.add(m_graphPredsAtStepCheckBox, BorderLayout.WEST);
    labAndSpinnerHolder.add(m_graphPredsAtStepSpinner, BorderLayout.EAST);
    graphOutputHolder.add(labAndSpinnerHolder, BorderLayout.NORTH);
    m_graphPredsAtStepCheckBox.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        m_graphPredsAtStepSpinner.setEnabled(m_graphPredsAtStepCheckBox
          .isSelected());
      }
    });
    m_graphPredsAtStepSpinner.setEnabled(false);

    JPanel comboAndRangeHolder = new JPanel();
    comboAndRangeHolder.setLayout(new BorderLayout());
    comboAndRangeHolder.add(m_graphTargetForStepsCheckBox, BorderLayout.NORTH);
    JPanel temp2 = new JPanel();
    temp2.setLayout(new BorderLayout());
    temp2.setBorder(BorderFactory.createEmptyBorder(0, 0, 1, 0));
    temp2.add(m_graphTargetAtStepsCombo, BorderLayout.EAST);

    temp2.add(m_targetComboLabel, BorderLayout.CENTER);
    JPanel temp3 = new JPanel();
    temp3.setLayout(new BorderLayout());
    temp3.add(temp2, BorderLayout.NORTH);

    JPanel textHolder = new JPanel();
    textHolder.setLayout(new BorderLayout());
    // textHolder.setBorder(BorderFactory.createEmptyBorder(1, 0, 0, 0));
    textHolder.add(m_stepRange, BorderLayout.EAST);
    m_stepRange.setPreferredSize(new Dimension((int) spinD.getWidth() * 2,
      (int) spinD.getHeight()));

    m_stepLab.setToolTipText("Comma separated list of step numbers to graph");
    m_stepRange.setToolTipText("Comma separated list of step numbers to graph");
    JPanel ll = new JPanel();
    ll.setLayout(new BorderLayout());
    ll.add(m_stepLab, BorderLayout.EAST);

    textHolder.add(ll, BorderLayout.CENTER);
    temp3.add(textHolder, BorderLayout.CENTER);

    JPanel ll2 = new JPanel();
    ll2.setLayout(new BorderLayout());
    ll2.add(temp3, BorderLayout.NORTH);
    comboAndRangeHolder.add(ll2, BorderLayout.CENTER);
    comboAndRangeHolder.add(m_graphFutureCheckBox, BorderLayout.SOUTH);
    m_graphFutureCheckBox.setSelected(true);

    m_stepLab.setEnabled(false);
    m_stepRange.setEnabled(false);
    m_targetComboLabel.setEnabled(false);
    m_graphTargetAtStepsCombo.setEnabled(false);
    m_graphTargetForStepsCheckBox.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        boolean enabled = m_graphTargetForStepsCheckBox.isSelected();
        m_stepLab.setEnabled(enabled);
        m_stepRange.setEnabled(enabled);
        m_targetComboLabel.setEnabled(enabled);
        m_graphTargetAtStepsCombo.setEnabled(enabled);
      }
    });

    graphOutputHolder.add(comboAndRangeHolder, BorderLayout.CENTER);

    m_configHolder.addTab("Output", null, outputOptsHolder, "Configure output");
  }

  private Instances createDateDerivedPeriodicList() {
    ArrayList atts = new ArrayList();
    atts.add(new Attribute("AM"));
    atts.add(new Attribute("DayOfWeek"));
    atts.add(new Attribute("DayOfMonth"));
    atts.add(new Attribute("NumDaysInMonth"));
    atts.add(new Attribute("Weekend"));
    atts.add(new Attribute("Month"));
    atts.add(new Attribute("Quarter"));

    // Custom periodics
    for (String name : m_customPeriodics.keySet()) {
      atts.add(new Attribute("c_" + name));
    }

    Instances insts = new Instances("Periodics", atts, 1);

    return insts;
  }

  private String displayAddEditDialog(List testList,
    String fieldName) {
    CustomPeriodicEditor ed = new CustomPeriodicEditor(testList);
    if (fieldName != null && fieldName.length() > 0) {
      ed.setFieldName(fieldName);
    }
    int result =
      JOptionPane.showConfirmDialog(this, ed, "Add/Edit custom periodic field",
        JOptionPane.OK_CANCEL_OPTION);

    if (result == JOptionPane.OK_OPTION) {
      if (testList.size() == 0) {
        // Nothing added to the list, so just cancel
        return "";
      }
      return ed.getFieldName();
    } else {
      return ""; // indicates cancel
    }
  }

  /**
   * Layout the overlay panel
   */
  protected void layoutOverlayPanel() {
    JPanel basePanel = new JPanel();
    basePanel.setLayout(new BorderLayout());

    JPanel temp = new JPanel();
    temp.setLayout(new BorderLayout());
    // temp
    JPanel checkHolder = new JPanel();
    checkHolder.setLayout(new BorderLayout());
    checkHolder.add(m_useOverlayData, BorderLayout.NORTH);
    m_useOverlayData.setEnabled(false);

    temp.add(checkHolder, BorderLayout.EAST);
    temp.add(m_overlaySelector, BorderLayout.CENTER);
    m_overlaySelector
      .setPreferredScrollableViewportSize(new Dimension(250, 70));

    JPanel botP = new JPanel();
    botP.setLayout(new BorderLayout());
    botP.setBorder(BorderFactory.createTitledBorder("Overlay data selection"));
    botP.add(temp, BorderLayout.CENTER);

    m_useOverlayData.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        if (m_instances != null && m_useOverlayData.isSelected()) {
          if (m_overlaySelector.getSelectionModel() != null) {
            if (m_overlayHeader == null) {
              m_overlayHeader = createAvailableOverlayList();
            } else {
              Instances newI = createAvailableOverlayList();
              if (newI == null) {
                m_overlaySelector.clearTableModel();
                m_overlayHeader = null;
                return;
              } else if (!newI.equalHeaders(m_overlayHeader)) {
                m_overlayHeader = newI;
              }
            }
            m_overlaySelector.setInstances(m_overlayHeader);
          }
        } else {
          m_overlaySelector.clearTableModel();
        }
      }
    });

    basePanel.add(botP, BorderLayout.CENTER);

    m_configHolder.addTab("Overlay data", null, basePanel,
      "Specify attributes that are to be considered as \"overlay\" data");
  }

  protected void savePeriodicsToFile() throws IOException {
    if (m_fileChooser == null) {
      m_fileChooser =
        new JFileChooser(new File(System.getProperty("user.home")));
      m_fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
      FileFilter filter =
        new ExtensionFileFilter(".periodics", "Time series date-derived "
          + "periodic attribute definitions (*periodics)");
      m_fileChooser.addChoosableFileFilter(filter);
    }
    int result = m_fileChooser.showSaveDialog(this);
    if (result == JFileChooser.APPROVE_OPTION) {
      File saveFile = m_fileChooser.getSelectedFile();
      if (saveFile.toString().indexOf(".periodics") < 0) {
        saveFile = new File(saveFile.toString() + ".periodics");
      }

      PrintWriter br =
        new PrintWriter(new BufferedWriter(new FileWriter(saveFile)));

      br.print("time-series-periodics\n");

      int[] selected = m_dateDerivedPeriodicSelector.getSelectedAttributes();
      if (m_dateDerivedPeriodicsHeader != null) {
        for (int s : selected) {
          String name = m_dateDerivedPeriodicsHeader.attribute(s).name();
          if (s < NUM_PREDEFINED_PERIODICS) {
            br.print("*pre-defined*:" + name + "\n");
          } else {

            // remove the "c_"
            name = name.replaceFirst("c_", "");
            ArrayList selectedP =
              m_customPeriodics.get(name);

            br.print("*custom*:" + name + "\n");
            for (int j = 0; j < selectedP.size(); j++) {
              br.print(selectedP.get(j).toString() + "\n");
            }
          }
        }
      }
      br.flush();
      br.close();
    }
  }

  protected void loadPeriodicsFromFile() throws IOException {
    if (m_fileChooser == null) {
      m_fileChooser =
        new JFileChooser(new File(System.getProperty("user.home")));
      m_fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
      FileFilter filter =
        new ExtensionFileFilter(".periodics", "Time series date-derived "
          + "periodic attribute definitions (*periodics)");
      m_fileChooser.addChoosableFileFilter(filter);
    }

    int result = m_fileChooser.showOpenDialog(this);
    if (result == JFileChooser.APPROVE_OPTION) {
      File loadFile = m_fileChooser.getSelectedFile();

      BufferedReader br = new BufferedReader(new FileReader(loadFile));
      String identifierLine = br.readLine();
      boolean ok = false;
      if (identifierLine != null) {
        if (identifierLine.equalsIgnoreCase("time-series-periodics")) {
          ok = true;
        }
      }

      if (!ok) {
        JOptionPane.showConfirmDialog(this, "\"" + loadFile.toString()
          + "\" does not"
          + "\nappear to be a periodic attribute definition file",
          "Unrecognised file type", JOptionPane.ERROR_MESSAGE);
        return;
      }

      String line = null;
      boolean withinCustom = false;
      m_customPeriodics.clear();
      List predefined = new ArrayList();
      ArrayList testList = null;
      String currentCustomName = null;
      while ((line = br.readLine()) != null) {
        if (line.startsWith("*pre-defined*:")) {
          if (withinCustom) {
            // finish custom
            m_customPeriodics.put(currentCustomName, testList);
          }

          withinCustom = false;
          line = line.substring(line.indexOf(":") + 1, line.length()).trim();
          // process predefined type
          predefined.add(line);
        } else if (line.startsWith("*custom*:")) {
          if (withinCustom) {
            // finish custom
            m_customPeriodics.put(currentCustomName, testList);
          }

          // start new custom
          testList = new ArrayList();
          currentCustomName =
            line.substring(line.indexOf(":") + 1, line.length()).trim();
          withinCustom = true;

        } else if (withinCustom) {
          // process custom part
          try {
            CustomPeriodicTest t = new CustomPeriodicTest(line);
            testList.add(t);
          } catch (IllegalArgumentException ex) {
            ex.printStackTrace();
            JOptionPane.showConfirmDialog(this,
              "A problem occurred while parsing\n"
                + "the following custom periodic test:\n" + line,
              "Error parsing custom test", JOptionPane.ERROR_MESSAGE);
            return;
          }

        }
      }

      br.close();

      // check for last custom (if exists)
      if (testList != null && testList.size() > 0) {
        m_customPeriodics.put(currentCustomName, testList);
      }

      m_dateDerivedPeriodicSelector.clearTableModel();
      Instances insts = createDateDerivedPeriodicList();
      m_dateDerivedPeriodicSelector.setInstances(insts);
      m_dateDerivedPeriodicsHeader = insts;

      boolean[] selected = new boolean[insts.numAttributes()];
      for (int i = 0; i < insts.numAttributes(); i++) {
        if (i < NUM_PREDEFINED_PERIODICS) {
          switch (i) {
          case 0:
            if (predefined.contains("AM")) {
              selected[i] = true;
            }
            break;
          case 1:
            if (predefined.contains("DayOfWeek")) {
              selected[i] = true;
            }
            break;
          case 2:
            if (predefined.contains("DayOfMonth")) {
              selected[i] = true;
            }
            break;
          case 3:
            if (predefined.contains("NumDaysInMonth")) {
              selected[i] = true;
            }
            break;
          case 4:
            if (predefined.contains("Weekend")) {
              selected[i] = true;
            }
            break;
          case 5:
            if (predefined.contains("Month")) {
              selected[i] = true;
            }
            break;
          case 6:
            if (predefined.contains("Quarter")) {
              selected[i] = true;
            }
            break;
          }
        } else {
          selected[i] = true;
        }
      }
      try {
        m_dateDerivedPeriodicSelector.setSelectedAttributes(selected);
      } catch (Exception ex) {
        ex.printStackTrace();
      }
    }
  }

  protected void layoutDateDerivedPeriodicPanel() {
    JPanel basePanel = new JPanel();
    basePanel.setLayout(new BorderLayout());

    JPanel temp = new JPanel();
    temp.setLayout(new BorderLayout());
    // temp
    JPanel checkHolder = new JPanel();
    checkHolder.setLayout(new BorderLayout());
    checkHolder.add(m_customizeDateDerivedPeriodics, BorderLayout.EAST);
    JPanel editButsP = new JPanel();
    editButsP.setLayout(new GridLayout(1, 5));
    editButsP.add(m_addCustomPeriodicBut);
    editButsP.add(m_deleteCustomPeriodicBut);
    editButsP.add(m_editCustomPeriodicBut);
    editButsP.add(m_savePeriodicBut);
    editButsP.add(m_loadPeriodicBut);

    m_loadPeriodicBut.setEnabled(false);
    m_savePeriodicBut.setEnabled(false);
    m_savePeriodicBut.setToolTipText("Save checked date-derived "
      + "periodic definitions to a file");
    m_savePeriodicBut.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        try {
          savePeriodicsToFile();
        } catch (IOException ex) {
          ex.printStackTrace();
        }
      }
    });

    m_loadPeriodicBut.setToolTipText("Load pre-saved date-derived periodic "
      + "definitions from a file");
    m_loadPeriodicBut.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        try {
          loadPeriodicsFromFile();
        } catch (IOException ex) {
          ex.printStackTrace();
        }
      }
    });

    checkHolder.add(editButsP, BorderLayout.WEST);
    m_editCustomPeriodicBut.setEnabled(false);
    m_editCustomPeriodicBut.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        int selectedRow =
          m_dateDerivedPeriodicSelector.getTable().getSelectedRow();
        if (selectedRow > NUM_PREDEFINED_PERIODICS - 1) {
          String fieldName =
            m_dateDerivedPeriodicsHeader.attribute(selectedRow).name();

          // need to trim off the "c_"
          fieldName = fieldName.replaceFirst("c_", "");
          ArrayList toEdit =
            m_customPeriodics.get(fieldName);
          if (toEdit == null) {
            System.err.println("Oh oh, couldn't find " + fieldName
              + " to edit!");
          } else {
            // now make a copy of it
            try {
              SerializedObject so = new SerializedObject(toEdit);
              toEdit = (ArrayList) so.getObject();

              String newFieldName = displayAddEditDialog(toEdit, fieldName);
              if (newFieldName.length() == 0) {
                // cancel selected so do nothing
              } else if (!newFieldName.equals(fieldName)) {
                // user has changed the field name. Delete the old one first
                m_customPeriodics.remove(fieldName);
                m_customPeriodics.put(newFieldName, toEdit);

                // now have to refresh the list in order to show the new name
                // what's been selected so far?
                int[] selected =
                  m_dateDerivedPeriodicSelector.getSelectedAttributes();
                Instances insts = createDateDerivedPeriodicList();
                m_dateDerivedPeriodicSelector.setInstances(insts);
                m_dateDerivedPeriodicsHeader = insts;

                boolean[] newSelected = new boolean[insts.numAttributes()];
                for (int i = 0; i < selected.length; i++) {
                  newSelected[selected[i]] = true;
                }
                try {
                  m_dateDerivedPeriodicSelector
                    .setSelectedAttributes(newSelected);
                } catch (Exception ex) {
                  ex.printStackTrace();
                }
              } else {
                // name unchanged, so can just update the map
                m_customPeriodics.put(fieldName, toEdit);
              }
            } catch (Exception e1) {
              e1.printStackTrace();
            }
          }
        }
      }
    });

    m_deleteCustomPeriodicBut.setEnabled(false);
    m_deleteCustomPeriodicBut.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        int selectedRow =
          m_dateDerivedPeriodicSelector.getTable().getSelectedRow();
        if (selectedRow > NUM_PREDEFINED_PERIODICS - 1) {
          String fieldName =
            m_dateDerivedPeriodicsHeader.attribute(selectedRow).name();

          // need to trim off the "c_"
          fieldName = fieldName.replaceFirst("c_", "");

          // Confirm delete
          int result =
            JOptionPane.showConfirmDialog(AdvancedConfigPanel.this,
              "Delete field " + fieldName + "?",
              "Delete custom periodic field", JOptionPane.YES_NO_OPTION);
          if (result == JOptionPane.NO_OPTION) {
            return;
          }

          m_customPeriodics.remove(fieldName);

          // what's been selected so far?
          int[] selected =
            m_dateDerivedPeriodicSelector.getSelectedAttributes();
          Instances oldList = m_dateDerivedPeriodicsHeader;

          // now need to rebuild the list
          Instances insts = createDateDerivedPeriodicList();
          m_dateDerivedPeriodicSelector.setInstances(insts);
          m_dateDerivedPeriodicsHeader = insts;

          // make sure that selected stuff stays selected
          boolean[] newSelected = new boolean[insts.numAttributes()];
          for (int element : selected) {
            Attribute toFind = oldList.attribute(element);
            Attribute toSet = insts.attribute(toFind.name());
            if (toSet != null) {
              newSelected[toSet.index()] = true;
            }
          }
          try {
            m_dateDerivedPeriodicSelector.setSelectedAttributes(newSelected);
          } catch (Exception e1) {
            e1.printStackTrace();
          }
        }
      }
    });

    m_addCustomPeriodicBut.setEnabled(false);
    m_addCustomPeriodicBut.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        ArrayList testList =
          new ArrayList();
        String fieldName = displayAddEditDialog(testList, null);
        if (fieldName.length() == 0) {
          // basically do nothing
        } else {
          // Add this new one to the Map
          m_customPeriodics.put(fieldName, testList);

          // what's been selected so far?
          int[] selected =
            m_dateDerivedPeriodicSelector.getSelectedAttributes();
          Instances insts = createDateDerivedPeriodicList();
          m_dateDerivedPeriodicSelector.setInstances(insts);
          m_dateDerivedPeriodicsHeader = insts;

          boolean[] newSelected = new boolean[insts.numAttributes()];
          for (int i = 0; i < selected.length; i++) {
            newSelected[selected[i]] = true;
          }

          // select the newly created field
          newSelected[newSelected.length - 1] = true;
          try {
            m_dateDerivedPeriodicSelector.setSelectedAttributes(newSelected);
          } catch (Exception e1) {
            e1.printStackTrace();
          }
        }
      }
    });

    temp.add(checkHolder, BorderLayout.NORTH);
    temp.add(m_dateDerivedPeriodicSelector, BorderLayout.CENTER);
    m_dateDerivedPeriodicSelector
      .setPreferredScrollableViewportSize(new Dimension(250, 80));
    JPanel botP = new JPanel();
    botP.setLayout(new BorderLayout());
    botP.setBorder(BorderFactory
      .createTitledBorder("Date-derived periodic creation"));
    botP.add(temp, BorderLayout.CENTER);

    JPanel primaryPeriodicP = new JPanel();
    primaryPeriodicP.setLayout(new BorderLayout());
    // primaryPeriodicP

    primaryPeriodicP.add(m_primaryPeriodicCombo, BorderLayout.NORTH);
    JPanel topP = new JPanel();
    topP.setLayout(new BorderLayout());
    topP.setBorder(BorderFactory.createTitledBorder("Periodic attribute"));
    topP.add(primaryPeriodicP, BorderLayout.EAST);

    JPanel temp2 = new JPanel();
    temp2.setLayout(new BorderLayout());
    temp2.add(topP, BorderLayout.EAST);
    temp2.add(botP, BorderLayout.CENTER);
    // basePanel.add(primaryPeriodicP, BorderLayout.EAST);
    basePanel.add(temp2, BorderLayout.CENTER);

    m_customizeDateDerivedPeriodics.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        if (m_instances != null && m_customizeDateDerivedPeriodics.isSelected()) {
          String selectedTimeStamp =
            m_simpleConfig.m_timeStampCombo.getSelectedItem().toString();
          Attribute timeStampAtt = m_instances.attribute(selectedTimeStamp);

          if (timeStampAtt != null && timeStampAtt.isDate()) {
            if (m_dateDerivedPeriodicSelector.getSelectionModel() != null) {
              Instances insts = createDateDerivedPeriodicList();

              m_dateDerivedPeriodicSelector.setInstances(insts);
              m_dateDerivedPeriodicsHeader = insts;
              m_addCustomPeriodicBut.setEnabled(true);
              m_savePeriodicBut.setEnabled(true);
              m_loadPeriodicBut.setEnabled(true);

              m_dateDerivedPeriodicSelector.getSelectionModel()
                .addListSelectionListener(new ListSelectionListener() {
                  @Override
                  public void valueChanged(ListSelectionEvent e) {
                    if (!e.getValueIsAdjusting()) {
                      if (m_dateDerivedPeriodicSelector.getTable()
                        .getSelectedRow() > NUM_PREDEFINED_PERIODICS - 1) {
                        m_editCustomPeriodicBut.setEnabled(true);
                        m_deleteCustomPeriodicBut.setEnabled(true);
                      } else {
                        m_editCustomPeriodicBut.setEnabled(false);
                        m_deleteCustomPeriodicBut.setEnabled(false);
                      }
                    }
                  }
                });
            }
          }
        } else {
          m_addCustomPeriodicBut.setEnabled(false);
          m_loadPeriodicBut.setEnabled(false);
          m_savePeriodicBut.setEnabled(false);
        }
      }
    });

    updatePrimaryPeriodic();

    // holder.add(basePanel, BorderLayout.WEST);
    m_configHolder.addTab("Periodic attributes", null, basePanel,
      "Specify/clustomize periodic attributes");
    updateDateDerivedPanel();
  }

  /**
   * Layout the learner panel
   */
  protected void layoutLearnerPanel() {
    JPanel basePanel = new JPanel();
    basePanel.setLayout(new BorderLayout());
    basePanel.setBorder(BorderFactory
      .createTitledBorder("Base learner configuration"));

    m_baseLearnerEditor.setClassType(Classifier.class);
    // m_baseLearnerEditor.setValue(new weka.classifiers.functions.SMOreg());
    m_baseLearnerEditor
      .setValue(new weka.classifiers.functions.LinearRegression());

    Capabilities capabilities = new Capabilities(null);
    capabilities.disableAll();
    capabilities.enable(Capability.NOMINAL_ATTRIBUTES);
    capabilities.enable(Capability.NUMERIC_ATTRIBUTES);
    capabilities.enable(Capability.NUMERIC_CLASS);
    capabilities.enableAllAttributeDependencies();
    capabilities.enableAllClassDependencies();
    m_baseLearnerEditor.setCapabilitiesFilter(capabilities);

    basePanel.add(m_baseLearnerPanel, BorderLayout.NORTH);
    // add(basePanel, BorderLayout.NORTH);
    m_configHolder.addTab("Base learner", null, basePanel,
      "Base learner configuration");
  }

  /**
   * Layout the lag panel
   */
  protected void layoutLagPanel() {
    // lag stuff
    JPanel lagPanel = new JPanel();
    lagPanel.setLayout(new GridLayout(1, 2));
    // lagPanel.setBorder(BorderFactory.createTitledBorder("Lag creation"));

    SpinnerNumberModel snm = new SpinnerNumberModel();
    snm.setValue(1);
    snm.setMinimum(1);
    m_minLagSpinner = new JSpinner(snm);
    Dimension spinD = m_minLagSpinner.getPreferredSize();
    spinD =
      new Dimension((int) (spinD.getWidth() * 1.5), (int) spinD.getHeight());
    m_minLagSpinner.setPreferredSize(spinD);

    JPanel temp1 = new JPanel();
    temp1.setBorder(BorderFactory.createEmptyBorder(0, 0, 1, 5));
    temp1.setLayout(new BorderLayout());
    final JLabel minLagLab = new JLabel("Minimum lag", JLabel.RIGHT);
    temp1.add(minLagLab, BorderLayout.CENTER);
    temp1.add(m_minLagSpinner, BorderLayout.EAST);
    JPanel spinnerHolder = new JPanel();
    spinnerHolder.setLayout(new BorderLayout());
    spinnerHolder.setBorder(BorderFactory.createTitledBorder("Lag length"));

    JPanel minMaxHolder = new JPanel();
    minMaxHolder.setLayout(new BorderLayout());
    minMaxHolder.add(temp1, BorderLayout.CENTER);

    m_powersOfTime.setSelected(true);
    m_timeLagProducts.setSelected(true);

    /*
     * JPanel missingLagHolder = new JPanel(); missingLagHolder.setLayout(new
     * BorderLayout());
     * missingLagHolder.add(m_removeLeadingInstancesWithMissingLags,
     * BorderLayout.EAST);
     */
    m_removeLeadingInstancesWithMissingLags
      .setToolTipText("Remove initial instances "
        + "where the values of lagged variables are unknown (missing)");
    /*
     * JPanel missingAndVar = new JPanel(); missingAndVar.setLayout(new
     * BorderLayout()); missingAndVar.add(missingLagHolder, BorderLayout.NORTH);
     */

    /*
     * JPanel vH = new JPanel(); vH.setLayout(new BorderLayout());
     * 
     * JPanel varianceHolder = new JPanel(); varianceHolder.setLayout(new
     * BorderLayout()); varianceHolder.add(m_adjustForVarianceCheckBox,
     * BorderLayout.EAST);
     */

    m_adjustForVarianceCheckBox.setSelected(false);
    // vH.add(varianceHolder, BorderLayout.NORTH);

    JPanel checkHolder = new JPanel();
    checkHolder.setLayout(new BorderLayout());
    checkHolder.add(m_useCustomLags, BorderLayout.EAST);
    minMaxHolder.add(checkHolder, BorderLayout.NORTH);

    // vH.add(checkHolder, BorderLayout.SOUTH);
    // missingAndVar.add(vH, BorderLayout.SOUTH);

    // spinnerHolder.add(missingAndVar, BorderLayout.NORTH);
    // spinnerHolder.add(minMaxHolder, BorderLayout.CENTER);
    spinnerHolder.add(minMaxHolder, BorderLayout.NORTH);
    JPanel moreOptsHolder = new JPanel();
    moreOptsHolder.setLayout( new BorderLayout(  ) );
    moreOptsHolder.add(m_moreOptsBut, BorderLayout.EAST);
    spinnerHolder.add(moreOptsHolder, BorderLayout.SOUTH);

    m_moreOptsBut.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        m_moreOptsBut.setEnabled(false);
        JPanel moreOptsPanel = new JPanel();
        moreOptsPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
        moreOptsPanel.setLayout(new GridLayout(0, 1));
        moreOptsPanel.add(m_removeLeadingInstancesWithMissingLags);
        moreOptsPanel.add(m_powersOfTime);
        moreOptsPanel.add(m_timeLagProducts);
        moreOptsPanel.add(m_adjustForVarianceCheckBox);

        JPanel butHolder = new JPanel();
        butHolder.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        JButton oK = new JButton("OK");
        butHolder.add(oK, BorderLayout.CENTER);

        JPanel allP = new JPanel();
        allP.setLayout(new BorderLayout());
        allP.add(moreOptsPanel, BorderLayout.CENTER);
        allP.add(butHolder, BorderLayout.SOUTH);

        final JDialog jd =
          new JDialog(PropertyDialog.getParentFrame(AdvancedConfigPanel.this),
            "More options");
        jd.getContentPane().setLayout(new BorderLayout());
        jd.getContentPane().add(allP, BorderLayout.CENTER);
        jd.addWindowListener(new java.awt.event.WindowAdapter() {
          @Override
          public void windowClosing(java.awt.event.WindowEvent w) {
            jd.dispose();
            m_moreOptsBut.setEnabled(true);
          }
        });

        oK.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent a) {
            m_moreOptsBut.setEnabled(true);
            jd.dispose();
          }
        });
        jd.pack();
        jd.setLocation(m_moreOptsBut.getLocationOnScreen());
        jd.setVisible(true);
      }
    });

    snm = new SpinnerNumberModel();
    snm.setValue(12);
    snm.setMinimum(1);
    m_maxLagSpinner = new JSpinner(snm);
    m_maxLagSpinner.setPreferredSize(spinD);
    JPanel temp2 = new JPanel();
    temp2.setLayout(new BorderLayout());
    temp2.setBorder(BorderFactory.createEmptyBorder(1, 0, 1, 0));
    final JLabel maxLagLab = new JLabel("Maximum lag", JLabel.RIGHT);
    temp2.add(maxLagLab, BorderLayout.CENTER);
    temp2.add(m_maxLagSpinner, BorderLayout.EAST);
    minMaxHolder.add(temp2, BorderLayout.EAST);

    final JLabel fineTuneLab =
      new JLabel("Fine tune lag selection", JLabel.RIGHT);
    JPanel fPanel = new JPanel();
    fPanel.setLayout(new BorderLayout());
    // fPanel.add(temp2, BorderLayout.NORTH); //--

    JPanel fineHolder = new JPanel();
    fineHolder.setLayout(new BorderLayout());
    fineHolder.setBorder(BorderFactory.createEmptyBorder(1, 0, 0, 0));
    fineHolder.add(fineTuneLab, BorderLayout.CENTER);
    fineHolder.add(m_fineTuneLagsField, BorderLayout.EAST);
    m_fineTuneLagsField.setPreferredSize(spinD);
    fineTuneLab.setEnabled(false);
    fineTuneLab.setToolTipText("Specify ranges to fine tune "
      + "lags within minimum and maximum (e.g. 2,3,6-8)");
    m_fineTuneLagsField.setEnabled(false);
    m_fineTuneLagsField.setToolTipText("Specify ranges to fine tune "
      + "lags within minimum and maximum (e.g. 2,3,6-8)");

    fPanel.add(fineHolder, BorderLayout.SOUTH);

    // spinnerHolder.add(fPanel, BorderLayout.SOUTH);
    spinnerHolder.add(fPanel, BorderLayout.CENTER);

    m_useCustomLags.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        boolean enabled = m_useCustomLags.isSelected();
        m_minLagSpinner.setEnabled(enabled);
        m_maxLagSpinner.setEnabled(enabled);
        minLagLab.setEnabled(enabled);
        maxLagLab.setEnabled(enabled);
        fineTuneLab.setEnabled(enabled);
        m_fineTuneLagsField.setEnabled(enabled);

        m_averageLongLags.setEnabled(enabled);
        if (!enabled) {
          m_averageLongLags.setSelected(false);
        }
        /*
         * m_averageLagsAfter.setEnabled(enabled);
         * m_numConsecutiveToAverage.setEnabled(enabled);
         */
      }
    });

    snm = new SpinnerNumberModel();
    snm.setValue(2);
    snm.setMinimum(1);
    m_averageLagsAfter = new JSpinner(snm);
    m_averageLagsAfter.setPreferredSize(spinD);

    snm = new SpinnerNumberModel();
    snm.setValue(2);
    snm.setMinimum(2);
    m_numConsecutiveToAverage = new JSpinner(snm);
    m_numConsecutiveToAverage.setPreferredSize(spinD);

    JPanel averageLagHolder = new JPanel();
    averageLagHolder.setBorder(BorderFactory.createTitledBorder("Averaging"));
    averageLagHolder.setLayout(new BorderLayout());
    JPanel avCheckHolder = new JPanel();
    avCheckHolder.setLayout(new BorderLayout());
    avCheckHolder.add(m_averageLongLags, BorderLayout.EAST);
    averageLagHolder.add(avCheckHolder, BorderLayout.NORTH);
    JPanel temp3 = new JPanel();
    temp3.setLayout(new BorderLayout());
    temp3.setBorder(BorderFactory.createEmptyBorder(0, 0, 1, 0));
    final JLabel avLab = new JLabel("Average lags longer than", JLabel.RIGHT);
    temp3.add(avLab, BorderLayout.CENTER);
    temp3.add(m_averageLagsAfter, BorderLayout.EAST);

    JPanel aH = new JPanel();
    aH.setLayout(new BorderLayout());
    aH.add(temp3, BorderLayout.NORTH);

    // averageLagHolder.add(temp3, BorderLayout.CENTER);
    JPanel temp4 = new JPanel();
    temp4.setLayout(new BorderLayout());
    temp4.setBorder(BorderFactory.createEmptyBorder(1, 0, 0, 0));
    final JLabel numConsLab =
      new JLabel("# consecutive lags to average", JLabel.RIGHT);
    temp4.add(numConsLab, BorderLayout.CENTER);
    temp4.add(m_numConsecutiveToAverage, BorderLayout.EAST);
    aH.add(temp4, BorderLayout.SOUTH);
    averageLagHolder.add(aH, BorderLayout.SOUTH);

    m_useCustomLags.setSelected(false);
    m_minLagSpinner.setEnabled(false);
    m_maxLagSpinner.setEnabled(false);
    minLagLab.setEnabled(false);
    maxLagLab.setEnabled(false);
    m_averageLongLags.setEnabled(false);
    avLab.setEnabled(false);
    m_averageLagsAfter.setEnabled(false);
    m_numConsecutiveToAverage.setEnabled(false);
    numConsLab.setEnabled(false);

    m_averageLongLags.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        if (m_useCustomLags.isSelected()) {
          boolean enabled = m_averageLongLags.isSelected();
          m_averageLagsAfter.setEnabled(enabled);
          m_numConsecutiveToAverage.setEnabled(enabled);
          avLab.setEnabled(enabled);
          numConsLab.setEnabled(enabled);
        }
      }
    });

    lagPanel.add(spinnerHolder);// , BorderLayout.CENTER);
    lagPanel.add(averageLagHolder);// , BorderLayout.EAST);

    JPanel tempP = new JPanel();
    tempP.setLayout(new BorderLayout());
    tempP.add(lagPanel, BorderLayout.NORTH);

    // add(tempP, BorderLayout.CENTER);
    m_configHolder.addTab("Lag creation", null, tempP,
      "Customize lagged variables");
  }

  /**
   * Apply the configuration defined in this panel to the supplied forecaster
   * 
   * @param forecaster the forecaster to apply the configuration to
   * @throws Exception if a problem occurs
   */
  public void applyToForecaster(WekaForecaster forecaster) throws Exception {
    if (forecaster != null) {
      TSLagMaker lagMaker = forecaster.getTSLagMaker();

      lagMaker
        .setRemoveLeadingInstancesWithUnknownLagValues(m_removeLeadingInstancesWithMissingLags
          .isSelected());
      lagMaker.setIncludePowersOfTime(m_powersOfTime.isSelected());
      lagMaker.setIncludeTimeLagProducts(m_timeLagProducts.isSelected());

      // Set variance adjustment
      lagMaker.setAdjustForVariance(m_adjustForVarianceCheckBox.isSelected());

      int minLag =
        ((SpinnerNumberModel) m_minLagSpinner.getModel()).getNumber()
          .intValue();
      int maxLag =
        ((SpinnerNumberModel) m_maxLagSpinner.getModel()).getNumber()
          .intValue();

      if (forecaster.getBaseForecaster() instanceof PrimingDataLearner) {
        // Make sure that the maximum lag length is at least equal to the
        // minimum number of training/priming instances that this base
        // forecaster
        // requires
        if (maxLag < ((PrimingDataLearner) forecaster.getBaseForecaster())
          .getMinRequiredTrainingPoints()) {
          maxLag =
            ((PrimingDataLearner) forecaster.getBaseForecaster())
              .getMinRequiredTrainingPoints();
          m_maxLagSpinner.setValue(maxLag);
          lagMaker.setMaxLag(maxLag);
        }
      }

      if (m_useCustomLags.isSelected()) {

        // set lag lengths from spinners
        if (maxLag < minLag) {
          throw new Exception(
            "Maximum lag value must be greater than or equal to"
              + " the minimum lag value");
        }

        if (m_instances != null && maxLag > m_instances.numInstances()) {
          throw new Exception(
            "The maximum lag can't exceed the number instances ("
              + m_instances.numInstances() + ") in the data!");
        }

        lagMaker.setMinLag(minLag);
        lagMaker.setMaxLag(maxLag);

        if (m_fineTuneLagsField.getText() != null
          && m_fineTuneLagsField.getText().length() > 0) {
          lagMaker.setLagRange(m_fineTuneLagsField.getText());
        } else {
          lagMaker.setLagRange("");
        }

        // set up averaging from spinners
        if (m_averageLongLags.isSelected()) {
          lagMaker.setAverageConsecutiveLongLags(true);

          int avLagsAfter =
            ((SpinnerNumberModel) m_averageLagsAfter.getModel()).getNumber()
              .intValue();
          int numToAv =
            ((SpinnerNumberModel) m_numConsecutiveToAverage.getModel())
              .getNumber().intValue();

          if (avLagsAfter < minLag || avLagsAfter > maxLag) {
            throw new Exception("Point at which to start lag averaging must "
              + "lie between the minimum and maximum lag value.");
          }

          lagMaker.setAverageLagsAfter(avLagsAfter);
          lagMaker.setNumConsecutiveLongLagsToAverage(numToAv);
        } else {
          lagMaker.setAverageConsecutiveLongLags(false);
        }
      } else {
        lagMaker.setAverageConsecutiveLongLags(false);
        lagMaker.setLagRange("");
      }

      // date-derived periodic customization
      if (getCustomizeDateDerivedPeriodics()) {
        // reset all to false
        lagMaker.setAddAMIndicator(false);
        lagMaker.setAddDayOfWeek(false);
        lagMaker.setAddMonthOfYear(false);
        lagMaker.setAddQuarterOfYear(false);
        lagMaker.setAddWeekendIndicator(false);
        lagMaker.setAddDayOfMonth(false);
        lagMaker.setAddNumDaysInMonth(false);

        int[] selected = m_dateDerivedPeriodicSelector.getSelectedAttributes();
        if (m_dateDerivedPeriodicsHeader != null) {
          Map> custom =
            new HashMap>();
          for (int s : selected) {
            String name = m_dateDerivedPeriodicsHeader.attribute(s).name();
            if (s < NUM_PREDEFINED_PERIODICS) {
              if (name.equals("AM")) {
                lagMaker.setAddAMIndicator(true);
              }
              if (name.equals("DayOfWeek")) {
                lagMaker.setAddDayOfWeek(true);
              }
              if (name.equals("DayOfMonth")) {
                lagMaker.setAddDayOfMonth(true);
              }
              if (name.equals("NumDaysInMonth")) {
                lagMaker.setAddNumDaysInMonth(true);
              }
              if (name.equals("Weekend")) {
                lagMaker.setAddWeekendIndicator(true);
              }
              if (name.equals("Month")) {
                lagMaker.setAddMonthOfYear(true);
              }
              if (name.equals("Quarter")) {
                lagMaker.setAddQuarterOfYear(true);
              }
            } else {
              // remove the "c_"
              name = name.replaceFirst("c_", "");
              ArrayList selectedP =
                m_customPeriodics.get(name);
              custom.put(name, selectedP);

            }
          }
          lagMaker.clearCustomPeriodics();
          if (custom.size() > 0) {
            lagMaker.setCustomPeriodics(custom);
          }
        }
      }

      // non-date primary periodic attribute
      String ppfn = m_primaryPeriodicCombo.getSelectedItem().toString();
      if (!ppfn.equals("")) {
        lagMaker.setPrimaryPeriodicFieldName(ppfn);
      } else {
        lagMaker.setPrimaryPeriodicFieldName("");
      }

      if (m_useOverlayData.isSelected() && m_overlayHeader != null
        && forecaster instanceof OverlayForecaster) {
        int[] selected = m_overlaySelector.getSelectedAttributes();
        String overlayList = "";
        for (int element : selected) {
          overlayList += m_overlayHeader.attribute(element).name() + ",";
        }

        if (overlayList.length() > 0) {
          overlayList = overlayList.substring(0, overlayList.lastIndexOf(','));
          ((OverlayForecaster) forecaster).setOverlayFields(overlayList);
        } else {
          ((OverlayForecaster) forecaster).setOverlayFields(null);
        }
      } else {
        if (forecaster instanceof OverlayForecaster) {
          ((OverlayForecaster) forecaster).setOverlayFields(null);
        }
      }
    }
  }

  /**
   * Apply the configuration defined in this panel to the supplied evaluation
   * object
   * 
   * @param eval the evaluation object to apply the configuration to
   * @param forecaster the forecaster in use
   * @throws Exception if a problem occurs
   */
  public void applyToEvaluation(TSEvaluation eval, WekaForecaster forecaster)
    throws Exception {

    // eval on training
    eval.setEvaluateOnTrainingData(m_trainingCheckBox.isSelected());

    if (!eval.getEvaluateOnTrainingData() && !eval.getEvaluateOnTestData()) {
      // throw new Exception("Must evaluate")
    }

    // evaluation modules
    int[] selected = m_evaluationMetrics.getSelectedAttributes();

    if (selected.length == 0) {
      throw new Exception("Must select at least one evaluation metric.");
    }

    List modsList = TSEvalModule.getModuleList();
    String modListS = "";
    for (int s : selected) {
      String name = m_evaluationModsHeader.attribute(s).name();
      for (TSEvalModule mod : modsList) {
        if (name.equals(mod.toString())) {
          modListS += mod.getEvalName() + ",";
        }
      }
    }

    modListS = modListS.substring(0, modListS.lastIndexOf(','));
    eval.setEvaluationModules(modListS);

    // TODO separate test set
  }

  /**
   * Inner class extending AttributeSelectionPanel that adds methods to get the
   * encapsulated JTable and clear the current table model.
   */
  protected class AttributeSelectionPanelExtended extends
    AttributeSelectionPanel {

    /** For serialization */
    private static final long serialVersionUID = -6863797282164060690L;

    public AttributeSelectionPanelExtended(boolean include, boolean remove,
      boolean invert, boolean pattern) {
      super(include, remove, invert, pattern);
    }

    public JTable getTable() {
      return m_Table;
    }

    public void clearTableModel() {
      m_Model = null;
      m_Table.setModel(new DefaultTableModel());
      m_Table.revalidate();
      m_Table.repaint();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy