Skip to content

Commit

Permalink
Merge pull request #1162 from CartoDB/histogram-buckets
Browse files Browse the repository at this point in the history
Enables user-defined buckets in viewportHistogram
  • Loading branch information
Raul Ochoa authored Nov 28, 2018
2 parents 0ebac2f + 4a3fa02 commit 63f4b91
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,24 @@ import { CLUSTER_FEATURE_COUNT } from '../../../../schema';
* Generates a histogram.
*
* The histogram can be based on a categorical expression, in which case each category will correspond to a histogram bar.
* The histogram can be based on a numeric expression, in which case the minimum and maximum will be computed automatically and bars will be generated
* at regular intervals between the minimum and maximum. The number of bars in this case is controllable through the `size` parameter.
*
* The histogram can be based on a numeric expression, the buckets for the histogram is controllable through the `sizeOrBuckets` parameter.
* For numeric values of sizeOrBuckets, the minimum and maximum will be computed automatically and bars will be generated at regular intervals between the minimum and maximum.
* When providing sizeOrBuckets as a list of buckets, the values will get assigned to the first bucket matching the criteria [bucketMin <= value < bucketMax].
*
* Histograms are useful to get insights and create widgets outside the scope of CARTO VL, see the following example for more info.
*
* @param {Number} input - expression to base the histogram
* @param {Number} size - Optional (defaults to 20). Number of bars to use if `x` is a numeric expression
* @param {Number|Array} sizeOrBuckets - Optional (defaults to 20). Number of bars to use if `x` is a numeric expression; or user-defined buckets for numeric expressions.
* @param {Number} weight - Optional. Weight each occurrence differently based on this weight, defaults to `1`, which will generate a simple, non-weighted count.
* @return {Histogram} Histogram
*
* @example <caption>Create and use an histogram. (String)</caption>
* const s = carto.expressions;
* const viz = new carto.Viz(`
* \@categoryHistogram: viewportHistogram($type)
* \@numericHistogram: viewportHistogram($amount, 3, 1)
* \@categoryHistogram: viewportHistogram($type)
* \@numericHistogram: viewportHistogram($amount, 3, 1)
* \@userDefinedHistogram: viewportHistogram($amount, [[0, 10], [10, 20], [20, 30]], 1)
* `);
* ...
* console.log(viz.variables.categoryHistogram.eval());
Expand Down Expand Up @@ -52,12 +55,13 @@ import { CLUSTER_FEATURE_COUNT } from '../../../../schema';
* @api
*/
export default class ViewportHistogram extends BaseExpression {
constructor (x, size = 20, weight = 1) {
constructor (x, sizeOrBuckets = 20, weight = 1) {
checkMaxArguments(arguments, 3, 'viewportHistogram');
super({ x: implicitCast(x), weight: implicitCast(weight) });

this.type = 'histogram';
this._size = size;
this._sizeOrBuckets = sizeOrBuckets;
this._hasBuckets = Array.isArray(sizeOrBuckets);
this._isViewport = true;

this.inlineMaker = () => null;
Expand All @@ -81,7 +85,7 @@ export default class ViewportHistogram extends BaseExpression {
}

this._cached = this.x.type === 'number'
? _getNumericValue(this._histogram, this._size)
? (this._hasBuckets ? _getBucketsValue(this._histogram, this._sizeOrBuckets) : _getNumericValue(this._histogram, this._sizeOrBuckets))
: _getCategoryValue(this._histogram);

return this._cached;
Expand Down Expand Up @@ -236,6 +240,29 @@ function _getNumericValue (histogram, size) {
});
}

function _getBucketsValue ([...histogram], buckets) {
const nBuckets = buckets.length;
const hist = Array(nBuckets).fill(0);

for (let i = 0, len = histogram.length; i < len; i++) {
const x = histogram[i][0];
for (let j = 0; j < nBuckets; j++) {
const bucket = buckets[j];
if (x >= bucket[0] && x < bucket[1]) {
hist[j] += histogram[i][1];
break;
}
}
}

return hist.map((count, index) => {
return {
x: buckets[index],
y: count
};
});
}

function _getCategoryValue (histogram) {
return [...histogram]
.map(([x, y]) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,21 @@ describe('src/renderer/viz/expressions/viewportAggregation', () => {
]);
});

it('viewportHistogram($price, [[0, 1], [1, 2]], 1) should eval to the correct histogram', () => {
const viewportHistogram = s.viewportHistogram($price, [[0, 1.5], [1.5, 3]], 1);
fakeDrawMetadata(viewportHistogram);
expect(viewportHistogram.value).toEqual([
{
x: [0, 1.5],
y: 2
},
{
x: [1.5, 3],
y: 2
}
]);
});

it('viewportHistogram($cat) should eval to the correct histogram', () => {
const viewportHistogram = s.viewportHistogram($cat);
fakeDrawMetadata(viewportHistogram);
Expand Down

0 comments on commit 63f4b91

Please sign in to comment.