summaryrefslogtreecommitdiff
path: root/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialGeneratedOutlier.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialGeneratedOutlier.java')
-rw-r--r--src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialGeneratedOutlier.java212
1 files changed, 212 insertions, 0 deletions
diff --git a/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialGeneratedOutlier.java b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialGeneratedOutlier.java
new file mode 100644
index 00000000..db40ff30
--- /dev/null
+++ b/src/de/lmu/ifi/dbs/elki/algorithm/outlier/trivial/TrivialGeneratedOutlier.java
@@ -0,0 +1,212 @@
+package de.lmu.ifi.dbs.elki.algorithm.outlier.trivial;
+/*
+ This file is part of ELKI:
+ Environment for Developing KDD-Applications Supported by Index-Structures
+
+ Copyright (C) 2012
+ 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.HashSet;
+
+import de.lmu.ifi.dbs.elki.algorithm.AbstractAlgorithm;
+import de.lmu.ifi.dbs.elki.algorithm.outlier.OutlierAlgorithm;
+import de.lmu.ifi.dbs.elki.data.NumberVector;
+import de.lmu.ifi.dbs.elki.data.model.Model;
+import de.lmu.ifi.dbs.elki.data.synthetic.bymodel.GeneratorSingleCluster;
+import de.lmu.ifi.dbs.elki.data.type.NoSupportedDataTypeException;
+import de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeInformation;
+import de.lmu.ifi.dbs.elki.data.type.TypeUtil;
+import de.lmu.ifi.dbs.elki.database.Database;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreFactory;
+import de.lmu.ifi.dbs.elki.database.datastore.DataStoreUtil;
+import de.lmu.ifi.dbs.elki.database.datastore.WritableDoubleDataStore;
+import de.lmu.ifi.dbs.elki.database.ids.DBID;
+import de.lmu.ifi.dbs.elki.database.relation.MaterializedRelation;
+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.math.statistics.distribution.ChiSquaredDistribution;
+import de.lmu.ifi.dbs.elki.math.statistics.distribution.Distribution;
+import de.lmu.ifi.dbs.elki.math.statistics.distribution.NormalDistribution;
+import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
+import de.lmu.ifi.dbs.elki.result.outlier.OutlierScoreMeta;
+import de.lmu.ifi.dbs.elki.result.outlier.ProbabilisticOutlierScore;
+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.DoubleParameter;
+
+/**
+ * Extract outlier score from the model the objects were generated by.
+ *
+ * This algorithm can only be applied to data that was freshly generated, to the
+ * generator model information is still available.
+ *
+ * @author Erich Schubert
+ */
+public class TrivialGeneratedOutlier extends AbstractAlgorithm<OutlierResult> implements OutlierAlgorithm {
+ /**
+ * Class logger
+ */
+ private static final Logging logger = Logging.getLogger(TrivialGeneratedOutlier.class);
+
+ /**
+ * Expected share of outliers
+ */
+ public static final OptionID EXPECT_ID = OptionID.getOrCreateOptionID("modeloutlier.expect", "Expected amount of outliers, for making the scores more intuitive.");
+
+ /**
+ * Expected share of outliers.
+ */
+ double expect = 0.01;
+
+ /**
+ * Constructor.
+ *
+ * @param expect Expected share of outliers
+ */
+ public TrivialGeneratedOutlier(double expect) {
+ super();
+ this.expect = expect;
+ }
+
+ /**
+ * Constructor.
+ */
+ public TrivialGeneratedOutlier() {
+ this(0.01);
+ }
+
+ @Override
+ public TypeInformation[] getInputTypeRestriction() {
+ return TypeUtil.array(TypeUtil.NUMBER_VECTOR_FIELD, new SimpleTypeInformation<Model>(Model.class), TypeUtil.GUESSED_LABEL);
+ }
+
+ @Override
+ public OutlierResult run(Database database) throws IllegalStateException {
+ Relation<NumberVector<?, ?>> vecs = database.getRelation(TypeUtil.NUMBER_VECTOR_FIELD);
+ Relation<Model> models = database.getRelation(new SimpleTypeInformation<Model>(Model.class));
+ // Prefer a true class label
+ try {
+ Relation<?> relation = database.getRelation(TypeUtil.CLASSLABEL);
+ return run(models, vecs, relation);
+ }
+ catch(NoSupportedDataTypeException e) {
+ // Otherwise, try any labellike.
+ return run(models, vecs, database.getRelation(TypeUtil.GUESSED_LABEL));
+ }
+ }
+
+ /**
+ * Run the algorithm
+ *
+ * @param models Model relation
+ * @param vecs Vector relation
+ * @param labels Label relation
+ * @return Outlier result
+ */
+ public OutlierResult run(Relation<Model> models, Relation<NumberVector<?, ?>> vecs, Relation<?> labels) {
+ WritableDoubleDataStore scores = DataStoreUtil.makeDoubleStorage(models.getDBIDs(), DataStoreFactory.HINT_HOT);
+
+ // Adjustment constant
+ final double minscore = expect / (expect + 1);
+
+ HashSet<GeneratorSingleCluster> generators = new HashSet<GeneratorSingleCluster>();
+ for(DBID id : models.iterDBIDs()) {
+ Model model = models.get(id);
+ if(model instanceof GeneratorSingleCluster) {
+ generators.add((GeneratorSingleCluster) model);
+ }
+ }
+ if(generators.size() == 0) {
+ logger.warning("No generator models found for dataset - all points will be considered outliers.");
+ }
+
+ for(DBID id : models.iterDBIDs()) {
+ double score = 0.0;
+ // Convert to a math vector
+ Vector v = vecs.get(id).getColumnVector();
+ for(GeneratorSingleCluster gen : generators) {
+ Vector tv = v;
+ // Transform backwards
+ if(gen.getTransformation() != null) {
+ tv = gen.getTransformation().applyInverse(v);
+ }
+ final int dim = tv.getDimensionality();
+ double lensq = 0.0;
+ int norm = 0;
+ for(int i = 0; i < dim; i++) {
+ Distribution dist = gen.getDistribution(i);
+ if(dist instanceof NormalDistribution) {
+ NormalDistribution d = (NormalDistribution) dist;
+ double delta = (tv.get(i) - d.getMean()) / d.getStddev();
+ lensq += delta * delta;
+ norm += 1;
+ }
+ }
+ if(norm > 0) {
+ // The squared distances are ChiSquared distributed
+ score = Math.max(score, 1 - ChiSquaredDistribution.cdf(lensq, norm));
+ }
+ }
+ // score inversion.
+ score = expect / (expect + score);
+ // adjust to 0 to 1 range:
+ score = (score - minscore) / (1 - minscore);
+ scores.putDouble(id, score);
+ }
+ Relation<Double> scoreres = new MaterializedRelation<Double>("Model outlier scores", "model-outlier", TypeUtil.DOUBLE, scores, models.getDBIDs());
+ OutlierScoreMeta meta = new ProbabilisticOutlierScore(0., 1.);
+ return new OutlierResult(meta, scoreres);
+ }
+
+ @Override
+ protected Logging getLogger() {
+ return logger;
+ }
+
+ /**
+ * Parameterization class.
+ *
+ * @author Erich Schubert
+ *
+ * @apiviz.exclude
+ */
+ public static class Parameterizer extends AbstractParameterizer {
+ /**
+ * Expected share of outliers
+ */
+ double expect;
+
+ @Override
+ protected void makeOptions(Parameterization config) {
+ super.makeOptions(config);
+ DoubleParameter expectP = new DoubleParameter(EXPECT_ID, 0.01);
+ if(config.grab(expectP)) {
+ expect = expectP.getValue();
+ }
+ }
+
+ @Override
+ protected TrivialGeneratedOutlier makeInstance() {
+ return new TrivialGeneratedOutlier(expect);
+ }
+ }
+} \ No newline at end of file