-
-
Notifications
You must be signed in to change notification settings - Fork 677
feat: add exponential moving average to vega-lite #9225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 3 commits
83ab83e
1db302e
cf2ed1c
3b60571
8f56013
261728b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,7 +27,7 @@ import {DataFlowNode} from './dataflow'; | |
| import {isRectBasedMark} from '../../mark'; | ||
| import {OFFSETTED_RECT_END_SUFFIX, OFFSETTED_RECT_START_SUFFIX} from './timeunit'; | ||
|
|
||
| type Measures = Dict<Partial<Record<AggregateOp, Set<string>>>>; | ||
| type Measures = Dict<Partial<Record<AggregateOp, {aliases: Set<string>; aggregate_param?: number}>>>; | ||
|
|
||
| function addDimension(dims: Set<string>, channel: Channel, fieldDef: FieldDef<string>, model: ModelWithField) { | ||
| const channelDef2 = isUnitModel(model) ? model.encoding[getSecondaryRangeChannel(channel)] : undefined; | ||
|
|
@@ -71,7 +71,9 @@ function mergeMeasures(parentMeasures: Measures, childMeasures: Measures) { | |
| for (const op of keys(ops)) { | ||
| if (field in parentMeasures) { | ||
| // add operator to existing measure field | ||
| parentMeasures[field][op] = new Set([...(parentMeasures[field][op] ?? []), ...ops[op]]); | ||
| parentMeasures[field][op] = { | ||
| aliases: new Set([...(parentMeasures[field][op]?.aliases ?? []), ...ops[op].aliases]) | ||
| }; | ||
| } else { | ||
| parentMeasures[field] = {[op]: ops[op]}; | ||
| } | ||
|
|
@@ -121,23 +123,23 @@ export class AggregateNode extends DataFlowNode { | |
| if (aggregate) { | ||
| if (aggregate === 'count') { | ||
| meas['*'] ??= {}; | ||
| meas['*']['count'] = new Set([vgField(fieldDef, {forAs: true})]); | ||
| meas['*']['count'] = {aliases: new Set([vgField(fieldDef, {forAs: true})])}; | ||
| } else { | ||
| if (isArgminDef(aggregate) || isArgmaxDef(aggregate)) { | ||
| const op = isArgminDef(aggregate) ? 'argmin' : 'argmax'; | ||
| const argField = aggregate[op]; | ||
| meas[argField] ??= {}; | ||
| meas[argField][op] = new Set([vgField({op, field: argField}, {forAs: true})]); | ||
| meas[argField][op] = {aliases: new Set([vgField({op, field: argField}, {forAs: true})])}; | ||
| } else { | ||
| meas[field] ??= {}; | ||
| meas[field][aggregate] = new Set([vgField(fieldDef, {forAs: true})]); | ||
| meas[field][aggregate] = {aliases: new Set([vgField(fieldDef, {forAs: true})])}; | ||
| } | ||
|
|
||
| // For scale channel with domain === 'unaggregated', add min/max so we can use their union as unaggregated domain | ||
| if (isScaleChannel(channel) && model.scaleDomain(channel) === 'unaggregated') { | ||
| meas[field] ??= {}; | ||
| meas[field]['min'] = new Set([vgField({field, aggregate: 'min'}, {forAs: true})]); | ||
| meas[field]['max'] = new Set([vgField({field, aggregate: 'max'}, {forAs: true})]); | ||
| meas[field]['min'] = {aliases: new Set([vgField({field, aggregate: 'min'}, {forAs: true})])}; | ||
| meas[field]['max'] = {aliases: new Set([vgField({field, aggregate: 'max'}, {forAs: true})])}; | ||
| } | ||
| } | ||
| } else { | ||
|
|
@@ -157,14 +159,18 @@ export class AggregateNode extends DataFlowNode { | |
| const meas: Measures = {}; | ||
|
|
||
| for (const s of t.aggregate) { | ||
| const {op, field, as} = s; | ||
| const {op, field, as, aggregate_param} = s; | ||
| if (op) { | ||
| if (op === 'count') { | ||
| meas['*'] ??= {}; | ||
| meas['*']['count'] = new Set([as ? as : vgField(s, {forAs: true})]); | ||
| meas['*']['count'] = {aliases: new Set([as ? as : vgField(s, {forAs: true})])}; | ||
| } else { | ||
| meas[field] ??= {}; | ||
| meas[field][op] = new Set([as ? as : vgField(s, {forAs: true})]); | ||
| meas[field][op] = {aliases: new Set([as ? as : vgField(s, {forAs: true})])}; | ||
|
|
||
| if (aggregate_param) { | ||
| meas[field][op].aggregate_param = aggregate_param; | ||
|
||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -202,7 +208,7 @@ export class AggregateNode extends DataFlowNode { | |
|
|
||
| for (const field of keys(this.measures)) { | ||
| for (const op of keys(this.measures[field])) { | ||
| const m = this.measures[field][op]; | ||
| const m = this.measures[field][op].aliases; | ||
| if (m.size === 0) { | ||
| out.add(`${op}_${field}`); | ||
| } else { | ||
|
|
@@ -222,13 +228,15 @@ export class AggregateNode extends DataFlowNode { | |
| const ops: AggregateOp[] = []; | ||
| const fields: string[] = []; | ||
| const as: string[] = []; | ||
| const aggregateParams: (number | null)[] = []; | ||
|
|
||
| for (const field of keys(this.measures)) { | ||
| for (const op of keys(this.measures[field])) { | ||
| for (const alias of this.measures[field][op]) { | ||
| for (const alias of this.measures[field][op].aliases) { | ||
| as.push(alias); | ||
| ops.push(op); | ||
| fields.push(field === '*' ? null : replacePathInField(field)); | ||
| aggregateParams.push(this.measures[field][op].aggregate_param || null); | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -241,6 +249,10 @@ export class AggregateNode extends DataFlowNode { | |
| as | ||
| }; | ||
|
|
||
| if (aggregateParams.some(param => typeof param === 'number')) { | ||
| result.aggregate_params = aggregateParams; | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -111,6 +111,11 @@ export interface AggregatedFieldDef { | |
| */ | ||
| field?: FieldName; | ||
|
|
||
| /** | ||
| * A parameter that can be passed to aggregation functions. The aggregation operation `"exponential"` requires it. | ||
| */ | ||
| aggregate_param?: number; | ||
|
||
|
|
||
| /** | ||
| * The output field names to use for each aggregated field. | ||
| */ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated our measures to hold the fieldnames (
aliases) and the aggregate param (aggregate_param).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now
aggregateParamThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. It's an internal name so it doesn't matter too much.