diff --git a/build/vega-lite-schema.json b/build/vega-lite-schema.json index a251280b95..5b773b2649 100644 --- a/build/vega-lite-schema.json +++ b/build/vega-lite-schema.json @@ -8145,6 +8145,14 @@ "description": "The minimum number of samples to take along the extent domain for plotting the density.\n\n__Default value:__ `25`", "type": "number" }, + "resolve": { + "description": "Indicates how parameters for multiple densities should be resolved. If `\"independent\"`, each density may have its own domain extent and dynamic number of curve sample steps. If `\"shared\"`, the KDE transform will ensure that all densities are defined over a shared domain and curve steps, enabling stacking.\n\n__Default value:__ `\"shared\"`", + "enum": [ + "independent", + "shared" + ], + "type": "string" + }, "steps": { "description": "The exact number of samples to take along the extent domain for plotting the density. If specified, overrides both minsteps and maxsteps to set an exact number of uniform samples. Potentially useful in conjunction with a fixed extent to ensure consistent sample points for stacked densities.", "type": "number" diff --git a/examples/compiled/area_density.png b/examples/compiled/area_density.png index 46d40fa913..767f797ae0 100644 Binary files a/examples/compiled/area_density.png and b/examples/compiled/area_density.png differ diff --git a/examples/compiled/area_density.svg b/examples/compiled/area_density.svg index d42b85c260..2f8c497b86 100644 --- a/examples/compiled/area_density.svg +++ b/examples/compiled/area_density.svg @@ -1 +1 @@ -12345678910IMDB Rating0.00.10.20.3density \ No newline at end of file +12345678910IMDB Rating0.00.10.20.3density \ No newline at end of file diff --git a/examples/compiled/area_density.vg.json b/examples/compiled/area_density.vg.json index 8fc8c8ca43..a34a2fc36d 100644 --- a/examples/compiled/area_density.vg.json +++ b/examples/compiled/area_density.vg.json @@ -15,7 +15,8 @@ "type": "kde", "field": "IMDB Rating", "bandwidth": 0.3, - "as": ["value", "density"] + "as": ["value", "density"], + "resolve": "shared" }, { "type": "impute", diff --git a/site/docs/transform/density.md b/site/docs/transform/density.md index bfe8d224d1..b7399b5663 100644 --- a/site/docs/transform/density.md +++ b/site/docs/transform/density.md @@ -21,7 +21,7 @@ The density transform performs one-dimensional [kernel density estimation](https ## Density Transform Definition -{% include table.html props="density,groupby,cumulative,counts,bandwidth,extent,minsteps,maxsteps,steps,as" source="DensityTransform" %} +{% include table.html props="density,groupby,cumulative,counts,bandwidth,extent,minsteps,maxsteps,resolve,steps,as" source="DensityTransform" %} ## Usage diff --git a/src/compile/data/density.ts b/src/compile/data/density.ts index 6b4317dc50..adbeb22682 100644 --- a/src/compile/data/density.ts +++ b/src/compile/data/density.ts @@ -19,6 +19,8 @@ export class DensityTransformNode extends DataFlowNode { this.transform = duplicate(transform); // duplicate to prevent side effects const specifiedAs = this.transform.as ?? [undefined, undefined]; this.transform.as = [specifiedAs[0] ?? 'value', specifiedAs[1] ?? 'density']; + const resolve = this.transform.resolve ?? 'shared'; + this.transform.resolve = resolve; } public dependentFields() { @@ -40,9 +42,7 @@ export class DensityTransformNode extends DataFlowNode { field: density, ...rest }; - if (this.transform.groupby) { - result.resolve = 'shared'; - } + result.resolve = this.transform.resolve; return result; } } diff --git a/src/transform.ts b/src/transform.ts index 33b10bb723..5019088f76 100644 --- a/src/transform.ts +++ b/src/transform.ts @@ -496,6 +496,14 @@ export interface DensityTransform { * __Default value:__ `["value", "density"]` */ as?: [FieldName, FieldName]; + /** + * Indicates how parameters for multiple densities should be resolved. + * If `"independent"`, each density may have its own domain extent and dynamic number of curve sample steps. + * If `"shared"`, the KDE transform will ensure that all densities are defined over a shared domain and curve steps, enabling stacking. + * + * __Default value:__ `"shared"` + */ + resolve?: 'independent' | 'shared'; } export function isDensity(t: Transform): t is DensityTransform { diff --git a/test/compile/data/density.test.ts b/test/compile/data/density.test.ts index 911510c88f..49e72d6dcc 100644 --- a/test/compile/data/density.test.ts +++ b/test/compile/data/density.test.ts @@ -41,6 +41,7 @@ describe('compile/data/fold', () => { expect(density.assemble()).toEqual({ type: 'kde', field: 'v', + resolve: 'shared', as: ['value', 'density'] }); }); @@ -54,21 +55,23 @@ describe('compile/data/fold', () => { expect(density.assemble()).toEqual({ type: 'kde', field: 'v', + resolve: 'shared', as: ['A', 'density'] }); }); - it('should add resolve shared if we group', () => { + it('should add resolve "independent" if we set it explicitly', () => { const transform: Transform = { density: 'v', - groupby: ['a'] + groupby: ['a'], + resolve: 'independent' }; const density = new DensityTransformNode(null, transform); expect(density.assemble()).toEqual({ type: 'kde', groupby: ['a'], field: 'v', - resolve: 'shared', + resolve: 'independent', as: ['value', 'density'] }); }); @@ -119,7 +122,7 @@ describe('compile/data/fold', () => { as: ['A', 'B'] }; const density = new DensityTransformNode(null, transform); - expect(density.hash()).toBe('DensityTransform {"as":["A","B"],"density":"v"}'); + expect(density.hash()).toBe('DensityTransform {"as":["A","B"],"density":"v","resolve":"shared"}'); }); });