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