-
Notifications
You must be signed in to change notification settings - Fork 627
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
feat: add exponential moving average to vega-lite #9225
base: main
Are you sure you want to change the base?
Changes from 4 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 {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 @@ | |
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 @@ | |
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 @@ | |
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I only added
I based the example off how the code works with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that makes sense. I wonder whether we want to use a similar API for transforms.
Current API in this pull request for reference
We often have more concise transform APIs than Vega and this would make the API more consistent between encodings and transforms. However, it would be a bit less constant with the API in https://vega.github.io/vega-lite/docs/aggregate.html#argmax
What do you think? I'm somewhat leaning towards There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @arvind +1s There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense! I'll update There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you. Btw, one thing @arvind brought up is that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Encoding level transforms and view level transforms are now updated. |
||
} | ||
} | ||
} | ||
} | ||
|
@@ -202,7 +208,7 @@ | |
|
||
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 @@ | |
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 @@ | |
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If I'm not misunderstanding something, I can open a pr to update There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, good catch. Yes, please send a pull request and I can merge it. We can add support for window aggregates in a follow up pull request. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! Opened vega/vega#3874 |
||
|
||
/** | ||
* 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
aggregateParam
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.
Sounds good. It's an internal name so it doesn't matter too much.