Java tutorial
/* * 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 <http://www.gnu.org/licenses/>. */ /* * WekaEvaluationValues.java * Copyright (C) 2009-2017 University of Waikato, Hamilton, New Zealand */ package adams.flow.transformer; import adams.core.QuickInfoHelper; import adams.data.spreadsheet.DefaultSpreadSheet; import adams.data.spreadsheet.Row; import adams.data.spreadsheet.SpreadSheet; import adams.data.weka.WekaLabelRange; import adams.flow.container.WekaEvaluationContainer; import adams.flow.core.EvaluationHelper; import adams.flow.core.EvaluationStatistic; import adams.flow.core.Token; import weka.classifiers.Evaluation; /** <!-- globalinfo-start --> * Generates a spreadsheet from statistics of an Evaluation object. * <br><br> <!-- globalinfo-end --> * <!-- flow-summary-start --> * Input/output:<br> * - accepts:<br> * weka.classifiers.Evaluation<br> * adams.flow.container.WekaEvaluationContainer<br> * - generates:<br> * adams.data.spreadsheet.SpreadSheet<br> * <br><br> * Container information:<br> * - adams.flow.container.WekaEvaluationContainer: Evaluation, Model * <br><br> <!-- flow-summary-end --> * <!-- options-start --> * Valid options are: <br><br> * * <pre>-D <int> (property: debugLevel) * The greater the number the more additional info the scheme may output to * the console (0 = off). * default: 0 * minimum: 0 * </pre> * * <pre>-name <java.lang.String> (property: name) * The name of the actor. * default: WekaEvaluationValues * </pre> * * <pre>-annotation <adams.core.base.BaseText> (property: annotations) * The annotations to attach to this actor. * default: * </pre> * * <pre>-skip (property: skip) * If set to true, transformation is skipped and the input token is just forwarded * as it is. * </pre> * * <pre>-stop-flow-on-error (property: stopFlowOnError) * If set to true, the flow gets stopped in case this actor encounters an error; * useful for critical actors. * </pre> * * <pre>-statistic <Number correct (nominal)|Number incorrect (nominal)|Number unclassified (nominal)|Percent correct (nominal)|Percent incorrect (nominal)|Percent unclassified (nominal)|Kappa statistic (nominal)|Mean absolute error|Root mean squared error|Relative absolute error|Root relative squared error|Correlation coefficient (numeric)|SF prior entropy|SF scheme entropy|SF entropy gain|SF mean prior entropy|SF mean scheme entropy|SF mean entropy gain|KB information (nominal)|KB mean information (nominal)|KB relative information (nominal)|True positive rate (nominal)|Num true positives (nominal)|False positive rate (nominal)|Num false positives (nominal)|True negative rate (nominal)|Num true negatives (nominal)|False negative rate (nominal)|Num false negatives (nominal)|IR precision (nominal)|IR recall (nominal)|F measure (nominal)|Matthews correlation coefficient (nominal)|Area under ROC (nominal)|Area under PRC (nominal)|Weighted true positive rate (nominal)|Weighted false positive rate (nominal)|Weighted true negative rate (nominal)|Weighted false negative rate (nominal)|Weighted IR precision (nominal)|Weighted IR recall (nominal)|Weighted F measure (nominal)|Weighted Matthews correlation coefficient (nominal)|Weighted area under ROC (nominal)|Weighted area under PRC (nominal)> [-statistic ...] (property: statisticValues) * The evaluation values to extract and turn into a spreadsheet. * default: PERCENT_CORRECT, ROOT_MEAN_SQUARED_ERROR, ROOT_RELATIVE_SQUARED_ERROR * </pre> * * <pre>-index <adams.core.Range> (property: classIndex) * The range of class label indices (eg used for AUC); A range is a comma-separated * list of single 1-based indices or sub-ranges of indices ('start-end'); ' * inv(...)' inverts the range '...'; the following placeholders can be used * as well: first, second, third, last_2, last_1, last * default: first * </pre> * <!-- options-end --> * * @author fracpete (fracpete at waikato dot ac dot nz) * @version $Revision$ */ public class WekaEvaluationValues extends AbstractTransformer { /** for serialization. */ private static final long serialVersionUID = -1977976026411517458L; /** the comparison fields. */ protected EvaluationStatistic[] m_StatisticValues; /** the range of the class labels. */ protected WekaLabelRange m_ClassIndex; /** * Returns a string describing the object. * * @return a description suitable for displaying in the gui */ @Override public String globalInfo() { return "Generates a spreadsheet from statistics of an Evaluation object."; } /** * Adds options to the internal list of options. */ @Override public void defineOptions() { super.defineOptions(); m_OptionManager.add("statistic", "statisticValues", new EvaluationStatistic[] { EvaluationStatistic.PERCENT_CORRECT, EvaluationStatistic.ROOT_MEAN_SQUARED_ERROR, EvaluationStatistic.ROOT_RELATIVE_SQUARED_ERROR }); m_OptionManager.add("index", "classIndex", new WekaLabelRange(WekaLabelRange.FIRST)); } /** * Sets the values to extract. * * @param value the value */ public void setStatisticValues(EvaluationStatistic[] value) { m_StatisticValues = value; reset(); } /** * Returns the values to extract. * * @return the value */ public EvaluationStatistic[] getStatisticValues() { return m_StatisticValues; } /** * Returns the tip text for this property. * * @return tip text for this property suitable for * displaying in the GUI or for listing the options. */ public String statisticValuesTipText() { return "The evaluation values to extract and turn into a spreadsheet."; } /** * Sets the range of class labels indices (1-based). * * @param value the label indices */ public void setClassIndex(WekaLabelRange value) { m_ClassIndex = value; reset(); } /** * Returns the current range of class label indices (1-based). * * @return the label indices */ public WekaLabelRange getClassIndex() { return m_ClassIndex; } /** * Returns the tip text for this property. * * @return tip text for this property suitable for * displaying in the GUI or for listing the options. */ public String classIndexTipText() { return "The range of class label indices (eg used for AUC)."; } /** * Returns a quick info about the actor, which will be displayed in the GUI. * * @return null if no info available, otherwise short string */ @Override public String getQuickInfo() { String result; result = QuickInfoHelper.toString(this, "classIndex", m_ClassIndex, "Labels: "); result += QuickInfoHelper.toString(this, "statisticValues", m_StatisticValues.length + " value" + (m_StatisticValues.length != 1 ? "s" : ""), ", "); return result; } /** * Returns the class that the consumer accepts. * * @return <!-- flow-accepts-start -->weka.classifiers.Evaluation.class, adams.flow.container.WekaEvaluationContainer.class<!-- flow-accepts-end --> */ @Override public Class[] accepts() { return new Class[] { Evaluation.class, WekaEvaluationContainer.class }; } /** * Adds the specified statistic to the spreadsheet. * * @param eval the {@link Evaluation} object to get the statist from * @param sheet the sheet to add the data to * @param statistic the statistic to add * @param classIndex the class index to use (for class-specific stats) * @param useIndex whether to use the index in the "Statistic" column * @return null if successfully added, otherwise error message */ protected String addStatistic(Evaluation eval, SpreadSheet sheet, EvaluationStatistic statistic, int classIndex, boolean useIndex) { String result; Row row; double value; String name; result = null; try { value = EvaluationHelper.getValue(eval, statistic, classIndex); row = sheet.addRow(); name = statistic.toDisplayShort(); if (useIndex && statistic.isPerClass()) name += " (" + eval.getHeader().classAttribute().value(classIndex) + ")"; row.addCell(0).setContent(name); row.addCell(1).setContent(Double.toString(value)); } catch (Exception e) { result = handleException("Error retrieving value for '" + statistic + "':\n", e); } return result; } /** * Executes the flow item. * * @return null if everything is fine, otherwise error message */ @Override protected String doExecute() { String result; Evaluation eval; SpreadSheet sheet; int[] indices; String msg; result = null; // fill spreadsheet if (m_InputToken.getPayload() instanceof WekaEvaluationContainer) eval = (Evaluation) ((WekaEvaluationContainer) m_InputToken.getPayload()) .getValue(WekaEvaluationContainer.VALUE_EVALUATION); else eval = (Evaluation) m_InputToken.getPayload(); m_ClassIndex.setData(eval.getHeader().classAttribute()); indices = m_ClassIndex.getIntIndices(); sheet = new DefaultSpreadSheet(); sheet.getHeaderRow().addCell("0").setContent("Statistic"); sheet.getHeaderRow().addCell("1").setContent("Value"); if (indices.length <= 1) { for (EvaluationStatistic statistic : m_StatisticValues) { msg = addStatistic(eval, sheet, statistic, (indices.length == 0 ? 0 : indices[0]), true); if (msg != null) { if (result == null) result = ""; else result += "\n"; result += msg; } } } else if (indices.length > 1) { // not class-specific stats for (EvaluationStatistic statistic : m_StatisticValues) { if (statistic.isPerClass()) continue; msg = addStatistic(eval, sheet, statistic, 0, false); // class index is irrelevant if (msg != null) { if (result == null) result = ""; else result += "\n"; result += msg; } } // class-specific stats for (int index : indices) { for (EvaluationStatistic statistic : m_StatisticValues) { if (!statistic.isPerClass()) continue; msg = addStatistic(eval, sheet, statistic, index, true); if (msg != null) { if (result == null) result = ""; else result += "\n"; result += msg; } } } } // generate output token m_OutputToken = new Token(sheet); return result; } /** * Returns the class of objects that it generates. * * @return <!-- flow-generates-start -->adams.data.spreadsheet.SpreadSheet.class<!-- flow-generates-end --> */ @Override public Class[] generates() { return new Class[] { SpreadSheet.class }; } }