1 /*
2  * This file is part of ELKI:
3  * Environment for Developing KDD-Applications Supported by Index-Structures
4  *
5  * Copyright (C) 2018
6  * ELKI Development Team
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Affero General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Affero General Public License for more details.
17  *
18  * You should have received a copy of the GNU Affero General Public License
19  * along with this program. If not, see <http://www.gnu.org/licenses/>.
20  */
21 package de.lmu.ifi.dbs.elki.utilities.scaling.outlier;
22 
23 import de.lmu.ifi.dbs.elki.database.ids.DBIDIter;
24 import de.lmu.ifi.dbs.elki.database.relation.DoubleRelation;
25 import de.lmu.ifi.dbs.elki.math.DoubleMinMax;
26 import de.lmu.ifi.dbs.elki.result.outlier.OutlierResult;
27 import de.lmu.ifi.dbs.elki.utilities.datastructures.arraylike.NumberArrayAdapter;
28 import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer;
29 import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID;
30 import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization;
31 import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.DoubleParameter;
32 import net.jafama.FastMath;
33 
34 /**
35  * Scaling that can map arbitrary positive values to a value in the range of
36  * [0:1].
37  * <p>
38  * Transformation is done by taking the square root, then doing a linear linear
39  * mapping onto 0:1 using the minimum values seen.
40  *
41  * @author Erich Schubert
42  * @since 0.3
43  */
44 public class OutlierSqrtScaling implements OutlierScaling {
45   /**
46    * Minimum and maximum values.
47    */
48   protected double min, max;
49 
50   /**
51    * Predefined minimum and maximum values.
52    */
53   protected Double pmin = null, pmax = null;
54 
55   /**
56    * Scaling factor
57    */
58   protected double factor;
59 
60   /**
61    * Constructor.
62    *
63    * @param pmin Predefined minimum
64    * @param pmax Predefined maximum
65    */
OutlierSqrtScaling(Double pmin, Double pmax)66   public OutlierSqrtScaling(Double pmin, Double pmax) {
67     super();
68     this.pmin = pmin;
69     this.pmax = pmax;
70   }
71 
72   @Override
getScaled(double value)73   public double getScaled(double value) {
74     assert (factor != 0) : "prepare() was not run prior to using the scaling function.";
75     return value <= min ? 0. : Math.min(1, (FastMath.sqrt(value - min) / factor));
76   }
77 
78   @Override
prepare(OutlierResult or)79   public void prepare(OutlierResult or) {
80     if(pmin == null || pmax == null) {
81       DoubleMinMax mm = new DoubleMinMax();
82       DoubleRelation scores = or.getScores();
83       for(DBIDIter id = scores.iterDBIDs(); id.valid(); id.advance()) {
84         double val = scores.doubleValue(id);
85         if(!Double.isInfinite(val)) {
86           mm.put(val);
87         }
88       }
89       min = (pmin == null) ? mm.getMin() : pmin;
90       max = (pmax == null) ? mm.getMax() : pmax;
91     }
92     factor = FastMath.sqrt(max - min);
93   }
94 
95   @Override
prepare(A array, NumberArrayAdapter<?, A> adapter)96   public <A> void prepare(A array, NumberArrayAdapter<?, A> adapter) {
97     if(pmin == null || pmax == null) {
98       DoubleMinMax mm = new DoubleMinMax();
99       final int size = adapter.size(array);
100       for(int i = 0; i < size; i++) {
101         double val = adapter.getDouble(array, i);
102         if(!Double.isInfinite(val)) {
103           mm.put(val);
104         }
105       }
106       min = (pmin == null) ? mm.getMin() : pmin;
107       max = (pmax == null) ? mm.getMax() : pmax;
108     }
109     factor = FastMath.sqrt(max - min);
110   }
111 
112   @Override
getMin()113   public double getMin() {
114     return 0.0;
115   }
116 
117   @Override
getMax()118   public double getMax() {
119     return 1.0;
120   }
121 
122   /**
123    * Parameterization class.
124    *
125    * @author Erich Schubert
126    */
127   public static class Parameterizer extends AbstractParameterizer {
128     /**
129      * Parameter to specify the fixed minimum to use.
130      */
131     public static final OptionID MIN_ID = new OptionID("sqrtscale.min", "Fixed minimum to use in sqrt scaling.");
132 
133     /**
134      * Parameter to specify the fixed maximum to use.
135      */
136     public static final OptionID MAX_ID = new OptionID("sqrtscale.max", "Fixed maximum to use in sqrt scaling.");
137 
138     /**
139      * Predefined minimum value.
140      */
141     protected double min;
142 
143     /**
144      * Predefined maximum value.
145      */
146     protected double max;
147 
148     @Override
makeOptions(Parameterization config)149     protected void makeOptions(Parameterization config) {
150       super.makeOptions(config);
151       DoubleParameter minP = new DoubleParameter(MIN_ID) //
152           .setOptional(true);
153       if(config.grab(minP)) {
154         min = minP.getValue();
155       }
156       DoubleParameter maxP = new DoubleParameter(MAX_ID) //
157           .setOptional(true);
158       if(config.grab(maxP)) {
159         max = maxP.getValue();
160       }
161     }
162 
163     @Override
makeInstance()164     protected OutlierSqrtScaling makeInstance() {
165       return new OutlierSqrtScaling(min, max);
166     }
167   }
168 }
169