001: package prefuse.util;
002:
003: import java.util.Arrays;
004:
005: import prefuse.Constants;
006:
007: /**
008: * Library of mathematical constants and methods not included in the
009: * {@link java.lang.Math} class.
010: *
011: * @author <a href="http://jheer.org">jeffrey heer</a>
012: */
013: public class MathLib {
014:
015: /** The value 2 * PI */
016: public static final double TWO_PI = 2 * Math.PI;
017: /** The natural logarithm of 10 */
018: public static final double LOG10 = Math.log(10);
019: /** The natural logarithm of 2 */
020: public static final double LOG2 = Math.log(2);
021:
022: private MathLib() {
023: // prevent instantiation
024: }
025:
026: /**
027: * The base 2 logarithm of the input value
028: * @param x the input value
029: * @return the base 2 logarithm
030: */
031: public static double log2(double x) {
032: return Math.log(x) / LOG2;
033: }
034:
035: /**
036: * The base 10 logarithm of the input value
037: * @param x the input value
038: * @return the base 10 logarithm
039: */
040: public static double log10(double x) {
041: return Math.log(x) / LOG10;
042: }
043:
044: /**
045: * The "safe" base 10 logarithm of the input value, handling
046: * negative values by simply making them positive and then
047: * negating the return value.
048: * @param x the input value
049: * @return the "negative-safe" base 10 logarithm
050: */
051: public static double safeLog10(double x) {
052: boolean neg = (x < 0.0);
053: if (neg) {
054: x = -x;
055: }
056: if (x < 10.0) {
057: x += (10.0 - x) / 10;
058: }
059: x = Math.log(x) / LOG10;
060:
061: return neg ? -x : x;
062: }
063:
064: /**
065: * The "safe" square root of the input value, handling
066: * negative values by simply making them positive and then
067: * negating the return value.
068: * @param x the input value
069: * @return the "negative-safe" square root
070: */
071: public static double safeSqrt(double x) {
072: return (x < 0 ? -Math.sqrt(-x) : Math.sqrt(x));
073: }
074:
075: /**
076: * Interpolates a value within a range using a specified scale,
077: * returning the fractional position of the value within that scale.
078: * @param scale The scale on which to perform the interpolation, one of
079: * {@link prefuse.Constants#LINEAR_SCALE},
080: * {@link prefuse.Constants#LOG_SCALE},
081: * {@link prefuse.Constants#SQRT_SCALE}, or
082: * {@link prefuse.Constants#QUANTILE_SCALE}.
083: * @param val the interpolation value, a fraction between 0 and 1.0.
084: * @param dist a double array describing the distribution of the data.
085: * For the {@link prefuse.Constants#QUANTILE_SCALE} option, this should
086: * be a collection of quantile boundaries, as determined by the
087: * {@link #quantiles(int, double[])} method. For any other scale type,
088: * the first value of the array must contain the minimum value of the
089: * distribution and the last value of the array must contain the
090: * maximum value of the distribution; all values in between will be
091: * ignored.
092: * @return the fractional position of the value within the scale,
093: * a double between 0 and 1.
094: */
095: public static double interp(int scale, double val, double dist[]) {
096: switch (scale) {
097: case Constants.LINEAR_SCALE:
098: return linearInterp(val, dist[0], dist[dist.length - 1]);
099: case Constants.LOG_SCALE:
100: return logInterp(val, dist[0], dist[dist.length - 1]);
101: case Constants.SQRT_SCALE:
102: return sqrtInterp(val, dist[0], dist[dist.length - 1]);
103: case Constants.QUANTILE_SCALE:
104: return quantile(val, dist);
105: }
106: throw new IllegalArgumentException("Unrecognized scale value: "
107: + scale);
108: }
109:
110: /**
111: * Interpolates a value between a given minimum and maximum value using
112: * a linear scale.
113: * @param val the interpolation value, a fraction between 0 and 1.0.
114: * @param min the minimum value of the interpolation range
115: * @param max the maximum value of the interpolation range
116: * @return the resulting interpolated value
117: */
118: public static double linearInterp(double val, double min, double max) {
119: double denominator = (max - min);
120: if (denominator == 0)
121: return 0;
122: return (val - min) / denominator;
123: }
124:
125: /**
126: * Interpolates a value between a given minimum and maximum value using
127: * a base-10 logarithmic scale.
128: * @param val the interpolation value, a fraction between 0 and 1.0.
129: * @param min the minimum value of the interpolation range
130: * @param max the maximum value of the interpolation range
131: * @return the resulting interpolated value
132: */
133: public static double logInterp(double val, double min, double max) {
134: double logMin = safeLog10(min);
135: double denominator = (safeLog10(max) - logMin);
136: if (denominator == 0)
137: return 0;
138: return (safeLog10(val) - logMin) / denominator;
139: }
140:
141: /**
142: * Interpolates a value between a given minimum and maximum value using
143: * a square root scale.
144: * @param val the interpolation value, a fraction between 0 and 1.0.
145: * @param min the minimum value of the interpolation range
146: * @param max the maximum value of the interpolation range
147: * @return the resulting interpolated value
148: */
149: public static double sqrtInterp(double val, double min, double max) {
150: double sqrtMin = safeSqrt(min);
151: double denominator = (safeSqrt(max) - sqrtMin);
152: if (denominator == 0)
153: return 0;
154: return (safeSqrt(val) - sqrtMin) / denominator;
155: }
156:
157: /**
158: * Compute the n-quantile boundaries for a set of values. The result is
159: * an n+1 size array holding the minimum value in the first entry and
160: * then n quantile boundaries in the subsequent entries.
161: * @param n the number of quantile boundaries. For example, a value of 4
162: * will break up the values into quartiles, while a value of 100 will break
163: * up the values into percentiles.
164: * @param values the array of double values to divide into quantiles
165: * @return an n+1 array of doubles containing the minimum value and
166: * the quantile boundary values, in that order
167: */
168: public static double[] quantiles(int n, double[] values) {
169: values = (double[]) values.clone();
170: Arrays.sort(values);
171: double[] qtls = new double[n + 1];
172: for (int i = 0; i <= n; ++i) {
173: qtls[i] = values[((values.length - 1) * i) / n];
174: }
175: return qtls;
176: }
177:
178: /**
179: * Get the quantile measure, as a value between 0 and 1, for a given
180: * value and set of quantile boundaries. For example, if the input value
181: * is the median of the distribution described by the quantile boundaries,
182: * this method will return 0.5. As another example, if the quantile
183: * boundaries represent percentiles, this value will return the percentile
184: * ranking of the input value according to the given boundaries.
185: * @param val the value for which to return the quantile ranking
186: * @param quantiles an array of quantile boundaries of a distribution
187: * @return the quantile ranking, a value between 0 and 1
188: * @see #quantiles(int, double[])
189: */
190: public static double quantile(double val, double[] quantiles) {
191: int x1 = 1;
192: int x2 = quantiles.length;
193: int i = x2 / 2;
194: while (x1 < x2) {
195: if (quantiles[i] == val) {
196: break;
197: } else if (quantiles[i] < val) {
198: x1 = i + 1;
199: } else {
200: x2 = i;
201: }
202: i = x1 + (x2 - x1) / 2;
203: }
204: return ((double) i) / (quantiles.length - 1);
205: }
206:
207: } // end of class MathLib
|