/* * RapidMiner * * Copyright (C) 2001-2008 by Rapid-I and the contributors * * Complete list of developers available at our web site: * * http://rapid-i.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.gui.viewer; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSlider; import javax.swing.ListSelectionModel; import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import com.rapidminer.gui.tools.ExtendedJList; import com.rapidminer.gui.tools.ExtendedJScrollPane; import com.rapidminer.gui.tools.ExtendedListModel; import com.rapidminer.operator.learner.associations.AssociationRule; import com.rapidminer.operator.learner.associations.AssociationRuleGenerator; import com.rapidminer.operator.learner.associations.AssociationRules; import com.rapidminer.operator.learner.associations.Item; /** * This is a gui component which can be used to define filter conditions for association rules. * * @author Ingo Mierswa * @version $Id: AssociationRuleFilter.java,v 1.4 2008/08/21 13:17:07 ingomierswa Exp $ */ public class AssociationRuleFilter extends JPanel { private static final long serialVersionUID = 5619543957729778883L; private static final int MAX_VALUE = 10000; private JComboBox criterionSelectorBox = new JComboBox(AssociationRuleGenerator.CRITERIA); private JSlider criterionMinSlider = new JSlider(SwingConstants.HORIZONTAL, 0, MAX_VALUE, MAX_VALUE / 10); private double[] minValues; private double[] maxValues; private JList conclusionList = null; private JComboBox conjunctionBox = new JComboBox(AssociationRuleFilterListener.CONJUNCTION_NAMES); private Item[] itemArray; private List<AssociationRuleFilterListener> listeners = new LinkedList<AssociationRuleFilterListener>(); private AssociationRules rules; public AssociationRuleFilter(AssociationRules rules) { this.rules = rules; this.itemArray = rules.getAllConclusionItems(); // init min and max values this.minValues = new double[AssociationRuleGenerator.CRITERIA.length]; this.maxValues = new double[AssociationRuleGenerator.CRITERIA.length]; for (int i = 0; i < minValues.length; i++) { minValues[i] = Double.POSITIVE_INFINITY; maxValues[i] = Double.NEGATIVE_INFINITY; } for (AssociationRule rule : rules) { if (!Double.isInfinite(rule.getConfidence())) { this.minValues[AssociationRuleGenerator.CONFIDENCE] = Math.min(this.minValues[AssociationRuleGenerator.CONFIDENCE], rule.getConfidence()); this.maxValues[AssociationRuleGenerator.CONFIDENCE] = Math.max(this.maxValues[AssociationRuleGenerator.CONFIDENCE], rule.getConfidence()); } if (!Double.isInfinite(rule.getConviction())) { this.minValues[AssociationRuleGenerator.CONVICTION] = Math.min(this.minValues[AssociationRuleGenerator.CONVICTION], rule.getConviction()); this.maxValues[AssociationRuleGenerator.CONVICTION] = Math.max(this.maxValues[AssociationRuleGenerator.CONVICTION], rule.getConviction()); } if (!Double.isInfinite(rule.getGain())) { this.minValues[AssociationRuleGenerator.GAIN] = Math.min(this.minValues[AssociationRuleGenerator.GAIN], rule.getGain()); this.maxValues[AssociationRuleGenerator.GAIN] = Math.max(this.maxValues[AssociationRuleGenerator.GAIN], rule.getGain()); } if (!Double.isInfinite(rule.getLaplace())) { this.minValues[AssociationRuleGenerator.LAPLACE] = Math.min(this.minValues[AssociationRuleGenerator.LAPLACE], rule.getLaplace()); this.maxValues[AssociationRuleGenerator.LAPLACE] = Math.max(this.maxValues[AssociationRuleGenerator.LAPLACE], rule.getLaplace()); } if (!Double.isInfinite(rule.getLift())) { this.minValues[AssociationRuleGenerator.LIFT] = Math.min(this.minValues[AssociationRuleGenerator.LIFT], rule.getLift()); this.maxValues[AssociationRuleGenerator.LIFT] = Math.max(this.maxValues[AssociationRuleGenerator.LIFT], rule.getLift()); } if (!Double.isInfinite(rule.getPs())) { this.minValues[AssociationRuleGenerator.PS] = Math.min(this.minValues[AssociationRuleGenerator.PS], rule.getPs()); this.maxValues[AssociationRuleGenerator.PS] = Math.max(this.maxValues[AssociationRuleGenerator.PS], rule.getPs()); } } // layout GridBagLayout layout = new GridBagLayout(); setLayout(layout); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; c.weightx = 1; c.weighty = 0; c.gridwidth = GridBagConstraints.REMAINDER; c.insets = new Insets(4, 4, 4, 4); // conjunction mode conjunctionBox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { adjustFilter(); } }); JLabel label = new JLabel("Conjunction Type:"); layout.setConstraints(label, c); add(label); layout.setConstraints(conjunctionBox, c); add(conjunctionBox); // conclusion list ExtendedListModel model = new ExtendedListModel(); for (Item item : itemArray) { model.addElement(item, "The item '" + item.toString() + "'."); } this.conclusionList = new ExtendedJList(model, 200); this.conclusionList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); conclusionList.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) { adjustFilter(); } } }); label = new JLabel("Conclusions:"); layout.setConstraints(label, c); add(label); ExtendedJScrollPane listPane = new ExtendedJScrollPane(conclusionList); listPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); c.weighty = 1; c.weightx = 0; layout.setConstraints(listPane, c); add(listPane); c.weighty = 0; c.weightx = 1; label = new JLabel("Min. Criterion:"); layout.setConstraints(label, c); add(label); criterionSelectorBox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { adjustFilter(); } }); layout.setConstraints(criterionSelectorBox, c); add(criterionSelectorBox); label = new JLabel("Min. Criterion Value:"); layout.setConstraints(label, c); add(label); criterionMinSlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { if (!criterionMinSlider.getValueIsAdjusting()) { adjustFilter(); } } }); layout.setConstraints(criterionMinSlider, c); add(criterionMinSlider); } private void adjustFilter() { int conjunctionMode = conjunctionBox.getSelectedIndex(); Item[] searchFilter = null; int[] selectedIndices = conclusionList.getSelectedIndices(); if ((selectedIndices.length > 0) && (selectedIndices.length <= itemArray.length)) { searchFilter = new Item[selectedIndices.length]; int counter = 0; for (int s : selectedIndices) { searchFilter[counter++] = itemArray[s]; } } double minRatio = criterionMinSlider.getValue() / (double)MAX_VALUE; fireFilteringEvent(searchFilter, conjunctionMode, minRatio); } public void addAssociationRuleFilterListener(AssociationRuleFilterListener listener) { this.listeners.add(listener); } public void removeAssociationRuleFilterListener(AssociationRuleFilterListener listener) { this.listeners.remove(listener); } private void fireFilteringEvent(Item[] searchFilter, int conjunctionMode, double minRatio) { boolean[] filter = getFilter(rules, searchFilter, conjunctionMode, minRatio); for (AssociationRuleFilterListener listener : listeners) { listener.setFilter(filter); } } private boolean[] getFilter(AssociationRules rules, Item[] filter, int conjunctionMode, double minRatio) { boolean[] mapping = new boolean[rules.getNumberOfRules()]; int counter = 0; for (AssociationRule rule : rules) { if (getCriterionValue(rule) >= getCriterionMinValue(minRatio)) { if (checkForItem(filter, rule, conjunctionMode)) { mapping[counter] = true; } else { mapping[counter] = false; } } else { mapping[counter] = false; } counter++; } return mapping; } private double getCriterionMinValue(double minRatio) { int criterionSelection = criterionSelectorBox.getSelectedIndex(); return minValues[criterionSelection] + ((maxValues[criterionSelection] - minValues[criterionSelection]) * minRatio); } private double getCriterionValue(AssociationRule rule) { int criterionSelection = criterionSelectorBox.getSelectedIndex(); switch (criterionSelection) { case AssociationRuleGenerator.LIFT: return rule.getLift(); case AssociationRuleGenerator.CONVICTION: return rule.getConviction(); case AssociationRuleGenerator.PS: return rule.getPs(); case AssociationRuleGenerator.GAIN: return rule.getGain(); case AssociationRuleGenerator.LAPLACE: return rule.getLaplace(); case AssociationRuleGenerator.CONFIDENCE: default: return rule.getConfidence(); } } private boolean checkForItem(Item[] filter, AssociationRule rule, int conjunctionMode) { if (filter == null) return true; if (conjunctionMode == AssociationRuleFilterListener.CONJUNCTION_OR) { boolean found = false; for (Item filterItem : filter) { Iterator<Item> c = rule.getConclusionItems(); while (c.hasNext()) { Item conclusionItem = c.next(); if (filterItem.equals(conclusionItem)) { found = true; break; } } if (found) break; } return found; } else { boolean found = true; for (Item filterItem : filter) { Iterator<Item> c = rule.getConclusionItems(); while (c.hasNext()) { Item conclusionItem = c.next(); if (!filterItem.equals(conclusionItem)) { found = false; break; } } if (!found) break; } return found; } } }