diff options
Diffstat (limited to 'src/de/lmu/ifi/dbs/elki/result')
60 files changed, 5877 insertions, 0 deletions
diff --git a/src/de/lmu/ifi/dbs/elki/result/AbstractHierarchicalResult.java b/src/de/lmu/ifi/dbs/elki/result/AbstractHierarchicalResult.java new file mode 100644 index 00000000..113779a1 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/AbstractHierarchicalResult.java @@ -0,0 +1,62 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * Abstract class for a result object with hierarchy + * + * @author Erich Schubert + */ +public abstract class AbstractHierarchicalResult implements HierarchicalResult { + /** + * The hierarchy storage. + */ + private ResultHierarchy hierarchy; + + /** + * Constructor. + */ + public AbstractHierarchicalResult() { + super(); + this.hierarchy = new ResultHierarchy(); + } + + @Override + public final ResultHierarchy getHierarchy() { + return hierarchy; + } + + @Override + public final void setHierarchy(ResultHierarchy hierarchy) { + this.hierarchy = hierarchy; + } + + /** + * Add a child result. + * + * @param child Child result + */ + public void addChildResult(Result child) { + hierarchy.add(this, child); + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/AprioriResult.java b/src/de/lmu/ifi/dbs/elki/result/AprioriResult.java new file mode 100644 index 00000000..3a8f2aec --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/AprioriResult.java @@ -0,0 +1,79 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.util.BitSet; +import java.util.List; +import java.util.Map; + +/** + * Result class for Apriori Algorithm. + * + * @author Erich Schubert + * + */ +public class AprioriResult extends BasicResult { + /** + * The frequent itemsets. + */ + private List<BitSet> solution; + + /** + * The supports of all itemsets. + */ + private Map<BitSet, Integer> supports; + + /** + * Constructor. + * + * @param name The long name (for pretty printing) + * @param shortname the short name (for filenames etc.) + * @param solution Frequent itemsets + * @param supports Supports for the itemsets + */ + public AprioriResult(String name, String shortname, List<BitSet> solution, Map<BitSet, Integer> supports) { + super(name, shortname); + this.solution = solution; + this.supports = supports; + } + + /** + * Returns the frequent item sets. + * + * @return the frequent item sets. + */ + public List<BitSet> getSolution() { + return solution; + } + + /** + * Returns the frequencies of the frequent item sets. + * + * @return the frequencies of the frequent item sets + */ + public Map<BitSet, Integer> getSupports() { + return supports; + } + + // TODO: text writer for AprioriResult! +} diff --git a/src/de/lmu/ifi/dbs/elki/result/BasicResult.java b/src/de/lmu/ifi/dbs/elki/result/BasicResult.java new file mode 100644 index 00000000..dc0f048a --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/BasicResult.java @@ -0,0 +1,66 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * Basic class for a result. Much like AbstractHierarchicalResult, except it + * stores the required short and long result names. + * + * @author Erich Schubert + * + * @apiviz.landmark + */ +// TODO: getter, setter for result names? Merge with AbstractHierarchicalResult? +public class BasicResult extends AbstractHierarchicalResult { + /** + * Result name, for presentation + */ + private String name; + + /** + * Result name, for output + */ + private String shortname; + + /** + * Result constructor. + * + * @param name The long name (for pretty printing) + * @param shortname the short name (for filenames etc.) + */ + public BasicResult(String name, String shortname) { + super(); + this.name = name; + this.shortname = shortname; + } + + @Override + public final String getLongName() { + return name; + } + + @Override + public final String getShortName() { + return shortname; + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/CollectionResult.java b/src/de/lmu/ifi/dbs/elki/result/CollectionResult.java new file mode 100644 index 00000000..eb6c3fe3 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/CollectionResult.java @@ -0,0 +1,109 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +/** + * Simple 'collection' type of result. + * For example, a list of NumberVectors. + * + * @author Erich Schubert + * + * @apiviz.landmark + * + * @param <O> data type + */ +public class CollectionResult<O> extends BasicResult implements IterableResult<O> { + /** + * The collection represented. + */ + private Collection<O> col; + + /** + * Meta information (printed into the header) + */ + private Collection<String> header; + + /** + * Constructor + * + * @param name The long name (for pretty printing) + * @param shortname the short name (for filenames etc.) + * @param col Collection represented + * @param header Auxiliary information for result headers + */ + public CollectionResult(String name, String shortname, Collection<O> col, Collection<String> header) { + super(name, shortname); + this.col = col; + this.header = header; + } + + /** + * Constructor + * + * @param name The long name (for pretty printing) + * @param shortname the short name (for filenames etc.) + * @param col Collection represented + */ + public CollectionResult(String name, String shortname, Collection<O> col) { + this(name, shortname, col, new ArrayList<String>()); + } + + /** + * Add header information + * + * @param s Header information string + */ + public void addHeader(String s) { + header.add(s); + } + + /** + * Get header information + * + * @return header information of the result + */ + public Collection<String> getHeader() { + return header; + } + + /** + * Implementation of the {@link IterableResult} interface, using the backing collection. + */ + @Override + public Iterator<O> iterator() { + return col.iterator(); + } + + /** + * Get the collection size. + * + * @return Collection size + */ + public int size() { + return col.size(); + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/DBIDSelection.java b/src/de/lmu/ifi/dbs/elki/result/DBIDSelection.java new file mode 100644 index 00000000..41c50bb1 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/DBIDSelection.java @@ -0,0 +1,58 @@ +package de.lmu.ifi.dbs.elki.result;
+/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +
+import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+
+/**
+ * Class representing selected Database-IDs and/or a selection range.
+ *
+ * @author Heidi Kolb
+ * @author Erich Schubert
+ */
+public class DBIDSelection {
+ /**
+ * Selected IDs
+ */
+ private DBIDs selectedIds = DBIDUtil.EMPTYDBIDS;
+
+ /**
+ * Constructor with new object IDs.
+ *
+ * @param selectedIds selection IDs
+ */
+ public DBIDSelection(DBIDs selectedIds) {
+ super();
+ this.selectedIds = selectedIds;
+ }
+
+ /**
+ * Getter for the selected IDs
+ *
+ * @return DBIDs
+ */
+ public DBIDs getSelectedIds() {
+ return selectedIds;
+ }
+}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/DiscardResultHandler.java b/src/de/lmu/ifi/dbs/elki/result/DiscardResultHandler.java new file mode 100644 index 00000000..a9f4a589 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/DiscardResultHandler.java @@ -0,0 +1,45 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + + +/** + * A dummy result handler that discards the actual result, for use in + * benchmarks. + * + * @author Erich Schubert + */ +public class DiscardResultHandler implements ResultHandler { + /** + * Default constructor. + */ + public DiscardResultHandler() { + // empty constructor + } + + @SuppressWarnings("unused") + @Override + public void processNewResult(HierarchicalResult baseResult, Result newResult) { + // always ignore the new result. + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/HierarchicalResult.java b/src/de/lmu/ifi/dbs/elki/result/HierarchicalResult.java new file mode 100644 index 00000000..a604d287 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/HierarchicalResult.java @@ -0,0 +1,49 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * Result with an internal hierarchy. + * + * Note: while this often seems a bit clumsy to use, the benefit of having this + * delegate is to keep the APIs simpler, and thus make ELKI development easier. + * + * @author Erich Schubert + * + * @apiviz.composedOf ResultHierarchy + */ +public interface HierarchicalResult extends Result { + /** + * Get the objects current hierarchy - may be {@code null}! + * + * @return current hierarchy. May be {@code null}! + */ + public ResultHierarchy getHierarchy(); + + /** + * Set (exchange) the hierarchy implementation (e.g. after merging!) + * + * @param hierarchy New hierarchy + */ + public void setHierarchy(ResultHierarchy hierarchy); +} diff --git a/src/de/lmu/ifi/dbs/elki/result/HistogramResult.java b/src/de/lmu/ifi/dbs/elki/result/HistogramResult.java new file mode 100644 index 00000000..94c1ce7b --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/HistogramResult.java @@ -0,0 +1,57 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.util.Collection; + +/** + * Histogram result. + * + * @author Erich Schubert + * + * @param <O> Object class (e.g. {@link de.lmu.ifi.dbs.elki.data.DoubleVector}) + */ +public class HistogramResult<O> extends CollectionResult<O> { + /** + * Constructor + * + * @param name The long name (for pretty printing) + * @param shortname the short name (for filenames etc.) + * @param col Collection + */ + public HistogramResult(String name, String shortname, Collection<O> col) { + super(name, shortname, col); + } + + /** + * Constructor + * + * @param name The long name (for pretty printing) + * @param shortname the short name (for filenames etc.) + * @param col Collection + * @param header Header information + */ + public HistogramResult(String name, String shortname, Collection<O> col, Collection<String> header) { + super(name, shortname, col, header); + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/IterableResult.java b/src/de/lmu/ifi/dbs/elki/result/IterableResult.java new file mode 100644 index 00000000..4affcb25 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/IterableResult.java @@ -0,0 +1,45 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.util.Iterator; + +/** + * Interface of an "iterable" result (e.g. a list, table) that can be printed one-by-one. + * (At least when the class O is TextWriteable) + * + * @author Erich Schubert + * + * @apiviz.landmark + * + * @param <O> object class. + */ +public interface IterableResult<O> extends Result, Iterable<O> { + /** + * Retrieve an iterator for the result. + * + * @return iterator + */ + @Override + public Iterator<O> iterator(); +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/KMLOutputHandler.java b/src/de/lmu/ifi/dbs/elki/result/KMLOutputHandler.java new file mode 100644 index 00000000..2d456f8e --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/KMLOutputHandler.java @@ -0,0 +1,409 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.awt.Color; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +import de.lmu.ifi.dbs.elki.data.spatial.Polygon; +import de.lmu.ifi.dbs.elki.data.spatial.PolygonsObject; +import de.lmu.ifi.dbs.elki.data.type.TypeUtil; +import de.lmu.ifi.dbs.elki.database.Database; +import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs; +import de.lmu.ifi.dbs.elki.database.ids.DBID; +import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil; +import de.lmu.ifi.dbs.elki.database.relation.Relation; +import de.lmu.ifi.dbs.elki.logging.Logging; +import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector; +import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult; +import de.lmu.ifi.dbs.elki.utilities.DatabaseUtil; +import de.lmu.ifi.dbs.elki.utilities.FormatUtil; +import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException; +import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer; +import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID; +import de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable; +import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization; +import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.FileParameter; +import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag; +import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter; +import de.lmu.ifi.dbs.elki.utilities.scaling.outlier.OutlierLinearScaling; +import de.lmu.ifi.dbs.elki.utilities.scaling.outlier.OutlierScalingFunction; + +/** + * Class to handle KML output. + * + * @author Erich Schubert + */ +// TODO: make configurable color scheme +public class KMLOutputHandler implements ResultHandler, Parameterizable { + /** + * Logger class to use. + */ + public static final Logging logger = Logging.getLogger(KMLOutputHandler.class); + + /** + * Number of styles to use (lower reduces rendering complexity a bit) + */ + private static final int NUMSTYLES = 20; + + /** + * Output file name + */ + File filename; + + /** + * Scaling function + */ + OutlierScalingFunction scaling; + + /** + * Compatibility mode. + */ + private boolean compat; + + /** + * Constructor. + * + * @param filename Output filename + * @param scaling Scaling function + * @param compat Compatibility mode + */ + public KMLOutputHandler(File filename, OutlierScalingFunction scaling, boolean compat) { + super(); + this.filename = filename; + this.scaling = scaling; + this.compat = compat; + } + + @Override + public void processNewResult(HierarchicalResult baseResult, Result newResult) { + ArrayList<OutlierResult> ors = ResultUtil.filterResults(newResult, OutlierResult.class); + if(ors.size() > 1) { + throw new AbortException("More than one outlier result found. The KML writer only supports a single outlier result!"); + } + if(ors.size() == 1) { + Database database = ResultUtil.findDatabase(baseResult); + try { + XMLOutputFactory factory = XMLOutputFactory.newInstance(); + ZipOutputStream out = new ZipOutputStream(new FileOutputStream(filename)); + out.putNextEntry(new ZipEntry("doc.kml")); + writeKMLData(factory.createXMLStreamWriter(out), ors.get(0), database); + out.closeEntry(); + out.flush(); + out.close(); + } + catch(XMLStreamException e) { + logger.exception(e); + throw new AbortException("XML error in KML output.", e); + } + catch(IOException e) { + logger.exception(e); + throw new AbortException("IO error in KML output.", e); + } + } + } + + private void writeKMLData(XMLStreamWriter out, OutlierResult outlierResult, Database database) throws XMLStreamException { + Relation<Double> scores = outlierResult.getScores(); + Relation<PolygonsObject> polys = database.getRelation(TypeUtil.POLYGON_TYPE); + Relation<String> labels = DatabaseUtil.guessObjectLabelRepresentation(database); + + Collection<Relation<?>> otherrel = new LinkedList<Relation<?>>(database.getRelations()); + otherrel.remove(scores); + otherrel.remove(polys); + otherrel.remove(labels); + otherrel.remove(database.getRelation(TypeUtil.DBID)); + + ArrayModifiableDBIDs ids = DBIDUtil.newArray(scores.getDBIDs()); + + scaling.prepare(outlierResult); + + out.writeStartDocument(); + out.writeCharacters("\n"); + out.writeStartElement("kml"); + out.writeDefaultNamespace("http://earth.google.com/kml/2.2"); + out.writeStartElement("Document"); + { + // TODO: can we automatically generate more helpful data here? + out.writeStartElement("name"); + out.writeCharacters("ELKI KML output for " + outlierResult.getLongName()); + out.writeEndElement(); // name + writeNewlineOnDebug(out); + // TODO: e.g. list the settings in the description? + out.writeStartElement("description"); + out.writeCharacters("ELKI KML output for " + outlierResult.getLongName()); + out.writeEndElement(); // description + writeNewlineOnDebug(out); + } + { + // TODO: generate styles from color scheme + for(int i = 0; i < NUMSTYLES; i++) { + Color col = getColorForValue(i / (NUMSTYLES - 1.0)); + out.writeStartElement("Style"); + out.writeAttribute("id", "s" + i); + writeNewlineOnDebug(out); + { + out.writeStartElement("LineStyle"); + out.writeStartElement("width"); + out.writeCharacters("0"); + out.writeEndElement(); // width + + out.writeEndElement(); // LineStyle + } + writeNewlineOnDebug(out); + { + out.writeStartElement("PolyStyle"); + out.writeStartElement("color"); + // KML uses AABBGGRR format! + out.writeCharacters(String.format("%02x%02x%02x%02x", col.getAlpha(), col.getBlue(), col.getGreen(), col.getRed())); + out.writeEndElement(); // color + // out.writeStartElement("fill"); + // out.writeCharacters("1"); // Default 1 + // out.writeEndElement(); // fill + out.writeStartElement("outline"); + out.writeCharacters("0"); + out.writeEndElement(); // outline + out.writeEndElement(); // PolyStyle + } + writeNewlineOnDebug(out); + out.writeEndElement(); // Style + writeNewlineOnDebug(out); + } + } + for(DBID id : outlierResult.getOrdering().iter(ids)) { + Double score = scores.get(id); + PolygonsObject poly = polys.get(id); + String label = labels.get(id); + if(score == null) { + logger.warning("No score for object " + id); + } + if(poly == null) { + logger.warning("No polygon for object " + id + " - skipping."); + continue; + } + out.writeStartElement("Placemark"); + { + out.writeStartElement("name"); + out.writeCharacters(score + " " + label); + out.writeEndElement(); // name + StringBuffer buf = makeDescription(otherrel, id); + out.writeStartElement("description"); + out.writeCData("<div>" + buf.toString() + "</div>"); + out.writeEndElement(); // description + out.writeStartElement("styleUrl"); + int style = (int) (scaling.getScaled(score) * NUMSTYLES); + style = Math.max(0, Math.min(style, NUMSTYLES - 1)); + out.writeCharacters("#s" + style); + out.writeEndElement(); // styleUrl + } + { + out.writeStartElement("Polygon"); + writeNewlineOnDebug(out); + if(compat) { + out.writeStartElement("altitudeMode"); + out.writeCharacters("relativeToGround"); + out.writeEndElement(); // close altitude mode + writeNewlineOnDebug(out); + } + // First polygon clockwise? + boolean first = true; + for(Polygon p : poly.getPolygons()) { + if(first) { + out.writeStartElement("outerBoundaryIs"); + } + else { + out.writeStartElement("innerBoundaryIs"); + } + out.writeStartElement("LinearRing"); + out.writeStartElement("coordinates"); + + // Reverse anti-clockwise polygons. + boolean reverse = (p.testClockwise() >= 0); + Iterator<Vector> it = reverse ? p.descendingIterator() : p.iterator(); + while(it.hasNext()) { + Vector v = it.next(); + out.writeCharacters(FormatUtil.format(v.getArrayRef(), ",")); + if(compat && (v.getDimensionality() == 2)) { + out.writeCharacters(",500"); + } + out.writeCharacters(" "); + } + out.writeEndElement(); // close coordinates + out.writeEndElement(); // close LinearRing + out.writeEndElement(); // close *BoundaryIs + first = false; + } + writeNewlineOnDebug(out); + out.writeEndElement(); // Polygon + } + out.writeEndElement(); // Placemark + writeNewlineOnDebug(out); + } + out.writeEndElement(); // Document + out.writeEndElement(); // kml + out.writeEndDocument(); + } + + /** + * Make an HTML description. + * + * @param relations Relations + * @param id Object ID + * @return Buffer + */ + private StringBuffer makeDescription(Collection<Relation<?>> relations, DBID id) { + StringBuffer buf = new StringBuffer(); + for(Relation<?> rel : relations) { + Object o = rel.get(id); + if(o == null) { + continue; + } + String s = o.toString(); + // FIXME: strip html characters + if(s != null) { + if(buf.length() > 0) { + buf.append("<br />"); + } + buf.append(s); + } + } + return buf; + } + + /** + * Print a newline when debugging. + * + * @param out Output XML stream + * @throws XMLStreamException + */ + private void writeNewlineOnDebug(XMLStreamWriter out) throws XMLStreamException { + if(logger.isDebugging()) { + out.writeCharacters("\n"); + } + } + + private static final Color getColorForValue(double val) { + // Color positions + double[] pos = new double[] { 0.0, 0.6, 0.8, 1.0 }; + // Colors at these positions + Color[] cols = new Color[] { new Color(0.0f, 0.0f, 0.0f, 0.6f), new Color(0.0f, 0.0f, 1.0f, 0.8f), new Color(1.0f, 0.0f, 0.0f, 0.9f), new Color(1.0f, 1.0f, 0.0f, 1.0f) }; + assert (pos.length == cols.length); + if(val < pos[0]) { + val = pos[0]; + } + // Linear interpolation: + for(int i = 1; i < pos.length; i++) { + if(val <= pos[i]) { + Color prev = cols[i - 1]; + Color next = cols[i]; + final double mix = (val - pos[i - 1]) / (pos[i] - pos[i - 1]); + final int r = (int) ((1 - mix) * prev.getRed() + mix * next.getRed()); + final int g = (int) ((1 - mix) * prev.getGreen() + mix * next.getGreen()); + final int b = (int) ((1 - mix) * prev.getBlue() + mix * next.getBlue()); + final int a = (int) ((1 - mix) * prev.getAlpha() + mix * next.getAlpha()); + Color col = new Color(r, g, b, a); + return col; + } + } + return cols[cols.length - 1]; + } + + /** + * Parameterization class + * + * @author Erich Schubert + * + * @apiviz.exclude + */ + public static class Parameterizer extends AbstractParameterizer { + /** + * Parameter for scaling functions + * + * <p> + * Key: {@code -kml.scaling} + * </p> + */ + public static final OptionID SCALING_ID = OptionID.getOrCreateOptionID("kml.scaling", "Additional scaling function for KML colorization."); + + /** + * Parameter for compatibility mode. + * + * <p> + * Key: {@code -kml.compat} + * </p> + */ + public static final OptionID COMPAT_ID = OptionID.getOrCreateOptionID("kml.compat", "Use simpler KML objects, compatibility mode."); + + /** + * Output file name + */ + File filename; + + /** + * Scaling function + */ + OutlierScalingFunction scaling; + + /** + * Compatibility mode + */ + boolean compat; + + @Override + protected void makeOptions(Parameterization config) { + super.makeOptions(config); + FileParameter outputP = new FileParameter(OptionID.OUTPUT, FileParameter.FileType.OUTPUT_FILE); + outputP.setShortDescription("Filename the KMZ file (compressed KML) is written to."); + if(config.grab(outputP)) { + filename = outputP.getValue(); + } + + ObjectParameter<OutlierScalingFunction> scalingP = new ObjectParameter<OutlierScalingFunction>(SCALING_ID, OutlierScalingFunction.class, OutlierLinearScaling.class); + if(config.grab(scalingP)) { + scaling = scalingP.instantiateClass(config); + } + + Flag compatF = new Flag(COMPAT_ID); + if(config.grab(compatF)) { + compat = compatF.getValue(); + } + } + + @Override + protected KMLOutputHandler makeInstance() { + return new KMLOutputHandler(filename, scaling, compat); + } + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/KNNDistanceOrderResult.java b/src/de/lmu/ifi/dbs/elki/result/KNNDistanceOrderResult.java new file mode 100644 index 00000000..8958cac2 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/KNNDistanceOrderResult.java @@ -0,0 +1,62 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.util.Iterator; +import java.util.List; + +import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; + +/** + * Wraps a list containing the knn distances. + * + * @author Arthur Zimek + * @param <D> the type of Distance used by this Result + * + */ +public class KNNDistanceOrderResult<D extends Distance<D>> extends BasicResult implements IterableResult<D> { + /** + * Store the kNN Distances + */ + private final List<D> knnDistances; + + /** + * Construct result + * + * @param name The long name (for pretty printing) + * @param shortname the short name (for filenames etc.) + * @param knnDistances distance list to wrap. + */ + public KNNDistanceOrderResult(String name, String shortname, final List<D> knnDistances) { + super(name, shortname); + this.knnDistances = knnDistances; + } + + /** + * Return an iterator. + */ + @Override + public Iterator<D> iterator() { + return knnDistances.iterator(); + } +} diff --git a/src/de/lmu/ifi/dbs/elki/result/OrderingFromDataStore.java b/src/de/lmu/ifi/dbs/elki/result/OrderingFromDataStore.java new file mode 100644 index 00000000..537f04d8 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/OrderingFromDataStore.java @@ -0,0 +1,169 @@ +package de.lmu.ifi.dbs.elki.result; + +/* + This file is part of ELKI: + Environment for Developing KDD-Applications Supported by Index-Structures + + Copyright (C) 2011 + Ludwig-Maximilians-Universität München + Lehr- und Forschungseinheit für Datenbanksysteme + ELKI Development Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import java.util.Collections; +import java.util.Comparator; + +import de.lmu.ifi.dbs.elki.database.datastore.DataStore; +import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs; +import de.lmu.ifi.dbs.elki.database.ids.DBID; +import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil; +import de.lmu.ifi.dbs.elki.database.ids.DBIDs; +import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator; +import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil; + +/** + * Result class providing an ordering backed by a hashmap. + * + * @author Erich Schubert + * + * @param <T> Data type in hash map + */ +public class OrderingFromDataStore<T extends Comparable<T>> extends BasicResult implements OrderingResult { + /** + * HashMap with object values + */ + protected DataStore<? extends T> map; + + /** + * Database IDs + */ + protected DBIDs ids; + + /** + * Comparator to use when sorting + */ + protected Comparator<T> comparator; + + /** + * Factor for ascending (+1) and descending (-1) ordering. + */ + int ascending; + + /** + * Constructor with comparator + * + * @param name The long name (for pretty printing) + * @param shortname the short name (for filenames etc.) + * @param ids DBIDs included + * @param map data hash map + * @param comparator comparator to use, may be null + * @param descending ascending (false) or descending (true) order. + */ + public OrderingFromDataStore(String name, String shortname, DBIDs ids, DataStore<? extends T> map, Comparator<T> comparator, boolean descending) { + super(name, shortname); + this.map = map; + this.ids = ids; + this.comparator = comparator; + this.ascending = descending ? -1 : 1; + } + + /** + * Constructor without comparator + * + * @param name The long name (for pretty printing) + * @param shortname the short name (for filenames etc.) + * @param ids DBIDs included + * @param map data hash map + * @param descending ascending (false) or descending (true) order. + */ + public OrderingFromDataStore(String name, String shortname, DBIDs ids, DataStore<? extends T> map, boolean descending) { + super(name, shortname); + this.map = map; + this.ids = ids; + this.comparator = null; + this.ascending = descending ? -1 : 1; + } + + /** + * Minimal Constructor + * + * @param name The long name (for pretty printing) + * @param shortname the short name (for filenames etc.) + * @param ids DBIDs included + * @param map data hash map + */ + public OrderingFromDataStore(String name, String shortname, DBIDs ids, DataStore<? extends T> map) { + super(name, shortname); + this.map = map; + this.ids = ids; + this.comparator = null; + this.ascending = 1; + } + + @Override + public DBIDs getDBIDs() { + return ids; + } + + @Override + public IterableIterator<DBID> iter(DBIDs ids) { + ArrayModifiableDBIDs sorted = DBIDUtil.newArray(ids); + if(comparator != null) { + Collections.sort(sorted, new DerivedComparator()); + } + else { + Collections.sort(sorted, new ImpliedComparator()); + } + return IterableUtil.fromIterable(sorted); + } + + /** + * Internal comparator, accessing the map to sort objects + * + * @author Erich Schubert + * + * @apiviz.exclude + */ + protected final class ImpliedComparator implements Comparator<DBID> { + @Override + public int compare(DBID id1, DBID id2) { + T k1 = map.get(id1); + T k2 = map.get(id2); + assert (k1 != null); + assert (k2 != null); + return ascending * k1.compareTo(k2); + } + } + + /** + * Internal comparator, accessing the map but then using the provided + * comparator to sort objects + * + * @author Erich Schubert + * + * @apiviz.exclude + */ + protected final class DerivedComparator implements Comparator<DBID> { + @Override + public int compare(DBID id1, DBID id2) { + T k1 = map.get(id1); + T k2 = map.get(id2); + assert (k1 != null); + assert (k2 != null); + return ascending * comparator.compare(k1, k2); + } + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/OrderingResult.java b/src/de/lmu/ifi/dbs/elki/result/OrderingResult.java new file mode 100644 index 00000000..57a30e16 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/OrderingResult.java @@ -0,0 +1,49 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import de.lmu.ifi.dbs.elki.database.ids.DBID; +import de.lmu.ifi.dbs.elki.database.ids.DBIDs; +import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator; + +/** + * Interface for a result providing an object ordering. + * + * @author Erich Schubert + */ +public interface OrderingResult extends Result { + /** + * Get the full set of DBIDs this ordering is defined for. + * + * @return DBIDs + */ + public DBIDs getDBIDs(); + + /** + * Sort the given ids according to this ordering and return an iterator. + * + * @param ids Collection of ids. + * @return iterator for sorted array of ids + */ + public IterableIterator<DBID> iter(DBIDs ids); +} diff --git a/src/de/lmu/ifi/dbs/elki/result/PixmapResult.java b/src/de/lmu/ifi/dbs/elki/result/PixmapResult.java new file mode 100644 index 00000000..eae54ad6 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/PixmapResult.java @@ -0,0 +1,47 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.awt.image.RenderedImage; +import java.io.File; + +/** + * Result encapsulating a single image. + * + * @author Erich Schubert + */ +public interface PixmapResult extends Result { + /** + * Get the image pixmap + * + * @return the image pixmap + */ + public RenderedImage getImage(); + + /** + * Get the image result as file (usually a temporary file). + * + * @return Image file + */ + public File getAsFile(); +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/RangeSelection.java b/src/de/lmu/ifi/dbs/elki/result/RangeSelection.java new file mode 100644 index 00000000..87da7a4b --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/RangeSelection.java @@ -0,0 +1,77 @@ +package de.lmu.ifi.dbs.elki.result;
+/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +
+import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
+import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleDoublePair;
+
+/**
+ * Class representing selected Database-IDs and/or a selection range.
+ *
+ * @author Heidi Kolb
+ * @author Erich Schubert
+ */
+public class RangeSelection extends DBIDSelection {
+ /**
+ * Selection range
+ */
+ private DoubleDoublePair[] ranges = null;
+
+ /**
+ * Constructor.
+ *
+ * @param selectedIds selected IDs
+ */
+ public RangeSelection(DBIDs selectedIds) {
+ super(selectedIds);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param selection
+ * @param ranges
+ */
+ public RangeSelection(DBIDs selection, DoubleDoublePair[] ranges) {
+ super(selection);
+ this.ranges = ranges;
+ }
+
+ /**
+ * Get the selection range.
+ *
+ * @return Selected range. May be null!
+ */
+ public DoubleDoublePair[] getRanges() {
+ return ranges;
+ }
+
+ /**
+ * Get a single selection range.
+ *
+ * @return Selected range. May be null!
+ */
+ public DoubleDoublePair getRange(int dim) {
+ return ranges[dim];
+ }
+}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/ReferencePointsResult.java b/src/de/lmu/ifi/dbs/elki/result/ReferencePointsResult.java new file mode 100644 index 00000000..5ec78478 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/ReferencePointsResult.java @@ -0,0 +1,58 @@ +package de.lmu.ifi.dbs.elki.result;
+/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +
+import java.util.Collection;
+
+
+/**
+ * Result used in passing the reference points to the visualizers.
+ *
+ * @author Erich Schubert
+ *
+ * @param <O> data type
+ */
+public class ReferencePointsResult<O> extends CollectionResult<O> {
+ /**
+ * Constructor with collection only.
+ *
+ * @param name The long name (for pretty printing)
+ * @param shortname the short name (for filenames etc.)
+ * @param col Reference Points
+ */
+ public ReferencePointsResult(String name, String shortname, Collection<O> col) {
+ super(name, shortname, col);
+ }
+
+ /**
+ * Full constructor.
+ *
+ * @param name The long name (for pretty printing)
+ * @param shortname the short name (for filenames etc.)
+ * @param col Reference Points
+ * @param header Header
+ */
+ public ReferencePointsResult(String name, String shortname, Collection<O> col, Collection<String> header) {
+ super(name, shortname, col, header);
+ }
+}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/Result.java b/src/de/lmu/ifi/dbs/elki/result/Result.java new file mode 100644 index 00000000..c3fd32ec --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/Result.java @@ -0,0 +1,49 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * Interface for arbitrary result objects. + * + * @author Erich Schubert + * + * @apiviz.landmark + * @apiviz.excludeSubtypes + */ +public interface Result { + /** + * A "pretty" name for the result, for use in titles, captions and menus. + * + * @return result name + */ + // TODO: turn this into an optional annotation? But: no inheritance? + public String getLongName(); + + /** + * A short name for the result, useful for file names. + * + * @return result name + */ + // TODO: turn this into an optional annotation? But: no inheritance? + public String getShortName(); +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/ResultAdapter.java b/src/de/lmu/ifi/dbs/elki/result/ResultAdapter.java new file mode 100644 index 00000000..8dca01dc --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/ResultAdapter.java @@ -0,0 +1,36 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * Marker interface for trivial "adapter" type of results. + * + * Such results can be hidden by a GUI, since they just provide a "view" on the main result. + * + * @author Erich Schubert + * + * @apiviz.exclude + */ +public interface ResultAdapter extends Result { + // Empty +} diff --git a/src/de/lmu/ifi/dbs/elki/result/ResultHandler.java b/src/de/lmu/ifi/dbs/elki/result/ResultHandler.java new file mode 100644 index 00000000..236dda40 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/ResultHandler.java @@ -0,0 +1,37 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable; + +/** + * Interface for any class that can handle results + * + * @author Erich Schubert + * + * @apiviz.landmark + * @apiviz.uses Result oneway - - processes + */ +public interface ResultHandler extends Parameterizable, ResultProcessor { + // Empty - moved to ResultProcessor, this interface merely serves UI purposes. +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/ResultHierarchy.java b/src/de/lmu/ifi/dbs/elki/result/ResultHierarchy.java new file mode 100644 index 00000000..20a681bb --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/ResultHierarchy.java @@ -0,0 +1,162 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.util.List; + +import javax.swing.event.EventListenerList; + +import de.lmu.ifi.dbs.elki.logging.Logging; +import de.lmu.ifi.dbs.elki.utilities.datastructures.hierarchy.HierarchyHashmapList; +import de.lmu.ifi.dbs.elki.utilities.datastructures.hierarchy.ModifiableHierarchy; + +/** + * Class to store a hierarchy of result objects. + * + * @author Erich Schubert + */ +// TODO: add listener merging! +public class ResultHierarchy extends HierarchyHashmapList<Result> { + /** + * Logger + */ + private static final Logging logger = Logging.getLogger(ResultHierarchy.class); + + /** + * Holds the listener. + */ + private EventListenerList listenerList = new EventListenerList(); + + /** + * Constructor. + */ + public ResultHierarchy() { + super(); + } + + @Override + public void add(Result parent, Result child) { + super.add(parent, child); + if(child instanceof HierarchicalResult) { + HierarchicalResult hr = (HierarchicalResult) child; + ModifiableHierarchy<Result> h = hr.getHierarchy(); + // Merge hierarchy + hr.setHierarchy(this); + // Add children of child + for(Result desc : h.getChildren(hr)) { + this.add(hr, desc); + if(desc instanceof HierarchicalResult) { + ((HierarchicalResult) desc).setHierarchy(this); + } + } + } + fireResultAdded(child, parent); + } + + @SuppressWarnings("unused") + @Override + public void remove(Result parent, Result child) { + // TODO: unlink from hierarchy, fire event + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("unused") + @Override + public void put(Result obj, List<Result> parents, List<Result> children) { + // TODO: can we support this somehow? Or reduce visibility? + throw new UnsupportedOperationException(); + } + + /** + * Register a result listener. + * + * @param listener Result listener. + */ + public void addResultListener(ResultListener listener) { + listenerList.add(ResultListener.class, listener); + } + + /** + * Remove a result listener. + * + * @param listener Result listener. + */ + public void removeResultListener(ResultListener listener) { + listenerList.remove(ResultListener.class, listener); + } + + /** + * Signal that a result has changed (public API) + * + * @param res Result that has changed. + */ + public void resultChanged(Result res) { + fireResultChanged(res); + } + + /** + * Informs all registered {@link ResultListener} that a new result was added. + * + * @param child New child result added + * @param parent Parent result that was added to + */ + private void fireResultAdded(Result child, Result parent) { + if(logger.isDebugging()) { + logger.debug("Result added: " + child + " <- " + parent); + } + for(ResultListener l : listenerList.getListeners(ResultListener.class)) { + l.resultAdded(child, parent); + } + } + + /** + * Informs all registered {@link ResultListener} that a result has changed. + * + * @param current Result that has changed + */ + private void fireResultChanged(Result current) { + if(logger.isDebugging()) { + logger.debug("Result changed: " + current); + } + for(ResultListener l : listenerList.getListeners(ResultListener.class)) { + l.resultChanged(current); + } + } + + /** + * Informs all registered {@link ResultListener} that a new result has been + * removed. + * + * @param child result that has been removed + * @param parent Parent result that has been removed + */ + @SuppressWarnings("unused") + private void fireResultRemoved(Result child, Result parent) { + if(logger.isDebugging()) { + logger.debug("Result removed: " + child + " <- " + parent); + } + for(ResultListener l : listenerList.getListeners(ResultListener.class)) { + l.resultRemoved(child, parent); + } + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/ResultListener.java b/src/de/lmu/ifi/dbs/elki/result/ResultListener.java new file mode 100644 index 00000000..2a79dd87 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/ResultListener.java @@ -0,0 +1,57 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.util.EventListener; + +/** + * Listener interface invoked when new results are added to the result tree. + * + * @author Erich Schubert + * + * @apiviz.has Result + */ +public interface ResultListener extends EventListener { + /** + * A new derived result was added. + * + * @param child New child result added + * @param parent Parent result that was added to + */ + public void resultAdded(Result child, Result parent); + + /** + * Notify that the current result has changed substantially. + * + * @param current Result that has changed. + */ + public void resultChanged(Result current); + + /** + * A result was removed. + * + * @param child result that was removed + * @param parent Parent result that was removed from + */ + public void resultRemoved(Result child, Result parent); +} diff --git a/src/de/lmu/ifi/dbs/elki/result/ResultProcessor.java b/src/de/lmu/ifi/dbs/elki/result/ResultProcessor.java new file mode 100644 index 00000000..1c220c4e --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/ResultProcessor.java @@ -0,0 +1,41 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + + +/** + * Interface for any class that can handle results + * + * @author Erich Schubert + * + * @apiviz.uses Result oneway - - processes + */ +public interface ResultProcessor { + /** + * Process a result. + * + * @param baseResult The base of the result tree. + * @param newResult Newly added result subtree. + */ + public abstract void processNewResult(final HierarchicalResult baseResult, final Result newResult); +} diff --git a/src/de/lmu/ifi/dbs/elki/result/ResultUtil.java b/src/de/lmu/ifi/dbs/elki/result/ResultUtil.java new file mode 100644 index 00000000..20b1a0be --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/ResultUtil.java @@ -0,0 +1,304 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import de.lmu.ifi.dbs.elki.algorithm.clustering.ClusteringAlgorithm; +import de.lmu.ifi.dbs.elki.algorithm.clustering.trivial.ByLabelHierarchicalClustering; +import de.lmu.ifi.dbs.elki.algorithm.clustering.trivial.TrivialAllInOne; +import de.lmu.ifi.dbs.elki.data.Clustering; +import de.lmu.ifi.dbs.elki.data.model.Model; +import de.lmu.ifi.dbs.elki.data.type.NoSupportedDataTypeException; +import de.lmu.ifi.dbs.elki.database.Database; +import de.lmu.ifi.dbs.elki.database.relation.Relation; +import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult; +import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil; +import de.lmu.ifi.dbs.elki.utilities.iterator.EmptyIterator; +import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator; +import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil; +import de.lmu.ifi.dbs.elki.utilities.iterator.MergedIterator; +import de.lmu.ifi.dbs.elki.utilities.iterator.OneItemIterator; +import de.lmu.ifi.dbs.elki.utilities.iterator.TypeFilterIterator; + +/** + * Utilities for handling result objects + * + * @author Erich Schubert + * + * @apiviz.uses Result oneway - - filters + */ +public class ResultUtil { + /** + * Collect all Annotation results from a Result + * + * @param r Result + * @return List of all annotation results + */ + public static List<Relation<?>> getRelations(Result r) { + if(r instanceof Relation<?>) { + List<Relation<?>> anns = new ArrayList<Relation<?>>(1); + anns.add((Relation<?>) r); + return anns; + } + if(r instanceof HierarchicalResult) { + return ClassGenericsUtil.castWithGenericsOrNull(List.class, filterResults((HierarchicalResult) r, Relation.class)); + } + return Collections.emptyList(); + } + + /** + * Collect all ordering results from a Result + * + * @param r Result + * @return List of ordering results + */ + public static List<OrderingResult> getOrderingResults(Result r) { + if(r instanceof OrderingResult) { + List<OrderingResult> ors = new ArrayList<OrderingResult>(1); + ors.add((OrderingResult) r); + return ors; + } + if(r instanceof HierarchicalResult) { + return filterResults((HierarchicalResult) r, OrderingResult.class); + } + return Collections.emptyList(); + } + + /** + * Collect all clustering results from a Result + * + * @param r Result + * @return List of clustering results + */ + public static List<Clustering<? extends Model>> getClusteringResults(Result r) { + if(r instanceof Clustering<?>) { + List<Clustering<?>> crs = new ArrayList<Clustering<?>>(1); + crs.add((Clustering<?>) r); + return crs; + } + if(r instanceof HierarchicalResult) { + return ClassGenericsUtil.castWithGenericsOrNull(List.class, filterResults((HierarchicalResult) r, Clustering.class)); + } + return Collections.emptyList(); + } + + /** + * Collect all collection results from a Result + * + * @param r Result + * @return List of collection results + */ + public static List<CollectionResult<?>> getCollectionResults(Result r) { + if(r instanceof CollectionResult<?>) { + List<CollectionResult<?>> crs = new ArrayList<CollectionResult<?>>(1); + crs.add((CollectionResult<?>) r); + return crs; + } + if(r instanceof HierarchicalResult) { + return ClassGenericsUtil.castWithGenericsOrNull(List.class, filterResults((HierarchicalResult) r, CollectionResult.class)); + } + return Collections.emptyList(); + } + + /** + * Return all Iterable results + * + * @param r Result + * @return List of iterable results + */ + public static List<IterableResult<?>> getIterableResults(Result r) { + if(r instanceof IterableResult<?>) { + List<IterableResult<?>> irs = new ArrayList<IterableResult<?>>(1); + irs.add((IterableResult<?>) r); + return irs; + } + if(r instanceof HierarchicalResult) { + return ClassGenericsUtil.castWithGenericsOrNull(List.class, filterResults((HierarchicalResult) r, IterableResult.class)); + } + return Collections.emptyList(); + } + + /** + * Collect all outlier results from a Result + * + * @param r Result + * @return List of outlier results + */ + public static List<OutlierResult> getOutlierResults(Result r) { + if(r instanceof OutlierResult) { + List<OutlierResult> ors = new ArrayList<OutlierResult>(1); + ors.add((OutlierResult) r); + return ors; + } + if(r instanceof HierarchicalResult) { + return filterResults((HierarchicalResult) r, OutlierResult.class); + } + return Collections.emptyList(); + } + + /** + * Collect all settings results from a Result + * + * @param r Result + * @return List of settings results + */ + public static List<SettingsResult> getSettingsResults(Result r) { + if(r instanceof SettingsResult) { + List<SettingsResult> ors = new ArrayList<SettingsResult>(1); + ors.add((SettingsResult) r); + return ors; + } + if(r instanceof HierarchicalResult) { + return filterResults((HierarchicalResult) r, SettingsResult.class); + } + return Collections.emptyList(); + } + + /** + * Return only results of the given restriction class + * + * @param <C> Class type + * @param restrictionClass Class restriction + * @return filtered results list + */ + // We can't ensure that restrictionClass matches C. + @SuppressWarnings("unchecked") + public static <C> ArrayList<C> filterResults(Result r, Class<?> restrictionClass) { + ArrayList<C> res = new ArrayList<C>(); + try { + res.add((C) restrictionClass.cast(r)); + } + catch(ClassCastException e) { + // ignore + } + if(r instanceof HierarchicalResult) { + for(Result result : ((HierarchicalResult) r).getHierarchy().iterDescendants(r)) { + try { + res.add((C) restrictionClass.cast(result)); + } + catch(ClassCastException e) { + // ignore + } + } + } + return res; + } + + @SuppressWarnings("unchecked") + public static <C extends Result> IterableIterator<C> filteredResults(Result r, Class<?> restrictionClass) { + final Class<C> rc = (Class<C>) restrictionClass; + // Include the current item + IterableIterator<C> curIter; + try { + curIter = new OneItemIterator<C>(rc.cast(r)); + } + catch(ClassCastException e) { + curIter = null; + } + if(r instanceof HierarchicalResult) { + ResultHierarchy hier = ((HierarchicalResult) r).getHierarchy(); + final Iterable<Result> iterDescendants = hier.iterDescendants(r); + final Iterator<C> others = new TypeFilterIterator<Result, C>(rc, iterDescendants); + if(curIter != null) { + return IterableUtil.fromIterator(new MergedIterator<C>(curIter, others)); + } + else { + return IterableUtil.fromIterator(others); + } + } + else { + if(curIter != null) { + return curIter; + } + else { + return EmptyIterator.STATIC(); + } + } + } + + /** + * Ensure that the result contains at least one Clustering. + * + * @param <O> Database type + * @param db Database to process + * @param result result + */ + public static <O> void ensureClusteringResult(final Database db, final Result result) { + Collection<Clustering<?>> clusterings = ResultUtil.filterResults(result, Clustering.class); + if(clusterings.size() == 0) { + try { + ClusteringAlgorithm<Clustering<Model>> split = new ByLabelHierarchicalClustering(); + Clustering<Model> c = split.run(db); + addChildResult(db, c); + } + catch(NoSupportedDataTypeException e) { + Clustering<Model> c = (new TrivialAllInOne()).run(db); + addChildResult(db, c); + } + } + } + + /** + * Ensure that there also is a selection container object. + * + * @param db Database + * @param result Result + */ + public static void ensureSelectionResult(final Database db, final Result result) { + Collection<SelectionResult> selections = ResultUtil.filterResults(result, SelectionResult.class); + if(selections.size() == 0) { + addChildResult(db, new SelectionResult()); + } + } + + /** + * Add a child result. + * + * @param parent Parent + * @param child Child + */ + public static void addChildResult(HierarchicalResult parent, Result child) { + parent.getHierarchy().add(parent, child); + } + + /** + * Find the first database result in the tree. + * + * @param baseResult Result tree base. + * @return Database + */ + public static Database findDatabase(Result baseResult) { + final IterableIterator<Database> iter = filteredResults(baseResult, Database.class); + if(iter.hasNext()) { + return iter.next(); + } + else { + return null; + } + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/ResultWriter.java b/src/de/lmu/ifi/dbs/elki/result/ResultWriter.java new file mode 100644 index 00000000..cc3e0f5a --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/ResultWriter.java @@ -0,0 +1,189 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.io.File; +import java.io.IOException; + +import de.lmu.ifi.dbs.elki.database.Database; +import de.lmu.ifi.dbs.elki.logging.Logging; +import de.lmu.ifi.dbs.elki.result.textwriter.MultipleFilesOutput; +import de.lmu.ifi.dbs.elki.result.textwriter.SingleStreamOutput; +import de.lmu.ifi.dbs.elki.result.textwriter.StreamFactory; +import de.lmu.ifi.dbs.elki.result.textwriter.TextWriter; +import de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException; +import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer; +import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID; +import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization; +import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.FileParameter; +import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag; + +/** + * Result handler that feeds the data into a TextWriter + * + * @author Erich Schubert + */ +public class ResultWriter implements ResultHandler { + /** + * The logger for this class. + */ + private static final Logging logger = Logging.getLogger(ResultWriter.class); + + /** + * Flag to control GZIP compression. + * <p> + * Key: {@code -out.gzip} + * </p> + */ + public static final OptionID GZIP_OUTPUT_ID = OptionID.getOrCreateOptionID("out.gzip", "Enable gzip compression of output files."); + + /** + * Flag to suppress overwrite warning. + * <p> + * Key: {@code -out.silentoverwrite} + * </p> + */ + public static final OptionID OVERWRITE_OPTION_ID = OptionID.getOrCreateOptionID("out.silentoverwrite", "Silently overwrite output files."); + + /** + * Holds the file to print results to. + */ + private File out; + + /** + * Whether or not to do gzip compression on output. + */ + private boolean gzip = false; + + /** + * Whether or not to warn on overwrite + */ + private boolean warnoverwrite = true; + + /** + * Constructor. + * + * @param out + * @param gzip + * @param warnoverwrite + */ + public ResultWriter(File out, boolean gzip, boolean warnoverwrite) { + super(); + this.out = out; + this.gzip = gzip; + this.warnoverwrite = warnoverwrite; + } + + @Override + public void processNewResult(HierarchicalResult baseresult, Result result) { + TextWriter writer = new TextWriter(); + + StreamFactory output; + try { + if(out == null) { + output = new SingleStreamOutput(gzip); + } + else if(out.exists()) { + if(out.isDirectory()) { + if(warnoverwrite && out.listFiles().length > 0) { + logger.warning("Output directory specified is not empty. Files will be overwritten and old files may be left over."); + } + output = new MultipleFilesOutput(out, gzip); + } + else { + if(warnoverwrite) { + logger.warning("Output file exists and will be overwritten!"); + } + output = new SingleStreamOutput(out, gzip); + } + } + else { + // If it doesn't exist yet, make a MultipleFilesOutput. + output = new MultipleFilesOutput(out, gzip); + } + } + catch(IOException e) { + throw new IllegalStateException("Error opening output.", e); + } + try { + Database db = ResultUtil.findDatabase(baseresult); + writer.output(db, result, output); + } + catch(IOException e) { + throw new IllegalStateException("Input/Output error while writing result.", e); + } + catch(UnableToComplyException e) { + throw new IllegalStateException("Unable to comply while writing result.", e); + } + output.closeAllStreams(); + } + + /** + * Parameterization class. + * + * @author Erich Schubert + * + * @apiviz.exclude + */ + public static class Parameterizer extends AbstractParameterizer { + /** + * Holds the file to print results to. + */ + private File out = null; + + /** + * Whether or not to do gzip compression on output. + */ + private boolean gzip = false; + + /** + * Whether or not to warn on overwrite + */ + private boolean warnoverwrite = true; + + @Override + protected void makeOptions(Parameterization config) { + super.makeOptions(config); + FileParameter outputP = new FileParameter(OptionID.OUTPUT, FileParameter.FileType.OUTPUT_FILE, true); + if(config.grab(outputP)) { + out = outputP.getValue(); + } + + Flag gzipF = new Flag(GZIP_OUTPUT_ID); + if(config.grab(gzipF)) { + gzip = gzipF.getValue(); + } + + Flag overwriteF = new Flag(OVERWRITE_OPTION_ID); + if(config.grab(overwriteF)) { + // note: inversed meaning + warnoverwrite = !overwriteF.getValue(); + } + } + + @Override + protected ResultWriter makeInstance() { + return new ResultWriter(out, gzip, warnoverwrite); + } + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/SelectionResult.java b/src/de/lmu/ifi/dbs/elki/result/SelectionResult.java new file mode 100644 index 00000000..b4b1de43 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/SelectionResult.java @@ -0,0 +1,72 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + + +/** + * Selection result wrapper. + * + * Note: we did not make the DBIDSelection a result in itself. Instead, the + * DBIDSelection object should be seen as static contents of this result. + * + * @author Erich Schubert + * + * @apiviz.composedOf DBIDSelection + */ +public class SelectionResult implements Result { + /** + * The actual selection + */ + DBIDSelection selection = null; + + /** + * Constructor. + */ + public SelectionResult() { + super(); + } + + /** + * @return the selection + */ + public DBIDSelection getSelection() { + return selection; + } + + /** + * @param selection the selection to set + */ + public void setSelection(DBIDSelection selection) { + this.selection = selection; + } + + @Override + public String getLongName() { + return "Selection"; + } + + @Override + public String getShortName() { + return "selection"; + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/SettingsResult.java b/src/de/lmu/ifi/dbs/elki/result/SettingsResult.java new file mode 100644 index 00000000..71e678ca --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/SettingsResult.java @@ -0,0 +1,59 @@ +package de.lmu.ifi.dbs.elki.result; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.util.Collection; + +import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter; +import de.lmu.ifi.dbs.elki.utilities.pairs.Pair; + +/** + * Result that keeps track of settings that were used in generating this + * particular result. + * + * @author Erich Schubert + */ +public class SettingsResult extends BasicResult { + /** + * Settings storage. + */ + Collection<Pair<Object, Parameter<?,?>>> settings; + + /** + * Constructor. + * + * @param settings Settings to store + */ + public SettingsResult(Collection<Pair<Object, Parameter<?,?>>> settings) { + super("Settings", "settings"); + this.settings = settings; + } + + /** + * Get the settings + * @return the settings + */ + public Collection<Pair<Object, Parameter<?,?>>> getSettings() { + return settings; + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/optics/ClusterOrderEntry.java b/src/de/lmu/ifi/dbs/elki/result/optics/ClusterOrderEntry.java new file mode 100644 index 00000000..6dd42e2d --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/optics/ClusterOrderEntry.java @@ -0,0 +1,62 @@ +package de.lmu.ifi.dbs.elki.result.optics; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import de.lmu.ifi.dbs.elki.database.ids.DBID; +import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; + +/** + * Generic Cluster Order Entry Interface. + * + * @author Erich Schubert + * + * @apiviz.landmark + * + * @apiviz.composedOf DBID + * @apiviz.composedOf Distance + * + * @param <D> Distance type + */ +public interface ClusterOrderEntry<D extends Distance<D>> { + /** + * Returns the object id of this entry. + * + * @return the object id of this entry + */ + public DBID getID(); + + /** + * Returns the id of the predecessor of this entry if this entry has a + * predecessor, null otherwise. + * + * @return the id of the predecessor of this entry + */ + public DBID getPredecessorID(); + + /** + * Returns the reachability distance of this entry + * + * @return the reachability distance of this entry + */ + public D getReachability(); +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/optics/ClusterOrderResult.java b/src/de/lmu/ifi/dbs/elki/result/optics/ClusterOrderResult.java new file mode 100644 index 00000000..d135f53e --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/optics/ClusterOrderResult.java @@ -0,0 +1,387 @@ +package de.lmu.ifi.dbs.elki.result.optics; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation; +import de.lmu.ifi.dbs.elki.data.type.TypeUtil; +import de.lmu.ifi.dbs.elki.database.Database; +import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs; +import de.lmu.ifi.dbs.elki.database.ids.DBID; +import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil; +import de.lmu.ifi.dbs.elki.database.ids.DBIDs; +import de.lmu.ifi.dbs.elki.database.ids.ModifiableDBIDs; +import de.lmu.ifi.dbs.elki.database.relation.Relation; +import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; +import de.lmu.ifi.dbs.elki.result.BasicResult; +import de.lmu.ifi.dbs.elki.result.IterableResult; +import de.lmu.ifi.dbs.elki.result.OrderingResult; +import de.lmu.ifi.dbs.elki.result.ResultAdapter; +import de.lmu.ifi.dbs.elki.result.ResultHierarchy; +import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator; +import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIteratorAdapter; +import de.lmu.ifi.dbs.elki.utilities.iterator.IterableUtil; + +/** + * Class to store the result of an ordering clustering algorithm such as OPTICS. + * + * @author Erich Schubert + * + * @apiviz.landmark + * + * @apiviz.has ClusterOrderEntry oneway - - contains + * @apiviz.composedOf ClusterOrderResult.ClusterOrderAdapter + * @apiviz.composedOf ClusterOrderResult.ReachabilityDistanceAdapter + * @apiviz.composedOf ClusterOrderResult.PredecessorAdapter + * + * @param <D> distance type. + */ +public class ClusterOrderResult<D extends Distance<D>> extends BasicResult implements IterableResult<ClusterOrderEntry<D>> { + /** + * Cluster order storage + */ + private ArrayList<ClusterOrderEntry<D>> clusterOrder; + + /** + * Map of object IDs to their cluster order entry + */ + private HashMap<DBID, ClusterOrderEntry<D>> map; + + /** + * The DBIDs we are defined for + */ + ModifiableDBIDs dbids; + + /** + * Constructor + * + * @param name The long name (for pretty printing) + * @param shortname the short name (for filenames etc.) + */ + public ClusterOrderResult(String name, String shortname) { + super(name, shortname); + clusterOrder = new ArrayList<ClusterOrderEntry<D>>(); + map = new HashMap<DBID, ClusterOrderEntry<D>>(); + dbids = DBIDUtil.newHashSet(); + + addChildResult(new ClusterOrderAdapter(clusterOrder)); + addChildResult(new ReachabilityDistanceAdapter(map, dbids)); + addChildResult(new PredecessorAdapter(map, dbids)); + } + + /** + * Retrieve the complete cluster order. + * + * @return cluster order + */ + public List<ClusterOrderEntry<D>> getClusterOrder() { + return clusterOrder; + } + + /** + * The cluster order is iterable + */ + @Override + public Iterator<ClusterOrderEntry<D>> iterator() { + return clusterOrder.iterator(); + } + + /** + * Add an object to the cluster order. + * + * @param id Object ID + * @param predecessor Predecessor ID + * @param reachability Reachability distance + */ + public void add(DBID id, DBID predecessor, D reachability) { + add(new GenericClusterOrderEntry<D>(id, predecessor, reachability)); + dbids.add(id); + } + + /** + * Add an object to the cluster order. + * + * @param ce Entry + */ + public void add(ClusterOrderEntry<D> ce) { + clusterOrder.add(ce); + map.put(ce.getID(), ce); + dbids.add(ce.getID()); + } + + /** + * Get the distance class + * + * @return distance class. Can be {@code null} for an all-undefined result! + */ + public Class<?> getDistanceClass() { + for(ClusterOrderEntry<D> ce : clusterOrder) { + D dist = ce.getReachability(); + if(dist != null) { + return dist.getClass(); + } + } + return null; + } + + /** + * Ordering part of the result. + * + * @author Erich Schubert + */ + class ClusterOrderAdapter implements OrderingResult, ResultAdapter { + /** + * Access reference. + */ + private ArrayList<ClusterOrderEntry<D>> clusterOrder; + + /** + * Constructor. + * + * @param clusterOrder order to return + */ + public ClusterOrderAdapter(final ArrayList<ClusterOrderEntry<D>> clusterOrder) { + super(); + this.clusterOrder = clusterOrder; + } + + @Override + public DBIDs getDBIDs() { + return dbids; + } + + /** + * Use the cluster order to sort the given collection ids. + * + * Implementation of the {@link OrderingResult} interface. + */ + @Override + public IterableIterator<DBID> iter(DBIDs ids) { + ArrayModifiableDBIDs res = DBIDUtil.newArray(ids.size()); + for(ClusterOrderEntry<D> e : clusterOrder) { + if(ids.contains(e.getID())) { + res.add(e.getID()); + } + } + + // TODO: elements in ids that are not in clusterOrder are lost! + return new IterableIteratorAdapter<DBID>(res); + } + + @Override + public String getLongName() { + return "Derived Object Order"; + } + + @Override + public String getShortName() { + return "clusterobjectorder"; + } + } + + /** + * Result containing the reachability distances. + * + * @author Erich Schubert + */ + class ReachabilityDistanceAdapter implements Relation<D>, ResultAdapter { + /** + * Access reference. + */ + private HashMap<DBID, ClusterOrderEntry<D>> map; + + /** + * DBIDs + */ + private DBIDs dbids; + + /** + * Constructor. + * + * @param map Map that stores the results. + * @param dbids DBIDs we are defined for. + */ + public ReachabilityDistanceAdapter(HashMap<DBID, ClusterOrderEntry<D>> map, DBIDs dbids) { + super(); + this.map = map; + this.dbids = dbids; + } + + @Override + public D get(DBID objID) { + return map.get(objID).getReachability(); + } + + @Override + public String getLongName() { + return "Reachability"; + } + + @Override + public String getShortName() { + return "reachability"; + } + + @Override + public DBIDs getDBIDs() { + return DBIDUtil.makeUnmodifiable(dbids); + } + + @Override + public IterableIterator<DBID> iterDBIDs() { + return IterableUtil.fromIterator(dbids.iterator()); + } + + @Override + public int size() { + return dbids.size(); + } + + @Override + public Database getDatabase() { + return null; // FIXME + } + + @SuppressWarnings("unused") + @Override + public void set(DBID id, D val) { + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("unused") + @Override + public void delete(DBID id) { + throw new UnsupportedOperationException(); + } + + @Override + public SimpleTypeInformation<D> getDataTypeInformation() { + return new SimpleTypeInformation<D>(Distance.class); + } + + @Override + public ResultHierarchy getHierarchy() { + return ClusterOrderResult.this.getHierarchy(); + } + + @Override + public void setHierarchy(ResultHierarchy hierarchy) { + ClusterOrderResult.this.setHierarchy(hierarchy); + } + } + + /** + * Result containing the predecessor ID. + * + * @author Erich Schubert + */ + class PredecessorAdapter implements Relation<DBID>, ResultAdapter { + /** + * Access reference. + */ + private HashMap<DBID, ClusterOrderEntry<D>> map; + + /** + * Database IDs + */ + private DBIDs dbids; + + /** + * Constructor. + * + * @param map Map that stores the results. + * @param dbids DBIDs we are defined for + */ + public PredecessorAdapter(HashMap<DBID, ClusterOrderEntry<D>> map, DBIDs dbids) { + super(); + this.map = map; + this.dbids = dbids; + } + + @Override + public DBID get(DBID objID) { + return map.get(objID).getPredecessorID(); + } + + @Override + public String getLongName() { + return "Predecessor"; + } + + @Override + public String getShortName() { + return "predecessor"; + } + + @Override + public DBIDs getDBIDs() { + return DBIDUtil.makeUnmodifiable(dbids); + } + + @Override + public IterableIterator<DBID> iterDBIDs() { + return IterableUtil.fromIterator(dbids.iterator()); + } + + @Override + public int size() { + return dbids.size(); + } + + @Override + public Database getDatabase() { + return null; // FIXME + } + + @SuppressWarnings("unused") + @Override + public void set(DBID id, DBID val) { + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("unused") + @Override + public void delete(DBID id) { + throw new UnsupportedOperationException(); + } + + @Override + public SimpleTypeInformation<DBID> getDataTypeInformation() { + return TypeUtil.DBID; + } + + @Override + public ResultHierarchy getHierarchy() { + return ClusterOrderResult.this.getHierarchy(); + } + + @Override + public void setHierarchy(ResultHierarchy hierarchy) { + ClusterOrderResult.this.setHierarchy(hierarchy); + } + } +} diff --git a/src/de/lmu/ifi/dbs/elki/result/optics/DoubleDistanceClusterOrderEntry.java b/src/de/lmu/ifi/dbs/elki/result/optics/DoubleDistanceClusterOrderEntry.java new file mode 100644 index 00000000..fe3c24bb --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/optics/DoubleDistanceClusterOrderEntry.java @@ -0,0 +1,152 @@ +package de.lmu.ifi.dbs.elki.result.optics; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import de.lmu.ifi.dbs.elki.database.ids.DBID; +import de.lmu.ifi.dbs.elki.distance.distancevalue.DoubleDistance; + +/** + * Provides an entry in a cluster order. + * + * @author Elke Achtert + */ +public class DoubleDistanceClusterOrderEntry implements Comparable<ClusterOrderEntry<DoubleDistance>>, ClusterOrderEntry<DoubleDistance> { + /** + * The id of the entry. + */ + private DBID objectID; + + /** + * The id of the entry's predecessor. + */ + private DBID predecessorID; + + /** + * The reachability of the entry. + */ + private double reachability; + + /** + * Creates a new entry in a cluster order with the specified parameters. + * + * @param objectID the id of the entry + * @param predecessorID the id of the entry's predecessor + * @param reachability the reachability of the entry + */ + public DoubleDistanceClusterOrderEntry(DBID objectID, DBID predecessorID, double reachability) { + this.objectID = objectID; + this.predecessorID = predecessorID; + this.reachability = reachability; + } + + /** + * Indicates whether some other object is "equal to" this one. + * + * NOTE: for the use in an UpdatableHeap, only the ID is compared! + * + * @param o the reference object with which to compare. + * @return <code>true</code> if this object has the same attribute values as + * the o argument; <code>false</code> otherwise. + */ + @Override + public boolean equals(Object o) { + if(this == o) { + return true; + } + if(o == null || !(o instanceof ClusterOrderEntry)) { + return false; + } + + final ClusterOrderEntry<?> that = (ClusterOrderEntry<?>) o; + // Compare by ID only, for UpdatableHeap! + return objectID.equals(that.getID()); + } + + /** + * Returns a hash code value for the object. + * + * @return the object id if this entry + */ + @Override + public int hashCode() { + return objectID.hashCode(); + } + + /** + * Returns a string representation of the object. + * + * @return a string representation of the object. + */ + @Override + public String toString() { + return objectID + "(" + predecessorID + "," + reachability + ")"; + } + + /** + * Returns the object id of this entry. + * + * @return the object id of this entry + */ + @Override + public DBID getID() { + return objectID; + } + + /** + * Returns the id of the predecessor of this entry if this entry has a + * predecessor, null otherwise. + * + * @return the id of the predecessor of this entry + */ + @Override + public DBID getPredecessorID() { + return predecessorID; + } + + /** + * Returns the reachability distance of this entry + * + * @return the reachability distance of this entry + */ + @Override + public DoubleDistance getReachability() { + return new DoubleDistance(reachability); + } + + @Override + public int compareTo(ClusterOrderEntry<DoubleDistance> o) { + if(o instanceof DoubleDistanceClusterOrderEntry) { + int delta = Double.compare(this.reachability, ((DoubleDistanceClusterOrderEntry) o).reachability); + if(delta != 0) { + return delta; + } + } + else { + int delta = this.getReachability().compareTo(o.getReachability()); + if(delta != 0) { + return delta; + } + } + return -getID().compareTo(o.getID()); + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/optics/GenericClusterOrderEntry.java b/src/de/lmu/ifi/dbs/elki/result/optics/GenericClusterOrderEntry.java new file mode 100644 index 00000000..d20ee35b --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/optics/GenericClusterOrderEntry.java @@ -0,0 +1,145 @@ +package de.lmu.ifi.dbs.elki.result.optics; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import de.lmu.ifi.dbs.elki.database.ids.DBID; +import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; + +/** + * Provides an entry in a cluster order. + * + * @author Elke Achtert + * @param <D> the type of Distance used by the ClusterOrderEntry + */ +public class GenericClusterOrderEntry<D extends Distance<D>> implements Comparable<ClusterOrderEntry<D>>, ClusterOrderEntry<D> { + /** + * The id of the entry. + */ + private DBID objectID; + + /** + * The id of the entry's predecessor. + */ + private DBID predecessorID; + + /** + * The reachability of the entry. + */ + private D reachability; + + /** + * Creates a new entry in a cluster order with the specified parameters. + * + * @param objectID the id of the entry + * @param predecessorID the id of the entry's predecessor + * @param reachability the reachability of the entry + */ + public GenericClusterOrderEntry(DBID objectID, DBID predecessorID, D reachability) { + this.objectID = objectID; + this.predecessorID = predecessorID; + this.reachability = reachability; + } + + /** + * Indicates whether some other object is "equal to" this one. + * + * NOTE: for the use in an UpdatableHeap, only the ID is compared! + * + * @param o the reference object with which to compare. + * @return <code>true</code> if this object has the same attribute values as + * the o argument; <code>false</code> otherwise. + */ + @Override + public boolean equals(Object o) { + if(this == o) { + return true; + } + if(o == null || !(o instanceof ClusterOrderEntry)) { + return false; + } + + final ClusterOrderEntry<?> that = (ClusterOrderEntry<?>) o; + // Compare by ID only, for UpdatableHeap! + return objectID.equals(that.getID()); + } + + /** + * Returns a hash code value for the object. + * + * @return the object id if this entry + */ + @Override + public int hashCode() { + return objectID.hashCode(); + } + + /** + * Returns a string representation of the object. + * + * @return a string representation of the object. + */ + @Override + public String toString() { + return objectID + "(" + predecessorID + "," + reachability + ")"; + } + + /** + * Returns the object id of this entry. + * + * @return the object id of this entry + */ + @Override + public DBID getID() { + return objectID; + } + + /** + * Returns the id of the predecessor of this entry if this entry has a + * predecessor, null otherwise. + * + * @return the id of the predecessor of this entry + */ + @Override + public DBID getPredecessorID() { + return predecessorID; + } + + /** + * Returns the reachability distance of this entry + * + * @return the reachability distance of this entry + */ + @Override + public D getReachability() { + return reachability; + } + + @Override + public int compareTo(ClusterOrderEntry<D> o) { + int delta = this.getReachability().compareTo(o.getReachability()); + if(delta != 0) { + return delta; + } + return -getID().compareTo(o.getID()); + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/optics/package-info.java b/src/de/lmu/ifi/dbs/elki/result/optics/package-info.java new file mode 100644 index 00000000..8cb59763 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/optics/package-info.java @@ -0,0 +1,26 @@ +/** + * <p>Result classes for OPTICS.</p> + */ +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +package de.lmu.ifi.dbs.elki.result.optics;
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/outlier/BasicOutlierScoreMeta.java b/src/de/lmu/ifi/dbs/elki/result/outlier/BasicOutlierScoreMeta.java new file mode 100644 index 00000000..0ea4e921 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/outlier/BasicOutlierScoreMeta.java @@ -0,0 +1,178 @@ +package de.lmu.ifi.dbs.elki.result.outlier; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import de.lmu.ifi.dbs.elki.logging.Logging; + +/** + * Basic outlier score. Straightforward implementation of the + * {@link OutlierScoreMeta} interface. + * + * @author Erich Schubert + */ +public class BasicOutlierScoreMeta implements OutlierScoreMeta { + /** + * Store the actual minimum + */ + double actualMinimum = Double.NaN; + + /** + * Store the actual maximum + */ + double actualMaximum = Double.NaN; + + /** + * Store the theoretical minimum + */ + double theoreticalMinimum = Double.NaN; + + /** + * Store the theoretical maximum + */ + double theoreticalMaximum = Double.NaN; + + /** + * Store the theoretical baseline + */ + double theoreticalBaseline = Double.NaN; + + /** + * Constructor with actual values only. + * + * @param actualMinimum actual minimum + * @param actualMaximum actual maximum + */ + public BasicOutlierScoreMeta(double actualMinimum, double actualMaximum) { + this(actualMinimum, actualMaximum, Double.NaN, Double.NaN, Double.NaN); + } + + /** + * Constructor with all range values + * + * @param actualMinimum actual minimum + * @param actualMaximum actual maximum + * @param theoreticalMinimum theoretical minimum + * @param theoreticalMaximum theoretical maximum + */ + public BasicOutlierScoreMeta(double actualMinimum, double actualMaximum, double theoreticalMinimum, double theoreticalMaximum) { + this(actualMinimum, actualMaximum, theoreticalMinimum, theoreticalMaximum, Double.NaN); + } + + /** + * Full constructor - all values. + * + * @param actualMinimum actual minimum + * @param actualMaximum actual maximum + * @param theoreticalMinimum theoretical minimum + * @param theoreticalMaximum theoretical maximum + * @param theoreticalBaseline theoretical baseline + */ + public BasicOutlierScoreMeta(double actualMinimum, double actualMaximum, double theoreticalMinimum, double theoreticalMaximum, double theoreticalBaseline) { + super(); + if(Double.isNaN(actualMinimum) || Double.isNaN(actualMaximum)) { + Logging.getLogger(this.getClass()).warning("Warning: Outlier Score meta initalized with NaN values: " + actualMinimum + " - " + actualMaximum); + } + this.actualMinimum = actualMinimum; + this.actualMaximum = actualMaximum; + this.theoreticalMinimum = theoreticalMinimum; + this.theoreticalMaximum = theoreticalMaximum; + this.theoreticalBaseline = theoreticalBaseline; + } + + @Override + public double getActualMaximum() { + return actualMaximum; + } + + @Override + public double getActualMinimum() { + return actualMinimum; + } + + @Override + public double getTheoreticalBaseline() { + return theoreticalBaseline; + } + + @Override + public double getTheoreticalMaximum() { + return theoreticalMaximum; + } + + @Override + public double getTheoreticalMinimum() { + return theoreticalMinimum; + } + + @Override + public double normalizeScore(double value) { + double center = 0.0; + if(!Double.isNaN(theoreticalBaseline) && !Double.isInfinite(theoreticalBaseline)) { + center = theoreticalBaseline; + } + else if(!Double.isNaN(theoreticalMinimum) && !Double.isInfinite(theoreticalMinimum)) { + center = theoreticalMinimum; + } + else if(!Double.isNaN(actualMinimum) && !Double.isInfinite(actualMinimum)) { + center = actualMinimum; + } + if(value < center) { + return 0.0; + } + double max = Double.NaN; + if(!Double.isNaN(theoreticalMaximum) && !Double.isInfinite(theoreticalMaximum)) { + max = theoreticalMaximum; + } + else if(!Double.isNaN(actualMaximum) && !Double.isInfinite(actualMaximum)) { + max = actualMaximum; + } + if(!Double.isNaN(max) && !Double.isInfinite(max) && max >= center) { + return (value - center) / (max - center); + } + return value - center; + } + + /** + * @param actualMinimum the actualMinimum to set + */ + public void setActualMinimum(double actualMinimum) { + this.actualMinimum = actualMinimum; + } + + /** + * @param actualMaximum the actualMaximum to set + */ + public void setActualMaximum(double actualMaximum) { + this.actualMaximum = actualMaximum; + } + + @Override + public String getLongName() { + return "Outlier Score Metadata"; + } + + @Override + public String getShortName() { + return "outlier-score-meta"; + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/outlier/InvertedOutlierScoreMeta.java b/src/de/lmu/ifi/dbs/elki/result/outlier/InvertedOutlierScoreMeta.java new file mode 100644 index 00000000..6fd66bb8 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/outlier/InvertedOutlierScoreMeta.java @@ -0,0 +1,91 @@ +package de.lmu.ifi.dbs.elki.result.outlier; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * Class to signal a value-inverted outlier score, i.e. low values are outliers. + * + * @author Erich Schubert + */ +public class InvertedOutlierScoreMeta extends BasicOutlierScoreMeta { + /** + * Constructor with all values. + * + * @param actualMinimum Actual minimum in data + * @param actualMaximum Actual maximum in data + * @param theoreticalMinimum Theoretical minimum of algorithm + * @param theoreticalMaximum Theoretical maximum of algorithm + * @param theoreticalBaseline Theoretical Baseline + */ + public InvertedOutlierScoreMeta(double actualMinimum, double actualMaximum, double theoreticalMinimum, double theoreticalMaximum, double theoreticalBaseline) { + super(actualMinimum, actualMaximum, theoreticalMinimum, theoreticalMaximum, theoreticalBaseline); + } + + /** + * Constructor with range values. + * + * @param actualMinimum Actual minimum in data + * @param actualMaximum Actual maximum in data + * @param theoreticalMinimum Theoretical minimum of algorithm + * @param theoreticalMaximum Theoretical maximum of algorithm + */ + public InvertedOutlierScoreMeta(double actualMinimum, double actualMaximum, double theoreticalMinimum, double theoreticalMaximum) { + super(actualMinimum, actualMaximum, theoreticalMinimum, theoreticalMaximum); + } + + /** + * Constructor with actual range only. + * + * @param actualMinimum Actual minimum in data + * @param actualMaximum Actual maximum in data + */ + public InvertedOutlierScoreMeta(double actualMinimum, double actualMaximum) { + super(actualMinimum, actualMaximum); + } + + @Override + public double normalizeScore(double value) { + double center = 0.0; + if (!Double.isNaN(theoreticalBaseline) && !Double.isInfinite(theoreticalBaseline)) { + center = theoreticalBaseline; + } else if (!Double.isNaN(theoreticalMaximum) && !Double.isInfinite(theoreticalMaximum)) { + center = theoreticalMaximum; + } else if (!Double.isNaN(actualMaximum) && !Double.isInfinite(actualMaximum)) { + center = actualMaximum; + } + if (value > center) { + return 0.0; + } + double min = Double.NaN; + if (!Double.isNaN(theoreticalMinimum) && !Double.isInfinite(theoreticalMinimum)) { + min = theoreticalMinimum; + } + else if (!Double.isNaN(actualMinimum) && !Double.isInfinite(actualMinimum)) { + min = actualMinimum; + } + if (!Double.isNaN(min) && !Double.isInfinite(min) && min != center) { + return (center - value) / (center - min); + } + return center - value; + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/outlier/OrderingFromRelation.java b/src/de/lmu/ifi/dbs/elki/result/outlier/OrderingFromRelation.java new file mode 100644 index 00000000..d4aa0cf7 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/outlier/OrderingFromRelation.java @@ -0,0 +1,115 @@ +package de.lmu.ifi.dbs.elki.result.outlier; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.util.Collections; +import java.util.Comparator; + +import de.lmu.ifi.dbs.elki.database.ids.ArrayModifiableDBIDs; +import de.lmu.ifi.dbs.elki.database.ids.DBID; +import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil; +import de.lmu.ifi.dbs.elki.database.ids.DBIDs; +import de.lmu.ifi.dbs.elki.database.relation.Relation; +import de.lmu.ifi.dbs.elki.result.OrderingResult; +import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIterator; +import de.lmu.ifi.dbs.elki.utilities.iterator.IterableIteratorAdapter; + +/** + * Ordering obtained from an outlier score. + * + * @author Erich Schubert + * + * @apiviz.uses Relation + */ +public class OrderingFromRelation implements OrderingResult { + /** + * Outlier scores. + */ + protected Relation<Double> scores; + + /** + * Factor for ascending (+1) and descending (-1) ordering. + */ + protected int ascending = +1; + + /** + * Constructor for outlier orderings + * + * @param scores outlier score result + * @param ascending Ascending when {@code true}, descending otherwise + */ + public OrderingFromRelation(Relation<Double> scores, boolean ascending) { + super(); + this.scores = scores; + this.ascending = ascending ? +1 : -1; + } + + /** + * Ascending constructor. + * + * @param scores + */ + public OrderingFromRelation(Relation<Double> scores) { + this(scores, true); + } + + @Override + public DBIDs getDBIDs() { + return scores.getDBIDs(); + } + + @Override + public IterableIterator<DBID> iter(DBIDs ids) { + ArrayModifiableDBIDs sorted = DBIDUtil.newArray(ids); + Collections.sort(sorted, new ImpliedComparator()); + return new IterableIteratorAdapter<DBID>(sorted); + } + + @Override + public String getLongName() { + return scores.getLongName()+" Order"; + } + + @Override + public String getShortName() { + return scores.getShortName()+"_order"; + } + + /** + * Internal comparator, accessing the map to sort objects + * + * @author Erich Schubert + * + * @apiviz.exclude + */ + protected final class ImpliedComparator implements Comparator<DBID> { + @Override + public int compare(DBID id1, DBID id2) { + Double k1 = scores.get(id1); + Double k2 = scores.get(id2); + assert (k1 != null); + assert (k2 != null); + return ascending * k2.compareTo(k1); + } + } +} diff --git a/src/de/lmu/ifi/dbs/elki/result/outlier/OutlierResult.java b/src/de/lmu/ifi/dbs/elki/result/outlier/OutlierResult.java new file mode 100644 index 00000000..ba8239ec --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/outlier/OutlierResult.java @@ -0,0 +1,97 @@ +package de.lmu.ifi.dbs.elki.result.outlier; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import de.lmu.ifi.dbs.elki.database.relation.Relation; +import de.lmu.ifi.dbs.elki.result.BasicResult; +import de.lmu.ifi.dbs.elki.result.OrderingResult; + +/** + * Wrap a typical Outlier result, keeping direct references to the main result + * parts. + * + * @author Erich Schubert + * + * @apiviz.composedOf OutlierScoreMeta + * @apiviz.composedOf Relation oneway - - contains + * @apiviz.composedOf OrderingFromRelation + */ +public class OutlierResult extends BasicResult { + /** + * Outlier score meta information + */ + private OutlierScoreMeta meta; + + /** + * Outlier scores. + */ + private Relation<Double> scores; + + /** + * Outlier ordering. + */ + private OrderingResult ordering; + + /** + * Constructor. + * + * @param meta Outlier score metadata. + * @param scores Scores result. + */ + public OutlierResult(OutlierScoreMeta meta, Relation<Double> scores) { + super(scores.getLongName(), scores.getShortName()); + this.meta = meta; + this.scores = scores; + this.ordering = new OrderingFromRelation(scores, !(meta instanceof InvertedOutlierScoreMeta)); + this.addChildResult(scores); + this.addChildResult(ordering); + this.addChildResult(meta); + } + + /** + * Get the outlier score meta data + * + * @return the outlier meta information + */ + public OutlierScoreMeta getOutlierMeta() { + return meta; + } + + /** + * Get the outlier scores association. + * + * @return the scores + */ + public Relation<Double> getScores() { + return scores; + } + + /** + * Get the outlier ordering + * + * @return the ordering + */ + public OrderingResult getOrdering() { + return ordering; + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/outlier/OutlierScoreMeta.java b/src/de/lmu/ifi/dbs/elki/result/outlier/OutlierScoreMeta.java new file mode 100644 index 00000000..da65f254 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/outlier/OutlierScoreMeta.java @@ -0,0 +1,83 @@ +package de.lmu.ifi.dbs.elki.result.outlier; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import de.lmu.ifi.dbs.elki.result.Result; + +/** + * Generic meta information about the value range of an outlier score. + * + * All values can be {@link Double#NaN} if not specified by the algorithm or not + * computed (actual values). For theoretical values, it is explicitly allowed to + * return {@link Double#NEGATIVE_INFINITY} or {@link Double#POSITIVE_INFINITY}. + * + * @author Erich Schubert + */ +public interface OutlierScoreMeta extends Result { + /** + * Get the actual minimum of the value range. + * + * @return minimum or {@link Double#NaN} + */ + public double getActualMinimum(); + + /** + * Get the actual maximum of the value range. + * + * @return maximum or {@link Double#NaN} + */ + public double getActualMaximum(); + + /** + * Get the theoretical minimum of the value range. + * + * @return theoretical minimum or {@link Double#NaN} + */ + public double getTheoreticalMinimum(); + + /** + * Get the theoretical maximum of the value range. + * + * This value may be {@link Double#NEGATIVE_INFINITY} or {@link Double#NaN}. + * + * @return theoretical maximum or {@link Double#NaN} + */ + public double getTheoreticalMaximum(); + + /** + * Get the theoretical baseline of the value range. + * + * It will be common to see {@link Double#POSITIVE_INFINITY} here. + * + * @return theoretical baseline or {@link Double#NaN} + */ + public double getTheoreticalBaseline(); + + /** + * Return a normalized value of the outlier score. + * + * @param value outlier score + * @return Normalized value (in 0.0-1.0) + */ + public double normalizeScore(double value); +} diff --git a/src/de/lmu/ifi/dbs/elki/result/outlier/ProbabilisticOutlierScore.java b/src/de/lmu/ifi/dbs/elki/result/outlier/ProbabilisticOutlierScore.java new file mode 100644 index 00000000..0f078a54 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/outlier/ProbabilisticOutlierScore.java @@ -0,0 +1,125 @@ +package de.lmu.ifi.dbs.elki.result.outlier; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * Outlier score that is a probability value in the range 0.0 - 1.0 + * + * But the baseline may be different from 0.0! + * + * @author Erich Schubert + */ +public class ProbabilisticOutlierScore implements OutlierScoreMeta { + /** + * Actual minimum seen, if given by the algorithm. + */ + private double actualMinimum = Double.NaN; + /** + * Actual maximum seen, if given by the algorithm. + */ + private double actualMaximum = Double.NaN; + /** + * Theoretical baseline specified by the algorithm. Defaults to 0.0 in short constructor. + */ + private double theoreticalBaseline = Double.NaN; + + /** + * Default constructor. No actual values, Baseline 0.0 + */ + public ProbabilisticOutlierScore() { + this(Double.NaN, Double.NaN, 0.0); + } + + /** + * Constructor with baseline only. + * + * @param theoreticalBaseline Baseline + */ + public ProbabilisticOutlierScore(double theoreticalBaseline) { + this(Double.NaN, Double.NaN, theoreticalBaseline); + } + + /** + * Constructor with actual values, and a baseline of 0.0 + * + * @param actualMinimum actual minimum seen + * @param actualMaximum actual maximum seen + */ + public ProbabilisticOutlierScore(double actualMinimum, double actualMaximum) { + this(actualMinimum, actualMaximum, 0.0); + } + + /** + * Full constructor. + * + * @param actualMinimum actual minimum seen + * @param actualMaximum actual maximum seen + * @param theoreticalBaseline theoretical baseline + */ + public ProbabilisticOutlierScore(double actualMinimum, double actualMaximum, double theoreticalBaseline) { + super(); + this.actualMinimum = actualMinimum; + this.actualMaximum = actualMaximum; + this.theoreticalBaseline = theoreticalBaseline; + } + + @Override + public double getActualMinimum() { + return actualMinimum; + } + + @Override + public double getActualMaximum() { + return actualMaximum; + } + + @Override + public double getTheoreticalBaseline() { + return theoreticalBaseline; + } + + @Override + public double getTheoreticalMaximum() { + return 1.0; + } + + @Override + public double getTheoreticalMinimum() { + return 0.0; + } + + @Override + public double normalizeScore(double value) { + return value; + } + + @Override + public String getLongName() { + return "Outlier Score Metadata"; + } + + @Override + public String getShortName() { + return "outlier-score-meta"; + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/outlier/QuotientOutlierScoreMeta.java b/src/de/lmu/ifi/dbs/elki/result/outlier/QuotientOutlierScoreMeta.java new file mode 100644 index 00000000..ef477b34 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/outlier/QuotientOutlierScoreMeta.java @@ -0,0 +1,71 @@ +package de.lmu.ifi.dbs.elki.result.outlier; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + + +/** + * Score for outlier values generated by a quotient. + * + * @author Erich Schubert + * + */ +public class QuotientOutlierScoreMeta extends BasicOutlierScoreMeta { + /** + * Constructor with actual minimum and maximum values. + * + * If possible, please also specify a baseline! + * + * @param actualMinimum Actual minimum + * @param actualMaximum Actual maximum + */ + public QuotientOutlierScoreMeta(double actualMinimum, double actualMaximum) { + super(actualMinimum, actualMaximum); + } + + /** + * Constructor with all range values. + * + * If possible, please also specify a baseline! + * + * @param actualMinimum Actual minimum + * @param actualMaximum Actual maximum + * @param theoreticalMinimum Theoretical minimum + * @param theoreticalMaximum Theoretical maximum + */ + public QuotientOutlierScoreMeta(double actualMinimum, double actualMaximum, double theoreticalMinimum, double theoreticalMaximum) { + super(actualMinimum, actualMaximum, theoreticalMinimum, theoreticalMaximum); + } + + /** + * Full constructor with all values. + * + * @param actualMinimum Actual minimum. + * @param actualMaximum Actual maximum. + * @param theoreticalMinimum Theoretical minimum. + * @param theoreticalMaximum Theoretical maximum. + * @param theoreticalBaseline Theoretical baseline. + */ + public QuotientOutlierScoreMeta(double actualMinimum, double actualMaximum, double theoreticalMinimum, double theoreticalMaximum, double theoreticalBaseline) { + super(actualMinimum, actualMaximum, theoreticalMinimum, theoreticalMaximum, theoreticalBaseline); + } +} diff --git a/src/de/lmu/ifi/dbs/elki/result/outlier/package-info.java b/src/de/lmu/ifi/dbs/elki/result/outlier/package-info.java new file mode 100644 index 00000000..c6b61bfe --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/outlier/package-info.java @@ -0,0 +1,27 @@ +/** + * <p>Outlier result classes</p> + * + */ +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +package de.lmu.ifi.dbs.elki.result.outlier;
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/package-info.java b/src/de/lmu/ifi/dbs/elki/result/package-info.java new file mode 100644 index 00000000..aba3dec6 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/package-info.java @@ -0,0 +1,33 @@ +/** + * <p>Result types, representation and handling</p> + * + * @apiviz.exclude java.util.* + * @apiviz.exclude de.lmu.ifi.dbs.elki.algorithm.* + * @apiviz.exclude de.lmu.ifi.dbs.elki.database.* + * @apiviz.exclude de.lmu.ifi.dbs.elki.evaluation.* + * @apiviz.exclude de.lmu.ifi.dbs.elki.visualization.* + * @apiviz.exclude de.lmu.ifi.dbs.elki.workflow.* + */ +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +package de.lmu.ifi.dbs.elki.result;
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/MultipleFilesOutput.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/MultipleFilesOutput.java new file mode 100644 index 00000000..91bdc959 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/MultipleFilesOutput.java @@ -0,0 +1,187 @@ +package de.lmu.ifi.dbs.elki.result.textwriter; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.HashMap; +import java.util.zip.GZIPOutputStream; + +import de.lmu.ifi.dbs.elki.logging.Logging; + +/** + * Manage output to multiple files. + * + * @author Erich Schubert + */ +public class MultipleFilesOutput implements StreamFactory { + /** + * File name extension. + */ + private final static String EXTENSION = ".txt"; + + /** + * GZip extra file extension + */ + private final static String GZIP_EXTENSION = ".gz"; + + /** + * Default stream to write to when no name is supplied. + */ + private PrintStream defaultStream = null; + + /** + * Base file name. + */ + private File basename; + + /** + * HashMap of open print streams. + */ + private HashMap<String, PrintStream> map = new HashMap<String, PrintStream>(); + + /** + * Control gzip compression of output. + */ + private boolean usegzip = false; + + /** + * Logger for debugging. + */ + private final static Logging logger = Logging.getLogger(MultipleFilesOutput.class); + + /** + * Constructor + * + * @param base Base file name (folder name) + */ + public MultipleFilesOutput(File base) { + this(base, false); + } + + /** + * Constructor + * + * @param base Base file name (folder name) + * @param gzip Use gzip compression. + */ + public MultipleFilesOutput(File base, boolean gzip) { + this.basename = base; + this.usegzip = gzip; + } + + /** + * Retrieve/open the default output stream. + * + * @return default output stream + * @throws IOException + */ + private PrintStream getDefaultStream() throws IOException { + if(defaultStream != null) { + return defaultStream; + } + defaultStream = newStream("default"); + return defaultStream; + } + + /** + * Open a new stream of the given name + * + * @param name file name (which will be appended to the base name) + * @return stream object for the given name + * @throws IOException + */ + private PrintStream newStream(String name) throws IOException { + if (logger.isDebuggingFiner()) { + logger.debugFiner("Requested stream: "+name); + } + PrintStream res = map.get(name); + if(res != null) { + return res; + } + // Ensure the directory exists: + if(!basename.exists()) { + basename.mkdirs(); + } + String fn = basename.getAbsolutePath() + File.separator + name + EXTENSION; + if (usegzip) { + fn = fn + GZIP_EXTENSION; + } + File n = new File(fn); + OutputStream os = new FileOutputStream(n); + if (usegzip) { + // wrap into gzip stream. + os = new GZIPOutputStream(os); + } + res = new PrintStream(os); + if (logger.isDebuggingFiner()) { + logger.debugFiner("Opened new output stream:"+fn); + } + // cache. + map.put(name, res); + return res; + } + + /** + * Retrieve the output stream for the given file name. + */ + @Override + public PrintStream openStream(String filename) throws IOException { + if(filename == null) { + return getDefaultStream(); + } + return newStream(filename); + } + + /** + * Get GZIP compression flag. + * + * @return if GZIP compression is enabled + */ + protected boolean isGzipCompression() { + return usegzip; + } + + /** + * Set GZIP compression flag. + * + * @param usegzip use GZIP compression + */ + protected void setGzipCompression(boolean usegzip) { + this.usegzip = usegzip; + } + + /** + * Close (and forget) all output streams. + */ + @Override + public synchronized void closeAllStreams() { + for (PrintStream s : map.values()) { + s.close(); + } + map.clear(); + } +} diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/SingleStreamOutput.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/SingleStreamOutput.java new file mode 100644 index 00000000..1ec93091 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/SingleStreamOutput.java @@ -0,0 +1,148 @@ +package de.lmu.ifi.dbs.elki.result.textwriter; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.zip.GZIPOutputStream; + +/** + * Class to output all result data to a single stream (e.g. Stdout, single file) + * + * @author Erich Schubert + * + */ +public class SingleStreamOutput implements StreamFactory { + /** + * Output stream + */ + private PrintStream stream; + + /** + * Constructor using stdout. + * @throws IOException on IO error + */ + public SingleStreamOutput() throws IOException { + this(FileDescriptor.out); + } + + /** + * Constructor using stdout + * + * @param gzip Use gzip compression + * @throws IOException on IO error + */ + public SingleStreamOutput(boolean gzip) throws IOException { + this(FileDescriptor.out, gzip); + } + + /** + * Constructor with given file name. + * + * @param out filename + * @throws IOException on IO error + */ + public SingleStreamOutput(File out) throws IOException { + this(new FileOutputStream(out)); + } + + /** + * Constructor with given file name. + * + * @param out filename + * @param gzip Use gzip compression + * @throws IOException on IO error + */ + public SingleStreamOutput(File out, boolean gzip) throws IOException { + this(new FileOutputStream(out), gzip); + } + + /** + * Constructor with given FileDescriptor + * + * @param out file descriptor + * @throws IOException on IO error + */ + public SingleStreamOutput(FileDescriptor out) throws IOException { + this(new FileOutputStream(out)); + } + + /** + * Constructor with given FileDescriptor + * + * @param out file descriptor + * @param gzip Use gzip compression + * @throws IOException on IO error + */ + public SingleStreamOutput(FileDescriptor out, boolean gzip) throws IOException { + this(new FileOutputStream(out), gzip); + } + + /** + * Constructor with given FileOutputStream. + * + * @param out File output stream + * @throws IOException on IO error + */ + public SingleStreamOutput(FileOutputStream out) throws IOException { + this(out, false); + } + + /** + * Constructor with given FileOutputStream. + * + * @param out File output stream + * @param gzip Use gzip compression + * @throws IOException on IO error + */ + public SingleStreamOutput(FileOutputStream out, boolean gzip) throws IOException { + OutputStream os = out; + if (gzip) { + // wrap into gzip stream. + os = new GZIPOutputStream(os); + } + this.stream = new PrintStream(os); + } + + /** + * Return the objects shared print stream. + * + * @param filename ignored filename for SingleStreamOutput, as the name suggests + */ + @Override + public PrintStream openStream(String filename) { + return stream; + } + + /** + * Close output stream. + */ + @Override + public void closeAllStreams() { + stream.close(); + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/StreamFactory.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/StreamFactory.java new file mode 100644 index 00000000..a769f6c8 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/StreamFactory.java @@ -0,0 +1,49 @@ +package de.lmu.ifi.dbs.elki.result.textwriter; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.io.IOException; +import java.io.PrintStream; + +/** + * Interface for output handling (single file, multiple files, ...) + * + * @author Erich Schubert + */ +public interface StreamFactory { + /** + * Retrieve a print stream for output using the given label. + * Note that multiple labels MAY result in the same PrintStream, so you + * should be printing to only one stream at a time to avoid mixing outputs. + * + * @param label Output label. + * @return stream object for the given label + * @throws IOException on IO error + */ + public PrintStream openStream(String label) throws IOException; + + /** + * Close (and forget) all streams the factory has opened. + */ + public void closeAllStreams(); +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriteable.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriteable.java new file mode 100644 index 00000000..84d18c40 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriteable.java @@ -0,0 +1,39 @@ +package de.lmu.ifi.dbs.elki.result.textwriter; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/** + * Interface for objects providing a text serialization suitable for + * human reading and storage in CSV files. + * + * @author Erich Schubert + */ +//TODO: split TextWriteable interface into data writing and metadata writing? +public interface TextWriteable { + /** + * Write self to the given {@link TextWriterStream} + * @param out Output writer + * @param label Label + */ + public void writeToText(TextWriterStream out, String label); +} diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriter.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriter.java new file mode 100644 index 00000000..e6fac1f8 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriter.java @@ -0,0 +1,431 @@ +package de.lmu.ifi.dbs.elki.result.textwriter; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintStream; +import java.util.BitSet; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import de.lmu.ifi.dbs.elki.data.Cluster; +import de.lmu.ifi.dbs.elki.data.Clustering; +import de.lmu.ifi.dbs.elki.data.FeatureVector; +import de.lmu.ifi.dbs.elki.data.HierarchicalClassLabel; +import de.lmu.ifi.dbs.elki.data.LabelList; +import de.lmu.ifi.dbs.elki.data.SimpleClassLabel; +import de.lmu.ifi.dbs.elki.data.model.Model; +import de.lmu.ifi.dbs.elki.data.type.TypeUtil; +import de.lmu.ifi.dbs.elki.database.Database; +import de.lmu.ifi.dbs.elki.database.ids.DBID; +import de.lmu.ifi.dbs.elki.database.ids.DBIDs; +import de.lmu.ifi.dbs.elki.database.relation.Relation; +import de.lmu.ifi.dbs.elki.datasource.bundle.SingleObjectBundle; +import de.lmu.ifi.dbs.elki.distance.distancevalue.Distance; +import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector; +import de.lmu.ifi.dbs.elki.result.CollectionResult; +import de.lmu.ifi.dbs.elki.result.HierarchicalResult; +import de.lmu.ifi.dbs.elki.result.IterableResult; +import de.lmu.ifi.dbs.elki.result.OrderingResult; +import de.lmu.ifi.dbs.elki.result.Result; +import de.lmu.ifi.dbs.elki.result.ResultUtil; +import de.lmu.ifi.dbs.elki.result.SettingsResult; +import de.lmu.ifi.dbs.elki.result.textwriter.naming.NamingScheme; +import de.lmu.ifi.dbs.elki.result.textwriter.naming.SimpleEnumeratingScheme; +import de.lmu.ifi.dbs.elki.result.textwriter.writers.TextWriterDoubleDoublePair; +import de.lmu.ifi.dbs.elki.result.textwriter.writers.TextWriterObjectArray; +import de.lmu.ifi.dbs.elki.result.textwriter.writers.TextWriterObjectComment; +import de.lmu.ifi.dbs.elki.result.textwriter.writers.TextWriterObjectInline; +import de.lmu.ifi.dbs.elki.result.textwriter.writers.TextWriterPair; +import de.lmu.ifi.dbs.elki.result.textwriter.writers.TextWriterTextWriteable; +import de.lmu.ifi.dbs.elki.result.textwriter.writers.TextWriterTriple; +import de.lmu.ifi.dbs.elki.result.textwriter.writers.TextWriterVector; +import de.lmu.ifi.dbs.elki.utilities.HandlerList; +import de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException; +import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.SerializedParameterization; +import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ClassParameter; +import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter; +import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleDoublePair; +import de.lmu.ifi.dbs.elki.utilities.pairs.Pair; +import de.lmu.ifi.dbs.elki.utilities.pairs.Triple; + +/** + * Class to write a result to human-readable text output + * + * @author Erich Schubert + * + * @apiviz.landmark + * @apiviz.uses TextWriterStream oneway - - writesTo + * @apiviz.composedOf TextWriterWriterInterface + * @apiviz.has NamingScheme + */ +public class TextWriter { + /** + * Extension for txt-files. + */ + public static final String FILE_EXTENSION = ".txt"; + + /** + * Hash map for supported classes in writer. + */ + public final static HandlerList<TextWriterWriterInterface<?>> writers = new HandlerList<TextWriterWriterInterface<?>>(); + + /** + * Add some default handlers + */ + static { + TextWriterObjectInline trivialwriter = new TextWriterObjectInline(); + writers.insertHandler(Object.class, new TextWriterObjectComment()); + writers.insertHandler(Pair.class, new TextWriterPair()); + writers.insertHandler(DoubleDoublePair.class, new TextWriterDoubleDoublePair()); + writers.insertHandler(Triple.class, new TextWriterTriple()); + writers.insertHandler(FeatureVector.class, trivialwriter); + // these object can be serialized inline with toString() + writers.insertHandler(String.class, trivialwriter); + writers.insertHandler(Double.class, trivialwriter); + writers.insertHandler(Integer.class, trivialwriter); + writers.insertHandler(String[].class, new TextWriterObjectArray<String>()); + writers.insertHandler(Double[].class, new TextWriterObjectArray<Double>()); + writers.insertHandler(Integer[].class, new TextWriterObjectArray<Integer>()); + writers.insertHandler(BitSet.class, trivialwriter); + writers.insertHandler(Vector.class, new TextWriterVector()); + writers.insertHandler(Distance.class, trivialwriter); + writers.insertHandler(SimpleClassLabel.class, trivialwriter); + writers.insertHandler(HierarchicalClassLabel.class, trivialwriter); + writers.insertHandler(LabelList.class, trivialwriter); + writers.insertHandler(DBID.class, trivialwriter); + // Objects that have an own writeToText method. + writers.insertHandler(TextWriteable.class, new TextWriterTextWriteable()); + } + + /** + * For producing unique filenames. + */ + protected Map<String, Object> filenames = new HashMap<String, Object>(); + + /** + * Try to find a unique file name. + * + * @param result Result we print + * @param filenamepre File name prefix to use + * @return unique filename + */ + protected String getFilename(Object result, String filenamepre) { + if(filenamepre == null || filenamepre.length() == 0) { + filenamepre = "result"; + } + int i = 0; + while(true) { + String filename; + if(i > 0) { + filename = filenamepre + "-" + i; + } + else { + filename = filenamepre; + } + Object existing = filenames.get(filename); + if(existing == null || existing == result) { + filenames.put(filename, result); + return filename; + } + i++; + } + } + + /** + * Writes a header providing information concerning the underlying database + * and the specified parameter-settings. + * + * @param out the print stream where to write + * @param sr the settings to be written into the header + */ + protected void printSettings(TextWriterStream out, List<SettingsResult> sr) { + out.commentPrintSeparator(); + out.commentPrintLn("Settings:"); + + if(sr != null) { + for(SettingsResult settings : sr) { + Object last = null; + for(Pair<Object, Parameter<?, ?>> setting : settings.getSettings()) { + if(setting.first != last && setting.first != null) { + if(last != null) { + out.commentPrintLn(""); + } + String name; + try { + if(setting.first instanceof Class) { + name = ((Class<?>) setting.first).getName(); + } + else { + name = setting.first.getClass().getName(); + } + if(ClassParameter.class.isInstance(setting.first)) { + name = ((ClassParameter<?>) setting.first).getValue().getName(); + } + } + catch(NullPointerException e) { + name = "[null]"; + } + out.commentPrintLn(name); + last = setting.first; + } + String name = setting.second.getOptionID().getName(); + String value = "[unset]"; + try { + if(setting.second.isDefined()) { + value = setting.second.getValueAsString(); + } + } + catch(NullPointerException e) { + value = "[null]"; + } + out.commentPrintLn(SerializedParameterization.OPTION_PREFIX + name + " " + value); + } + } + } + + out.commentPrintSeparator(); + out.flush(); + } + + /** + * Stream output. + * + * @param db Database object + * @param r Result class + * @param streamOpener output stream manager + * @throws UnableToComplyException when no usable results were found + * @throws IOException on IO error + */ + public void output(Database db, Result r, StreamFactory streamOpener) throws UnableToComplyException, IOException { + List<Relation<?>> ra = null; + List<OrderingResult> ro = null; + List<Clustering<? extends Model>> rc = null; + List<IterableResult<?>> ri = null; + List<SettingsResult> rs = null; + HashSet<Result> otherres = null; + + ra = ResultUtil.getRelations(r); + ro = ResultUtil.getOrderingResults(r); + rc = ResultUtil.getClusteringResults(r); + ri = ResultUtil.getIterableResults(r); + rs = ResultUtil.getSettingsResults(r); + // collect other results + if(r instanceof HierarchicalResult) { + final List<Result> resultList = ResultUtil.filterResults((HierarchicalResult) r, Result.class); + otherres = new HashSet<Result>(resultList); + otherres.removeAll(ra); + otherres.removeAll(ro); + otherres.removeAll(rc); + otherres.removeAll(ri); + otherres.removeAll(rs); + otherres.remove(db); + Iterator<Result> it = otherres.iterator(); + while(it.hasNext()) { + if(it.next() instanceof HierarchicalResult) { + it.remove(); + } + } + } + + if(ra == null && ro == null && rc == null && ri == null) { + throw new UnableToComplyException("No printable result found."); + } + + if(ri != null && ri.size() > 0) { + // TODO: associations are not passed to ri results. + for(IterableResult<?> rii : ri) { + writeIterableResult(streamOpener, rii, rs); + } + } + if(rc != null && rc.size() > 0) { + for(Clustering<?> c : rc) { + NamingScheme naming = new SimpleEnumeratingScheme(c); + for(Cluster<?> clus : c.getAllClusters()) { + writeClusterResult(db, streamOpener, clus, ra, naming, rs); + } + } + } + if(ro != null && ro.size() > 0) { + for(OrderingResult ror : ro) { + writeOrderingResult(db, streamOpener, ror, ra, rs); + } + } + if(otherres != null && otherres.size() > 0) { + for(Result otherr : otherres) { + writeOtherResult(streamOpener, otherr, rs); + } + } + } + + private void printObject(TextWriterStream out, Database db, final DBID objID, List<Relation<?>> ra) throws UnableToComplyException, IOException { + SingleObjectBundle bundle = db.getBundle(objID); + // Write database element itself. + for(int i = 0; i < bundle.metaLength(); i++) { + Object obj = bundle.data(i); + TextWriterWriterInterface<?> owriter = out.getWriterFor(obj); + if(owriter == null) { + throw new UnableToComplyException("No handler for database object itself: " + obj.getClass().getSimpleName()); + } + String lbl = null; + // TODO: ugly compatibility hack... + if(TypeUtil.DBID.isAssignableFromType(bundle.meta(i))) { + lbl = "ID"; + } + owriter.writeObject(out, lbl, obj); + } + + // print the annotations + if(ra != null) { + for(Relation<?> a : ra) { + String label = a.getShortName(); + Object value = a.get(objID); + if(value == null) { + continue; + } + TextWriterWriterInterface<?> writer = out.getWriterFor(value); + if(writer == null) { + // Ignore + continue; + } + writer.writeObject(out, label, value); + } + } + out.flush(); + out.flush(); + } + + private void writeOtherResult(StreamFactory streamOpener, Result r, List<SettingsResult> rs) throws UnableToComplyException, IOException { + PrintStream outStream = streamOpener.openStream(getFilename(r, r.getShortName())); + TextWriterStream out = new TextWriterStream(outStream, writers); + TextWriterWriterInterface<?> owriter = out.getWriterFor(r); + if(owriter == null) { + throw new UnableToComplyException("No handler for result class: " + r.getClass().getSimpleName()); + } + // Write settings preamble + printSettings(out, rs); + // Write data + owriter.writeObject(out, null, r); + out.flush(); + } + + private void writeClusterResult(Database db, StreamFactory streamOpener, Cluster<?> clus, List<Relation<?>> ra, NamingScheme naming, List<SettingsResult> sr) throws FileNotFoundException, UnableToComplyException, IOException { + String filename = null; + if(naming != null) { + filename = filenameFromLabel(naming.getNameFor(clus)); + } + else { + filename = "cluster"; + } + + PrintStream outStream = streamOpener.openStream(getFilename(clus, filename)); + TextWriterStream out = new TextWriterStream(outStream, writers); + printSettings(out, sr); + + // Write cluster information + out.commentPrintLn("Cluster: " + naming.getNameFor(clus)); + if(clus.getParents().size() > 0) { + StringBuffer buf = new StringBuffer(); + buf.append("Parents:"); + for(Cluster<?> parent : clus.getParents()) { + buf.append(" ").append(naming.getNameFor(parent)); + } + out.commentPrintLn(buf.toString()); + } + if(clus.getChildren().size() > 0) { + StringBuffer buf = new StringBuffer(); + buf.append("Children:"); + for(Cluster<?> child : clus.getChildren()) { + buf.append(" ").append(naming.getNameFor(child)); + } + out.commentPrintLn(buf.toString()); + } + out.flush(); + + // print ids. + DBIDs ids = clus.getIDs(); + Iterator<DBID> iter = ids.iterator(); + + while(iter.hasNext()) { + printObject(out, db, iter.next(), ra); + } + out.commentPrintSeparator(); + out.flush(); + } + + private void writeIterableResult(StreamFactory streamOpener, IterableResult<?> ri, List<SettingsResult> sr) throws UnableToComplyException, IOException { + PrintStream outStream = streamOpener.openStream(getFilename(ri, ri.getShortName())); + TextWriterStream out = new TextWriterStream(outStream, writers); + printSettings(out, sr); + + // hack to print collectionResult header information + if(ri instanceof CollectionResult<?>) { + final Collection<String> hdr = ((CollectionResult<?>) ri).getHeader(); + if(hdr != null) { + for(String header : hdr) { + out.commentPrintLn(header); + } + out.flush(); + } + } + Iterator<?> i = ri.iterator(); + while(i.hasNext()) { + Object o = i.next(); + TextWriterWriterInterface<?> writer = out.getWriterFor(o); + if(writer != null) { + writer.writeObject(out, null, o); + } + out.flush(); + } + out.commentPrintSeparator(); + out.flush(); + } + + private void writeOrderingResult(Database db, StreamFactory streamOpener, OrderingResult or, List<Relation<?>> ra, List<SettingsResult> sr) throws IOException, UnableToComplyException { + PrintStream outStream = streamOpener.openStream(getFilename(or, or.getShortName())); + TextWriterStream out = new TextWriterStream(outStream, writers); + printSettings(out, sr); + + Iterator<DBID> i = or.iter(or.getDBIDs()); + while(i.hasNext()) { + printObject(out, db, i.next(), ra); + } + out.commentPrintSeparator(); + out.flush(); + } + + /** + * Derive a file name from the cluster label. + * + * @param label cluster label + * @return cleaned label suitable for file names. + */ + private String filenameFromLabel(String label) { + return label.toLowerCase().replaceAll("[^a-zA-Z0-9_.\\[\\]-]", "_"); + } +} diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriterStream.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriterStream.java new file mode 100644 index 00000000..9d50311f --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriterStream.java @@ -0,0 +1,255 @@ +package de.lmu.ifi.dbs.elki.result.textwriter; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.io.PrintStream; + +import de.lmu.ifi.dbs.elki.utilities.HandlerList; + +/** + * Representation of an output stream to a text file. + * + * @author Erich Schubert + * + * @apiviz.uses de.lmu.ifi.dbs.elki.result.textwriter.StreamFactory oneway - - wraps + */ +public class TextWriterStream { + /** + * Actual stream to write to. + */ + private PrintStream outStream; + + /** + * Buffer for inline data to output. + */ + private StringBuffer inline; + + /** + * Buffer for comment data to output. + */ + private StringBuffer comment; + + /** + * Handlers for various object types. + */ + private HandlerList<TextWriterWriterInterface<?>> writers; + + /** + * String to separate different entries while printing. + */ + public static final String SEPARATOR = " "; + + /** + * String to separate different entries while printing. + */ + public static final String QUOTE = "# "; + + /** + * Comment separator line. + * Since this will be printed without {@link #QUOTE} infront, it should be quoted string itself. + */ + public static final String COMMENTSEP = "###############################################################"; + + /** + * System newline character(s) + */ + private final static String NEWLINE = System.getProperty("line.separator"); + + /** + * Marker used in text serialization (and re-parsing) + */ + public final static String SER_MARKER = "Serialization class:"; + + /** + * Force incomments flag + */ + // TODO: solve this more gracefully + private boolean forceincomments = false; + + /** + * Constructor. + * + * @param out Actual stream to write to + * @param writers Handlers for various data types + */ + public TextWriterStream(PrintStream out, HandlerList<TextWriterWriterInterface<?>> writers) { + this.outStream = out; + this.writers = writers; + inline = new StringBuffer(); + comment = new StringBuffer(); + } + + /** + * Print an object into the comments section + * + * @param line object to print into commments + */ + public void commentPrint(Object line) { + comment.append(line); + } + + /** + * Print an object into the comments section with trailing newline. + * + * @param line object to print into comments + */ + public void commentPrintLn(Object line) { + comment.append(line); + comment.append(NEWLINE); + } + + /** + * Print a newline into the comments section. + */ + public void commentPrintLn() { + comment.append(NEWLINE); + } + + /** + * Print a separator line in the comments section. + */ + public void commentPrintSeparator() { + comment.append(COMMENTSEP + NEWLINE); + } + + /** + * Print data into the inline part of the file. + * Data is sanitized: newlines are replaced with spaces, and text + * containing separators is put in quotes. Quotes and escape characters + * are escaped. + * + * @param o object to print + */ + public void inlinePrint(Object o) { + if (forceincomments) { + commentPrint(o); + return; + } + if (inline.length() > 0) { + inline.append(SEPARATOR); + } + // remove newlines + String str = o.toString().replace(NEWLINE," "); + // escaping + str = str.replace("\\","\\\\").replace("\"","\\\""); + // when needed, add quotes. + if (str.contains(SEPARATOR)) { + str = "\""+str+"\""; + } + inline.append(str); + } + + /** + * Print data into the inline part of the file WITHOUT checking for + * separators (and thus quoting). + * + * @param o object to print. + */ + public void inlinePrintNoQuotes(Object o) { + if (forceincomments) { + commentPrint(o); + return; + } + if (inline.length() > 0) { + inline.append(SEPARATOR); + } + // remove newlines + String str = o.toString().replace(NEWLINE," "); + // escaping + str = str.replace("\\","\\\\").replace("\"","\\\""); + inline.append(str); + } + + /** + * Flush output: + * write inline data, then write comment section. Reset streams. + */ + public void flush() { + if (inline.length() > 0) { + outStream.println(inline); + } + inline.setLength(0); + if (comment.length() > 0) { + quotePrintln(outStream, comment.toString()); + } + comment.setLength(0); + } + + /** + * Quoted println. All lines written will be prefixed with {@link #QUOTE} + * + * @param outStream output stream to write to + * @param data data to print + */ + private void quotePrintln(PrintStream outStream, String data) { + String[] lines = data.split("\r\n|\r|\n"); + for (String line : lines) { + if (line.equals(COMMENTSEP)) { + outStream.println(COMMENTSEP); + } else { + outStream.println(QUOTE + line); + } + } + } + + /** + * Retrieve an appropriate writer from the handler list. + * + * @param o query object + * @return appropriate write, if available + */ + public TextWriterWriterInterface<?> getWriterFor(Object o) { + return writers.getHandler(o); + } + + /** + * Restore a vector undoing any normalization that was applied. + * (This class does not support normalization, it is only provided + * by derived classes, which will then have to use generics.) + * + * @param <O> Object class + * @param v vector to restore + * @return restored value. + */ + public <O> O normalizationRestore(O v) { + return v; + } + + /** + * Test force-in-comments flag. + * + * @return flag value + */ + protected boolean isForceincomments() { + return forceincomments; + } + + /** + * Set the force-in-comments flag. + * + * @param forceincomments the new flag value + */ + protected void setForceincomments(boolean forceincomments) { + this.forceincomments = forceincomments; + } +} diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriterWriterInterface.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriterWriterInterface.java new file mode 100644 index 00000000..71f8cf12 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/TextWriterWriterInterface.java @@ -0,0 +1,61 @@ +package de.lmu.ifi.dbs.elki.result.textwriter; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.io.IOException; + +import de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException; + +/** + * Base class for object writers. + * + * @author Erich Schubert + * + * @param <O> Object type (usually the class itself) + */ +public abstract class TextWriterWriterInterface<O> { + /** + * Write a given object to the output stream. + * + * @param out Output stream + * @param label Label to prefix + * @param object object to output + * @throws UnableToComplyException on errors + * @throws IOException on IO errors + */ + public abstract void write(TextWriterStream out, String label, O object) throws UnableToComplyException, IOException; + + /** + * Non-type-checking version. + * + * @param out Output stream + * @param label Label to prefix + * @param object object to output + * @throws UnableToComplyException on errors + * @throws IOException on IO errors + */ + @SuppressWarnings("unchecked") + public final void writeObject(TextWriterStream out, String label, Object object) throws UnableToComplyException, IOException { + write(out, label, (O) object); + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/naming/NamingScheme.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/naming/NamingScheme.java new file mode 100644 index 00000000..5d885b35 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/naming/NamingScheme.java @@ -0,0 +1,42 @@ +package de.lmu.ifi.dbs.elki.result.textwriter.naming; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import de.lmu.ifi.dbs.elki.data.Cluster; + +/** + * Naming scheme implementation for clusterings. + * + * @author Erich Schubert + * + * @apiviz.uses Cluster + */ +public interface NamingScheme { + /** + * Retrieve a name for the given cluster. + * + * @param cluster cluster to get a name for + * @return cluster name + */ + public String getNameFor(Cluster<?> cluster); +} diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/naming/SimpleEnumeratingScheme.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/naming/SimpleEnumeratingScheme.java new file mode 100644 index 00000000..102a017f --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/naming/SimpleEnumeratingScheme.java @@ -0,0 +1,109 @@ +package de.lmu.ifi.dbs.elki.result.textwriter.naming; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.util.HashMap; +import java.util.Map; + +import de.lmu.ifi.dbs.elki.data.Cluster; +import de.lmu.ifi.dbs.elki.data.Clustering; + +/** + * Simple enumerating naming scheme. Cluster names are generated as follows: - + * if the cluster has a name assigned, use it - otherwise use getNameAutomatic() + * as name, and add an enumeration postfix + * + * @author Erich Schubert + * + * @apiviz.uses Clustering + */ +public class SimpleEnumeratingScheme implements NamingScheme { + /** + * Clustering this scheme is applied to. + */ + private Clustering<?> clustering; + + /** + * count how often each name occurred so far. + */ + private Map<String, Integer> namecount = new HashMap<String, Integer>(); + + /** + * Assigned cluster names. + */ + private Map<Cluster<?>, String> names = new HashMap<Cluster<?>, String>(); + + /** + * This is the postfix added to the first cluster, which will be removed when + * there is only one cluster of this name. + */ + private final static String nullpostfix = " " + Integer.toString(0); + + /** + * Constructor. + * + * @param clustering Clustering result to name. + */ + public SimpleEnumeratingScheme(Clustering<?> clustering) { + super(); + this.clustering = clustering; + updateNames(); + } + + /** + * Assign names to each cluster (which doesn't have a name yet) + */ + private void updateNames() { + for(Cluster<?> cluster : clustering.getAllClusters()) { + if(names.get(cluster) == null) { + String sugname = cluster.getNameAutomatic(); + Integer count = namecount.get(sugname); + if(count == null) { + count = new Integer(0); + } + names.put(cluster, sugname + " " + count.toString()); + count++; + namecount.put(sugname, count); + } + } + } + + /** + * Retrieve the cluster name. When a name has not yet been assigned, call + * {@link #updateNames} + */ + @Override + public String getNameFor(Cluster<?> cluster) { + String nam = names.get(cluster); + if(nam == null) { + updateNames(); + nam = names.get(cluster); + } + if(nam.endsWith(nullpostfix)) { + if(namecount.get(nam.substring(0, nam.length() - nullpostfix.length())) == 1) { + nam = nam.substring(0, nam.length() - nullpostfix.length()); + } + } + return nam; + } +}
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/naming/package-info.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/naming/package-info.java new file mode 100644 index 00000000..8d10da45 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/naming/package-info.java @@ -0,0 +1,26 @@ +/** + * <p>Naming schemes for clusters (for output when an algorithm doesn't generate cluster names).</p> + */ +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +package de.lmu.ifi.dbs.elki.result.textwriter.naming;
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/package-info.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/package-info.java new file mode 100644 index 00000000..b2abeb5b --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/package-info.java @@ -0,0 +1,27 @@ +/** + * <p>Text serialization (CSV, Gnuplot, Console, ...)</p> + * + */ +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +package de.lmu.ifi.dbs.elki.result.textwriter;
\ No newline at end of file diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterDoubleDoublePair.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterDoubleDoublePair.java new file mode 100644 index 00000000..b8f25b6a --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterDoubleDoublePair.java @@ -0,0 +1,53 @@ +package de.lmu.ifi.dbs.elki.result.textwriter.writers; + +/* + This file is part of ELKI: + Environment for Developing KDD-Applications Supported by Index-Structures + + Copyright (C) 2011 + Ludwig-Maximilians-Universität München + Lehr- und Forschungseinheit für Datenbanksysteme + ELKI Development Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterStream; +import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterWriterInterface; +import de.lmu.ifi.dbs.elki.utilities.pairs.DoubleDoublePair; + +/** + * Write a pair + * + * @author Erich Schubert + * + */ +public class TextWriterDoubleDoublePair extends TextWriterWriterInterface<DoubleDoublePair> { + /** + * Serialize a pair, component-wise + */ + @Override + public void write(TextWriterStream out, String label, DoubleDoublePair object) { + if(object != null) { + String res; + if(label != null) { + res = label + "=" + object.first + "," + object.second; + } + else { + res = object.first + " " + object.second; + } + out.inlinePrintNoQuotes(res); + } + } +} diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterObjectArray.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterObjectArray.java new file mode 100644 index 00000000..2ab3428c --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterObjectArray.java @@ -0,0 +1,51 @@ +package de.lmu.ifi.dbs.elki.result.textwriter.writers; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterStream; +import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterWriterInterface; + +/** + * Write an object into the inline section, using the objects toString method. + * + * @author Erich Schubert + * + */ +public class TextWriterObjectArray<T> extends TextWriterWriterInterface<T[]> { + /** + * Serialize an object into the inline section. + */ + @Override + public void write(TextWriterStream out, String label, T[] v) { + StringBuffer buf = new StringBuffer(); + if(label != null) { + buf.append(label).append("="); + } + if(v != null) { + for (T o : v) { + buf.append(o.toString()); + } + } + out.inlinePrintNoQuotes(buf.toString()); + } +} diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterObjectComment.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterObjectComment.java new file mode 100644 index 00000000..05b6db0d --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterObjectComment.java @@ -0,0 +1,50 @@ +package de.lmu.ifi.dbs.elki.result.textwriter.writers; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterStream; +import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterWriterInterface; + +/** + * Write an object into the comments section, using the objects toString() + * method. + * + * @author Erich Schubert + * + */ +public class TextWriterObjectComment extends TextWriterWriterInterface<Object> { + /** + * Put an object into the comment section + */ + @Override + public void write(TextWriterStream out, String label, Object object) { + String res = ""; + if(label != null) { + res = res + label + "="; + } + if(object != null) { + res = res + object.toString(); + } + out.commentPrintLn(res); + } +} diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterObjectInline.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterObjectInline.java new file mode 100644 index 00000000..e327cc8f --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterObjectInline.java @@ -0,0 +1,54 @@ +package de.lmu.ifi.dbs.elki.result.textwriter.writers; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterStream; +import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterWriterInterface; + +/** + * Write an object into the inline section, using the objects toString method. + * + * @author Erich Schubert + * + */ +public class TextWriterObjectInline extends TextWriterWriterInterface<Object> { + /** + * Serialize an object into the inline section. + */ + @Override + public void write(TextWriterStream out, String label, Object object) { + String res = ""; + if(label != null) { + res = res + label + "="; + } + if(object != null) { + if(label != null) { + res = res + object.toString().replace(" ", ""); + } + else { + res = res + object.toString(); + } + } + out.inlinePrintNoQuotes(res); + } +} diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterPair.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterPair.java new file mode 100644 index 00000000..3566f94a --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterPair.java @@ -0,0 +1,64 @@ +package de.lmu.ifi.dbs.elki.result.textwriter.writers; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.io.IOException; + +import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterStream; +import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterWriterInterface; +import de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException; +import de.lmu.ifi.dbs.elki.utilities.pairs.Pair; + +/** + * Write a pair + * + * @author Erich Schubert + * + */ +public class TextWriterPair extends TextWriterWriterInterface<Pair<?,?>> { + /** + * Serialize a pair, component-wise + */ + @Override + @SuppressWarnings("unchecked") + public void write(TextWriterStream out, String label, Pair<?,?> object) throws UnableToComplyException, IOException { + if (object != null) { + Object first = object.getFirst(); + if (first != null) { + TextWriterWriterInterface<Object> tw = (TextWriterWriterInterface<Object>) out.getWriterFor(first); + if (tw == null) { + throw new UnableToComplyException("No handler for database object itself: " + first.getClass().getSimpleName()); + } + tw.write(out, label, first); + } + Object second = object.getSecond(); + if (second != null) { + TextWriterWriterInterface<Object> tw = (TextWriterWriterInterface<Object>) out.getWriterFor(second); + if (tw == null) { + throw new UnableToComplyException("No handler for database object itself: " + second.getClass().getSimpleName()); + } + tw.write(out, label, second); + } + } + } +} diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterTextWriteable.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterTextWriteable.java new file mode 100644 index 00000000..29a5abb2 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterTextWriteable.java @@ -0,0 +1,44 @@ +package de.lmu.ifi.dbs.elki.result.textwriter.writers; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import de.lmu.ifi.dbs.elki.result.textwriter.TextWriteable; +import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterStream; +import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterWriterInterface; + +/** + * Write an object, using the objects own {@link TextWriteable} interface. + * + * @author Erich Schubert + * + * @apiviz.uses TextWriteable + */ +public class TextWriterTextWriteable extends TextWriterWriterInterface<TextWriteable> { + /** + * Use the objects own text serialization. + */ + @Override + public void write(TextWriterStream out, String label, TextWriteable obj) { + obj.writeToText(out, label); + } +} diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterTriple.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterTriple.java new file mode 100644 index 00000000..1fb1025e --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterTriple.java @@ -0,0 +1,72 @@ +package de.lmu.ifi.dbs.elki.result.textwriter.writers; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.io.IOException; + +import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterStream; +import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterWriterInterface; +import de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException; +import de.lmu.ifi.dbs.elki.utilities.pairs.Triple; + +/** + * Write a triple + * + * @author Erich Schubert + * + */ +public class TextWriterTriple extends TextWriterWriterInterface<Triple<?,?,?>> { + /** + * Serialize a triple, component-wise + */ + @Override + @SuppressWarnings("unchecked") + public void write(TextWriterStream out, String label, Triple<?,?,?> object) throws UnableToComplyException, IOException { + if (object != null) { + Object first = object.getFirst(); + if (first != null) { + TextWriterWriterInterface<Object> tw = (TextWriterWriterInterface<Object>) out.getWriterFor(first); + if (tw == null) { + throw new UnableToComplyException("No handler for database object itself: " + first.getClass().getSimpleName()); + } + tw.write(out, label, first); + } + Object second = object.getSecond(); + if (second != null) { + TextWriterWriterInterface<Object> tw = (TextWriterWriterInterface<Object>) out.getWriterFor(second); + if (tw == null) { + throw new UnableToComplyException("No handler for database object itself: " + second.getClass().getSimpleName()); + } + tw.write(out, label, second); + } + Object third = object.getThird(); + if (third != null) { + TextWriterWriterInterface<Object> tw = (TextWriterWriterInterface<Object>) out.getWriterFor(third); + if (tw == null) { + throw new UnableToComplyException("No handler for database object itself: " + third.getClass().getSimpleName()); + } + tw.write(out, label, third); + } + } + } +} diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterVector.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterVector.java new file mode 100644 index 00000000..ef708f8e --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/TextWriterVector.java @@ -0,0 +1,49 @@ +package de.lmu.ifi.dbs.elki.result.textwriter.writers; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import de.lmu.ifi.dbs.elki.math.linearalgebra.Vector; +import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterStream; +import de.lmu.ifi.dbs.elki.result.textwriter.TextWriterWriterInterface; + +/** + * Write an object into the inline section, using the objects toString method. + * + * @author Erich Schubert + */ +public class TextWriterVector extends TextWriterWriterInterface<Vector> { + /** + * Serialize an object into the inline section. + */ + @Override + public void write(TextWriterStream out, String label, Vector v) { + String res = ""; + if(label != null) { + res = res + label + "="; + } + if(v != null) { + res = res + v.toStringNoWhitespace(); + } + out.inlinePrintNoQuotes(res); + } +} diff --git a/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/package-info.java b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/package-info.java new file mode 100644 index 00000000..66c5f706 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/result/textwriter/writers/package-info.java @@ -0,0 +1,27 @@ +/** + * <p>Serialization handlers for individual data types.</p> + * + */ +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +package de.lmu.ifi.dbs.elki.result.textwriter.writers;
\ No newline at end of file |