/* * 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/>. */ /* * DefaultAssociationRule.java * Copyright (C) 2010-2012 University of Waikato, Hamilton, New Zealand * */ package weka.associations; import java.io.Serializable; import java.util.Collection; import weka.core.Tag; import weka.core.Utils; /** * Class for storing and manipulating an association rule. * * @author Mark Hall (mhall{[at]}pentaho{[dot]}com). */ public class DefaultAssociationRule extends AssociationRule implements Serializable { /** For serialization */ private static final long serialVersionUID = -661269018702294489L; /** Enum for holding different metric types */ public static enum METRIC_TYPE { CONFIDENCE("conf") { double compute(int premiseSupport, int consequenceSupport, int totalSupport, int totalTransactions) { return (double)totalSupport / (double)premiseSupport; } }, LIFT("lift") { double compute(int premiseSupport, int consequenceSupport, int totalSupport, int totalTransactions) { double confidence = METRIC_TYPE.CONFIDENCE.compute(premiseSupport, consequenceSupport, totalSupport, totalTransactions); return confidence / ((double)consequenceSupport / (double)totalTransactions); } }, LEVERAGE("lev") { double compute(int premiseSupport, int consequenceSupport, int totalSupport, int totalTransactions) { double coverageForItemSet = (double)totalSupport / (double)totalTransactions; double expectedCoverageIfIndependent = ((double)premiseSupport / (double)totalTransactions) * ((double)consequenceSupport / (double)totalTransactions); return coverageForItemSet - expectedCoverageIfIndependent; } }, CONVICTION("conv") { double compute(int premiseSupport, int consequenceSupport, int totalSupport, int totalTransactions) { double num = (double)premiseSupport * (double)(totalTransactions - consequenceSupport) / (double)totalTransactions; double denom = premiseSupport - totalSupport + 1; return num / denom; } }; private final String m_stringVal; METRIC_TYPE(String name) { m_stringVal = name; } abstract double compute(int premiseSupport, int consequenceSupport, int totalSupport, int totalTransactions); public String toString() { return m_stringVal; } public String toStringMetric(int premiseSupport, int consequenceSupport, int totalSupport, int totalTransactions) { return m_stringVal + ":(" + Utils.doubleToString(compute(premiseSupport, consequenceSupport, totalSupport, totalTransactions), 2) + ")"; } public String toXML(int premiseSupport, int consequenceSupport, int totalSupport, int totalTransactions) { String result = "<CRITERE name=\"" + m_stringVal + "\" value=\" " + Utils.doubleToString(compute(premiseSupport, consequenceSupport, totalSupport, totalTransactions), 2) + "\"/>"; return result; } } /** Tags for display in the GUI */ public static final Tag[] TAGS_SELECTION = { new Tag(METRIC_TYPE.CONFIDENCE.ordinal(), "Confidence"), new Tag(METRIC_TYPE.LIFT.ordinal(), "Lift"), new Tag(METRIC_TYPE.LEVERAGE.ordinal(), "Leverage"), new Tag(METRIC_TYPE.CONVICTION.ordinal(), "Conviction") }; /** The metric type for this rule */ protected DefaultAssociationRule.METRIC_TYPE m_metricType = METRIC_TYPE.CONFIDENCE; /** The premise of the rule */ protected Collection<Item> m_premise; /** The consequence of the rule */ protected Collection<Item> m_consequence; /** The support for the premise */ protected int m_premiseSupport; /** The support for the consequence */ protected int m_consequenceSupport; /** The total support for the item set (premise + consequence) */ protected int m_totalSupport; /** The total number of transactions in the data */ protected int m_totalTransactions; /** * Construct a new default association rule. * * @param premise the premise of the rule * @param consequence the consequence of the rule * @param metric the metric for the rule * @param premiseSupport the support of the premise * @param consequenceSupport the support of the consequence * @param totalSupport the total support of the rule * @param totalTransactions the number of transactions in the data */ public DefaultAssociationRule(Collection<Item> premise, Collection<Item> consequence, METRIC_TYPE metric, int premiseSupport, int consequenceSupport, int totalSupport, int totalTransactions) { m_premise = premise; m_consequence = consequence; m_metricType = metric; m_premiseSupport = premiseSupport; m_consequenceSupport = consequenceSupport; m_totalSupport = totalSupport; m_totalTransactions = totalTransactions; } /* (non-Javadoc) * @see weka.associations.AssociationRule#getPremise() */ public Collection<Item> getPremise() { return m_premise; } /* (non-Javadoc) * @see weka.associations.AssociationRule#getConsequence() */ public Collection<Item> getConsequence() { return m_consequence; } /* (non-Javadoc) * @see weka.associations.AssociationRule#getPrimaryMetricName() */ public String getPrimaryMetricName() { return TAGS_SELECTION[m_metricType.ordinal()].getReadable(); } /* (non-Javadoc) * @see weka.associations.AssociationRule#getPrimaryMetricValue() */ public double getPrimaryMetricValue() { return m_metricType.compute(m_premiseSupport, m_consequenceSupport, m_totalSupport, m_totalTransactions); } /* (non-Javadoc) * @see weka.associations.AssociationRule#getNamedMetricValue(java.lang.String) */ public double getNamedMetricValue(String metricName) throws Exception { DefaultAssociationRule.METRIC_TYPE requested = null; for (DefaultAssociationRule.METRIC_TYPE m : METRIC_TYPE.values()) { if (TAGS_SELECTION[m.ordinal()].getReadable().equals(metricName)) { requested = m; } } if (requested == null) { throw new Exception("[AssociationRule] Unknown metric: " + metricName); } return requested.compute(m_premiseSupport, m_consequenceSupport, m_totalSupport, m_totalTransactions); } /* (non-Javadoc) * @see weka.associations.AssociationRule#getNumberOfMetricsForRule() */ public int getNumberOfMetricsForRule() { return METRIC_TYPE.values().length; } /* (non-Javadoc) * @see weka.associations.AssociationRule#getMetricNamesForRule() */ public String[] getMetricNamesForRule() { String[] metricNames = new String[TAGS_SELECTION.length]; for (int i = 0; i < TAGS_SELECTION.length; i++) { metricNames[i] = TAGS_SELECTION[i].getReadable(); } return metricNames; } /* (non-Javadoc) * @see weka.associations.AssociationRule#getMetricValuesForRule() */ public double[] getMetricValuesForRule() throws Exception { double[] values = new double[TAGS_SELECTION.length]; for (int i = 0; i < TAGS_SELECTION.length; i++) { values[i] = getNamedMetricValue(TAGS_SELECTION[i].getReadable()); } return values; } /* (non-Javadoc) * @see weka.associations.AssociationRule#getPremiseSupport() */ public int getPremiseSupport() { return m_premiseSupport; } /* (non-Javadoc) * @see weka.associations.AssociationRule#getConsequenceSupport() */ public int getConsequenceSupport() { return m_consequenceSupport; } /* (non-Javadoc) * @see weka.associations.AssociationRule#getTotalSupport() */ public int getTotalSupport() { return m_totalSupport; } /* (non-Javadoc) * @see weka.associations.AssociationRule#getTotalTransactions() */ public int getTotalTransactions() { return m_totalTransactions; } /** * Get a textual description of this rule. * * @return a textual description of this rule. */ public String toString() { StringBuffer result = new StringBuffer(); result.append(m_premise.toString() + ": " + m_premiseSupport + " ==> " + m_consequence.toString() + ": " + m_totalSupport + " "); for (DefaultAssociationRule.METRIC_TYPE m : METRIC_TYPE.values()) { if (m.equals(m_metricType)) { result.append("<" + m.toStringMetric(m_premiseSupport, m_consequenceSupport, m_totalSupport, m_totalTransactions) + "> "); } else { result.append("" + m.toStringMetric(m_premiseSupport, m_consequenceSupport, m_totalSupport, m_totalTransactions) + " "); } } return result.toString(); } }