Skip to content

Commit 2697975

Browse files
committed
add two example transform modules
1 parent 8ab9d0c commit 2697975

File tree

2 files changed

+286
-0
lines changed

2 files changed

+286
-0
lines changed
+161
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
'use strict';
2+
3+
// var Lib = require('@src/lib');
4+
var Lib = require('../../../../src/lib');
5+
6+
/*eslint no-unused-vars: 0*/
7+
8+
9+
// so that Plotly.register knows what to do with it
10+
exports.moduleType = 'transform';
11+
12+
// determines to link between transform type and transform module
13+
exports.name = 'filter';
14+
15+
// ... as trace attributes
16+
exports.attributes = {
17+
operation: {
18+
valType: 'enumerated',
19+
values: ['=', '<', '>'],
20+
dflt: '='
21+
},
22+
value: {
23+
valType: 'number',
24+
dflt: 0
25+
},
26+
filtersrc: {
27+
valType: 'enumerated',
28+
values: ['x', 'y'],
29+
dflt: 'x'
30+
}
31+
};
32+
33+
/**
34+
* Supply transform attributes defaults
35+
*
36+
* @param {object} transformIn
37+
* object linked to trace.transforms[i] with 'type' set to exports.name
38+
* @param {object} fullData
39+
* the plot's full data
40+
* @param {object} layout
41+
* the plot's (not-so-full) layout
42+
*
43+
* @return {object} transformOut
44+
* copy of transformIn that contains attribute defaults
45+
*/
46+
exports.supplyDefaults = function(transformIn, fullData, layout) {
47+
var transformOut = {};
48+
49+
function coerce(attr, dflt) {
50+
return Lib.coerce(transformIn, transformOut, exports.attributes, attr, dflt);
51+
}
52+
53+
coerce('operation');
54+
coerce('value');
55+
coerce('filtersrc');
56+
57+
// or some more complex logic using fullData and layout
58+
59+
return transformOut;
60+
};
61+
62+
/**
63+
* Apply transform !!!
64+
*
65+
* @param {array} data
66+
* array of transformed traces (is [fullTrace] upon first transform)
67+
*
68+
* @param {object} state
69+
* state object which includes:
70+
* - transform {object} full transform attributes
71+
* - fullTrace {object} full trace object which is being transformed
72+
* - fullData {array} full pre-transform(s) data array
73+
* - layout {object} the plot's (not-so-full) layout
74+
*
75+
* @return {object} newData
76+
* array of transformed traces
77+
*/
78+
exports.transform = function(data, state) {
79+
80+
// one-to-one case
81+
82+
var newData = data.map(function(trace) {
83+
return transformOne(trace, state);
84+
});
85+
86+
return newData;
87+
};
88+
89+
function transformOne(trace, state) {
90+
var newTrace = Lib.extendDeep({}, trace);
91+
92+
var opts = state.transform;
93+
var src = opts.filtersrc;
94+
var filterFunc = getFilterFunc(opts);
95+
var len = trace[src].length;
96+
var arrayAttrs = findArrayAttributes(trace);
97+
98+
arrayAttrs.forEach(function(attr) {
99+
Lib.nestedProperty(newTrace, attr).set([]);
100+
});
101+
102+
function fill(attr, i) {
103+
var arr = Lib.nestedProperty(trace, attr).get();
104+
var newArr = Lib.nestedProperty(newTrace, attr).get();
105+
106+
newArr.push(arr[i]);
107+
}
108+
109+
for(var i = 0; i < len; i++) {
110+
var v = trace[src][i];
111+
112+
if(!filterFunc(v)) continue;
113+
114+
for(var j = 0; j < arrayAttrs.length; j++) {
115+
fill(arrayAttrs[j], i);
116+
}
117+
}
118+
119+
return newTrace;
120+
}
121+
122+
function getFilterFunc(opts) {
123+
var value = opts.value;
124+
125+
switch(opts.operation) {
126+
case '=':
127+
return function(v) { return v === value; };
128+
case '<':
129+
return function(v) { return v < value; };
130+
case '>':
131+
return function(v) { return v > value; };
132+
}
133+
}
134+
135+
function findArrayAttributes(obj, root) {
136+
root = root || '';
137+
138+
var list = [];
139+
140+
Object.keys(obj).forEach(function(k) {
141+
var val = obj[k];
142+
143+
if(k.charAt(0) === '_') return;
144+
145+
if(k === 'transforms') {
146+
val.forEach(function(item, i) {
147+
list = list.concat(
148+
findArrayAttributes(item, root + k + '[' + i + ']' + '.')
149+
);
150+
});
151+
}
152+
else if(Lib.isPlainObject(val)) {
153+
list = list.concat(findArrayAttributes(val, root + k + '.'));
154+
}
155+
else if(Array.isArray(val)) {
156+
list.push(root + k);
157+
}
158+
});
159+
160+
return list;
161+
}
+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
'use strict';
2+
3+
// var Lib = require('@src/lib');
4+
var Lib = require('../../../../src/lib');
5+
6+
/*eslint no-unused-vars: 0*/
7+
8+
9+
// so that Plotly.register knows what to do with it
10+
exports.moduleType = 'transform';
11+
12+
// determines to link between transform type and transform module
13+
exports.name = 'groupby';
14+
15+
// ... as trace attributes
16+
exports.attributes = {
17+
active: {
18+
valType: 'boolean',
19+
dflt: true
20+
},
21+
groups: {
22+
valType: 'data_array',
23+
dflt: []
24+
},
25+
groupColors: {
26+
valType: 'any',
27+
dflt: {}
28+
}
29+
};
30+
31+
/**
32+
* Supply transform attributes defaults
33+
*
34+
* @param {object} transformIn
35+
* object linked to trace.transforms[i] with 'type' set to exports.name
36+
* @param {object} fullData
37+
* the plot's full data
38+
* @param {object} layout
39+
* the plot's (not-so-full) layout
40+
*
41+
* @return {object} transformOut
42+
* copy of transformIn that contains attribute defaults
43+
*/
44+
exports.supplyDefaults = function(transformIn, fullData, layout) {
45+
var transformOut = {};
46+
47+
function coerce(attr, dflt) {
48+
return Lib.coerce(transformIn, transformOut, exports.attributes, attr, dflt);
49+
}
50+
51+
var active = coerce('active');
52+
53+
if(!active) return transformOut;
54+
55+
coerce('groups');
56+
coerce('groupColors');
57+
58+
// or some more complex logic using fullData and layout
59+
60+
return transformOut;
61+
};
62+
63+
/**
64+
* Apply transform !!!
65+
*
66+
* @param {array} data
67+
* array of transformed traces (is [fullTrace] upon first transform)
68+
*
69+
* @param {object} state
70+
* state object which includes:
71+
* - transform {object} full transform attributes
72+
* - fullTrace {object} full trace object which is being transformed
73+
* - fullData {array} full pre-transform(s) data array
74+
* - layout {object} the plot's (not-so-full) layout
75+
*
76+
* @return {object} newData
77+
* array of transformed traces
78+
*/
79+
exports.transform = function(data, state) {
80+
81+
// one-to-many case
82+
83+
var newData = [];
84+
85+
data.forEach(function(trace) {
86+
newData = newData.concat(transformOne(trace, state));
87+
});
88+
89+
return newData;
90+
};
91+
92+
function transformOne(trace, state) {
93+
var opts = state.transform;
94+
var groups = opts.groups;
95+
96+
var groupNames = groups.filter(function(g, i, self) {
97+
return self.indexOf(g) === i;
98+
});
99+
100+
var newData = new Array(groupNames.length);
101+
var len = Math.min(trace.x.length, trace.y.length, groups.length);
102+
103+
for(var i = 0; i < groupNames.length; i++) {
104+
var groupName = groupNames[i];
105+
106+
// TODO is this the best pattern ???
107+
// maybe we could abstract this out
108+
var newTrace = newData[i] = Lib.extendDeep({}, trace);
109+
110+
newTrace.x = [];
111+
newTrace.y = [];
112+
113+
for(var j = 0; j < len; j++) {
114+
if(groups[j] !== groupName) continue;
115+
116+
newTrace.x.push(trace.x[j]);
117+
newTrace.y.push(trace.y[j]);
118+
}
119+
120+
newTrace.name = groupName;
121+
newTrace.marker.color = opts.groupColors[groupName];
122+
}
123+
124+
return newData;
125+
}

0 commit comments

Comments
 (0)