/* * This file is part of the JFeatureLib project: https://github.com/locked-fg/JFeatureLib * JFeatureLib is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * JFeatureLib 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with JFeatureLib; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * You are kindly asked to refer to the papers of the according authors which * should be mentioned in the Javadocs of the respective classes as well as the * JFeatureLib project itself. * * Hints how to cite the projects can be found at * https://github.com/locked-fg/JFeatureLib/wiki/Citation */ package de.lmu.ifi.dbs.jfeaturelib.utils; import de.lmu.ifi.dbs.utilities.Arrays2; import java.util.Arrays; /** * The * <code>Interpolated1DHistogram</code> class is used to create interpolates * histograms for a certain range of values ([min,max[) with a defined range of * bins. Values that are added to the histogram are interpolated to ne nearest * neighboring bin according to the distance of the value to the center of the * bin. If the according neighbor is out of the range of the histogram, then the * according interpolated value is skipped silently and the value of one bin is * raised only: * * <pre> * Interpolated1DHistogram ih = new Interpolated1DHistogram(0,3,3); * ih.add(0); // histogram: 0.5, 0, 0 * ih.add(1.5); // histogram: 0.5, 1, 0 * </pre> * * @author graf * @since 11/4/2011 */ public class Interpolated1DHistogram { /** * inclusive minimum value of the histogram range */ final double min; /** * exclusive maximum value of the histogram range */ final double max; /** * The actual histogram data values. */ final double[] bins; /** * the lower bounds (= left borders) of each bin */ final double[] lowerKey; /** * width of a histogram bin */ final double binWidth; /** * Initializes the histogram with user defined min, max and count values * * @param min inclusive lower bound * @param max exclusive upper bound * @param binCount amount of bins between min and max */ public Interpolated1DHistogram(double min, double max, int binCount) { if (min >= max) { throw new IllegalArgumentException("min must be > max. But was " + min + " / " + max); } this.min = min; this.max = max; this.binWidth = (this.max - this.min) / binCount; this.lowerKey = new double[binCount]; this.bins = new double[binCount]; for (int i = 0; i < binCount; i++) { lowerKey[i] = min + i * binWidth; } } /** * Raises the histogram at the specivied key position by 1 (might decrease * due to interpolation). * * @param pos in the range of [min, max[ */ public void add(double pos) { add(pos, 1); } /** * Raises the histogram at the specivied key position by * <code>value</code> (might decrease due to interpolation). * * @param pos in the range of [min, max[ * @param value */ public void add(double pos, double value) { if (pos < min || pos >= max || Double.isNaN(pos)) { throw new IllegalArgumentException("value must be in [" + min + ", " + max + "[ but was: " + pos); } final int index = getBinFor(pos); final double mid = lowerKey[index] + (0.5 * binWidth); // direct hit (unlikely with doubles) if (pos - mid == 0) { bins[index] += value; } // right of mid, interpolate to following bin else if (pos > mid) { double weight = (pos - mid) / binWidth; bins[index] += (1 - weight) * value; // interpolate to right bin if (index + 1 < bins.length) { bins[index + 1] += weight * value; } } // value left of mid, interpolate to previous bin else if (pos < mid) { double weight = (mid - pos) / binWidth; bins[index] += (1 - weight) * value; // interpolate to left bin if (index - 1 >= 0) { bins[index - 1] += weight * value; } } } /** * Returns a reference(!) to the data array. * * @return the data array */ public double[] getData() { return bins; } /** * Resets the data array by filling it with zero. */ public void clear() { Arrays.fill(bins, 0); } /** * Returns the array index for the given value. * * As this is not a public method, the input and outputs are NOT checked for * ArrayIndexOutOfBounds! * * <pre> * i = new Interpolated1DHistogram(0,10,10); * i.getBinFor(0.1); // returns 0 * </pre> * * @param pos * @return array index */ int getBinFor(double pos) { assert (pos <= max) : "pos > max: " + pos + " > " + max; int index = Arrays.binarySearch(lowerKey, pos); if (index >= 0) { return index; } else { return -index - 2; } } /** * Returns a string representation of the histogram. * * The string is a comma separated concatenation of the values * * @return string representation of the ata array */ @Override public String toString() { return "[" + Arrays2.join(bins, ", ", "%7.2f") + "]"; } }