001: /*
002: * This program is free software; you can redistribute it and/or modify
003: * it under the terms of the GNU General Public License as published by
004: * the Free Software Foundation; either version 2 of the License, or
005: * (at your option) any later version.
006: *
007: * This program is distributed in the hope that it will be useful,
008: * but WITHOUT ANY WARRANTY; without even the implied warranty of
009: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
010: * GNU General Public License for more details.
011: *
012: * You should have received a copy of the GNU General Public License
013: * along with this program; if not, write to the Free Software
014: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
015: */
016:
017: /*
018: * MahalanobisEstimator.java
019: * Copyright (C) 1999 University of Waikato, Hamilton, New Zealand
020: *
021: */
022:
023: package weka.estimators;
024:
025: import weka.core.Capabilities.Capability;
026: import weka.core.matrix.Matrix;
027: import weka.core.Capabilities;
028: import weka.core.Utils;
029:
030: /**
031: * Simple probability estimator that places a single normal distribution
032: * over the observed values.
033: *
034: * @author Len Trigg (trigg@cs.waikato.ac.nz)
035: * @version $Revision: 1.7 $
036: */
037: public class MahalanobisEstimator extends Estimator implements
038: IncrementalEstimator {
039:
040: /** for serialization */
041: private static final long serialVersionUID = 8950225468990043868L;
042:
043: /** The inverse of the covariance matrix */
044: private Matrix m_CovarianceInverse;
045:
046: /** The determinant of the covariance matrix */
047: private double m_Determinant;
048:
049: /**
050: * The difference between the conditioning value and the conditioning mean
051: */
052: private double m_ConstDelta;
053:
054: /** The mean of the values */
055: private double m_ValueMean;
056:
057: /** 2 * PI */
058: private static double TWO_PI = 2 * Math.PI;
059:
060: /**
061: * Returns value for normal kernel
062: *
063: * @param x the argument to the kernel function
064: * @param variance the variance
065: * @return the value for a normal kernel
066: */
067: private double normalKernel(double x) {
068:
069: Matrix this Point = new Matrix(1, 2);
070: this Point.set(0, 0, x);
071: this Point.set(0, 1, m_ConstDelta);
072: return Math.exp(-this Point.times(m_CovarianceInverse).times(
073: this Point.transpose()).get(0, 0) / 2)
074: / (Math.sqrt(TWO_PI) * m_Determinant);
075: }
076:
077: /**
078: * Constructor
079: *
080: * @param covariance
081: * @param constDelta
082: * @param valueMean
083: */
084: public MahalanobisEstimator(Matrix covariance, double constDelta,
085: double valueMean) {
086:
087: m_CovarianceInverse = null;
088: if ((covariance.getRowDimension() == 2)
089: && (covariance.getColumnDimension() == 2)) {
090: double a = covariance.get(0, 0);
091: double b = covariance.get(0, 1);
092: double c = covariance.get(1, 0);
093: double d = covariance.get(1, 1);
094: if (a == 0) {
095: a = c;
096: c = 0;
097: double temp = b;
098: b = d;
099: d = temp;
100: }
101: if (a == 0) {
102: return;
103: }
104: double denom = d - c * b / a;
105: if (denom == 0) {
106: return;
107: }
108: m_Determinant = covariance.get(0, 0) * covariance.get(1, 1)
109: - covariance.get(1, 0) * covariance.get(0, 1);
110: m_CovarianceInverse = new Matrix(2, 2);
111: m_CovarianceInverse.set(0, 0, 1.0 / a + b * c / a / a
112: / denom);
113: m_CovarianceInverse.set(0, 1, -b / a / denom);
114: m_CovarianceInverse.set(1, 0, -c / a / denom);
115: m_CovarianceInverse.set(1, 1, 1.0 / denom);
116: m_ConstDelta = constDelta;
117: m_ValueMean = valueMean;
118: }
119: }
120:
121: /**
122: * Add a new data value to the current estimator. Does nothing because the
123: * data is provided in the constructor.
124: *
125: * @param data the new data value
126: * @param weight the weight assigned to the data value
127: */
128: public void addValue(double data, double weight) {
129:
130: }
131:
132: /**
133: * Get a probability estimate for a value
134: *
135: * @param data the value to estimate the probability of
136: * @return the estimated probability of the supplied value
137: */
138: public double getProbability(double data) {
139:
140: double delta = data - m_ValueMean;
141: if (m_CovarianceInverse == null) {
142: return 0;
143: }
144: return normalKernel(delta);
145: }
146:
147: /** Display a representation of this estimator */
148: public String toString() {
149:
150: if (m_CovarianceInverse == null) {
151: return "No covariance inverse\n";
152: }
153: return "Mahalanovis Distribution. Mean = "
154: + Utils.doubleToString(m_ValueMean, 4, 2)
155: + " ConditionalOffset = "
156: + Utils.doubleToString(m_ConstDelta, 4, 2) + "\n"
157: + "Covariance Matrix: Determinant = " + m_Determinant
158: + " Inverse:\n" + m_CovarianceInverse;
159: }
160:
161: /**
162: * Returns default capabilities of the classifier.
163: *
164: * @return the capabilities of this classifier
165: */
166: public Capabilities getCapabilities() {
167: Capabilities result = super .getCapabilities();
168:
169: // attributes
170: result.enable(Capability.NUMERIC_ATTRIBUTES);
171: return result;
172: }
173:
174: /**
175: * Main method for testing this class.
176: *
177: * @param argv should contain a sequence of numeric values
178: */
179: public static void main(String[] argv) {
180:
181: try {
182: double delta = 0.5;
183: double xmean = 0;
184: double lower = 0;
185: double upper = 10;
186: Matrix covariance = new Matrix(2, 2);
187: covariance.set(0, 0, 2);
188: covariance.set(0, 1, -3);
189: covariance.set(1, 0, -4);
190: covariance.set(1, 1, 5);
191: if (argv.length > 0) {
192: covariance.set(0, 0, Double.valueOf(argv[0])
193: .doubleValue());
194: }
195: if (argv.length > 1) {
196: covariance.set(0, 1, Double.valueOf(argv[1])
197: .doubleValue());
198: }
199: if (argv.length > 2) {
200: covariance.set(1, 0, Double.valueOf(argv[2])
201: .doubleValue());
202: }
203: if (argv.length > 3) {
204: covariance.set(1, 1, Double.valueOf(argv[3])
205: .doubleValue());
206: }
207: if (argv.length > 4) {
208: delta = Double.valueOf(argv[4]).doubleValue();
209: }
210: if (argv.length > 5) {
211: xmean = Double.valueOf(argv[5]).doubleValue();
212: }
213:
214: MahalanobisEstimator newEst = new MahalanobisEstimator(
215: covariance, delta, xmean);
216: if (argv.length > 6) {
217: lower = Double.valueOf(argv[6]).doubleValue();
218: if (argv.length > 7) {
219: upper = Double.valueOf(argv[7]).doubleValue();
220: }
221: double increment = (upper - lower) / 50;
222: for (double current = lower; current <= upper; current += increment)
223: System.out.println(current + " "
224: + newEst.getProbability(current));
225: } else {
226: System.out.println("Covariance Matrix\n" + covariance);
227: System.out.println(newEst);
228: }
229: } catch (Exception e) {
230: System.out.println(e.getMessage());
231: }
232: }
233: }
|