/* * 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/>. */ /* * NormDiscrete.java * Copyright (C) 2008-2012 University of Waikato, Hamilton, New Zealand * */ package weka.core.pmml; import java.util.ArrayList; import org.w3c.dom.Element; import weka.core.Attribute; import weka.core.Utils; /** * Class encapsulating a NormDiscrete Expression. Creates an * indicator for a particular discrete value. * * @author Mark Hall (mhall{[at]}pentaho{[dot]}com) * @version $Revision 1.0 $ */ public class NormDiscrete extends Expression { /** * For serialization */ private static final long serialVersionUID = -8854409417983908220L; /** The name of the field to lookup our value in */ protected String m_fieldName; /** The actual attribute itself */ protected Attribute m_field; /** The index of the attribute */ protected int m_fieldIndex = -1; /** The actual value (as a String) that will correspond to an output of 1 */ protected String m_fieldValue; /** True if a replacement for missing values has been specified */ protected boolean m_mapMissingDefined = false; /** The value of the missing value replacement (if defined) */ protected double m_mapMissingTo; /** * If we are referring to a nominal (rather than String) attribute * then this holds the index of the value in question. Will be faster * than searching for the value each time. */ protected int m_fieldValueIndex = -1; /** * Constructor. Reads the field name and field value for this NormDiscrete * Expression. * * @param normDisc the Element encapsulating this NormDiscrete * @param opType the optype for this expression (taken from either the * enclosing DefineFunction or DerivedField) * @param fieldDefs an ArrayList of Attributes for the fields that this * Expression might need to access * enclosing DefineFunction or DerivedField) * @throws Exception if there is a problem parsing this Apply Expression */ public NormDiscrete(Element normDisc, FieldMetaInfo.Optype opType, ArrayList<Attribute> fieldDefs) throws Exception { super(opType, fieldDefs); if (opType != FieldMetaInfo.Optype.CONTINUOUS) { throw new Exception("[NormDiscrete] can only have a continuous optype"); } m_fieldName = normDisc.getAttribute("field"); m_fieldValue = normDisc.getAttribute("value"); String mapMissing = normDisc.getAttribute("mapMissingTo"); if (mapMissing != null && mapMissing.length() > 0) { m_mapMissingTo = Double.parseDouble(mapMissing); m_mapMissingDefined = true; } if (fieldDefs != null) { setUpField(); } } /** * Set the field definitions for this Expression to use * * @param fieldDefs the field definitions to use * @throws Exception if there is a problem setting the field definitions */ public void setFieldDefs(ArrayList<Attribute> fieldDefs) throws Exception { super.setFieldDefs(fieldDefs); setUpField(); } /** * Find the named field, set up the index(es) etc. * * @throws Exception if a problem occurs. */ private void setUpField() throws Exception { m_fieldIndex = -1; m_fieldValueIndex = -1; m_field = null; if (m_fieldDefs != null) { m_fieldIndex = getFieldDefIndex(m_fieldName); if (m_fieldIndex < 0) { throw new Exception("[NormDiscrete] Can't find field " + m_fieldName + " in the supplied field definitions."); } m_field = m_fieldDefs.get(m_fieldIndex); if (!(m_field.isString() || m_field.isNominal())) { throw new Exception("[NormDiscrete] reference field " + m_fieldName +" must be categorical"); } if (m_field.isNominal()) { // set up the value index m_fieldValueIndex = m_field.indexOfValue(m_fieldValue); if (m_fieldValueIndex < 0) { throw new Exception("[NormDiscrete] Unable to find value " + m_fieldValue + " in nominal attribute " + m_field.name()); } } else if (m_field.isString()) { // add our value to this attribute (if it is already there // then this will have no effect). m_fieldValueIndex = m_field.addStringValue(m_fieldValue); } } } /** * Return the structure of the result of applying this Expression * as an Attribute. * * @return the structure of the result of applying this Expression as an * Attribute. */ protected Attribute getOutputDef() { return new Attribute(m_fieldName + "=" + m_fieldValue); } /** * Get the result of evaluating the expression. In the case * of a continuous optype, a real number is returned; in * the case of a categorical/ordinal optype, the index of the nominal * value is returned as a double. * * @param incoming the incoming parameter values * @return the result of evaluating the expression * @throws Exception if there is a problem computing the result */ public double getResult(double[] incoming) throws Exception { double result = 0.0; if (Utils.isMissingValue(incoming[m_fieldIndex])) { if (m_mapMissingDefined) { result = m_mapMissingTo; // return the replacement } else { result = incoming[m_fieldIndex]; // just return the missing value } } else { if (m_fieldValueIndex == (int)incoming[m_fieldIndex]) { result = 1.0; } } return result; } /** * Always throws an Exception since the result of NormDiscrete must * be continuous. * * @param incoming the incoming parameter values * @throws Exception always */ public String getResultCategorical(double[] incoming) throws Exception { throw new Exception("[NormDiscrete] Can't return the result as a categorical value!"); } public String toString(String pad) { StringBuffer buff = new StringBuffer(); buff.append("NormDiscrete: " + m_fieldName + "=" + m_fieldValue); if (m_mapMissingDefined) { buff.append("\n" + pad + "map missing values to: " + m_mapMissingTo); } return buff.toString(); } }