/* * 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.input; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.util.logging.Logger; import at.tuwien.ifs.somtoolbox.SOMToolboxException; import at.tuwien.ifs.somtoolbox.util.FileUtils; import at.tuwien.ifs.somtoolbox.util.StdErrProgressWriter; import at.tuwien.ifs.somtoolbox.util.StringUtils; import at.tuwien.ifs.somtoolbox.visualization.SmoothedDataHistograms; /** * Reads and encapsules the input data - winner information. This means units that are the best-matching ones for the * single input data, and the distances to those units (the distances are used e.g. by the * {@link SmoothedDataHistograms} in their weighted & normalised form. * * @author Michael Dittenbach * @author Rudolf Mayer * @version $Id: SOMLibDataWinnerMapping.java 3988 2011-01-10 15:24:47Z mayer $ */ public class SOMLibDataWinnerMapping { /** Maximum data winners that will be written per unit, unless specified otherwise. */ public static final int MAX_DATA_WINNERS = 300; /** * Inner class holding the list of a certain number of best-matching units for an certain input datum. * * @author Michael Dittenbach */ private class DataInformation { private double[] dists = null; private String label = null; private int[] xPos = null; private int[] yPos = null; private int[] zPos = null; /** * Sole constructor taking the number of units as an argument. * * @param numUnits the number of best-matching units. */ private DataInformation(int numUnits) { xPos = new int[numUnits]; yPos = new int[numUnits]; zPos = new int[numUnits]; dists = new double[numUnits]; } @Override protected DataInformation clone() { DataInformation clone = new DataInformation(this.dists.length); clone.label = this.label; for (int i = 0; i < clone.dists.length; i++) { clone.dists[i] = this.dists[i]; clone.xPos[i] = this.xPos[i]; clone.yPos[i] = this.yPos[i]; clone.zPos[i] = this.zPos[i]; } return clone; } } private DataInformation dataInfo[] = null; private int numBMUs = 0; private int numVectors = 0; private String metric; private boolean is3D, is3Dcached = false; /** * Sole constructor taking the name of the data-winner mapping file as an argument. * * @param fileName the name of the data-winner mapping file. * @throws FileNotFoundException if the file with the given name is not found. * @throws SOMLibFileFormatException if the format of the file is corrupt. */ public SOMLibDataWinnerMapping(String fileName) throws FileNotFoundException, SOMLibFileFormatException { this(); readDataWinnerMappingFile(fileName); } /** * */ private SOMLibDataWinnerMapping() { } /** * Returns an array of <code>double</code> values containing the distances between the input datum and the * best-matching units sorted ascending. If the argument <code>datum</code> is invalid, an * <code>ArrayIndexOutOfBoundsException</code> will be thrown. The calling function is not obliged to catch it. * * @param datum the index of the input datum in the list. * @return an array of double values containing the distances between the input datum and the best-matching units * sorted ascending. */ public double[] getDists(int datum) { return dataInfo[datum].dists; } /** * Returns the number of best-matching units per input datum. * * @return the number of best-matching units per input datum. */ public int getNumBMUs() { return numBMUs; } /** * Returns the number of input vectors. * * @return the number of input vectors. */ public int getNumVectors() { return numVectors; } /** * Returns an array of <code>int</code> values containing the horizontal positions of the the best-matching units of * the input datum. If the argument <code>datum</code> is invalid, an <code>ArrayIndexOutOfBoundsException</code> * will be thrown. The calling function is not obliged to catch it. * * @param datum the index of the input datum in the list. * @return an array of <code>int</code> values containing the horizontal positions of the the best-matching units of * the input datum. */ public int[] getXPos(int datum) { return dataInfo[datum].xPos; } /** * Returns an array of <code>int</code> values containing the vertical positions of the the best-matching units of * the input datum. If the argument <code>datum</code> is invalid, an <code>ArrayIndexOutOfBoundsException</code> * will be thrown. The calling function is not obliged to catch it. * * @param datum the index of the input datum in the list. * @return an array of <code>int</code> values containing the vertical positions of the the best-matching units of * the input datum. */ public int[] getYPos(int datum) { return dataInfo[datum].yPos; } /** * Returns an array of <code>int</code> values containing the depth positions of the the best-matching units of the * input datum. If the argument <code>datum</code> is invalid, an <code>ArrayIndexOutOfBoundsException</code> will * be thrown. The calling function is not obliged to catch it. * * @param datum the index of the input datum in the list. * @return an array of <code>int</code> values containing the depth positions of the the best-matching units of the * input datum. */ public int[] getZPos(int datum) { return dataInfo[datum].zPos; } public String[] getLabels() { String[] labels = new String[dataInfo.length]; for (int i = 0; i < labels.length; i++) { labels[i] = dataInfo[i].label; } return labels; } /** * Finds the position of input vector by comparing its label. * * @param label The label of the input vector * @return Position of the input vector * @throws SOMToolboxException when label is not found in data winner mapping file */ public int getVectPos(String label) throws SOMToolboxException { if (label != null && label.length() > 0) { for (int i = 0; i < dataInfo.length; i++) { if (label.equals(dataInfo[i].label)) { return i; } } } throw new SOMToolboxException("Could not find label '" + label + "' in DataWinnerMapping file!"); } /** * Reads from the file and fills the data structure. * * @param fileName the name of the file to open. * @throws FileNotFoundException if the file with the given name is not found. * @throws SOMLibFileFormatException if the format of the file is corrupt. */ public void readDataWinnerMappingFile(String fileName) throws FileNotFoundException, SOMLibFileFormatException { BufferedReader br = FileUtils.openFile("Data winner mapping file", fileName); String line = null; double fileFormatVersion = 1.0; boolean numVectorsRead = false; boolean numBMUsRead = false; int lineNumber = 0; // PROCESS HEADER with arbitrary number of comment lines & lines starting with $ try { while ((line = br.readLine()) != null) { lineNumber++; if (line.startsWith("#") || line.equals("")) { // ignore comments and empty lines continue; } if (!line.startsWith("$")) { break; } if (line.startsWith("$FILE_FORMAT_VERSION")) { try { fileFormatVersion = Double.parseDouble(line.split(StringUtils.REGEX_SPACE_OR_TAB, 2)[1]); } catch (Exception e) { throw new SOMLibFileFormatException("Unknown $FILE_FORMAT_VERSION"); } } else if (line.startsWith("$NUM_WINNERS ")) { String[] lineElements = line.split(StringUtils.REGEX_SPACE_OR_TAB); if (lineElements.length > 1) { numBMUs = Integer.parseInt(lineElements[1]); numBMUsRead = true; } else { throw new SOMLibFileFormatException("Data winner mapping file format corrupt in line # " + lineNumber + ". $NUM_WINNERS corrupt."); } } else if (line.startsWith("$NUM_VECTORS ")) { String[] lineElements = line.split(StringUtils.REGEX_SPACE_OR_TAB); if (lineElements.length > 1) { numVectors = Integer.parseInt(lineElements[1]); numVectorsRead = true; } else { throw new SOMLibFileFormatException("Data winner mapping file format corrupt in line # " + lineNumber + ". $NUM_VECTORS corrupt."); } } else if (line.startsWith("$METRIC ")) { String[] lineElements = line.split(StringUtils.REGEX_SPACE_OR_TAB); if (lineElements.length == 2) { metric = lineElements[1]; } else { System.out.println(); throw new SOMLibFileFormatException("Data winner mapping file format corrupt in line #" + lineNumber + ". $METRIC corrupt."); } } else { Logger.getLogger("at.tuwien.ifs.somtoolbox").warning( "Unkown Header line #" + lineNumber + ": '" + line + "', ingoring."); } } if (!numVectorsRead || !numBMUsRead) { throw new SOMLibFileFormatException("Data winner mapping file format corrupt: Incomplete header!"); } Logger.getLogger("at.tuwien.ifs.somtoolbox").info("Data winner mapping file version " + fileFormatVersion); /* read rest of files */ dataInfo = new DataInformation[numVectors]; String[] lineElements = null; StdErrProgressWriter progressWriter = new StdErrProgressWriter(numVectors, "Reading winners of input datum ", 10); for (int d = 0; d < numVectors; d++) { dataInfo[d] = new DataInformation(numBMUs); if (fileFormatVersion >= 1.1) { // in this version the file label is on a single line (allowing the label to contain spaces) dataInfo[d].label = line; // the elements are then in the next line line = br.readLine(); } lineElements = line.split(StringUtils.REGEX_SPACE_OR_TAB); int el = 0; // line element position if (fileFormatVersion == 1.0) { dataInfo[d].label = lineElements[el++]; } else if (lineElements[el].equals("")) { el++; // in fileformat >= 1.1 there is a leading space on that line, so that the first element has // to be skipped } for (int w = 0; w < numBMUs; w++) { // numUnits dataInfo[d].xPos[w] = Integer.parseInt(lineElements[el++]); dataInfo[d].yPos[w] = Integer.parseInt(lineElements[el++]); if (fileFormatVersion >= 1.2) { // zPos exists since v1.2 dataInfo[d].zPos[w] = Integer.parseInt(lineElements[el++]); } else { dataInfo[d].zPos[w] = 0; } dataInfo[d].dists[w] = Double.parseDouble(lineElements[el++]); } progressWriter.progress(d + 1); line = br.readLine(); lineNumber++; } br.close(); } catch (IOException e) { throw new SOMLibFileFormatException("Could not read from data winner mapping file. " + e.getMessage()); } catch (NumberFormatException e) { throw new SOMLibFileFormatException("Data winner mapping number format corrupt in line #" + lineNumber + ":" + e.getMessage()); } } public boolean is3D() { if (!is3Dcached) { for (DataInformation element : dataInfo) { for (int zPo : element.zPos) { if (zPo > 0) { is3D = true; break; } } if (is3D) { break; } } is3Dcached = true; } return is3D; } public String getMetric() { return metric; } @Override public SOMLibDataWinnerMapping clone() { SOMLibDataWinnerMapping clone = new SOMLibDataWinnerMapping(); clone.metric = this.metric; clone.numBMUs = this.numBMUs; clone.numVectors = this.numVectors; clone.dataInfo = new DataInformation[this.dataInfo.length]; for (int i = 0; i < clone.dataInfo.length; i++) { clone.dataInfo[i] = this.dataInfo[i].clone(); } return clone; } /** * Rotate the winner mapping. * * @param quadrants rotate the winners for <code>quadrants x 90</code> degrees (clockwise) */ public void rotate(int quadrants, final int xSize, final int ySize) { switch (quadrants) { case 1: // 90 Deg. for (int i = 0; i < dataInfo.length; i++) { for (int j = 0; j < dataInfo[i].xPos.length; j++) { int yPos = dataInfo[i].yPos[j]; dataInfo[i].yPos[j] = xSize - 1 - dataInfo[i].xPos[j]; dataInfo[i].xPos[j] = yPos; } } break; case 2: // 180 Deg. for (int i = 0; i < dataInfo.length; i++) { for (int j = 0; j < dataInfo[i].xPos.length; j++) { int yPos = dataInfo[i].yPos[j]; dataInfo[i].yPos[j] = xSize - 1 - dataInfo[i].xPos[j]; dataInfo[i].xPos[j] = ySize - 1 - yPos; } } break; case 3: // 270 Deg. for (int i = 0; i < dataInfo.length; i++) { for (int j = 0; j < dataInfo[i].xPos.length; j++) { int yPos = dataInfo[i].yPos[j]; dataInfo[i].yPos[j] = dataInfo[i].xPos[j]; dataInfo[i].xPos[j] = xSize - 1 - yPos; } } break; default: // nop } } /** * Flip around the horizontal axis */ public void flipH(final int ySize) { for (int i = 0; i < dataInfo.length; i++) { for (int j = 0; j < dataInfo[i].yPos.length; j++) { dataInfo[i].yPos[j] = ySize - 1 - dataInfo[i].yPos[j]; } } } /** * Flip around the vertical axis */ public void flipV(final int xSize) { for (int i = 0; i < dataInfo.length; i++) { for (int j = 0; j < dataInfo[i].xPos.length; j++) { dataInfo[i].xPos[j] = xSize - 1 - dataInfo[i].xPos[j]; } } } public DataInformation[] getInternalDataRepresentationCopy() { return this.clone().dataInfo; } public static String getFileNameSuffix() { return ".dwm"; } public static void main(String[] args) { try { new SOMLibDataWinnerMapping(args[0]); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (SOMLibFileFormatException e) { e.printStackTrace(); } } }