/* * 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.reportgenerator; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.Vector; import java.util.logging.Logger; import javax.imageio.ImageIO; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.stream.FileImageOutputStream; import org.apache.commons.lang.StringUtils; import cern.colt.matrix.impl.DenseDoubleMatrix1D; import at.tuwien.ifs.commons.util.MathUtils; import at.tuwien.ifs.somtoolbox.SOMToolboxException; import at.tuwien.ifs.somtoolbox.apps.viewer.CommonSOMViewerStateData; import at.tuwien.ifs.somtoolbox.apps.viewer.GeneralUnitPNode; import at.tuwien.ifs.somtoolbox.apps.viewer.MapPNode; import at.tuwien.ifs.somtoolbox.apps.viewer.PieChartPNode; import at.tuwien.ifs.somtoolbox.data.InputDatum; import at.tuwien.ifs.somtoolbox.data.SOMLibClassInformation; import at.tuwien.ifs.somtoolbox.data.SOMVisualisationData; import at.tuwien.ifs.somtoolbox.data.SharedSOMVisualisationData; import at.tuwien.ifs.somtoolbox.input.SOMLibFileFormatException; import at.tuwien.ifs.somtoolbox.input.SOMLibFormatInputReader; import at.tuwien.ifs.somtoolbox.layers.GrowingLayer; import at.tuwien.ifs.somtoolbox.layers.Layer; import at.tuwien.ifs.somtoolbox.layers.LayerAccessException; import at.tuwien.ifs.somtoolbox.layers.Unit; import at.tuwien.ifs.somtoolbox.layers.metrics.MetricException; import at.tuwien.ifs.somtoolbox.layers.quality.EntropyMeasure; import at.tuwien.ifs.somtoolbox.layers.quality.IntrinsicDistance; import at.tuwien.ifs.somtoolbox.layers.quality.InversionMeasure; import at.tuwien.ifs.somtoolbox.layers.quality.MetricMultiScaling; import at.tuwien.ifs.somtoolbox.layers.quality.QualityMeasureNotFoundException; import at.tuwien.ifs.somtoolbox.layers.quality.QuantizationError; import at.tuwien.ifs.somtoolbox.layers.quality.SOMDistortion; import at.tuwien.ifs.somtoolbox.layers.quality.SammonMeasure; import at.tuwien.ifs.somtoolbox.layers.quality.SilhouetteValue; import at.tuwien.ifs.somtoolbox.layers.quality.SpearmanCoefficient; import at.tuwien.ifs.somtoolbox.layers.quality.TopographicError; import at.tuwien.ifs.somtoolbox.models.AbstractNetworkModel; import at.tuwien.ifs.somtoolbox.models.GrowingSOM; import at.tuwien.ifs.somtoolbox.output.labeling.LabelSOM; import at.tuwien.ifs.somtoolbox.properties.PropertiesException; import at.tuwien.ifs.somtoolbox.properties.SOMProperties; import at.tuwien.ifs.somtoolbox.reportgenerator.QEContainers.InputQEContainer; import at.tuwien.ifs.somtoolbox.reportgenerator.QEContainers.MapQEContainer; import at.tuwien.ifs.somtoolbox.reportgenerator.QEContainers.QMContainer; import at.tuwien.ifs.somtoolbox.reportgenerator.QEContainers.UnitQEContainer; import at.tuwien.ifs.somtoolbox.util.FileUtils; import at.tuwien.ifs.somtoolbox.util.VisualisationUtils; import at.tuwien.ifs.somtoolbox.util.comparables.UnitDistance; import at.tuwien.ifs.somtoolbox.visualization.EntropyVisualizer; import at.tuwien.ifs.somtoolbox.visualization.FlowBorderlineVisualizer; import at.tuwien.ifs.somtoolbox.visualization.IntrinsicDistanceVisualizer; import at.tuwien.ifs.somtoolbox.visualization.MappingDistortionVisualizer; import at.tuwien.ifs.somtoolbox.visualization.MetroMapVisualizer; import at.tuwien.ifs.somtoolbox.visualization.Palette; import at.tuwien.ifs.somtoolbox.visualization.Palettes; import at.tuwien.ifs.somtoolbox.visualization.QuantizationErrorVisualizer; import at.tuwien.ifs.somtoolbox.visualization.SilhouetteVisualizer; import at.tuwien.ifs.somtoolbox.visualization.SmoothedCountHistograms; import at.tuwien.ifs.somtoolbox.visualization.SmoothedDataHistograms; import at.tuwien.ifs.somtoolbox.visualization.ThematicClassMapVisualizer; import at.tuwien.ifs.somtoolbox.visualization.TopographicErrorVisualizer; import at.tuwien.ifs.somtoolbox.visualization.TopographicProductVisualizer; import at.tuwien.ifs.somtoolbox.visualization.TrustwothinessVisualizer; import at.tuwien.ifs.somtoolbox.visualization.UMatrix; import at.tuwien.ifs.somtoolbox.visualization.Visualizations; import at.tuwien.ifs.somtoolbox.visualization.clustering.ClusterNode; import at.tuwien.ifs.somtoolbox.visualization.clustering.ClusteringAbortedException; import at.tuwien.ifs.somtoolbox.visualization.clustering.ClusteringTree; import at.tuwien.ifs.somtoolbox.visualization.clustering.WardsLinkageTreeBuilderAll; /** * @author Sebastian Skritek (0226286, Sebastian.Skritek@gmx.at) * @author Martin Waitzbauer (0226025) * @version $Id: TestRunResult.java 3800 2010-09-13 14:03:43Z frank $ */ public class TestRunResult { public QMContainer QMContainer = null; public DatasetInformation datasetInfo = null; private MySOMLibMapDescription mapDescription = null; private SOMProperties props = null; private String mapFilePath = null; private String unitFilePath = null; private String weightFilePath = null; private String propertyFilePath = null; private String dwFilePath = null; private HashMap<String, String> texts = new HashMap<String, String>(); protected SharedSOMVisualisationData visData = null; private SOMLibFormatInputReader inputReader = null; private int runId = -1; private CommonSOMViewerStateData state = null; private ClusteringTree clusterTree = null; // private String classImage = ""; private GrowingSOM som = null; private ThematicClassMapVisualizer thematic_visualizer = (ThematicClassMapVisualizer) Visualizations.getVisualizationByName( "Thematic Class Map").getVis(); public EntropyVisualizer entropy_visualizer = (EntropyVisualizer) Visualizations.getVisualizationByName( "Entropy Visualiser").getVis(); private SmoothedDataHistograms sdh_visualizer = (SmoothedDataHistograms) Visualizations.getVisualizationByName( "Smoothed Data Histograms").getVis(); private TrustwothinessVisualizer trustworthyness_visualizer = (TrustwothinessVisualizer) Visualizations.getVisualizationByName( "Trustworthiness").getVis(); private TopographicProductVisualizer topographicproduct_visualizer = (TopographicProductVisualizer) Visualizations.getVisualizationByName( "Topographic Product").getVis(); private int type; private int[][][] classDistribution = null; private MetroMapVisualizer metroVis = (MetroMapVisualizer) Visualizations.getVisualizationByName("Metro Map").getVis(); /** Constructor for an already trained SOM & State, e.g. when using the report generator from the SOM Viewer */ public TestRunResult(DatasetInformation datasetInfo, String mapFilePath, String propertyFilePath, String unitFilePath, String weightFilePath, String dwFilePath, int runId, int type, CommonSOMViewerStateData state) { this.state = state; this.type = type; this.runId = runId; if (!state.growingSOM.isLabelled()) { new LabelSOM().label(state.growingSOM, state.inputDataObjects.getInputData(), AbstractNetworkModel.DEFAULT_LABEL_COUNT); } // load the map description and the property file try { if (StringUtils.isNotEmpty(mapFilePath)) { this.mapDescription = new MySOMLibMapDescription(mapFilePath); } if (StringUtils.isNotEmpty(propertyFilePath)) { this.props = new SOMProperties(propertyFilePath); } } catch (IOException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot read map description file of run " + this.runId + ". Reason: " + e); this.mapDescription = null; } catch (PropertiesException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot load the properties file for run " + this.runId + ". Will try without. Reason: " + e); this.props = null; } this.mapFilePath = mapFilePath; this.unitFilePath = unitFilePath; this.weightFilePath = weightFilePath; this.propertyFilePath = propertyFilePath; this.dwFilePath = dwFilePath; this.datasetInfo = datasetInfo; this.som = state.growingSOM; this.visData = state.inputDataObjects; } /** Constructor without Trained SOM, e.g. when running stand-alone */ public TestRunResult(DatasetInformation datasetInfo, String mapFilePath, String propertyFilePath, String unitFilePath, String weightFilePath, String dwFilePath, int runId, int type) { this.type = type; this.runId = runId; // load the map description and the property file try { if (StringUtils.isNotEmpty(mapFilePath)) { this.mapDescription = new MySOMLibMapDescription(mapFilePath); } if (StringUtils.isNotEmpty(propertyFilePath)) { this.props = new SOMProperties(propertyFilePath); } } catch (IOException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot read map description file of run " + this.runId + ". Reason: " + e); this.mapDescription = null; } catch (PropertiesException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot load the properties file for run " + this.runId + ". Will try without. Reason: " + e); this.props = null; } this.mapFilePath = mapFilePath; this.unitFilePath = unitFilePath; this.weightFilePath = weightFilePath; this.propertyFilePath = propertyFilePath; this.dwFilePath = dwFilePath; this.datasetInfo = datasetInfo; this.state = this.getStateObject(); } public DatasetInformation getDatasetInfo() { return this.datasetInfo; } public String getMapFilePath() { return this.mapFilePath; } public String getUnitFilePath() { return this.unitFilePath; } public String getDWFilePath() { return this.dwFilePath; } public String getWeightFilePath() { return this.weightFilePath; } public int getRunId() { return this.runId; } public int getNumberOfInputsOnUnit(int x, int y) { if (this.classDistribution == null) { this.prepareClassDistribution(); } return MathUtils.getSumOf(this.classDistribution[x][y]); } public boolean hasMapDescription() { return this.mapDescription != null; } public boolean hasPropertyFilenpath() { return new File(this.propertyFilePath).exists(); } public String getPropertyFilePath() { return this.propertyFilePath; } public Object getMapProperty(String property) { if (this.mapDescription != null) { return this.mapDescription.getProperty(property); } else { return null; } } public boolean hasUnitOn(int x, int y) { return this.getGrowingSOM().getLayer().isValidUnitLocation(x, y); } public double getSigma() { if (this.props == null) { return -1; } return this.props.sigma(); } public double getTau() { if (this.props == null) { return -1; } return this.props.tau(); } public double getTau2() { return -1; } public int getNumberOfMaps() { return 1; } /** * returns the mqe of the trained SOM returns the mqe of the trained SOM, as specified by the QuantizationError * Object - see there for more information * * @return the mqe of the trained map */ public MapQEContainer getMapMQE() { QuantizationError qe = this.somQE(); MapQEContainer qeContainer = null; try { qeContainer = new MapQEContainer(qe.getMapQuality("mqe")); } catch (QualityMeasureNotFoundException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot retrieve map mqe for run " + this.runId + ". Will work without. Reason: " + e); return new MapQEContainer(0); } return qeContainer; } /** * returns the mmqe of the trained SOM returns the mmqe of the trained SOM, as specified by the QuantizationError * Object - see there for more information * * @return the mmqe of the trained map */ public MapQEContainer getMapMMQE() { QuantizationError qe = this.somQE(); MapQEContainer qeContainer = null; try { qeContainer = new MapQEContainer(qe.getMapQuality("mmqe")); } catch (QualityMeasureNotFoundException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot retrieve map mmqe for run " + this.runId + ". Will work without. Reason: " + e); return new MapQEContainer(0); } return qeContainer; } /** * returns the smallest quantization error of all units where at least one input vector is mapped on * * @return object containing the smallest QE and some information about the unit */ public UnitQEContainer getMinUnitQE() { GrowingLayer layer = this.getGrowingSOM().getLayer(); QuantizationError qe = this.somQE(); double[][] errors; double minError; Vector<Integer> argminx = new Vector<Integer>(); Vector<Integer> argminy = new Vector<Integer>(); try { errors = qe.getUnitQualities("qe"); minError = errors[0][0]; argminx.add(new Integer(0)); argminy.add(new Integer(0)); for (int i = 0; i < errors.length; i++) { for (int j = 0; j < errors[i].length; j++) { // look whether the qe is smaller if (errors[i][j] < minError && layer.hasMappedInput(i, j)) { // new minimum minError = errors[i][j]; argminx.removeAllElements(); argminy.removeAllElements(); argminx.add(new Integer(i)); argminy.add(new Integer(j)); } else if (errors[i][j] == minError && layer.hasMappedInput(i, j)) { // additional minimum argminx.add(new Integer(i)); argminy.add(new Integer(j)); } } } Unit[] units = new Unit[argminx.size()]; for (int i = 0; i < argminx.size(); i++) { units[i] = layer.getUnit(argminx.get(i).intValue(), argminy.get(i).intValue()); } return new UnitQEContainer(units, minError); } catch (QualityMeasureNotFoundException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot get unit qualities for run " + this.runId + ". Reason: " + e); return null; } catch (LayerAccessException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot get unit in for run " + this.runId + ". Reason: " + e); return null; } } /** * returns the topographic error of the map (using quality measure "TE_Map") * * @return the topographic error of the map or -1 (in case of any problems) */ public double getMapTE() { TopographicError toperr = new TopographicError(this.getGrowingSOM().getLayer(), this.datasetInfo.getInputData()); try { return toperr.getMapQuality("TE_Map"); } catch (QualityMeasureNotFoundException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot calculate qtopographic error of SOM from run " + this.runId + ". Reason: " + e); return -1; } } /** * returns the maximal topographic error of any unit on the map (together with the unit) * * @return an object storing information about both: the biggest topographic error of the map as well as of the unit * containing this error, null in case of any error */ public UnitQEContainer getMaxUnitTE() { GrowingLayer layer = this.getGrowingSOM().getLayer(); TopographicError toperr = new TopographicError(layer, this.datasetInfo.getInputData()); double[][] errors; double maxError; Vector<Integer> argmaxx = new Vector<Integer>(); Vector<Integer> argmaxy = new Vector<Integer>(); try { errors = toperr.getUnitQualities("TE_Unit"); maxError = errors[0][0]; argmaxx.add(new Integer(0)); argmaxy.add(new Integer(0)); for (int i = 0; i < errors.length; i++) { for (int j = 0; j < errors[i].length; j++) { // look whether the te is smaller if (errors[i][j] > maxError && layer.hasMappedInput(i, j)) { // new maximium maxError = errors[i][j]; argmaxx.removeAllElements(); argmaxy.removeAllElements(); argmaxx.add(new Integer(i)); argmaxy.add(new Integer(j)); } else if (errors[i][j] == maxError && layer.hasMappedInput(i, j)) { // additional minimum argmaxx.add(new Integer(i)); argmaxy.add(new Integer(j)); } } } Unit[] units = new Unit[argmaxx.size()]; for (int i = 0; i < argmaxx.size(); i++) { units[i] = layer.getUnit(argmaxx.get(i).intValue(), argmaxy.get(i).intValue()); } return new UnitQEContainer(units, maxError); } catch (QualityMeasureNotFoundException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot calculate max topographic error of units on SOM from run " + this.runId + ". Reason: " + e); return null; } catch (LayerAccessException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot calculate max topographic error of units on SOM from run " + this.runId + ". Reason: " + e); return null; } } /** * returns the silhouette value of the map (using quality measure "silhouette") * * @return the silhouette value of the map or -1 (in case of any problems) */ public double getMapSilouette() { SilhouetteValue sil = new SilhouetteValue(this.getGrowingSOM().getLayer(), this.datasetInfo.getInputData()); try { return sil.getMapQuality("silhouettevalue"); } catch (QualityMeasureNotFoundException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot calculate silhouette value of SOM from run " + this.runId + ". Reason: " + e); return -1; } } /** * returns the number of units on the map having no topographic error * * @return the number of units without topographic error, -1 in case of any error */ public int getNumberOfTElessUnits() { Layer layer = this.getGrowingSOM().getLayer(); TopographicError toperr = new TopographicError(layer, this.datasetInfo.getInputData()); double[][] errors; int count = 0; try { errors = toperr.getUnitQualities("TE_Unit"); for (double[] error : errors) { for (double element : error) { if (element == 0) { count++; } } } return count; } catch (QualityMeasureNotFoundException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot calculate min topographic error of units on SOM from run " + this.runId + ". Reason: " + e); return -1; } } /** * returns the number of units on the map having no Silouette Value * * @return the number of units without silouette value error, -1 in case of any error */ public int getNumberOfSilouettelessUnits() { Layer layer = this.getGrowingSOM().getLayer(); SilhouetteValue toperr = new SilhouetteValue(layer, this.datasetInfo.getInputData()); double[][] errors; int count = 0; try { errors = toperr.getUnitQualities("silhouette"); for (double[] error : errors) { for (double element : error) { if (Double.isNaN(element)) { count++; } } } return count; } catch (QualityMeasureNotFoundException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot calculate min silhouette values of units on SOM from run " + this.runId + ". Reason: " + e); return -1; } } /** * returns the minimal topographic error (> 0!!) of any unit on the map (together with the unit) * * @return an object storing information about both: the smallest topographic error (> 0!!) of the map as well as of * the unit containing this error, null in case of an error */ public UnitQEContainer getMinUnitTE() { GrowingLayer layer = this.getGrowingSOM().getLayer(); TopographicError toperr = new TopographicError(layer, this.datasetInfo.getInputData()); double[][] errors; double minError; Vector<Integer> argminx = new Vector<Integer>(); Vector<Integer> argminy = new Vector<Integer>(); try { errors = toperr.getUnitQualities("TE_Unit"); minError = -1; argminx.add(new Integer(0)); argminy.add(new Integer(0)); for (int i = 0; i < errors.length; i++) { for (int j = 0; j < errors[i].length; j++) { if (errors[i][j] == 0) { continue; } // look whether the qe is smaller if ((minError < 0 || errors[i][j] < minError) && layer.hasMappedInput(i, j)) { // new minimum minError = errors[i][j]; argminx.removeAllElements(); argminy.removeAllElements(); argminx.add(new Integer(i)); argminy.add(new Integer(j)); } else if (errors[i][j] == minError && layer.hasMappedInput(i, j)) { // additional minimum argminx.add(new Integer(i)); argminy.add(new Integer(j)); } } } Unit[] units = new Unit[argminx.size()]; for (int i = 0; i < argminx.size(); i++) { units[i] = layer.getUnit(argminx.get(i).intValue(), argminy.get(i).intValue()); } return new UnitQEContainer(units, minError); } catch (QualityMeasureNotFoundException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot calculate min topographic error of units on SOM from run " + this.runId + ". Reason: " + e); return null; } catch (LayerAccessException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot calculate min topographic error of units on SOM from run " + this.runId + ". Reason: " + e); return null; } } /** * returns the minimal silhouette Value of any unit on the map (together with the unit) * * @return an object storing information about both: the smallest silhouette error (> 0!!) of the map as well as of * the unit containing this error, null in case of an error */ public UnitQEContainer getMinUnitSilouette() { GrowingLayer layer = this.getGrowingSOM().getLayer(); SilhouetteValue sil = new SilhouetteValue(layer, this.datasetInfo.getInputData()); double[][] errors; double minError; Vector<Integer> argminx = new Vector<Integer>(); Vector<Integer> argminy = new Vector<Integer>(); try { errors = sil.getUnitQualities("silhouette"); minError = Double.MAX_VALUE; argminx.add(new Integer(0)); argminy.add(new Integer(0)); for (int i = 0; i < errors.length; i++) { for (int j = 0; j < errors[i].length; j++) { if (errors[i][j] == 0) { continue; } // look whether the qe is smaller if (errors[i][j] < minError && layer.hasMappedInput(i, j)) { // new minimum minError = errors[i][j]; argminx.removeAllElements(); argminy.removeAllElements(); argminx.add(new Integer(i)); argminy.add(new Integer(j)); } else if (errors[i][j] == minError && layer.hasMappedInput(i, j)) { // additional minimum argminx.add(new Integer(i)); argminy.add(new Integer(j)); } } } Unit[] units = new Unit[argminx.size()]; for (int i = 0; i < argminx.size(); i++) { units[i] = layer.getUnit(argminx.get(i).intValue(), argminy.get(i).intValue()); } return new UnitQEContainer(units, minError); } catch (QualityMeasureNotFoundException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot calculate min silhouette value of units on SOM from run " + this.runId + ". Reason: " + e); return null; } catch (LayerAccessException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot calculate min silhouette value of units on SOM from run " + this.runId + ". Reason: " + e); return null; } } /** * Returns the quantization error of the unit at the specified coordinates That is, for all inputs mapped to this * unit, the distance between the weigh vector of the input item and the unit is summed and returned * * @param x the x-coordinate of the unit on the map * @param y the y-coordinate of the unit on the map * @return the qe of the unit at the specified coordinates, -1 in case of an error */ public double getQEForUnit(int x, int y) { try { return this.somQE().getUnitQualities("qe")[x][y]; } catch (QualityMeasureNotFoundException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot retrieve quantization error for unit " + x + "," + y + " in run " + this.runId + ". Reason: " + e); return -1; } } /** * returns the biggest quantization error of all units where at least one input vector is mapped on * * @return an object containing both, the value of the qe as well as information about the unit this qe is located * on, null in case of any error */ public UnitQEContainer getMaxUnitQE() { GrowingLayer layer = this.getGrowingSOM().getLayer(); QuantizationError qe = this.somQE(); double[][] errors; double maxError; Vector<Integer> argmaxx = new Vector<Integer>(); Vector<Integer> argmaxy = new Vector<Integer>(); try { errors = qe.getUnitQualities("qe"); maxError = errors[0][0]; argmaxx.add(new Integer(0)); argmaxy.add(new Integer(0)); for (int i = 0; i < errors.length; i++) { for (int j = 0; j < errors[i].length; j++) { // look whether the qe is smaller if (errors[i][j] > maxError && layer.hasMappedInput(i, j)) { // new minimum maxError = errors[i][j]; argmaxx.removeAllElements(); argmaxy.removeAllElements(); argmaxx.add(new Integer(i)); argmaxy.add(new Integer(j)); } else if (errors[i][j] == maxError && layer.hasMappedInput(i, j)) { // additional minimum argmaxx.add(new Integer(i)); argmaxy.add(new Integer(j)); } } } Unit[] units = new Unit[argmaxx.size()]; for (int i = 0; i < argmaxx.size(); i++) { units[i] = layer.getUnit(argmaxx.get(i).intValue(), argmaxy.get(i).intValue()); } return new UnitQEContainer(units, maxError); } catch (QualityMeasureNotFoundException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot get unit qualities for run " + this.runId + ". Reason: " + e); return null; } catch (LayerAccessException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot get unit in for run " + this.runId + ". Reason: " + e); return null; } } /** * returns the smallest mean quantization error of all units where at least one input vector is mapped on. (that is: * sum of distances / number of vectors) * * @return an object containing the value of the smallest mqe of all units on the map as well as information about * the unit the error is located on. null in case of any problem. */ public UnitQEContainer getMinUnitMQE() { GrowingLayer layer = this.getGrowingSOM().getLayer(); QuantizationError qe = this.somQE(); double[][] errors; double minError; Vector<Integer> argminx = new Vector<Integer>(); Vector<Integer> argminy = new Vector<Integer>(); try { errors = qe.getUnitQualities("mqe"); minError = errors[0][0]; argminx.add(new Integer(0)); argminy.add(new Integer(0)); for (int i = 0; i < errors.length; i++) { for (int j = 0; j < errors[i].length; j++) { // look whether the qe is smaller if (errors[i][j] < minError && layer.hasMappedInput(i, j)) { // new minimum minError = errors[i][j]; argminx.removeAllElements(); argminy.removeAllElements(); argminx.add(new Integer(i)); argminy.add(new Integer(j)); } else if (errors[i][j] == minError && layer.hasMappedInput(i, j)) { // additional minimum argminx.add(new Integer(i)); argminy.add(new Integer(j)); } } } Unit[] units = new Unit[argminx.size()]; for (int i = 0; i < argminx.size(); i++) { units[i] = layer.getUnit(argminx.get(i).intValue(), argminy.get(i).intValue()); } return new UnitQEContainer(units, minError); } catch (QualityMeasureNotFoundException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot get unit qualities for run " + this.runId + ". Reason: " + e); return null; } catch (LayerAccessException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot get unit in for run " + this.runId + ". Reason: " + e); return null; } } /** * returs the mean quantization error for the unit with the specified coordinates the mqe of a unit = sum or qe * errors / number of input items mapped to this unit. This value is returned * * @param x the x-coordinate of the unit on the map * @param y the y-coordinate of the unit on the map * @return the mqe of the specified unit or -1 in case of any error. */ public double getMQEForUnit(int x, int y) { try { return this.somQE().getUnitQualities("mqe")[x][y]; } catch (QualityMeasureNotFoundException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot retrieve mean quantization error for unit " + x + "," + y + " in run " + this.runId + ". Reason: " + e); return -1; } } /** * returns the biggest mean quantization error of all units where at least one input vector is mapped on. (that is: * sum of distances / number of vectors) * * @return an object containing the value of the biggest mqe as well as information about the unit this error is * located on, null in case of any problems */ public UnitQEContainer getMaxUnitMQE() { GrowingLayer layer = this.getGrowingSOM().getLayer(); QuantizationError qe = this.somQE(); double[][] errors; double maxError; Vector<Integer> argmaxx = new Vector<Integer>(); Vector<Integer> argmaxy = new Vector<Integer>(); try { errors = qe.getUnitQualities("mqe"); maxError = errors[0][0]; argmaxx.add(new Integer(0)); argmaxy.add(new Integer(0)); for (int i = 0; i < errors.length; i++) { for (int j = 0; j < errors[i].length; j++) { // look whether the qe is smaller if (errors[i][j] > maxError && layer.hasMappedInput(i, j)) { // new minimum maxError = errors[i][j]; argmaxx.removeAllElements(); argmaxy.removeAllElements(); argmaxx.add(new Integer(i)); argmaxy.add(new Integer(j)); } else if (errors[i][j] == maxError && layer.hasMappedInput(i, j)) { // additional minimum argmaxx.add(new Integer(i)); argmaxy.add(new Integer(j)); } } } Unit[] units = new Unit[argmaxx.size()]; for (int i = 0; i < argmaxx.size(); i++) { units[i] = layer.getUnit(argmaxx.get(i).intValue(), argmaxy.get(i).intValue()); } return new UnitQEContainer(units, maxError); } catch (QualityMeasureNotFoundException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot get unit qualities for run " + this.runId + ". Reason: " + e); return null; } catch (LayerAccessException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot get unit in for run " + this.runId + ". Reason: " + e); return null; } } /** * returns the unit the specified input item is mapped to * * @param input the input item, for which the unit it is mapped to shall be returned * @return the unit onto which the given input item is mapped */ public Unit getUnitMappedTo(InputDatum input) { GrowingSOM som = this.getGrowingSOM(); GrowingLayer layer = som.getLayer(); Unit mappedTo = layer.getUnitForDatum(input.getLabel()); return mappedTo; } /** * calculates the qe for the specified input vector Calculates the quantization error of the input vector specified * by its name, that is the distance of the given input vector to the unit it is mapped to. * * @param name the name specifying the input vector * @return an object containing the value of the qe of the specified input */ public InputQEContainer getInputMappedQE(String name) { double distance = 0; GrowingSOM som = this.getGrowingSOM(); GrowingLayer layer = som.getLayer(); Unit mappedTo = layer.getUnitForDatum(name); if (mappedTo == null) { return null; } distance = mappedTo.getMappedInputDistance(mappedTo.getInputIndex(name)); InputQEContainer qeContainer = new InputQEContainer(); qeContainer.addMapUnit(mappedTo); qeContainer.setQE(distance); qeContainer.addInput(this.datasetInfo.getInputDatum(name)); return qeContainer; } /** * mappes the vector to the current SOM and returns x and y coordinates of the winning unit. searches the winning * unit on the SOM for the given weight vector (that ist the SOM whose weight vector is closest to the given vecotr * and returns the coordinates of this unit. * * @param vector the vector of the item that shall be mapped to the unit * @return and array containing x and y coordinates of the winning unit */ public int[] getMappedUnit(double[] vector) { GrowingLayer layer = this.getGrowingSOM().getLayer(); InputDatum classMean = new InputDatum("class_mean_vector", new DenseDoubleMatrix1D(vector)); Unit winner = layer.getWinner(classMean); return new int[] { winner.getXPos(), winner.getYPos() }; } /** * returns the mean x and y coordinates (and the standard derivation) of a class the following is done: for all * input elements of the given class, the sum of the x coordinates of the unit the item is mapped onto is build, and * then divided by the number of input items in this class. (the same for y) That is, the mean unit in terms of the * units the item are really mapped onto is calculated. (meanx = sum(x)/n, where n is the number of input items in * the class, and the sum is over all input items, and x is the x-coordinate of the unit the item is mapped onto - * equivalent for y). And the standard derivation of this value is also calculated * * @param classId the id of the class for which the mean unit shall be calculated * @return the mean x and y coordinates (at index 0=x, 1=y) as well as their standard derivation (2=x, 3=y) */ public int[] getClassMeanUnit(int classId) { GrowingLayer layer = this.getGrowingSOM().getLayer(); double[] mean = new double[4]; for (int i = 0; i < mean.length; i++) { mean[i] = 0; } // get the labels of the input vectors belonging to this class String[] ins = this.datasetInfo.getInputLabelsofClass(classId); int n = ins.length; // mean for (String in : ins) { Unit temp = layer.getUnitForDatum(in); if (temp.getXPos() > 20) { System.out.println("yepp- " + in); System.exit(0); } mean[0] += (double) temp.getXPos() / (double) n; mean[1] += (double) temp.getYPos() / (double) n; } // variance/radius for (String in : ins) { Unit temp = layer.getUnitForDatum(in); mean[2] += (temp.getXPos() - mean[0]) * (temp.getXPos() - mean[0]) / (n - 1); mean[3] += (temp.getYPos() - mean[1]) * (temp.getYPos() - mean[1]) / (n - 1); } mean[2] = Math.sqrt(mean[2]); mean[3] = Math.sqrt(mean[3]); return new int[] { (int) Math.round(mean[0]), (int) Math.round(mean[1]), (int) Math.round(mean[2]), (int) Math.round(mean[3]) }; } /** * creates an image that visualizes the "mean unit" of a class, its standard derivation and the unit onto which the * mean weight vector of the class would be mapped Creates an image, that visualizes the units of the SOM. The * position of the mean unit (defined by the indexes 0,1 of the meanUnit parameter) is marked by a square, the * position of the unit onto which the mean weight vector would be mapped by a circle. The radius (standard * derivation of the mean unit) is denoted by a rectangle, going meanUnit[2] units to the left and right of the mean * unit, and meanUnit[3] items to the top and bottom the image is named run_RUNID_classCenter_CLASSID.jpg"; * * @param meanUnit [0]: the x coordinate of the mean unit, [1]: the y coordinate, [2]: the derivation in * x-direction, [3]: the derivation in y direction * @param mappedMeanVector [0]: the x coordinate of the unit onto which the mean weight vector of the class would be * mapped, [1]: the y coordinate * @param classId the id of the class for which the image shall be made * @param outputDir the directory into which the image shall be saved. (neither checked nor created) */ public boolean visualizeClassLayout(int[] meanUnit, int[] mappedMeanVector, int classId, String outputDir) { String visName = "class center visualization of class " + classId; int maxImageSize = 500; // the max. number of pixels the larger side of the image may have int xunits = getSomXSize(); // number of units in x direction int yunits = getSomYSize(); // number of units in y direction int maxUnits = Math.max(xunits, yunits); int preferredUnitSize = 10; int unitSize = preferredUnitSize; if (maxUnits * preferredUnitSize > maxImageSize) { // the we have to use a smaller unit size unitSize = maxImageSize / maxUnits; if (unitSize < 1) { unitSize = 1; // well a unit needs some space } } int width = unitSize * xunits + 10; // the width of the complete image int height = unitSize * yunits + 10; // the height of the complete image int x = 0; // x position of a unit in the image int y = 0; // y position of a unit in the image BufferedImage buffImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D graphics = buffImage.createGraphics(); graphics.setBackground(Color.WHITE); graphics.fillRect(0, 0, width, height); graphics.setColor(Color.black); Rectangle rect = new Rectangle(5, 5, width - 10, height - 10); // draw the border of the SOM graphics.draw(rect); // ... rect.setBounds(0, 0, unitSize, unitSize); // prepare the Rectangle representing a unit graphics.setColor(Color.CYAN); // we use a not such intensive color // draw the units of the map - looks nicer than only the coloured cluster graphics.setColor(Color.black); for (int i = 0; i < xunits; i++) { for (int j = 0; j < yunits; j++) { x = 5 + i * unitSize; y = 5 + j * unitSize; rect.setLocation(x, y); graphics.draw(rect); } } // now draw: int[] rgb = this.datasetInfo.getClassColorRGB(classId); graphics.setColor(new Color(rgb[0], rgb[1], rgb[2])); // the unit where the mean vector is mapped tp x = 6 + mappedMeanVector[0] * unitSize; y = 6 + mappedMeanVector[1] * unitSize; graphics.fillOval(x, y, unitSize - 2, unitSize - 2); graphics.setColor(Color.BLACK); graphics.drawOval(x, y, unitSize - 2, unitSize - 2); graphics.setColor(new Color(rgb[0], rgb[1], rgb[2])); // the unit that is the mean unit x = 5 + meanUnit[0] * unitSize; y = 5 + meanUnit[1] * unitSize; graphics.fillRect(x, y, unitSize, unitSize); graphics.setColor(Color.BLACK); graphics.drawRect(x, y, unitSize, unitSize); graphics.setStroke(new BasicStroke(2)); graphics.setColor(new Color(rgb[0], rgb[1], rgb[2])); // a rectangle showing the radius of the class int xcorr = 0; int ycorr = 0; int w = 0; int h = 0; x = meanUnit[0] - meanUnit[2]; y = meanUnit[1] - meanUnit[3]; if (x < 0) { xcorr = x; x = 0; } if (y < 0) { ycorr = y; y = 0; } x = 5 + x * unitSize; y = 5 + y * unitSize; w = (1 + 2 * meanUnit[2] + xcorr) * unitSize; h = (1 + 2 * meanUnit[3] + ycorr) * unitSize; graphics.drawRect(x, y, w, h); String filename = "run_" + this.runId + "_classCenter_" + classId + ".jpg"; return writeImagesAsJPG(outputDir, filename, buffImage, visName); } /** * calculates the qe for the specified input vector Calculates the quantization error of the input vector specified * by its name, that is the distance of the given input vector to the winner unit on the som. (it is not clear yet, * whether this unit must be the same as the unit this element is mapped onto, or whether during training the map * can change in a way that the mapped unit is not the winner at the end of the training. There would be a chance if * an input vector at the beginning of the training is lying between two units, but during the rest of the training, * the unit chosen moves away from the input vector, while the other one comes closer ...) * * @param name the name specifying the input vector */ public InputQEContainer getInputWinnerQE(String name) { GrowingSOM som = this.getGrowingSOM(); GrowingLayer layer = som.getLayer(); UnitDistance[] winnersAndDistances = layer.getWinnersAndDistances(this.datasetInfo.getInputDatum(name), 1); InputQEContainer inputQE = new InputQEContainer(); inputQE.addMapUnit(winnersAndDistances[0].getUnit()); inputQE.addInput(this.datasetInfo.getInputDatum(name)); inputQE.setQE(winnersAndDistances[0].getDistance()); return inputQE; } /** * returns the min qe of any input item compared with the unit it is mapped to returns the value of the smallest * distance between the weight vector of an input item and the unit this item is mapped to. It also returns the * input item(s) and unit(s) between those error arises * * @return object containing the information specified above */ public InputQEContainer getMinInputMappedQE() { int numbData = this.datasetInfo.getNumberOfInputVectors(); InputQEContainer curMinContainer = this.getInputMappedQE(this.datasetInfo.getInputDatum(0).getLabel()); InputQEContainer tempContainer = null; double curMin = curMinContainer.getQE(); for (int i = 0; i < numbData; i++) { tempContainer = this.getInputMappedQE(this.datasetInfo.getInputDatum(i).getLabel()); if (tempContainer.getQE() < curMin) { // new minimum found curMin = tempContainer.getQE(); curMinContainer.clearInputs(); curMinContainer.clearMapUnits(); curMinContainer.setQE(tempContainer.getQE()); curMinContainer.addInput(tempContainer.getInput(0)); curMinContainer.addMapUnit(tempContainer.getMapUnit(0)); } else if (tempContainer.getQE() == curMin) { // add to existing minima curMinContainer.addInput(tempContainer.getInput(0)); curMinContainer.addMapUnit(tempContainer.getMapUnit(0)); } } return curMinContainer; } /** * returns the max qe of any input item compared with the unit it is mapped to returns the value of the greatest * distance between the weight vector of an input item and the unit this item is mapped to. It also returns the * input item(s) and unit(s) between those error arises * * @return object containing the information specified above */ public InputQEContainer getMaxInputMappedQE() { int numbData = this.datasetInfo.getNumberOfInputVectors(); InputQEContainer curMaxContainer = this.getInputMappedQE(this.datasetInfo.getInputDatum(0).getLabel()); InputQEContainer tempContainer = null; double curMax = curMaxContainer.getQE(); for (int i = 0; i < numbData; i++) { tempContainer = this.getInputMappedQE(this.datasetInfo.getInputDatum(i).getLabel()); if (tempContainer.getQE() > curMax) { // new maxima found curMax = tempContainer.getQE(); curMaxContainer.clearInputs(); curMaxContainer.clearMapUnits(); curMaxContainer.setQE(tempContainer.getQE()); curMaxContainer.addInput(tempContainer.getInput(0)); curMaxContainer.addMapUnit(tempContainer.getMapUnit(0)); } else if (tempContainer.getQE() == curMax) { // add to existing maxima curMaxContainer.addInput(tempContainer.getInput(0)); curMaxContainer.addMapUnit(tempContainer.getMapUnit(0)); } } return curMaxContainer; } /** * returns the min qe of any input item compared with all units on the map returns the value of the smallest * distance between the weight vector of an input item and alle the weight vectors of units on the map. It also * returns the input item(s) and unit(s) between those error arises. * * @return object containing the information specified above */ public InputQEContainer getMinInputWinnerQE() { int numbData = this.datasetInfo.getNumberOfInputVectors(); InputQEContainer curMinContainer = this.getInputWinnerQE(this.datasetInfo.getInputDatum(0).getLabel()); InputQEContainer tempContainer = null; double curMin = curMinContainer.getQE(); for (int i = 0; i < numbData; i++) { tempContainer = this.getInputWinnerQE(this.datasetInfo.getInputDatum(i).getLabel()); if (tempContainer.getQE() < curMin) { // new minimum found curMin = tempContainer.getQE(); curMinContainer.clearInputs(); curMinContainer.clearMapUnits(); curMinContainer.setQE(tempContainer.getQE()); curMinContainer.addInput(tempContainer.getInput(0)); curMinContainer.addMapUnit(tempContainer.getMapUnit(0)); } else if (tempContainer.getQE() == curMin) { // add to existing minima curMinContainer.addInput(tempContainer.getInput(0)); curMinContainer.addMapUnit(tempContainer.getMapUnit(0)); } } return curMinContainer; } /** * returns the max qe of any input item compared with all units on the map returns the value of the greatest * distance between the weight vector of an input item and alle the weight vectors of units on the map. It also * returns the input item(s) and unit(s) between those error arises. * * @return object containing the information specified above */ public InputQEContainer getMaxInputWinnerQE() { int numbData = this.datasetInfo.getNumberOfInputVectors(); InputQEContainer curMaxContainer = this.getInputWinnerQE(this.datasetInfo.getInputDatum(0).getLabel()); InputQEContainer tempContainer = null; double curMax = curMaxContainer.getQE(); for (int i = 0; i < numbData; i++) { tempContainer = this.getInputWinnerQE(this.datasetInfo.getInputDatum(i).getLabel()); if (tempContainer.getQE() > curMax) { // new maxima found curMax = tempContainer.getQE(); curMaxContainer.clearInputs(); curMaxContainer.clearMapUnits(); curMaxContainer.setQE(tempContainer.getQE()); curMaxContainer.addInput(tempContainer.getInput(0)); curMaxContainer.addMapUnit(tempContainer.getMapUnit(0)); } else if (tempContainer.getQE() == curMax) { // add to existing maxima curMaxContainer.addInput(tempContainer.getInput(0)); curMaxContainer.addMapUnit(tempContainer.getMapUnit(0)); } } return curMaxContainer; } /** * returns the cluster node representing the cluster at the specified level * * @param level the level for which the cluster node shall be returned * @param numbClusters should be 1 * @return the cluster node representing the specfied value */ public ClusterNode getClusterNodes(int level, int numbClusters) { ClusterNode node = null; this.getClusterTree().getClusteringInto(numbClusters); node = this.getClusterTree().findNode(level); return node; } /** * returns the QuantizationError Object for the SOM specified by this TestRunResult Creates the some according to * the data stored by this object, and returns the Quantization Error Object provided by the Layer (which can be * retrieved from the GrowingSOM) * * @return the QuantizationError object for this SOM */ private QuantizationError somQE() { GrowingSOM som = this.getGrowingSOM(); Layer somLayer = som.getLayer(); return new QuantizationError(somLayer, this.datasetInfo.getInputData()); } /** returns the SOM learned within the run this object represents */ public GrowingSOM getGrowingSOM() { if (this.som == null) { this.som = new GrowingSOM(this.getInputReader()); this.visData = this.getVisData(); this.som.setSharedInputObjects(this.visData); // label the SOM, if it's not labelled yet if (!som.isLabelled()) { new LabelSOM().label(this.som, this.datasetInfo.getInputData(), this.datasetInfo.getVectorDim()); } SOMLibFormatInputReader somlib = this.getInputReader(); Palette[] palettes = Palettes.getAvailablePalettes(); Palette defaultPalette = Palettes.getDefaultPalette(); int defaultPaletteIndex = Palettes.getPaletteIndex(defaultPalette); Visualizations.initVisualizations(visData, somlib, defaultPaletteIndex, defaultPalette, palettes); } return som; } /** * returns for i = 0 ... numberOfClasses onto how many units on the map have exactly items of i different classes * are mapped on * * @return array of length numberOfClasses + 1, where the index denotes the number of classes, and the value the * number of corresponding units */ public int[] getClassPurity1() { if (this.classDistribution == null) { this.prepareClassDistribution(); } int[] purity = new int[this.datasetInfo.getNumberOfClasses() + 1]; for (int i = 0; i < purity.length; i++) { purity[i] = 0; } int count = 0; for (int[][] element : this.classDistribution) { for (int[] element2 : element) { count = 0; for (int element3 : element2) { if (element3 > 0) { count++; } } purity[count]++; } } return purity; } /** * returns an textual interpretation of how good the SOM separates the different classes this is done quite simple * by grouping the error to: * <ul> * <li>less than 5%</li> * <li>less than 10%</li> * <li>less than 20%</li> * <li>less than 50%</li> * <li>less than 70%</li> * <li>less than 85%</li> * <li>less or equal than 100%</li> * * @param purity an array storing at each index i how many units exist that have items of i different classes mapped * onto it * @return a string giving an estimation of how good the SOM separates the classes */ public String getClassPurity2(int[] purity) { String concl = ""; double corr = (double) (MathUtils.getSumOf(purity) - purity[0] - purity[1]) / (double) (MathUtils.getSumOf(purity) - purity[0]); if (corr < 0.05) { concl = "The SOM separates the classes very fine. (" + String.format("%.2f", corr * 100) + "% of non-empty units contain more than one class)"; } else if (corr < 0.1) { concl = "The SOM separates the classes quite good. (" + String.format("%.2f", corr * 100) + "% of non-empty units contain more than one class)"; } else if (corr < 0.2) { concl = "The SOM separates the classes good. (" + String.format("%.2f", corr * 100) + "% of non-empty units contain more than one class)"; } else if (corr < 0.5) { concl = "The classes are separated average by the SOM. (" + String.format("%.2f", corr * 100) + "% of non-empty units contain more than one class)"; } else if (corr < 0.7) { concl = "The SOM separates the classes poor. (" + String.format("%.2f", corr * 100) + "% of non-empty units contain more than one class)"; } else if (corr < 0.85) { concl = "The SOM separates the classes very poor. (" + String.format("%.2f", corr * 100) + "% of non-empty units contain more than one class)"; } else { concl = "The SOM separates the classes very poor, there's hardly any unit containing only one class. (" + String.format("%.2f", corr * 100) + "% of non-empty units contain more than one class)"; } double average = (double) this.getGrowingSOM().getLayer().getXSize() * (double) this.getGrowingSOM().getLayer().getYSize() / this.datasetInfo.getNumberOfClasses(); concl += " This is also supported by the fact, that for " + this.getGrowingSOM().getLayer().getXSize() + " x " + this.getGrowingSOM().getLayer().getYSize() + " units " + "of the Map, " + String.format("%.2f", average) + " units are reserved in average for one Class," + "with each Class having an average span of " + String.format("%.2f", this.getMeanClassSpread()) + " units"; return concl; } /** * helper function to initialize the Shared data object for visualizations * * @return a SharedSOMVisualiationData object storing the paths to the important files */ public SharedSOMVisualisationData getVisData() { if (this.visData == null) { visData = new SharedSOMVisualisationData(); visData.setFileName(SOMVisualisationData.DATA_WINNER_MAPPING, this.dwFilePath); visData.setFileName(SOMVisualisationData.CLASS_INFO, this.datasetInfo.getClassInformationFilename()); visData.setFileName(SOMVisualisationData.INPUT_VECTOR, this.datasetInfo.getInputDataFilename()); visData.setFileName(SOMVisualisationData.TEMPLATE_VECTOR, this.datasetInfo.getTemplateFilename()); visData.setFileName(SOMVisualisationData.LINKAGE_MAP, ""); visData.readAvailableData(); } return this.visData; } /** * UPDATE creates a jpg showing the class distribtion on the trained SOM The jpg is saved to the output dir with the * following name: run_RUNID_FILENAME * * @param outputDir string defining the output directory where the image shall be saved (neither checked nor * created) * @param filename the filename that shall be used for saving the image (run_RUNID_ is prefixed) * @param ClassID Specifies the Class to be shown, when ClasID is -1, teh whoel class dictribution image is painted * @return true if everything worked fine, false in case of any error (false does not give any information about * whether the image exists or not) */ public boolean createClassDistributionImage(String outputDir, String filename, int ClassID) { if (!this.datasetInfo.classInfoAvailable()) { return false; } int width = getSomXSize() * 10; int height = getSomYSize() * 10; // this.thematic_visualizer.setInputObjects(this.getVisData()); String visName = "class distribution visualization"; try { BufferedImage buffImage; if (ClassID > -1) { buffImage = thematic_visualizer.createVisualization(0, this.getGrowingSOM(), width, height, ClassID); } else { buffImage = thematic_visualizer.createVisualization(0, this.getGrowingSOM(), width, height); } VisualisationUtils.drawBorder(buffImage); return writeImagesAsJPG(outputDir, filename, buffImage, visName); } catch (SOMToolboxException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Problems creating the " + visName + " for run " + this.runId + ". Reason: " + e); return false; } } /** Creates a D-Matrix Image */ public boolean createDMatrixImage(String outputDir, String filename, int unitScale) { UMatrix visualizer = new UMatrix(); String visName = "dmatrix visualization"; try { BufferedImage buffImage = visualizer.createVisualization(1, this.getGrowingSOM(), getSomXSize() * unitScale, getSomYSize() * unitScale); return writeImagesAsJPG(outputDir, filename, buffImage, visName); } catch (SOMToolboxException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Problems creating the " + visName + " for run " + this.runId + ". Reason: " + e); return false; } } private boolean writeImagesAsJPG(String outputDir, String filename, BufferedImage buffImage, String visName) { outputDir = FileUtils.prepareOutputDir(outputDir); try { Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("jpeg"); ImageWriter writer = iter.next(); ImageWriteParam iwp = writer.getDefaultWriteParam(); iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); iwp.setCompressionQuality(1);// 1 specifies minimum compression and maximum quality writer.setOutput(new FileImageOutputStream(new File(outputDir + "run_" + this.runId + "_" + filename))); writer.write(buffImage); } catch (IOException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Problems creating the " + visName + " for run " + this.runId + ". Reason: " + e); return false; } return true; } private int getSomYSize() { return this.getGrowingSOM().getLayer().getYSize(); } private int getSomXSize() { return this.getGrowingSOM().getLayer().getXSize(); } /** * creates an image visualizing the quantization error of this SOM the image is saved under the given path and name, * although a prefix run_runId_ is added to the image name * * @param outputDir the path to the output dir where the image shall be saved (must exists, is neither checked nor * created) * @param filename the name under which the image (together with the prefix run_[runId]_ ) is saved * @param variant 0 = normal, 1 =mean Quant * @return true if everything worked fine, false if there was any problem. False does neither assure that the image * exists, nor that is does not */ public boolean createQuantizationErrorImage(String outputDir, String filename, int variant) { QuantizationErrorVisualizer visualizer = new QuantizationErrorVisualizer(); visualizer.setPalette(Palettes.getPaletteByName("Cartography Color, 128 Gradient (SOMToolbox 0.4.x)")); int unitScale = 30; int width = getSomXSize() * unitScale; int height = getSomYSize() * unitScale; String visName = "quantization error visualization"; BufferedImage buffImage = visualizer.createVisualization(variant, this.getGrowingSOM(), width, height); VisualisationUtils.drawUnitGrid(buffImage, getGrowingSOM(), width, height); return writeImagesAsJPG(outputDir, filename, buffImage, visName); } /** Creates a Silhouette image */ public boolean createSilouetteImage(String outputDir, String filename, int variant) { SilhouetteVisualizer visualizer = (SilhouetteVisualizer) Visualizations.getVisualizationByName( "Silhouette Value").getVis(); visualizer.setPalette(Palettes.getPaletteByName("Cartography Color, 128 Gradient (SOMToolbox 0.4.x)")); int unitScale = 30; int width = getSomXSize() * unitScale; int height = getSomYSize() * unitScale; String visName = "Silhouette visualization"; BufferedImage buffImage = visualizer.createVisualization(variant, this.getGrowingSOM(), width, height); VisualisationUtils.drawUnitGrid(buffImage, getGrowingSOM(), width, height); return writeImagesAsJPG(outputDir, filename, buffImage, visName); } /** * creates an image visualizing the topographic error of this SOM the image is saved under the given path and name, * although a prefix run_runId_ is added to the image name * * @param outputDir the path to the output dir where the image shall be saved (must exists, is neither checked nor * created) * @param filename the name under which the image (together with the prefix run_[runId]_ ) is saved * @return true if everything worked fine, false if there was any problem. False does neither assure that the image * exists, nor that is does not */ public boolean createTopographicErrorImage(String outputDir, String filename) { TopographicErrorVisualizer visualizer = (TopographicErrorVisualizer) Visualizations.getVisualizationByName( "Topographic Error neighbourhood - 4 units").getVis(); // visualizer.setInputObjects(this.getVisData()); int unitScale = 30; int width = getSomXSize() * unitScale; int height = getSomYSize() * unitScale; String visName = "topographic error visualization"; try { BufferedImage buffImage = visualizer.createVisualization(0, this.getGrowingSOM(), width, height); VisualisationUtils.drawUnitGrid(buffImage, getGrowingSOM(), width, height); writeImagesAsJPG(outputDir, filename, buffImage, visName); } catch (SOMToolboxException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Problems creating the " + visName + " for run " + this.runId + ". Reason: " + e); return false; } return true; } /** Creates a Intrinsic Distance Image */ public boolean createIntrinsicDistanceImage(String outputDir, String filename) { IntrinsicDistanceVisualizer visualizer = (IntrinsicDistanceVisualizer) Visualizations.getVisualizationByName( "Intrinsic Distance").getVis(); int unitScale = 30; int width = getSomXSize() * unitScale; int height = getSomYSize() * unitScale; String visName = "Intrinisic Distance visualization"; try { BufferedImage buffImage = visualizer.createVisualization(2, this.getGrowingSOM(), width, height); VisualisationUtils.drawUnitGrid(buffImage, getGrowingSOM(), width, height); return writeImagesAsJPG(outputDir, filename, buffImage, visName); } catch (SOMToolboxException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Problems creating the " + visName + " for run " + this.runId + ". Reason: " + e); return false; } } /** * create the U-D-Matrix visualization of the SOM the image is saved under the given path (which is neither checked * nor created) under the given name, only the prefix run_runId_ is added to the filename. * * @param outputDir the path to the output dir where the image shall be saved (must exists, is neither checked nor * created * @param filename the name under which the image (together with the prefix run_[runId]_ ) is saved * @param unitScale defines how many pixels shall be used to represent one unit (so one unit has size * unitScalexunitScale pixels * @param index which visualisation to create; 0 = U Matrix, 1 = D Matrix; * @return true if everything worked fine, false if there was any problem. False does neither assure that the image * exists, nor that is does not */ public boolean createUDMatrixImage(String outputDir, String filename, int unitScale, int index) { int width = getSomXSize() * unitScale; int height = getSomYSize() * unitScale; UMatrix visualizer = new UMatrix(); visualizer.setPalette(Palettes.getPaletteByName("RGB, 256 Gradient")); this.createPaletteImage(outputDir, "palette_17.jpg", "RGB, 256 Gradient"); String visName = "umatrix visualization"; try { BufferedImage buffImage = visualizer.createVisualization(index, this.getGrowingSOM(), width, height); VisualisationUtils.drawUnitGrid(buffImage, getGrowingSOM(), width, height); return writeImagesAsJPG(outputDir, filename, buffImage, visName); } catch (SOMToolboxException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Problems creating the " + visName + " for run " + this.runId + ". Reason: " + e); return false; } } /** * create the Flow & BorderLine visualization of the SOM the image is saved under the given path (which is neither * checked nor created) under the given name, only the prefix run_runId_ is added to the filename. * * @param outputDir the path to the output dir where the image shall be saved (must exists, is neither checked nor * created * @param filename the name under which the image (together with the prefix run_[runId]_ ) is saved * @param unitScale defines how many pixels shall be used to represent one unit (so one unit has size * unitScalexunitScale pixels * @return true if everything worked fine, false if there was any problem. False does neither assure that the image * exists, nor that is does not */ public boolean createFlowBorderLineImage(String outputDir, String filename, int unitScale) { FlowBorderlineVisualizer visualizer = new FlowBorderlineVisualizer(); String visName = "FlowBorderlineVisualizer visualization"; int width = getSomXSize() * unitScale; int height = getSomYSize() * unitScale; try { BufferedImage buffImage = visualizer.createVisualization(2, this.getGrowingSOM(), width, height); VisualisationUtils.drawUnitGrid(buffImage, getGrowingSOM(), width, height); return writeImagesAsJPG(outputDir, filename, buffImage, visName); } catch (SOMToolboxException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Problems creating the " + visName + " for run " + this.runId + ". Reason: " + e); return false; } } /** * create the Distortion visualization of the SOM the image is saved under the given path (which is neither checked * nor created) under the given name, only the prefix run_runId_ is added to the filename. * * @param outputDir the path to the output dir where the image shall be saved (must exists, is neither checked nor * created * @param filename the name under which the image (together with the prefix run_[runId]_ ) is saved * @param unitScale defines how many pixels shall be used to represent one unit (so one unit has size * unitScalexunitScale pixels * @param index the index * @return true if everything worked fine, false if there was any problem. False does neither assure that the image * exists, nor that is does not */ public boolean createDistortionImage(String outputDir, String filename, int unitScale, int index) { int width = getSomXSize() * unitScale; int height = getSomYSize() * unitScale; MappingDistortionVisualizer visualizer = new MappingDistortionVisualizer(); String visName = "Distortion visualization"; try { BufferedImage buffImage = visualizer.createVisualization(index, this.getGrowingSOM(), width, height); VisualisationUtils.drawUnitGrid(buffImage, getGrowingSOM(), width, height); return writeImagesAsJPG(outputDir, filename, buffImage, visName); } catch (SOMToolboxException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Problems creating the " + visName + " for run " + this.runId + ". Reason: " + e); return false; } } /** * create a SDH visualization of the SOM the image is saved under the given path (which is neither checked nor * created) under the given name, only the prefix run_runId_ is added to the filename. * * @param outputDir the path to the output dir where the image shall be saved (must exists, is neither checked nor * created * @param filename the name under which the image (together with the prefix run_[runId]_ ) is saved * @param unitScale defines how many pixels shall be used to represent one unit (so one unit has size * unitScalexunitScale pixels * @param Latex used for latex output */ public void createSDHImages(String outputDir, String filename, int unitScale, boolean Latex, int step) { int X = this.getGrowingSOM().getLayer().getXSize(); int Y = this.getGrowingSOM().getLayer().getYSize(); this.sdh_visualizer = new SmoothedDataHistograms(); sdh_visualizer.setPalette(Palettes.getPaletteByName("Cartography Color, 256 Gradient, less water")); outputDir = FileUtils.prepareOutputDir(outputDir); outputDir += "SDH_pics_" + this.runId + File.separator; FileUtils.clearOutputDir(outputDir); int dim = this.getGrowingSOM().getLayer().getXSize() * this.getGrowingSOM().getLayer().getYSize(); String visName = "SDH Visualization"; if (Latex) { step = dim / 3;// create only 3 pictures in latex output } try { for (int smoothing = 1; smoothing < dim; smoothing += step) { String name = "s" + smoothing + "_" + filename; BufferedImage buffImage = sdh_visualizer.getVisualization(0, smoothing, this.getGrowingSOM(), X * unitScale, Y * unitScale); VisualisationUtils.drawUnitGrid(buffImage, getGrowingSOM(), X * unitScale, Y * unitScale); writeImagesAsJPG(outputDir, name, buffImage, visName); Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").info( "Created SDH image " + smoothing + " / " + (dim - 1)); } } catch (SOMToolboxException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Problems creating the " + visName + " for run " + this.runId + ". Reason: " + e); } } /** * create TrustWortyNess pictures of the SOM the image is saved under the given path (which is neither checked nor * created) under the given name, only the prefix run_runId_ is added to the filename. * * @param outputDir the path to the output dir where the image shall be saved (must exists, is neither checked nor * created * @param filename the name under which the image (together with the prefix run_[runId]_ ) is saved * @param unitScale defines how many pixels shall be used to represent one unit (so one unit has size * unitScalexunitScale pixels * @param Latex used for latex output */ public void createTrustworthyNessImages(String outputDir, String filename, int unitScale, boolean Latex, int step) { int height = getSomYSize() * unitScale; int width = getSomXSize() * unitScale; this.trustworthyness_visualizer = new TrustwothinessVisualizer(); outputDir = FileUtils.prepareOutputDir(outputDir); outputDir += "TrustworthyNessImages_pics_" + this.runId + File.separator; FileUtils.clearOutputDir(outputDir); int dim = this.getGrowingSOM().getLayer().getXSize() * this.getGrowingSOM().getLayer().getYSize(); // get visualization String visName = "Trustworthy Visualization"; if (!Latex) { for (int i = 1; i < dim; i = i + step) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").info( "Created Trustworthyness image " + i + " / " + (getGrowingSOM().getLayer().getXSize() * getGrowingSOM().getLayer().getYSize() - 1)); BufferedImage buffImage = trustworthyness_visualizer.createTWImage(getGrowingSOM(), width, height, i); VisualisationUtils.drawUnitGrid(buffImage, getGrowingSOM(), width, height); String name = "tw_" + i + filename; writeImagesAsJPG(outputDir, name, buffImage, visName); } } else { // create only 3 pictures in latex output for (int i = 1; i < dim; i = i + dim / 3) { BufferedImage buffImage = trustworthyness_visualizer.createTWImage(this.getGrowingSOM(), width, height, i); VisualisationUtils.drawUnitGrid(buffImage, getGrowingSOM(), width, height); String name = "tw" + i + filename; writeImagesAsJPG(outputDir, name, buffImage, visName); } } } /** * create a TopographicProduct visualization of the SOM the image is saved under the given path (which is neither * checked nor created) under the given name, only the prefix run_runId_ is added to the filename. * * @param outputDir the path to the output dir where the image shall be saved (must exists, is neither checked nor * created * @param filename the name under which the image (together with the prefix run_[runId]_ ) is saved * @param unitScale defines how many pixels shall be used to represent one unit (so one unit has size * unitScalexunitScale pixels * @param Latex used for latex output */ public void createTopographicProductImages(String outputDir, String filename, int unitScale, boolean Latex, int step) { int X = this.getGrowingSOM().getLayer().getXSize(); int Y = this.getGrowingSOM().getLayer().getYSize(); this.topographicproduct_visualizer = new TopographicProductVisualizer(); outputDir = FileUtils.prepareOutputDir(outputDir); outputDir += "TopographicProduct_pics_" + this.runId + File.separator; FileUtils.clearOutputDir(outputDir); int dim = this.getGrowingSOM().getLayer().getXSize() * this.getGrowingSOM().getLayer().getYSize(); // get visualization String visName = "TopographicProduct Visualization"; if (!Latex) { for (int i = 1; i < dim; i = i + step) { String name = "tp_" + i + filename; BufferedImage buffImage = topographicproduct_visualizer.createTPImage(this.getGrowingSOM(), getSomXSize() * unitScale, getSomYSize() * unitScale, i); VisualisationUtils.drawUnitGrid(buffImage, getGrowingSOM(), X * unitScale, Y * unitScale); writeImagesAsJPG(outputDir, name, buffImage, visName); } } else { // create only 3 pictures in latex output for (int i = 1; i < dim; i = i + dim / 3) { String name = "tw" + i + filename; BufferedImage buffImage = topographicproduct_visualizer.createTPImage(this.getGrowingSOM(), getSomXSize() * unitScale, getSomYSize() * unitScale, i); VisualisationUtils.drawUnitGrid(buffImage, getGrowingSOM(), X * unitScale, Y * unitScale); writeImagesAsJPG(outputDir, name, buffImage, visName); } } } /** * creates a visualization of the distribution of the input data on the SOM the image is saved under the given path * (which is neither checked nor created) under the given name, only the prefix run_runId_ is added to the filename. * * @param outputDir the path to the output dir where the image shall be saved (must exists, is neither checked nor * created * @param filename the name under which the image (together with the prefix run_[runId]_ ) is saved */ public boolean createInputDistributionImage(String outputDir, String filename) { SmoothedCountHistograms visualizer = new SmoothedCountHistograms(); visualizer.setPalette(Palettes.getPaletteByName("Cartography Color, 256 Gradient, less water")); // we first have to find out the data distribution we want to visualize Unit[][] us = this.getGrowingSOM().getLayer().get2DUnits(); double[][] hist = new double[us.length][us[0].length]; for (int i = 0; i < us.length; i++) { for (int j = 0; j < us[i].length; j++) { hist[i][j] = us[i][j].getNumberOfMappedInputs(); } } visualizer.setHistogram(hist); String visName = "visualization of the distribution of input values"; try { BufferedImage buffImage = visualizer.createVisualization(0, this.getGrowingSOM(), getSomXSize() * 10, getSomYSize() * 10); return writeImagesAsJPG(outputDir, filename, buffImage, visName); } catch (SOMLibFileFormatException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Problems creating the " + visName + " for run " + this.runId + ". Reason: " + e); return false; } catch (SOMToolboxException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Problems creating the " + visName + " for run " + this.runId + ". Reason: " + e); return false; } } /** * Creates a SingleMetroMapComponentImage with the given component index. Note that the dimensions of the Pictures * are fixed. */ public boolean createSingleMetroMapComponentImage(String outputDir, String filename, int component) { String visName = "Single Component MetroMap image of input values"; BufferedImage buffImage = metroVis.createMetromapImage(0, getGrowingSOM(), this.getGrowingSOM().getLayer().getXSize() * 10, this.getGrowingSOM().getLayer().getYSize() * 10, component); return writeImagesAsJPG(outputDir, "component_" + filename, buffImage, visName); } /** * Creates an Entropy Image, and saves it at the given Location. * * @param ClassID if ClassID is set to -1, the Picture generated by default is that of all classes of the SOM. If * ClassID is > -1, the Class with the specified ClassID-index is displayed */ public boolean createEntropyImage(String outputDir, String filename, int ClassID) { if (!datasetInfo.classInfoAvailable()) { return false; } this.entropy_visualizer.setPalette(Palettes.getPaletteByName("Redscale, 32 Gradient")); String visName = "visualization of the entropy distribution"; try { BufferedImage buffImage = entropy_visualizer.createVisualization(ClassID, this.getGrowingSOM(), getSomXSize() * 10, getSomYSize() * 10); VisualisationUtils.drawBorder(buffImage); return writeImagesAsJPG(outputDir, filename, buffImage, visName); } catch (SOMToolboxException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Problems creating the " + visName + " for run " + this.runId + ". Reason: " + e); return false; } } /** * Creates a Palette Image on the color palette on the specified Index<br/> * FIXME: move this way from this class */ public void createPaletteImage(String outputDir, String filename, String palName) { Palette pal = Palettes.getPaletteByName(palName); BufferedImage img = new BufferedImage(265, 40, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = img.createGraphics(); int scaleHeight = 0; int scaleWidth = 0; Rectangle2D.Double square = new Rectangle2D.Double(0, 0, 265, 40); g2d.setColor(Color.white); g2d.fill(square); g2d.draw(square); Font font = new Font("Monospaced", Font.PLAIN, 9); g2d.setColor(Color.BLACK); g2d.setFont(font); FontMetrics metrics = g2d.getFontMetrics(font); scaleHeight = metrics.getHeight() + 2; scaleWidth = metrics.stringWidth("100"); int Width = 215; int Height = 34; // System.out.println("Font: " + scaleWidth + "x" + scaleHeight); int werte = Width / (scaleWidth * 3); float step = 100f / werte; for (int i = 0; i <= werte; i++) { String text = Math.round(i * step) + ""; int x = scaleWidth / 2 - metrics.stringWidth(text) / 2 + Math.round((Width - scaleWidth) * i * step / 100); int y = Height - metrics.getDescent(); g2d.drawString(text, x, y); } int xStart = scaleWidth / 2; int xEnd = Width - scaleWidth / 2; int paletteHeight = Height - scaleHeight; // - insets.top - insets.bottom - scaleHeight; Color[] colors = pal.getColors(); float step1 = (float) (xEnd - xStart) / (float) colors.length; for (int i = 0; i < colors.length; i++) { g2d.setColor(colors[i]); g2d.fillRect(xStart + Math.round(step1 * i), 0, Math.round(step1 + 1), paletteHeight); } try { ImageIO.write(img, "jpg", new File(outputDir + filename)); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * calculates for each unit on the SOM, how many members of each class are mapped to it the resulting value is * stored in a 3-dim. array, where the first dimension denots the x position of the unit, the second the y position, * and the third the id of the class. There the number of input items mapped to this unit belongig to this class is * saved. this array is stored in this.classDistribution */ private void prepareClassDistribution() { // x, y, class if (this.datasetInfo.getNumberOfClasses() > 0) { this.classDistribution = new int[getSomXSize()][getSomYSize()][this.datasetInfo.getNumberOfClasses()]; } else { this.classDistribution = new int[getSomXSize()][getSomYSize()][1]; } // initialize the array with 0: for (int x = 0; x < this.classDistribution.length; x++) { for (int y = 0; y < this.classDistribution[x].length; y++) { for (int c = 0; c < this.classDistribution[x][y].length; c++) { this.classDistribution[x][y][c] = 0; } } } // now start counting (I couldn't find these information anywhere :( GrowingSOM som = this.getGrowingSOM(); Layer layer = som.getLayer(); for (int i = 0; i < this.datasetInfo.getNumberOfInputVectors(); i++) { InputDatum input = this.datasetInfo.getInputDatum(i); Unit unit = layer.getUnitForDatum(input.getLabel()); if (this.datasetInfo.getNumberOfClasses() > 0) { this.classDistribution[unit.getXPos()][unit.getYPos()][this.datasetInfo.getClassIndexOfInput(input.getLabel())]++; } else { this.classDistribution[unit.getXPos()][unit.getYPos()][0]++; } } } /** * creates for each unit having at least one input item mapped to it an image of the class distribution the images * are stored in an subdirectory of the specified directory (which is neither checked nor created) named * "pieCharts_runId". All content in this directory is deleted before the images are created. * * @param outputDir the path to the output dir where the image shall be saved (must exists, is neither checked nor * created */ public void createPieChartImages(String outputDir) { // calculate which classes are in which units this.prepareClassDistribution(); outputDir = FileUtils.prepareOutputDir(outputDir); outputDir += "pieCharts_" + this.runId + File.separator; FileUtils.clearOutputDir(outputDir); int unitWidth = 60; int unitHeight = 60; final SOMLibClassInformation classInfo = som.getSharedInputObjects().getClassInfo(); final Color[] classColors = classInfo.getClassColors(); // ok - now that we know the class distribution within each unit, we paint the diagrams for (int x = 0; x < this.classDistribution.length; x++) { for (int y = 0; y < this.classDistribution[x].length; y++) { if (MathUtils.getSumOf(this.classDistribution[x][y]) > 0) { String visName = "pie charts"; BufferedImage buffImage = new BufferedImage(50, 50, BufferedImage.TYPE_INT_RGB); Graphics2D graphics = buffImage.createGraphics(); graphics.setBackground(Color.WHITE); graphics.fillRect(0, 0, buffImage.getWidth(), buffImage.getHeight()); try { if (som.getLayer().getUnit(x, y) != null && som.getLayer().getUnit(x, y).getNumberOfMappedInputs() > 0) { int[] values = classInfo.computeClassDistribution(som.getLayer().getUnit(x, y).getMappedInputNames()); PieChartPNode.drawPlot(graphics, values, classColors, x * unitWidth, y * unitHeight, unitWidth, unitHeight); } } catch (LayerAccessException e) { // should not happen e.printStackTrace(); } String filename = x + "_" + y + "_" + MathUtils.getSumOf(this.classDistribution[x][y]) + ".jpg"; writeImagesAsJPG(outputDir, filename, buffImage, visName); } } } } /** * creates and returns the clustering tree for this SOM the cluster method used is WardsLinkageTreeBuilder. If an * error occurs, null is probably returned. * * @return the clustering tree for this som, or null in case of an error */ public ClusteringTree getClusterTree() { if (this.clusterTree == null) { MapPNode mapPNode = new MapPNode(null, getGrowingSOM(), getGrowingSOM().getLayer(), state); WardsLinkageTreeBuilderAll clustering = new WardsLinkageTreeBuilderAll(); GeneralUnitPNode[][] unitNodes = new GeneralUnitPNode[getSomXSize()][getSomYSize()]; for (int x = 0; x < unitNodes.length; x++) { for (int y = 0; y < unitNodes[x].length; y++) { unitNodes[x][y] = mapPNode.getUnit(x, y); } } try { this.clusterTree = clustering.createTree(unitNodes); } catch (ClusteringAbortedException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Problems trying to create clusters for run " + this.runId + ". Reason: " + e); return null; } } return this.clusterTree; } /** * tries to sort all possible clusters by their "stability" that is, tries to give a measure about how good a * cluster is. Thereby, as simple qualification method, simply the improvement of the distance between all units * within a cluster and the mean vector of a cluster, divided by the number of units within a cluster compared to * the same value in the parent cluster is chosen. This works surprisingly well. (that is: biggest difference of * parentvalue - my value) * * @param node the node in the cluster tree, whose subtree shall be scanned through * @param parentValue the value of the quality measure in the parent node * @param boundary a boundary for the level of clusters, where the calculation shall be stopped, that is a boundary * for the depth the subtree of node shall be scanned * @param stableClusterList a sorted list of clusters, into which the clusters of this subtree shall be inserted- * @return an ordered list containing all the clusters specified in the last argument as well as all clusters in the * subtree rooted in node "node" having level smaller than boundary */ public Vector<double[]> getStableClusters2(ClusterNode node, double parentValue, int boundary, Vector<double[]> stableClusterList) { if (node.getLevel() > boundary) { return stableClusterList; } ClusterNode lc = node.getChild1(); ClusterNode rc = node.getChild2(); // berechne die Summe der Entfernungen zwischen units in cluster und mean Vector GeneralUnitPNode[] units = node.getNodes(); double dist = 0; try { for (GeneralUnitPNode unit : units) { dist += this.getGrowingSOM().getLayer().getMetric().distance(unit.getUnit().getWeightVector(), node.getMeanVector()); } } catch (MetricException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Error calcuating distance in run " + this.runId + " ... Reason: " + e); dist = -1; } // dist = dist / (double)units.length; double gain = parentValue - dist; boolean added = false; for (int i = 0; i < stableClusterList.size(); i++) { if (gain > stableClusterList.get(i)[0]) { stableClusterList.add(i, new double[] { gain, node.getLevel() }); added = true; break; } } if (!added) { stableClusterList.add(new double[] { gain, node.getLevel() }); } // descend if (lc.getLevel() + 1 <= boundary) { // first child stableClusterList = this.getStableClusters2(lc, dist, boundary, stableClusterList); } if (rc.getLevel() + 1 <= boundary) { // second Child stableClusterList = this.getStableClusters2(rc, dist, boundary, stableClusterList); } return stableClusterList; } /** * returns for the given cluster, how many input elements of the different classses are mapped to units of this * cluster returns an array, that contains for each class, how many input vectors belonging to this class are mapped * to units of the cluster * * @param clusterLevel the level of the cluster * @return the number of inputs for each cluss within the cluster */ public int[] getClassDistributionInCluster(int clusterLevel) { int[] distribution = new int[this.datasetInfo.getNumberOfClasses()]; int[] temp; Unit unit; if (this.classDistribution == null) { this.prepareClassDistribution(); } ClusterNode node = this.getClusterTree().findNode(clusterLevel); GeneralUnitPNode[] units = node.getNodes(); for (int i = 0; i < distribution.length; i++) { distribution[i] = 0; } if (units == null) { return distribution; } for (GeneralUnitPNode unit2 : units) { unit = unit2.getUnit(); temp = this.classDistribution[unit.getXPos()][unit.getYPos()]; for (int j = 0; j < temp.length; j++) { distribution[j] += temp[j]; } } return distribution; } /** * creates a visualization of the given cluster the visualization is an image of the SOM, where the units are * displayed, an the units belonging to the cluster are coloured. Further, the name of the cluster is written in the * cluster (as least tried to)<br/> * <br/> * WARNING!! nodeDepths should contain the depths of the cluster nodes. But for level i, the according depth is not * in nodeDepths[i-1] or [i], but at [i+1]!!!! * * @param clusterLevel the level of the cluster to be visualized (uniquly identifies the cluster within the tree) * @param outputDir the path to the output dir where the image shall be saved (must exists, is neither checked nor * created * @param nodeDepths the array containing the depths of the cluster nodes in the cluster tree. Read warning above!!! * @param preferredUnitSize the length a unit-side has in the ideal case (that is if there aren't too many units), * that is, a unit gets dimenstions preferredUnitSizexpreferredUnitSize * @return true if no error was encountered during execution, false otherwise (if false is returned, nothing can be * stated about whether the image exists or not) */ public boolean visualizeCluster(int clusterLevel, String outputDir, int[] nodeDepths, int preferredUnitSize) { String visName = "cluster visualization of level " + clusterLevel; // retrieve the nodes belonging to the cluster ClusterNode node = this.getClusterTree().findNode(clusterLevel); GeneralUnitPNode[] units = node.getNodes(); if (units.length == 0) { return false; // no unit in this cluster => it's no interesting cluster at all } int maxImageSize = 500; // the max. number of pixels the larger side of the image may have int xunits = getSomXSize(); // number of units in x direction int yunits = getSomYSize(); // number of units in y direction int maxUnits = Math.max(xunits, yunits); int unitSize = preferredUnitSize; if (maxUnits * preferredUnitSize > maxImageSize) { // the we have to use a smaller unit size unitSize = maxImageSize / maxUnits; if (unitSize == 0) { unitSize = 1; // well a unit needs some space } } int width = unitSize * xunits + 10; // the width of the complete image int height = unitSize * yunits + 10; // the height of the complete image int x = 0; // x position of a unit in the image int y = 0; // y position of a unit in the image double scale = -1; BufferedImage buffImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D graphics = buffImage.createGraphics(); graphics.setBackground(Color.WHITE); graphics.fillRect(0, 0, width, height); graphics.setColor(Color.black); Rectangle rect = new Rectangle(5, 5, width - 10, height - 10); // draw the border of the SOM graphics.draw(rect); // ... rect.setBounds(0, 0, unitSize, unitSize); // prepare the Rectangle representing a unit graphics.setColor(Color.CYAN); // we use a not such intensive color if (units.length > 0) { scale = unitSize / units[0].getWidth(); } // now we paint the units for (GeneralUnitPNode unit : units) { x = 5 + (int) (unit.getX() / unit.getWidth()) * unitSize; y = 5 + (int) (unit.getY() / unit.getHeight()) * unitSize; rect.setLocation(x, y); graphics.fill(rect); } // draw the units of the map - looks nicer than only the coloured cluster graphics.setColor(Color.black); for (int i = 0; i < xunits; i++) { for (int j = 0; j < yunits; j++) { x = 5 + i * unitSize; y = 5 + j * unitSize; rect.setLocation(x, y); graphics.draw(rect); } } // and finally we try to label the cluster Vector<String> clusterNames = this.datasetInfo.getClusterName(node, (int) CommonSOMViewerStateData.getInstance().clusterByValue, nodeDepths[node.getLevel() + 1]); graphics.setColor(Color.RED); Point2D.Double center = node.getCentroid(); int twidth = 0; for (int j = 0; j < clusterNames.size(); j++) { // if(j > 0) label += "\n "; // label += clusterNames.get(j); if (clusterNames.get(j).length() > twidth) { twidth = clusterNames.get(j).length(); } } x = 5 + (int) (center.getX() * scale); y = 5 + (int) (center.getY() * scale); twidth = twidth * 10; int theight = clusterNames.size() * 20; // correct x and y position of text if (x + twidth > unitSize * xunits + 10) { x = Math.max(0, unitSize * xunits + 10 - twidth); } if (y + theight > unitSize * yunits + 10) { y = Math.max(0, unitSize * yunits + 10 - theight); } // we only label the image if we have enough space for the text in a feasible size if (twidth < unitSize * xunits) { for (int i = 0; i < clusterNames.size(); i++) { graphics.drawString(clusterNames.get(i), x, y + i * 20); } } String filename = "clusterLevel_" + clusterLevel + "_" + preferredUnitSize + ".jpg"; return writeImagesAsJPG(outputDir, filename, buffImage, visName); } /** * returns an instance of input reader the following files are handed to the reader: this.weightFilePath, * this.unitFilePath, this.mapFilePath the reader created is saved in this.inputReader * * @return an instance of input reader. */ protected SOMLibFormatInputReader getInputReader() { if (this.inputReader == null) { try { this.inputReader = new SOMLibFormatInputReader(this.weightFilePath, this.unitFilePath, this.mapFilePath); } catch (FileNotFoundException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").severe( "Cannot find file for creating input reader: " + e); } catch (SOMLibFileFormatException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").severe("Cannot create input reader: " + e); } } return this.inputReader; } /** * returns an instance of CommonSOMViewerStateData, that stores basic properties for visualization of SOMs and is * needed for creating most of the images * * @return and instance of this object, initialized with only default values. */ private CommonSOMViewerStateData getStateObject() { if (this.state == null) { this.state = new CommonSOMViewerStateData(); this.state.inputDataObjects = this.getVisData(); this.state.clusterWithLabels = 2; this.state.labelsWithValues = false; } return this.state; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* UNUSED TRIES */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* the following two functions are two different tries to determine good clusters */ /** Quality measure for clusters that didn't work */ public Vector<double[]> getStableClusters1(ClusterNode node, int count, int boundary, Vector<double[]> stableClusterList) { if (node.getLevel() > boundary) { return stableClusterList; } ClusterNode lc = node.getChild1(); ClusterNode rc = node.getChild2(); // calculate cluster stability double curStable = (double) (node.getLevel() - count - 1) / (double) node.getLevel(); boolean added = false; for (int i = 0; i < stableClusterList.size(); i++) { if (curStable > stableClusterList.get(i)[0]) { stableClusterList.add(i, new double[] { curStable, node.getLevel() }); added = true; break; } } if (!added) { stableClusterList.add(new double[] { curStable, node.getLevel() }); } // descend if (lc.getLevel() + 1 <= boundary) { // first child stableClusterList = this.getStableClusters1(lc, node.getLevel(), boundary, stableClusterList); } if (rc.getLevel() + 1 <= boundary) { // second Child stableClusterList = this.getStableClusters1(rc, node.getLevel(), boundary, stableClusterList); } return stableClusterList; } /** quality measure for clusters that didn't work */ public Vector<double[]> getStableClusters3(ClusterNode node, double parentValue, int boundary, Vector<double[]> stableClusterList) { if (node.getLevel() > boundary) { return stableClusterList; } ClusterNode lc = node.getChild1(); ClusterNode rc = node.getChild2(); // berechne die Summe der Entfernungen zwischen units in cluster und mean Vector GeneralUnitPNode[] units = node.getNodes(); double dist = 0; try { for (GeneralUnitPNode unit : units) { dist += this.getGrowingSOM().getLayer().getMetric().distance(unit.getUnit().getWeightVector(), node.getMeanVector()); } } catch (MetricException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Error calcuating distance in run " + this.runId + " ... Reason: " + e); dist = -1; } if (units.length > 0) { dist = dist / units.length; } double gain = Math.abs(parentValue - dist); boolean added = false; for (int i = 0; i < stableClusterList.size(); i++) { if (gain > stableClusterList.get(i)[0]) { stableClusterList.add(i, new double[] { gain, node.getLevel() }); added = true; break; } } if (!added) { stableClusterList.add(new double[] { gain, node.getLevel() }); } // descend if (lc.getLevel() + 1 <= boundary) { // first child stableClusterList = this.getStableClusters2(lc, dist, boundary, stableClusterList); } if (rc.getLevel() + 1 <= boundary) { // second Child stableClusterList = this.getStableClusters2(rc, dist, boundary, stableClusterList); } return stableClusterList; } /** Returns an Array containing the Range in SOMUnits from all Classes, or null if no Class info is available */ public int[] getClassRangesInUnits() { int[] values = new int[this.datasetInfo.getNumberOfClasses()]; boolean[] added = new boolean[this.datasetInfo.getNumberOfClasses()]; /* * thjis is used to check wheter a class individual has already been * added to the array.Since we only want to know how many units have * inidividuals of a class */ Arrays.fill(added, false); if (this.datasetInfo.classInfoAvailable()) { int sizeX = this.getGrowingSOM().getLayer().getXSize(); int sizeY = this.getGrowingSOM().getLayer().getYSize(); try { for (int i = 0; i < sizeX; i++) { for (int j = 0; j < sizeY; j++) { Unit unit = this.getGrowingSOM().getLayer().getUnit(i, j); if (unit != null && unit.getNumberOfMappedInputs() > 0) { for (int c = 0; c < unit.getNumberOfMappedInputs(); c++) { if (added[this.datasetInfo.getClassIndexOfInput(unit.getMappedInputNames()[c])] == false) { values[this.datasetInfo.getClassIndexOfInput(unit.getMappedInputNames()[c])] += 1; added[this.datasetInfo.getClassIndexOfInput(unit.getMappedInputNames()[c])] = true; } } } Arrays.fill(added, false); /* set alle boolean values to false again, and go to the next unit */ } } } catch (LayerAccessException e) { e.printStackTrace(); } return values; } else { return null; } } /** * Returns the mean Class spread of all Classes in Units; * * @return double the mean class spread, unit measured */ public double getMeanClassSpread() { int[] values = this.getClassRangesInUnits(); int number = 0; if (values != null) { for (int value : values) { number += value; } return (double) number / values.length; } else { return number; } } /** * returns Entropy (Max/Min) error Region * * @param mode (1 = max, 2 = min) */ public double getRegionEntropyError(int mode) { double val = -1.0; switch (mode) { case 1: val = this.entropy_visualizer.getMaximumEntropy(); break; case 2: val = this.entropy_visualizer.getMinimumEntropy(); break; } return val; } /** Returns the maximum Entropy Error Region's classnames */ public String[][] getMaxEErrorRegionNames() { return this.entropy_visualizer.getMaximumEntropyRegionNames(); } /** Returns the percentage of regions with 0 entropy */ public double getPercOfZeroEntropyRegions() { return this.entropy_visualizer.getPercOfZeroEntropyRegions(); } /** Returns the entropy for class with index */ public double getClassEntropy(int index) { return this.entropy_visualizer.ClassEntropy(index); } /** Returns the class with the maximum EntropyError */ public int getMaximumEEClassIndex() { double max = 0.0; int counter = 0; for (int i = 0; i < this.datasetInfo.getNumberOfClasses(); i++) { if (this.getClassEntropy(i) > max) { max = this.getClassEntropy(i); counter = i; } } return counter; } public HashMap<String, String> getTexts() { return texts; } /** * returns every other class+hits contained in the regions with classmembers from the given index, or null if index * exceeds the possible number of classes */ public String[][] getClassMix(int index) { return this.entropy_visualizer.ClassEntropyNames(index); } /** gets the Type of the Testrunresult (HTML, LATEX) */ public int getType() { return this.type; } /** Returns the SOMs Dimensions */ public int getSOMDimensions() { return this.getGrowingSOM().getLayer().getXSize() * this.getGrowingSOM().getLayer().getYSize(); } /** returns an array containing the counting for each class for the given unit */ public int[] getClassesForUnit(int x, int y) { if (this.classDistribution == null) { this.prepareClassDistribution(); } return this.classDistribution[x][y]; } /** Returns the Class Distribution array for each Unit of the Map */ public int[][][] getClassDistribution() { if (this.classDistribution == null) { this.prepareClassDistribution(); } return this.classDistribution; } public ArrayList<int[]> getAllUnitsContainingClass(int index) { ArrayList<int[]> UnitCoords = new ArrayList<int[]>(); if (this.classDistribution == null) { this.prepareClassDistribution(); } for (int x = 0; x < this.classDistribution.length; x++) { for (int y = 0; y < this.classDistribution[x].length; y++) { if (classDistribution[x][y][index] > 0) { int[] temp = new int[4]; temp[0] = x; temp[1] = y; temp[2] = classDistribution[x][y][index]; UnitCoords.add(temp); } } } return UnitCoords; } public int getNumberofClasses() { return this.datasetInfo.getNumberOfClasses(); } /** * Fills the QMContainer Object with all quality Measures that were specified in the report */ public void fillQMContainer() throws QualityMeasureNotFoundException { this.QMContainer = new QMContainer(this); QuantizationError qerr = new QuantizationError(this.getGrowingSOM().getLayer(), this.datasetInfo.getInputData()); TopographicError toperr = new TopographicError(this.getGrowingSOM().getLayer(), this.datasetInfo.getInputData()); IntrinsicDistance idist = new IntrinsicDistance(this.getGrowingSOM().getLayer(), this.datasetInfo.getInputData()); SilhouetteValue sil = new SilhouetteValue(this.getGrowingSOM().getLayer(), this.datasetInfo.getInputData()); SOMDistortion dist = new SOMDistortion(this.getGrowingSOM().getLayer(), this.datasetInfo.getInputData()); SpearmanCoefficient spear = new SpearmanCoefficient(this.getGrowingSOM().getLayer(), this.datasetInfo.getInputData()); SammonMeasure sammon = new SammonMeasure(this.getGrowingSOM().getLayer(), this.datasetInfo.getInputData()); MetricMultiScaling mscal = new MetricMultiScaling(this.getGrowingSOM().getLayer(), this.datasetInfo.getInputData()); InversionMeasure inv = new InversionMeasure(this.getGrowingSOM().getLayer(), this.datasetInfo.getInputData()); EntropyMeasure entropy = new EntropyMeasure(this.getGrowingSOM().getLayer(), this.datasetInfo.getInputData()); double[][] qerrUnits = qerr.getUnitQualities("qe"); double[][] mqerrUnits = qerr.getUnitQualities("mqe"); double[][] idistUnits = idist.getUnitQualities("ID_Unit"); double[][] teUnits = toperr.getUnitQualities("TE_Unit"); double[][] silUnits = sil.getUnitQualities("silhouette"); double[][] distUnits = dist.getUnitQualities("unitTotal"); double[][] entropyUnits = entropy.getUnitQualities("entropy"); ArrayList<String> wanted_QM = this.datasetInfo.getEP().getSelectedQualitMeasure(); if (wanted_QM.contains("Quantization Error")) { this.QMContainer.putUnitQualities("Quantization Error", qerrUnits); } if (wanted_QM.contains("Mean Quantization Error")) { this.QMContainer.putUnitQualities("Mean Quantization Error", mqerrUnits); } if (wanted_QM.contains("Intrinsic Distance")) { this.QMContainer.putUnitQualities("Intrinsic Distance", idistUnits); } if (wanted_QM.contains("Topographic Error")) { this.QMContainer.putUnitQualities("Topographic Error", teUnits); } if (wanted_QM.contains("Silhouette Value")) { this.QMContainer.putUnitQualities("Silhouette Value", silUnits); } if (wanted_QM.contains("Distortion Values")) { this.QMContainer.putUnitQualities("Distortion Values", distUnits); } if (wanted_QM.contains("Entropy Error")) { this.QMContainer.putUnitQualities("Entropy Error", entropyUnits); } if (wanted_QM.contains("Topographic Error")) { this.QMContainer.putMapQualities("Topographic Error", toperr.getMapQuality("TE_Map")); } if (wanted_QM.contains("Spearman Coefficient")) { this.QMContainer.putMapQualities("Spearman Coefficient", spear.getMapQuality("spearmanCoefficient")); } if (wanted_QM.contains("Silhouette Value")) { this.QMContainer.putMapQualities("Silhouette Value", sil.getMapQuality("silhouettevalue")); } if (wanted_QM.contains("Sammon Measure")) { this.QMContainer.putMapQualities("Sammon Measure", sammon.getMapQuality("sammon")); } if (wanted_QM.contains("Mean Quantization Error")) { this.QMContainer.putMapQualities("Mean Quantization Error", qerr.getMapQuality("mmqe")); } if (wanted_QM.contains("Quantization Error")) { this.QMContainer.putMapQualities("Quantization Error", qerr.getMapQuality("mqe")); } if (wanted_QM.contains("Metric Multiscaling")) { this.QMContainer.putMapQualities("Metric Multiscaling", mscal.getMapQuality("metricmultiscaling")); } if (wanted_QM.contains("Inversion Measure")) { this.QMContainer.putMapQualities("Inversion Measure", inv.getMapQuality("inversion")); } if (wanted_QM.contains("Intrinsic Distance")) { this.QMContainer.putMapQualities("Intrinsic Distance", idist.getMapQuality("ID_Map")); } if (wanted_QM.contains("Intrinsic Distance")) { this.QMContainer.putMapQualities("Entropy Error", entropy.getMapQuality("entropy")); } for (int i = 0; i < this.QMContainer.UnitQualityMeasureNames.size(); i++) { this.QMContainer.classifyUnits(this.QMContainer.UnitQualityMeasureNames.get(i)); } } public QMContainer getQMContainer() { try { if (this.QMContainer == null) { this.fillQMContainer(); } return this.QMContainer; } catch (QualityMeasureNotFoundException e) { Logger.getLogger("at.tuwien.ifs.somtoolbox.reports").warning( "Cannot get unit qualities for run " + this.runId + ". Reason: " + e); return null; } } }