Count and sum animations fix (#189)
* changed unit test json Signed-off-by: Chinmay Singh <chsingh@microsoft.com> * updated domain Signed-off-by: Chinmay Singh <chsingh@microsoft.com> * custom animation for sum Signed-off-by: Chinmay Singh <chsingh@microsoft.com> * stacked layout Signed-off-by: Chinmay Singh <chsingh@microsoft.com> * adjust y-axis encoding Signed-off-by: Chinmay Singh <chsingh@microsoft.com> * automatic tasks * use existing elements for timing * grid specs for sum
This commit is contained in:
Родитель
586939acb3
Коммит
459ded6441
|
@ -1,30 +1,34 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Mocha Tests",
|
||||
"cwd": "${workspaceFolder}/inst/htmlwidgets/js/src/",
|
||||
"program": "${workspaceFolder}/inst/htmlwidgets/js/src/node_modules/mocha/bin/_mocha",
|
||||
"args": [
|
||||
"--reporter",
|
||||
"dot",
|
||||
"--require",
|
||||
"esm",
|
||||
"--require",
|
||||
"@babel/register",
|
||||
"--colors",
|
||||
"${workspaceFolder}/inst/htmlwidgets/js/src/test/**/*.js",
|
||||
|
||||
],
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
]
|
||||
}
|
||||
]
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Edge",
|
||||
"request": "launch",
|
||||
"type": "msedge",
|
||||
"url": "http://localhost:3000/inst/htmlwidgets/dev-app/",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Mocha Tests",
|
||||
"cwd": "${workspaceFolder}/inst/htmlwidgets/js/src/",
|
||||
"program": "${workspaceFolder}/inst/htmlwidgets/js/src/node_modules/mocha/bin/_mocha",
|
||||
"args": [
|
||||
"--reporter",
|
||||
"dot",
|
||||
"--require",
|
||||
"esm",
|
||||
"--require",
|
||||
"@babel/register",
|
||||
"--colors",
|
||||
"${workspaceFolder}/inst/htmlwidgets/js/src/test/**/*.js"
|
||||
],
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"skipFiles": ["<node_internals>/**"]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -21,6 +21,19 @@
|
|||
"problemMatcher": [],
|
||||
"label": "npm: test - inst/htmlwidgets/js/src",
|
||||
"detail": "mocha"
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "dev",
|
||||
"path": "inst/htmlwidgets/js/src",
|
||||
"problemMatcher": [],
|
||||
"label": "npm: dev - inst/htmlwidgets/js/src",
|
||||
"detail": "rollup -c -w",
|
||||
"presentation": {
|
||||
"clear": true,
|
||||
"panel": "dedicated"
|
||||
},
|
||||
"runOptions": { "runOn": "folderOpen" }
|
||||
}
|
||||
]
|
||||
}
|
|
@ -67,16 +67,16 @@
|
|||
<script src="../js/src/dist/datamations.min.js"></script>
|
||||
|
||||
<script>
|
||||
const animationType = "mean";
|
||||
const animationType = "sum";
|
||||
const base = "../../../sandbox/custom_animations/";
|
||||
let app = null;
|
||||
|
||||
d3.json(
|
||||
`${base}/custom-animations-${animationType}-R.json`
|
||||
`${base}/custom-animations-${animationType}-manual.json`
|
||||
// 'https://raw.githubusercontent.com/microsoft/datamations/specs_mutate/sandbox/mutations/mutation_specs_multiple_variables-R.json'
|
||||
).then(
|
||||
(specs) => {
|
||||
app = datamations.App("app", { specs, frameDur: 3000 });
|
||||
app = datamations.App("app", { specs, frameDur: 3000, autoPlay: true});
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -18741,7 +18741,7 @@
|
|||
const newData = step.nextData.tick;
|
||||
const joinKey = joinKeyGen("tick");
|
||||
const timings = computeTiming(
|
||||
currData,
|
||||
ticks.data(),
|
||||
newData,
|
||||
step.timing,
|
||||
joinKey,
|
||||
|
@ -18925,7 +18925,7 @@
|
|||
const newData = step.nextData.label;
|
||||
const joinKey = joinKeyGen("label");
|
||||
const timings = computeTiming(
|
||||
currData,
|
||||
labels.data(),
|
||||
newData,
|
||||
step.timing,
|
||||
joinKey,
|
||||
|
@ -19132,7 +19132,7 @@
|
|||
const newData = step.nextData.grid;
|
||||
const joinKey = joinKeyGen("grid");
|
||||
const timings = computeTiming(
|
||||
currData,
|
||||
grids.data(),
|
||||
newData,
|
||||
step.timing,
|
||||
joinKey,
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -7,7 +7,7 @@
|
|||
"scripts": {
|
||||
"test": "mocha",
|
||||
"build": "rollup -c",
|
||||
"dev": "rollup -c -w"
|
||||
"dev": "serve ../../../../ | rollup -c -w"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Giorgi Ghviniashvili",
|
||||
|
@ -33,7 +33,8 @@
|
|||
"rollup": "^2.70.1",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"rollup-plugin-terser": "^7.0.2"
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"serve": "^13.0.2"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
|
|
@ -76,6 +76,69 @@ export const getCountStep = (source, target, shrink = false) => {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a spec for sum animation
|
||||
* @param {Object} source source spec
|
||||
* @param {Object} target target spec
|
||||
* @param {Object} shrink if truthy, circles will be pulled up
|
||||
* @returns a vega lite spec
|
||||
*/
|
||||
export const getSumStep = (source, target, shrink = false) => {
|
||||
const { width, height } = target.spec || target
|
||||
let values = source.data.values.slice()
|
||||
const sourceMeta = source.meta
|
||||
|
||||
// generate rules layer
|
||||
const rules = sourceMeta.rules.map((d, i) => {
|
||||
const n = sourceMeta.rules.length
|
||||
return {
|
||||
transform: [{ filter: d.filter }],
|
||||
name: `rule-${i + 1}`,
|
||||
mark: {
|
||||
type: 'rule',
|
||||
x: { expr: `${i + 1} * (width / ${n + 1}) - 5` },
|
||||
x2: { expr: `${i + 1} * (width / ${n + 1}) + 5` }
|
||||
},
|
||||
encoding: {
|
||||
y: {
|
||||
field: CONF.Y_FIELD,
|
||||
type: 'quantitative',
|
||||
aggregate: 'max'
|
||||
// axis: null,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (shrink) {
|
||||
values = values.map((d, i) => {
|
||||
const y = target.data.values[i][CONF.Y_FIELD]
|
||||
return {
|
||||
...d,
|
||||
[CONF.Y_FIELD]: y
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
$schema: CONF.SCHEME,
|
||||
width,
|
||||
height,
|
||||
data: {
|
||||
name: 'source',
|
||||
values
|
||||
},
|
||||
layer: [
|
||||
{
|
||||
name: 'main',
|
||||
mark: source.mark,
|
||||
encoding: source.encoding
|
||||
},
|
||||
...rules
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a spec for median and quantile animations
|
||||
* @param {Object} source source spec
|
||||
|
@ -662,6 +725,22 @@ export const CustomAnimations = {
|
|||
const pullUp = getCountStep(rawSource, target, true)
|
||||
return [stacks, rules, pullUp, target]
|
||||
},
|
||||
/**
|
||||
* steps:
|
||||
* 1) stack sets
|
||||
* 2) put rules (lines) using aggregate sum
|
||||
* 3) replace with sum bubbles (aggregate sum) (basically target spec)
|
||||
* @param {Object} rawSource source spec
|
||||
* @param {Object} target target spec
|
||||
* @returns an array of vega-lite specs
|
||||
*/
|
||||
sum: async (rawSource, target) => {
|
||||
const stacks = await getGridSpec(rawSource, 10, true)
|
||||
delete stacks.encoding.y.axis
|
||||
const rules = getSumStep(rawSource, target, false)
|
||||
const pullUp = getSumStep(rawSource, target, true)
|
||||
return [stacks, rules, pullUp, target]
|
||||
},
|
||||
/**
|
||||
* min animation steps:
|
||||
* 1) source spec
|
||||
|
|
|
@ -445,6 +445,10 @@ function prep_specs_groupby (states, groupby, summarize) {
|
|||
|
||||
spec = generate_vega_specs(data, meta, spec_encoding, facet_encoding, facet_dims)
|
||||
specs_list.push(spec)
|
||||
|
||||
if ((operation === 'count') && groupby.length === 1) {
|
||||
specs_list.push(spec)
|
||||
}
|
||||
}
|
||||
return specs_list
|
||||
}
|
||||
|
@ -692,7 +696,9 @@ function prep_specs_summarize (states, groupby, summarize, output) {
|
|||
spec_encoding = { x: x_encoding, y: y_encoding, color, tooltip }
|
||||
}
|
||||
let spec = generate_vega_specs(data, meta, spec_encoding, facet_encoding, facet_dims)
|
||||
specs_list.push(spec)
|
||||
if (!((operation === 'count') && groupby.length === 1)) {
|
||||
specs_list.push(spec)
|
||||
}
|
||||
|
||||
min = states[0]
|
||||
.map((item) => {
|
||||
|
@ -718,7 +724,7 @@ function prep_specs_summarize (states, groupby, summarize, output) {
|
|||
|
||||
meta = {
|
||||
axes: groupby.length > 1,
|
||||
description: 'Plot ' + operation + ' ' + y_axis + ' of each group'
|
||||
description: 'Plot ' + operation + ((operation === 'count' || operation === 'sum') && groupby.length === 1 ? '' : ' ' + y_axis) + ' of each group'
|
||||
}
|
||||
|
||||
y_encoding = {
|
||||
|
@ -818,8 +824,22 @@ function prep_specs_summarize (states, groupby, summarize, output) {
|
|||
if (['mean', 'median', 'min', 'max'].includes(operation)) {
|
||||
meta.custom_animation = operation
|
||||
}
|
||||
else if (operation === 'count') {
|
||||
y_encoding.title = [operation + ' of', y_axis]
|
||||
else if (operation === 'count' || operation === 'sum') {
|
||||
if (groupby.length === 1) {
|
||||
min = data.map((item) => { return item.datamations_y }).reduce((prev, current) => {
|
||||
return Math.min(prev, current)
|
||||
})
|
||||
max = data.map((item) => { return item.datamations_y }).reduce((prev, current) => {
|
||||
return Math.max(prev, current)
|
||||
})
|
||||
meta.custom_animation = operation
|
||||
y_encoding.scale.domain = [_.round(min, 13), _.round(max, 13)]
|
||||
delete tooltip[0].title
|
||||
delete y_encoding.title
|
||||
}
|
||||
else {
|
||||
y_encoding.title = [operation + ' of', y_axis]
|
||||
}
|
||||
}
|
||||
|
||||
spec_encoding = { x: x_encoding, y: y_encoding, tooltip }
|
||||
|
@ -1069,7 +1089,9 @@ function prep_specs_summarize (states, groupby, summarize, output) {
|
|||
spec_encoding = { x: x_encoding, y: y_encoding, tooltip }
|
||||
if (groupby.length > 1) spec_encoding = { x: x_encoding, y: y_encoding, color, tooltip }
|
||||
spec = generate_vega_specs(data, meta, spec_encoding, facet_encoding, facet_dims, operation === 'mean')
|
||||
specs_list.push(spec)
|
||||
if (!((operation === 'count' || operation === 'sum') && groupby.length === 1)) {
|
||||
specs_list.push(spec)
|
||||
}
|
||||
|
||||
return specs_list
|
||||
}
|
||||
|
|
|
@ -108,7 +108,8 @@ export function generateGrid (spec, rows = 10, stacked = false) {
|
|||
specWidth = specWidth / grouped.length
|
||||
}
|
||||
|
||||
const maxCols = Math.ceil(d3.max(specValues, d => d.n) / rows)
|
||||
const maxCols = specValues[0].n ? Math.ceil(d3.max(specValues, d => d.n) / rows) : specValues.length / rows
|
||||
|
||||
let splitOptions = []
|
||||
|
||||
if (splitField) {
|
||||
|
@ -118,6 +119,7 @@ export function generateGrid (spec, rows = 10, stacked = false) {
|
|||
}
|
||||
|
||||
let counter = 1
|
||||
var sum = {}
|
||||
|
||||
const reduce = (v) => {
|
||||
const arr = []
|
||||
|
@ -167,6 +169,27 @@ export function generateGrid (spec, rows = 10, stacked = false) {
|
|||
|
||||
counter++
|
||||
}
|
||||
|
||||
// use sum as the y value
|
||||
if (!d.n) {
|
||||
const x = d[CONF.X_FIELD]
|
||||
const y = d[CONF.Y_FIELD]
|
||||
if (sum[x]) {
|
||||
sum[x] = sum[x] + y
|
||||
}
|
||||
else {
|
||||
sum[x] = y
|
||||
}
|
||||
arr.push({
|
||||
...datum,
|
||||
// ...colorFieldObj,
|
||||
gemini_id: d.gemini_ids ? d.gemini_ids[i] : counter,
|
||||
[CONF.X_FIELD]: stacked ? xCenter : x,
|
||||
[CONF.Y_FIELD]: stacked ? sum[x] : y,
|
||||
[CONF.Y_FIELD + '_tooltip']: stacked ? sum[x] : y
|
||||
})
|
||||
counter++
|
||||
}
|
||||
})
|
||||
|
||||
return arr
|
||||
|
|
|
@ -305,7 +305,7 @@ describe('small salary', function () {
|
|||
fs.readFile('../../../../inst/specs/max_specs_two_columns.json', 'utf8', function (err, fileContents) {
|
||||
if (err) throw err
|
||||
max_spec_two_column = JSON.parse(fileContents)
|
||||
fs.readFile('../../../../inst/specs/sum_specs.json', 'utf8', function (err, fileContents) {
|
||||
fs.readFile('../../../../sandbox/custom_animations/custom-animations-sum-manual.json', 'utf8', function (err, fileContents) {
|
||||
if (err) throw err
|
||||
sum_specs = JSON.parse(fileContents)
|
||||
fs.readFile('../../../../inst/specs/sum_specs_two_columns.json', 'utf8', function (err, fileContents) {
|
||||
|
@ -317,7 +317,7 @@ describe('small salary', function () {
|
|||
fs.readFile('../../../../inst/specs/prod_specs_two_columns.json', 'utf8', function (err, fileContents) {
|
||||
if (err) throw err
|
||||
prod_specs_two_columns = JSON.parse(fileContents)
|
||||
fs.readFile('../../../../inst/specs/count_specs_one_column.json', 'utf8', function (err, fileContents) {
|
||||
fs.readFile('../../../../sandbox/custom_animations/custom-animations-count-R.json', 'utf8', function (err, fileContents) {
|
||||
if (err) throw err
|
||||
count_spec = JSON.parse(fileContents)
|
||||
fs.readFile('../../../../inst/specs/count_specs_two_columns.json', 'utf8', function (err, fileContents) {
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче