Generate vega-lite-feature by using Compass
yhoonkim opened this issue · comments
Compass can help to generate all kinds of vega-lite specifications.
- use Compass to generate spec
- It is also required to implement the function converting vega-lite objects to vega-lite feature objects.
Input for Compass
- Variables List / Schema
- Generation Parameters
- Variable
- Maximum variable that output visualization can have (4)
- Transformation?
- aggregate: mean or no aggregate // yhoonkim should read about the rules in
aggregation
in Compass - no binning, no time unit
- aggregate: mean or no aggregate // yhoonkim should read about the rules in
- Mark Type, Channels that I allowed: See below,
- rescaling (log, no-rescale)
- Variable
var marktypesAll = ['bar','point','line','area'];
var channelsAll = ['x','y','shape','color','size','row','column'];
var aggregateAll = ['mean'];
Code structure would be roughly like this. (Will provide you some code later)
-
Generate sets of variables
-
Generate transformations of each set of variable
2.1) Generate different aggregation
2.2) Generate different rescaling -
Map each transformed variable to encoding
The code should be something like this. Haven’t tested, but should be pretty close.
// data = JSON data (e.g., from cars.json)
// fieldDefs = List of fieldDef object {field:..., type: ...}
const stats = dl.summary(data).reduce(function(s, profile) {
s[profile.field] = profile;
return s;
}, {
'*': {
max: data.length,
min: 0
}
});
const specs = genProjections(fieldDefs, stats, {
maxAdditionalVariables: 3
}).reduce(function(fieldSets, fieldSet) {
return genScales(fieldSets, fieldSet, {rescaleQuantitative: [undefined, 'log']});
}, []).reduce(function(fieldSets, fieldSet) {
return genAggregate(fieldSets, fieldSet, stats, {
// TODO: add AggregationOption parameter here
});
}, []).reduce(function(specs, fieldSet) {
return genSpecs(specs, fieldSet, stats, {
// TODO: add SpecOption parameter here
})
}, []);
genScales
seems working well. It generated all kinds of possible fieldSets
with scale.
But when it goes into genSpecs
, it only emits the specs that have all fields. I guess it field's selected : undefined
case is not working for genScales
now. (It's working well for genScales
.)
var data = JSON.parse(fs.readFileSync('./data/cars.json','utf8'));
var fieldDefs = [];
fieldDefs.push({'field': 'Acceleration', 'type': 'quantitative' });
fieldDefs.push({'field': 'Origin', 'type': 'nominal' });
const stats = dl.summary(data).reduce(function(s, profile) {
s[profile.field] = profile;
return s;
}, {
'*': {
max: data.length,
min: 0
}
});
const specs = cp.gen.projections(fieldDefs, stats, {
maxAdditionalVariables: 3
})
.reduce(function(fieldSets, fieldSet) {
return genScales(fieldSets, fieldSet, {rescaleQuantitative: [undefined, 'log']});
}, []);
console.log(specs.length); // 5
var specs2 = specs.reduce(function(specs, fieldSet) {
return cp.gen.specs(specs, fieldSet, stats, {
// TODO: add SpecOption parameter here
})
}, []);
console.log(specs2.length); // 2
Should I post this issue on Compass or leave it here?
@kanitw
I figured out why genSpecs
didn't work.
export default function genSpecsFromFieldDefs(output, fieldDefs, stats, opt: SpecOption = {}, nested?): any {
// opt must be augmented before being passed to genEncodings or getMarks
opt = util.extend({}, DEFAULT_SPEC_OPTION, opt);
var encodings = genEncodings([], fieldDefs, stats, opt);
if (nested) {
return encodings.reduce(function(dict, encoding) {
var encodingShorthand = shortenEncoding(encoding);
dict[encodingShorthand] = genSpecsFromEncodings([], encoding, stats, opt);
return dict;
}, {});
} else {
return encodings.reduce(function(list, encoding) {
return genSpecsFromEncodings(list, encoding, stats, opt);
}, []);
}
}
In the above, it didn't emit the result to output.
So I changed the last else
like
var specs = encodings.reduce(function(list, encoding) {
return genSpecsFromEncodings(list, encoding, stats, opt);
}, []);
output.push(specs);
return output;
and confirmed that it worked for me( in your code above[https://github.com//issues/2#issuecomment-172133665] ).
But I found the other issue that
genAggregate
can not add the aggregation on scaled fieldSets.
Here is my code.
var cp = require('./bower_components/viscompass/compass');
var dl = require('./bower_components/datalib/datalib');
var fs = require('fs');
var genScales = require('./bower_components/viscompass/src/gen/scales').default;
var channel_1 = require('./bower_components/vega-lite/src/channel');
var mark_1 = require('./bower_components/vega-lite/src/mark');
var data = JSON.parse(fs.readFileSync('./data/cars.json','utf8'));
var fieldDefs = [];
fieldDefs.push({'field': 'Acceleration', 'type': 'quantitative' });
// fieldDefs.push({'field': 'Horsepower', 'type': 'quantitative' });
fieldDefs.push({'field': 'Origin', 'type': 'nominal' });
// fieldDefs = List of fieldDef object {field:..., type: ...}
const stats = dl.summary(data).reduce(function(s, profile) {
s[profile.field] = profile;
return s;
}, {
'*': {
max: data.length,
min: 0
}
});
var fieldSets = cp.gen.projections(fieldDefs, stats, {
maxAdditionalVariables: 3
}).reduce(function(fieldSets, fieldSet) {
return genScales(fieldSets, fieldSet, {rescaleQuantitative: [undefined, 'log']});
}, []);
console.log(fieldSets); // A
fieldSets = fieldSets.reduce(function(fieldSets, fieldSet) {
return cp.gen.aggregates(fieldSets, fieldSet, stats, {
// TODO: add AggregationOption parameter here
});
}, []);
console.log(fieldSets); // B
You can see that B don't include scale
which A had.
Now, I figured out the way I avoid. I just changed the order of calling.
var fieldSets = cp.gen.projections(fieldDefs, stats, {
maxAdditionalVariables: 3
})
.reduce(function(fieldSets, fieldSet) {
return cp.gen.aggregates(fieldSets, fieldSet, stats, {
// TODO: add AggregationOption parameter here
});
}, [])
.reduce(function(fieldSets, fieldSet) {
return genScales(fieldSets, fieldSet, {rescaleQuantitative: [undefined, 'log']});
}, []);