-
Notifications
You must be signed in to change notification settings - Fork 629
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
Add Stack Transform #3771
Add Stack Transform #3771
Changes from all commits
cdd5773
38ea65c
091d309
6171fe1
d1de3e8
d60db8e
bde6bdb
6bbd2d6
4e2c547
e4dc989
89d411a
1283354
7fc947a
92a3ebf
65b927a
6cf9b82
8c87a04
9fd5c68
fe39dfa
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 |
---|---|---|
@@ -1,13 +1,13 @@ | ||
import {isArray} from 'vega-util'; | ||
import {isArray, isString} from 'vega-util'; | ||
import {FieldDef, isFieldDef, vgField} from '../../fielddef'; | ||
import {StackOffset} from '../../stack'; | ||
import {StackTransform} from '../../transform'; | ||
import {duplicate} from '../../util'; | ||
import {VgSort, VgTransform} from '../../vega.schema'; | ||
import {VgComparatorOrder, VgSort, VgTransform} from '../../vega.schema'; | ||
import {sortParams} from '../common'; | ||
import {UnitModel} from './../unit'; | ||
import {DataFlowNode} from './dataflow'; | ||
|
||
|
||
function getStackByFields(model: UnitModel): string[] { | ||
return model.stack.stackBy.reduce((fields, by) => { | ||
const fieldDef = by.fieldDef; | ||
|
@@ -21,35 +21,53 @@ function getStackByFields(model: UnitModel): string[] { | |
} | ||
|
||
export interface StackComponent { | ||
|
||
/** | ||
* Faceted field. | ||
*/ | ||
facetby: string[]; | ||
|
||
dimensionFieldDef: FieldDef<string>; | ||
dimensionFieldDef?: FieldDef<string>; | ||
|
||
/** | ||
* Stack measure's field | ||
* Stack measure's field. Used in makeFromEncoding. | ||
*/ | ||
field: string; | ||
stackField: string; | ||
|
||
/** | ||
* Level of detail fields for each level in the stacked charts such as color or detail. | ||
* Used in makeFromEncoding. | ||
*/ | ||
stackby: string[]; | ||
stackby?: string[]; | ||
|
||
/** | ||
* Field that determines order of levels in the stacked charts. | ||
* Used in both but optional in transform. | ||
*/ | ||
sort: VgSort; | ||
|
||
/** Mode for stacking marks. */ | ||
/** Mode for stacking marks. | ||
*/ | ||
offset: StackOffset; | ||
|
||
/** | ||
* Whether to impute the data before stacking. | ||
* Whether to impute the data before stacking. Used only in makeFromEncoding. | ||
*/ | ||
impute?: boolean; | ||
|
||
/** | ||
* The data fields to group by. | ||
*/ | ||
groupby?: string[]; | ||
/** | ||
* Output field names of each stack field. | ||
*/ | ||
impute: boolean; | ||
as: string[]; | ||
|
||
} | ||
|
||
function isValidAsArray(as: string[] | string): as is string[] { | ||
return isArray(as) && as.every(s => isString(s)) && as.length >1; | ||
} | ||
|
||
export class StackNode extends DataFlowNode { | ||
|
@@ -65,7 +83,42 @@ export class StackNode extends DataFlowNode { | |
this._stack = stack; | ||
} | ||
|
||
public static make(parent: DataFlowNode, model: UnitModel) { | ||
public static makeFromTransform(parent: DataFlowNode, stackTransform: StackTransform) { | ||
|
||
const {stack, groupby, as, offset='zero'} = stackTransform; | ||
|
||
const sortFields: string[] = []; | ||
const sortOrder: VgComparatorOrder[] = []; | ||
if (stackTransform.sort !== undefined) { | ||
for (const sortField of stackTransform.sort) { | ||
sortFields.push(sortField.field); | ||
sortOrder.push(sortField.order === undefined ? 'ascending' : sortField.order as VgComparatorOrder); | ||
} | ||
} | ||
const sort: VgSort = { | ||
field: sortFields, | ||
order: sortOrder, | ||
}; | ||
let normalizedAs: Array<string>; | ||
if (isValidAsArray(as)) { | ||
normalizedAs = as; | ||
} else if(isString(as)) { | ||
normalizedAs = [as, as + '_end']; | ||
} else { | ||
normalizedAs = [stackTransform.stack + '_start', stackTransform.stack + '_end']; | ||
} | ||
|
||
return new StackNode (parent, { | ||
stackField: stack, | ||
groupby, | ||
offset, | ||
sort, | ||
facetby: [], | ||
as: normalizedAs | ||
}); | ||
|
||
} | ||
public static makeFromEncoding(parent: DataFlowNode, model: UnitModel) { | ||
|
||
const stackProperties = model.stack; | ||
|
||
|
@@ -93,15 +146,19 @@ export class StackNode extends DataFlowNode { | |
return s; | ||
}, {field:[], order: []}); | ||
} | ||
// Refactored to add "as" in the make phase so that we can get producedFields | ||
// from the as property | ||
const field = model.vgField(stackProperties.fieldChannel); | ||
|
||
return new StackNode(parent, { | ||
dimensionFieldDef, | ||
field: model.vgField(stackProperties.fieldChannel), | ||
stackField:field, | ||
facetby: [], | ||
stackby, | ||
sort, | ||
offset: stackProperties.offset, | ||
impute: stackProperties.impute, | ||
as: [field + '_start', field + '_end'] | ||
}); | ||
} | ||
|
||
|
@@ -116,7 +173,7 @@ export class StackNode extends DataFlowNode { | |
public dependentFields() { | ||
const out = {}; | ||
|
||
out[this._stack.field] = true; | ||
out[this._stack.stackField] = true; | ||
|
||
this.getGroupbyFields().forEach(f => out[f] = true); | ||
this._stack.facetby.forEach(f => out[f] = true); | ||
|
@@ -127,16 +184,14 @@ export class StackNode extends DataFlowNode { | |
} | ||
|
||
public producedFields() { | ||
const out = {}; | ||
|
||
out[this._stack.field + '_start'] = true; | ||
out[this._stack.field + '_end'] = true; | ||
|
||
return out; | ||
return this._stack.as.reduce((result, item) => { | ||
result[item] = true; | ||
return result; | ||
}, {}); | ||
} | ||
|
||
private getGroupbyFields() { | ||
const {dimensionFieldDef, impute} = this._stack; | ||
const {dimensionFieldDef, impute, groupby} = this._stack; | ||
if (dimensionFieldDef) { | ||
if (dimensionFieldDef.bin) { | ||
if (impute) { | ||
|
@@ -152,15 +207,14 @@ export class StackNode extends DataFlowNode { | |
} | ||
return [vgField(dimensionFieldDef)]; | ||
} | ||
return []; | ||
return groupby || []; | ||
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'm ok with this for now. Once you decouple Impute from stacking (#1514), we should make (Basically, if you look at the code, the final groupby in the stack transform = groupBy that's from dimension fieldDef + facetBy) |
||
} | ||
|
||
public assemble(): VgTransform[] { | ||
const transform: VgTransform[] = []; | ||
const {facetby, dimensionFieldDef, stackField: field, stackby, sort, offset, impute, as} = this._stack; | ||
|
||
const {facetby, field: stackField, dimensionFieldDef, impute, offset, sort, stackby} = this._stack; | ||
|
||
// Impute | ||
// Impute | ||
if (impute && dimensionFieldDef) { | ||
const dimensionField = dimensionFieldDef ? vgField(dimensionFieldDef, {binSuffix: 'mid'}): undefined; | ||
|
||
|
@@ -180,7 +234,7 @@ export class StackNode extends DataFlowNode { | |
|
||
transform.push({ | ||
type: 'impute', | ||
field: stackField, | ||
field, | ||
groupby: stackby, | ||
key: dimensionField, | ||
method: 'value', | ||
|
@@ -192,12 +246,9 @@ export class StackNode extends DataFlowNode { | |
transform.push({ | ||
type: 'stack', | ||
groupby: this.getGroupbyFields().concat(facetby), | ||
field: stackField, | ||
field, | ||
sort, | ||
as: [ | ||
stackField + '_start', | ||
stackField + '_end' | ||
], | ||
as, | ||
offset | ||
}); | ||
|
||
|
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.
This code is called repeatedly through this method. In a separate PR, we should refactor this part #3772.