1 /** 2 * Constructs a default quantile scale. The arguments to this constructor are 3 * optional, and equivalent to calling {@link #domain}. The default domain is 4 * the empty set, and the default range is [0,1]. 5 * 6 * @class Represents a quantile scale; a function that maps from a value within 7 * a sortable domain to a quantized numeric range. Typically, the domain is a 8 * set of numbers, but any sortable value (such as strings) can be used as the 9 * domain of a quantile scale. The range defaults to [0,1], with 0 corresponding 10 * to the smallest value in the domain, 1 the largest, .5 the median, etc. 11 * 12 * <p>By default, the number of quantiles in the range corresponds to the number 13 * of values in the domain. The {@link #quantiles} method can be used to specify 14 * an explicit number of quantiles; for example, <tt>quantiles(4)</tt> produces 15 * a standard quartile scale. A quartile scale's range is a set of four discrete 16 * values, such as [0, 1/3, 2/3, 1]. Calling the {@link #range} method will 17 * scale these discrete values accordingly, similar to {@link 18 * pv.Scale.ordinal#splitFlush}. 19 * 20 * <p>For example, given the strings ["c", "a", "b"], a default quantile scale: 21 * 22 * <pre>pv.Scale.quantile("c", "a", "b")</pre> 23 * 24 * will return 0 for "a", .5 for "b", and 1 for "c". 25 * 26 * @extends pv.Scale 27 */ 28 pv.Scale.quantile = function() { 29 var n = -1, // number of quantiles 30 j = -1, // max quantile index 31 q = [], // quantile boundaries 32 d = [], // domain 33 y = pv.Scale.linear(); // range 34 35 /** @private */ 36 function scale(x) { 37 return y(Math.max(0, Math.min(j, pv.search.index(q, x) - 1)) / j); 38 } 39 40 /** 41 * Sets or gets the quantile boundaries. By default, each element in the 42 * domain is in its own quantile. If the argument to this method is a number, 43 * it specifies the number of equal-sized quantiles by which to divide the 44 * domain. 45 * 46 * <p>If no arguments are specified, this method returns the quantile 47 * boundaries; the first element is always the minimum value of the domain, 48 * and the last element is the maximum value of the domain. Thus, the length 49 * of the returned array is always one greater than the number of quantiles. 50 * 51 * @function 52 * @name pv.Scale.quantile.prototype.quantiles 53 * @param {number} x the number of quantiles. 54 */ 55 scale.quantiles = function(x) { 56 if (arguments.length) { 57 n = Number(x); 58 if (n < 0) { 59 q = [d[0]].concat(d); 60 j = d.length - 1; 61 } else { 62 q = []; 63 q[0] = d[0]; 64 for (var i = 1; i <= n; i++) { 65 q[i] = d[~~(i * (d.length - 1) / n)]; 66 } 67 j = n - 1; 68 } 69 return this; 70 } 71 return q; 72 }; 73 74 /** 75 * Sets or gets the input domain. This method can be invoked several ways: 76 * 77 * <p>1. <tt>domain(values...)</tt> 78 * 79 * <p>Specifying the domain as a series of values is the most explicit and 80 * recommended approach. However, if the domain values are derived from data, 81 * you may find the second method more appropriate. 82 * 83 * <p>2. <tt>domain(array, f)</tt> 84 * 85 * <p>Rather than enumerating the domain values as explicit arguments to this 86 * method, you can specify a single argument of an array. In addition, you can 87 * specify an optional accessor function to extract the domain values from the 88 * array. 89 * 90 * <p>3. <tt>domain()</tt> 91 * 92 * <p>Invoking the <tt>domain</tt> method with no arguments returns the 93 * current domain as an array. 94 * 95 * @function 96 * @name pv.Scale.quantile.prototype.domain 97 * @param {...} domain... domain values. 98 * @returns {pv.Scale.quantile} <tt>this</tt>, or the current domain. 99 */ 100 scale.domain = function(array, f) { 101 if (arguments.length) { 102 d = (array instanceof Array) 103 ? pv.map(array, f) 104 : Array.prototype.slice.call(arguments); 105 d.sort(pv.naturalOrder); 106 scale.quantiles(n); // recompute quantiles 107 return this; 108 } 109 return d; 110 }; 111 112 /** 113 * Sets or gets the output range. This method can be invoked several ways: 114 * 115 * <p>1. <tt>range(min, ..., max)</tt> 116 * 117 * <p>The range may be specified as a series of numbers or colors. Most 118 * commonly, two numbers are specified: the minimum and maximum pixel values. 119 * For a color scale, values may be specified as {@link pv.Color}s or 120 * equivalent strings. For a diverging scale, or other subdivided non-uniform 121 * scales, multiple values can be specified. For example: 122 * 123 * <pre> .range("red", "white", "green")</pre> 124 * 125 * <p>Currently, only numbers and colors are supported as range values. The 126 * number of range values must exactly match the number of domain values, or 127 * the behavior of the scale is undefined. 128 * 129 * <p>2. <tt>range()</tt> 130 * 131 * <p>Invoking the <tt>range</tt> method with no arguments returns the current 132 * range as an array of numbers or colors. 133 * 134 * @function 135 * @name pv.Scale.quantile.prototype.range 136 * @param {...} range... range values. 137 * @returns {pv.Scale.quantile} <tt>this</tt>, or the current range. 138 */ 139 scale.range = function() { 140 if (arguments.length) { 141 y.range.apply(y, arguments); 142 return this; 143 } 144 return y.range(); 145 }; 146 147 /** 148 * Returns a view of this scale by the specified accessor function <tt>f</tt>. 149 * Given a scale <tt>y</tt>, <tt>y.by(function(d) d.foo)</tt> is equivalent to 150 * <tt>function(d) y(d.foo)</tt>. 151 * 152 * <p>This method is provided for convenience, such that scales can be 153 * succinctly defined inline. For example, given an array of data elements 154 * that have a <tt>score</tt> attribute with the domain [0, 1], the height 155 * property could be specified as: 156 * 157 * <pre>.height(pv.Scale.linear().range(0, 480).by(function(d) d.score))</pre> 158 * 159 * This is equivalent to: 160 * 161 * <pre>.height(function(d) d.score * 480)</pre> 162 * 163 * This method should be used judiciously; it is typically more clear to 164 * invoke the scale directly, passing in the value to be scaled. 165 * 166 * @function 167 * @name pv.Scale.quantile.prototype.by 168 * @param {function} f an accessor function. 169 * @returns {pv.Scale.quantile} a view of this scale by the specified 170 * accessor function. 171 */ 172 scale.by = function(f) { 173 function by() { return scale(f.apply(this, arguments)); } 174 for (var method in scale) by[method] = scale[method]; 175 return by; 176 }; 177 178 scale.domain.apply(scale, arguments); 179 return scale; 180 }; 181