/* * ARX: Powerful Data Anonymization * Copyright 2012 - 2017 Fabian Prasser, Florian Kohlmayer and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.deidentifier.arx.metric.v2; import java.io.IOException; import java.io.ObjectInputStream; import java.util.Arrays; import org.deidentifier.arx.metric.InformationLoss; /** * This class implements an information loss which can be represented as a * decimal number per quasi-identifier. * * @author Fabian Prasser * @author Florian Kohlmayer */ public class ILMultiDimensionalRank extends AbstractILMultiDimensional { /** SVUID. */ private static final long serialVersionUID = 591145071792293317L; /** Aggregate. */ private double[] aggregate = null; /** Geometric mean. */ private double mean = 0d; /** * Clone constructor. * * @param mean * @param aggregate * @param values * @param weights */ private ILMultiDimensionalRank(final double mean, final double[] aggregate, final double[] values, final double[] weights) { super(values, weights); this.mean = mean; this.aggregate = aggregate; } /** * Creates a new instance. * * @param values * @param weights */ ILMultiDimensionalRank(final double[] values, final double[] weights) { super(values, weights); this.aggregate = getAggregate(); this.mean = getMean(); } @Override public InformationLoss<double[]> clone() { return new ILMultiDimensionalRank(mean, aggregate, getValues(), getWeights()); } @Override public int compareTo(InformationLoss<?> other) { if (other == null) { throw new IllegalArgumentException("Argument must not be null"); } else { double[] otherValue = convert(other).aggregate; double[] thisValue = aggregate; for (int i = 0; i < otherValue.length; i++) { int cmp = Double.compare(thisValue[i], otherValue[i]); if (cmp != 0) return cmp; } return 0; } } @Override public double[] getValue() { return this.getValues(); } @Override public int hashCode() { return Arrays.hashCode(this.aggregate); } @Override public double relativeTo(InformationLoss<?> min, InformationLoss<?> max) { // TODO: Fix this crap double _min = convert(min).mean; double _max = convert(max).mean; if (_max - _min == 0d) return 0d; double result = (this.mean - _min) / (_max - _min); return result < 0d ? 0d : (result > 1d ? 1d : result); } @Override public String toString() { return Arrays.toString(this.aggregate); } /** * Implements the aggregation function. * * @return */ private double[] getAggregate() { double[] values = getValues(); double[] weights = getWeights(); double[] result = new double[values.length]; for (int i = 0; i < values.length; i++) { result[i] = values[i] * weights[i]; } sortDescending(result); return result; } /** * Returns the geometric mean. Handles zero values by adding 1 to each component * and subtracting 1 from the result. * * @return */ private double getMean() { double[] values = getValues(); double result = 1.0d; for (int i = 0; i < values.length; i++) { result *= Math.pow(values[i] + 1.0d, 1.0d / (double) values.length); } return result - 1d; } /** * Overwritten to handle changes in how the mean is computed. * * @param stream * @throws IOException * @throws ClassNotFoundException */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); mean = getMean(); } /** * Sorts the array in descending order. * * @param value */ private void sortDescending(double[] value) { Arrays.sort(value); for (int i = 0; i < value.length / 2; i++) { int other = value.length - (i + 1); double temp = value[i]; value[i] = value[other]; value[other] = temp; } } @Override protected ILMultiDimensionalRank convert(InformationLoss<?> other) { if (other == null) return null; if (!other.getClass().equals(this.getClass())) { throw new IllegalArgumentException("Incompatible class (" + other.getClass().getSimpleName() + ")"); } else { return (ILMultiDimensionalRank) other; } } @Override protected void setValues(double[] values) { super.setValues(values); this.aggregate = getAggregate(); this.mean = getMean(); } }