/* * Copyright 2004-2010 Information & Software Engineering Group (188/1) * Institute of Software Technology and Interactive Systems * Vienna University of Technology, Austria * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.ifs.tuwien.ac.at/dm/somtoolbox/license.html * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package at.tuwien.ifs.somtoolbox.visualization.clustering; import java.util.Iterator; import java.util.TreeSet; import java.util.logging.Logger; import at.tuwien.ifs.somtoolbox.apps.viewer.GeneralUnitPNode; /** * Adaption of the Ward's Linkage Clustering Algorithm. This Version only calculates the distances to directly * neighbouring clusters. This class is not compatible with mnemonic SOMs (and probably also not compatible with * hierarchical SOMs).<br/> * Use {@link WardsLinkageTreeBuilderAll} for the "real" Ward's clustering. * * @author Angela Roiger * @version $Id: WardsLinkageTreeBuilder.java 3925 2010-11-09 10:52:31Z mayer $ */ public class WardsLinkageTreeBuilder extends AbstractWardsLinkageTreeBuilder { /** * Calculation of the Clustering. This code is only compatible with rectangular, non hierarchical SOMs! * * @param units the GeneralUnitPNode Array containing all the units of the SOM * @return the ClusteringTree (i.e. the top node of the tree) */ @Override public ClusteringTree createTree(GeneralUnitPNode[][] units) throws ClusteringAbortedException { Logger.getLogger("at.tuwien.ifs.somtoolbox").info("Start Clustering "); this.level = units.length * units[0].length; // initialize monitor resetMonitor(2 * level); TreeSet<NodeDistance> dists = calculateInitialDistances(units); NodeDistance tmpDist; ClusterNode newNode = null; while (dists.size() > 0) { tmpDist = dists.first(); dists.remove(tmpDist); level--; incrementMonitor(); allowAborting(); newNode = new ClusterNode(tmpDist.n1, tmpDist.n2, level); // remove not needed connections and change distances from n1,n2 to newNode TreeSet<NodeDistance> storeChanges = new TreeSet<NodeDistance>(); for (Iterator<NodeDistance> i = dists.iterator(); i.hasNext();) { NodeDistance x = i.next(); if (x.n1 == tmpDist.n1 || x.n1 == tmpDist.n2) { x.n1 = newNode; } if (x.n2 == tmpDist.n1 || x.n2 == tmpDist.n2) { x.n2 = newNode; } if (x.n1 == x.n2) { i.remove(); } // mehrfache verbindungen entfernen... // TODO // recalculate distances for connections containing the newNode // remove them from the set, and add again later... if ((x.n1 == newNode || x.n2 == newNode) && x.n1 != x.n2) { storeChanges.add(new NodeDistance(x.n1, x.n2, calcESSincrease(x.n1, x.n2))); i.remove(); } } dists.addAll(storeChanges); } finishMonitor(); Logger.getLogger("at.tuwien.ifs.somtoolbox").info("Finished Clustering - Wards Linkage"); return new ClusteringTree(newNode, units.length); } /** * Calculates the initial distances from each unit to its neighbours to the right, bottom, bottom-right and * bottom-left. * * @param units A GeneralUnitPNode[][] containing the Units of the som * @return a TreeSet of NodeDistances containing the distances between the units starting with the smallest. */ private TreeSet<NodeDistance> calculateInitialDistances(GeneralUnitPNode[][] units) throws ClusteringAbortedException { /* * To make this code compatible with mnemonic soms: Take care only distances between existing units are calculated only calculate (store) the * distance between 2 clusters once */ int xdim = units.length; int ydim = units[0].length; ClusterNode[][] tmp = new ClusterNode[xdim][ydim]; TreeSet<NodeDistance> dists = new TreeSet<NodeDistance>(); // create all basic Nodes for (int i = 0; i < xdim; i++) { for (int j = 0; j < ydim; j++) { tmp[i][j] = new ClusterNode(units[i][j], level); } } // calculate initial distances: for (int i = 0; i < xdim; i++) { for (int j = 0; j < ydim; j++) { incrementMonitor(); allowAborting(); try { if (i < xdim - 1) { dists.add(new NodeDistance(tmp[i][j], tmp[i + 1][j], calcESSincrease(tmp[i][j], tmp[i + 1][j]))); } if (j < ydim - 1) { dists.add(new NodeDistance(tmp[i][j], tmp[i][j + 1], calcESSincrease(tmp[i][j], tmp[i][j + 1]))); if (i < xdim - 1) { dists.add(new NodeDistance(tmp[i][j], tmp[i + 1][j + 1], calcESSincrease(tmp[i][j], tmp[i + 1][j + 1]))); } } if (j > 1 && i < xdim - 1) { dists.add(new NodeDistance(tmp[i][j], tmp[i + 1][j - 1], calcESSincrease(tmp[i][j], tmp[i + 1][j - 1]))); } } catch (Exception e) { Logger.getLogger("at.tuwien.ifs.somtoolbox").severe("Cannot create clustering: " + e.getMessage()); } } } return dists; } @Override public String getClusteringAlgName() { return "Ward's Linkage (onlyNeighbourDistances)"; } }