Файл: src/public/plugins/metrics-graphics/misc/process.js
Строк: 339
<?php
function mg_process_scale_ticks (args, axis) {
var accessor;
var scale_ticks;
var max;
if (axis === 'x') {
accessor = args.x_accessor;
scale_ticks = args.scales.X.ticks(args.xax_count);
max = args.processed.max_x;
} else if (axis === 'y') {
accessor = args.y_accessor;
scale_ticks = args.scales.Y.ticks(args.yax_count)
max = args.processed.max_y;
}
function log10 (val) {
if (val === 1000) {
return 3;
}
if (val === 1000000) {
return 7;
}
return Math.log(val) / Math.LN10;
}
if ((axis === 'x' && args.x_scale_type === 'log')
|| (axis === 'y' && args.y_scale_type === 'log')
) {
// get out only whole logs
scale_ticks = scale_ticks.filter(function (d) {
return Math.abs(log10(d)) % 1 < 1e-6 || Math.abs(log10(d)) % 1 > 1 - 1e-6;
});
}
// filter out fraction ticks if our data is ints and if xmax > number of generated ticks
var number_of_ticks = scale_ticks.length;
// is our data object all ints?
var data_is_int = true;
args.data.forEach(function (d, i) {
d.forEach(function (d, i) {
if (d[accessor] % 1 !== 0) {
data_is_int = false;
return false;
}
});
});
if (data_is_int && number_of_ticks > max && args.format === 'count') {
// remove non-integer ticks
scale_ticks = scale_ticks.filter(function (d) {
return d % 1 === 0;
});
}
if (axis === 'x') {
args.processed.x_ticks = scale_ticks;
} else if(axis === 'y') {
args.processed.y_ticks = scale_ticks;
}
}
function raw_data_transformation (args) {
'use strict';
// dupe our data so we can modify it without adverse effect
args.data = MG.clone(args.data);
// we need to account for a few data format cases:
// #0 {bar1:___, bar2:___} // single object (for, say, bar charts)
// #1 [{key:__, value:__}, ...] // unnested obj-arrays
// #2 [[{key:__, value:__}, ...], [{key:__, value:__}, ...]] // nested obj-arrays
// #3 [[4323, 2343],..] // unnested 2d array
// #4 [[[4323, 2343],..] , [[4323, 2343],..]] // nested 2d array
args.single_object = false; // for bar charts.
args.array_of_objects = false;
args.array_of_arrays = false;
args.nested_array_of_arrays = false;
args.nested_array_of_objects = false;
// is the data object a nested array?
if (is_array_of_arrays(args.data)) {
args.nested_array_of_objects = args.data.map(function(d) {
return is_array_of_objects_or_empty(d);
}); // Case #2
args.nested_array_of_arrays = args.data.map(function(d) {
return is_array_of_arrays(d);
}); // Case #4
} else {
args.array_of_objects = is_array_of_objects(args.data); // Case #1
args.array_of_arrays = is_array_of_arrays(args.data); // Case #3
}
if (args.chart_type === 'line') {
if (args.array_of_objects || args.array_of_arrays) {
args.data = [args.data];
}
} else {
if (!(args.data[0] instanceof Array)) {
args.data = [args.data];
}
}
// if the y_accessor is an array, break it up and store the result in args.data
mg_process_multiple_x_accessors(args);
mg_process_multiple_y_accessors(args);
// if user supplies keyword in args.color, change to arg.colors.
// this is so that the API remains fairly sensible and legible.
if (args.color !== undefined) {
args.colors = args.color;
}
// if user has supplied args.colors, and that value is a string, turn it into an array.
if (args.colors !== null && typeof args.colors === 'string') {
args.colors = [args.colors];
}
// sort x-axis data
if (args.chart_type === 'line' && args.x_sort === true) {
for (var i = 0; i < args.data.length; i++) {
args.data[i].sort(function(a, b) {
return a[args.x_accessor] - b[args.x_accessor];
});
}
}
return this;
}
function mg_process_multiple_accessors(args, which_accessor) {
// turns an array of accessors into ...
if (args[which_accessor] instanceof Array) {
args.data = args.data.map(function(_d) {
return args[which_accessor].map(function(ya) {
return _d.map(function(di) {
di = MG.clone(di);
if (di[ya] === undefined) {
return undefined;
}
di['multiline_' + which_accessor] = di[ya];
return di;
}).filter(function(di) {
return di !== undefined;
});
});
})[0];
args[which_accessor] = 'multiline_' + which_accessor;
}
}
function mg_process_multiple_x_accessors(args) { mg_process_multiple_accessors(args, 'x_accessor'); }
function mg_process_multiple_y_accessors(args) { mg_process_multiple_accessors(args, 'y_accessor'); }
MG.raw_data_transformation = raw_data_transformation;
function process_line(args) {
'use strict';
var time_frame;
// do we have a time-series?
var is_time_series = d3.sum(args.data.map(function(series) {
return series.length > 0 && series[0][args.x_accessor] instanceof Date;
})) > 0;
// force linear interpolation when missing_is_hidden is enabled
if (args.missing_is_hidden) {
args.interpolate = 'linear';
}
// are we replacing missing y values with zeros?
if ((args.missing_is_zero || args.missing_is_hidden)
&& args.chart_type === 'line'
&& is_time_series
) {
for (var i = 0; i < args.data.length; i++) {
// we need to have a dataset of length > 2, so if it's less than that, skip
if (args.data[i].length <= 1) {
continue;
}
var first = args.data[i][0];
var last = args.data[i][args.data[i].length-1];
// initialize our new array for storing the processed data
var processed_data = [];
// we'll be starting from the day after our first date
var start_date = MG.clone(first[args.x_accessor]).setDate(first[args.x_accessor].getDate() + 1);
// if we've set a max_x, add data points up to there
var from = (args.min_x) ? args.min_x : start_date;
var upto = (args.max_x) ? args.max_x : last[args.x_accessor];
time_frame = mg_get_time_frame((upto-from)/1000);
if (time_frame == 'default' && args.missing_is_hidden_accessor === null) {
for (var d = new Date(from); d <= upto; d.setDate(d.getDate() + 1)) {
var o = {};
d.setHours(0, 0, 0, 0);
// add the first date item, we'll be starting from the day after our first date
if (Date.parse(d) === Date.parse(new Date(start_date))) {
processed_data.push(MG.clone(args.data[i][0]));
}
// check to see if we already have this date in our data object
var existing_o = null;
args.data[i].forEach(function(val, i) {
if (Date.parse(val[args.x_accessor]) === Date.parse(new Date(d))) {
existing_o = val;
return false;
}
});
// if we don't have this date in our data object, add it and set it to zero
if (!existing_o) {
o[args.x_accessor] = new Date(d);
o[args.y_accessor] = 0;
o['_missing'] = true; //we want to distinguish between zero-value and missing observations
processed_data.push(o);
}
// if the data point has, say, a 'missing' attribute set or if its
// y-value is null identify it internally as missing
else if (existing_o[args.missing_is_hidden_accessor]
|| existing_o[args.y_accessor] === null
) {
existing_o['_missing'] = true;
processed_data.push(existing_o);
}
//otherwise, use the existing object for that date
else {
processed_data.push(existing_o);
}
}
} else {
for (var j = 0; j < args.data[i].length; j += 1) {
var obj = MG.clone(args.data[i][j]);
obj['_missing'] = args.data[i][j][args.missing_is_hidden_accessor];
processed_data.push(obj);
}
}
// update our date object
args.data[i] = processed_data;
}
}
return this;
}
MG.process_line = process_line;
function process_histogram(args) {
'use strict';
// if args.binned == false, then we need to bin the data appropriately.
// if args.binned == true, then we need to make sure to compute the relevant computed data.
// the outcome of either of these should be something in args.computed_data.
// the histogram plotting function will be looking there for the data to plot.
// we need to compute an array of objects.
// each object has an x, y, and dx.
// histogram data is always single dimension
var our_data = args.data[0];
var extracted_data;
if (args.binned === false) {
// use d3's built-in layout.histogram functionality to compute what you need.
if (typeof(our_data[0]) === 'object') {
// we are dealing with an array of objects. Extract the data value of interest.
extracted_data = our_data
.map(function(d) {
return d[args.x_accessor];
});
} else if (typeof(our_data[0]) === 'number') {
// we are dealing with a simple array of numbers. No extraction needed.
extracted_data = our_data;
} else {
console.log('TypeError: expected an array of numbers, found ' + typeof(our_data[0]));
return;
}
var hist = d3.layout.histogram();
if (args.bins) {
hist = hist.bins(args.bins);
}
args.processed_data = hist(extracted_data)
.map(function(d) {
// extract only the data we need per data point.
return {'x': d.x, 'y': d.y, 'dx': d.dx};
});
} else {
// here, we just need to reconstruct the array of objects
// take the x accessor and y accessor.
// pull the data as x and y. y is count.
args.processed_data = our_data.map(function(d) {
return {'x': d[args.x_accessor], 'y': d[args.y_accessor]};
});
var this_pt;
var next_pt;
// we still need to compute the dx component for each data point
for (var i=0; i < args.processed_data.length; i++) {
this_pt = args.processed_data[i];
if (i === args.processed_data.length - 1) {
this_pt.dx = args.processed_data[i-1].dx;
} else {
next_pt = args.processed_data[i+1];
this_pt.dx = next_pt.x - this_pt.x;
}
}
}
// capture the original data and accessors before replacing args.data
if (!args.processed) {
args.processed = {};
}
args.processed.original_data = args.data;
args.processed.original_x_accessor = args.x_accessor;
args.processed.original_y_accessor = args.y_accessor;
args.data = [args.processed_data];
args.x_accessor = args.processed_x_accessor;
args.y_accessor = args.processed_y_accessor;
return this;
}
MG.process_histogram = process_histogram;
// for use with bar charts, etc.
function process_categorical_variables(args) {
'use strict';
var extracted_data, processed_data={}, pd=[];
//var our_data = args.data[0];
var label_accessor = args.bar_orientation === 'vertical' ? args.x_accessor : args.y_accessor;
var data_accessor = args.bar_orientation === 'vertical' ? args.y_accessor : args.x_accessor;
if (args.binned === false) {
args.categorical_variables = [];
if (typeof(our_data[0]) === 'object') {
// we are dealing with an array of objects, extract the data value of interest
extracted_data = our_data
.map(function(d) {
return d[label_accessor];
});
} else {
extracted_data = our_data;
}
var this_dp;
for (var i = 0; i < extracted_data.length; i++) {
this_dp=extracted_data[i];
if (args.categorical_variables.indexOf(this_dp) === -1) args.categorical_variables.push(this_dp);
if (!processed_data.hasOwnProperty(this_dp)) processed_data[this_dp] = 0;
processed_data[this_dp] += 1;
}
processed_data = Object.keys(processed_data).map(function(d) {
var obj = {};
obj[data_accessor] = processed_data[d];
obj[label_accessor] = d;
return obj;
});
} else {
processed_data = args.data[0];
args.categorical_variables = d3.set(processed_data.map(function(d) {
return d[label_accessor];
})).values();
args.categorical_variables.reverse();
}
args.data = [processed_data];
return this;
}
MG.process_categorical_variables = process_categorical_variables;
function process_point(args) {
'use strict';
var data = args.data[0];
var x = data.map(function(d) { return d[args.x_accessor]; });
var y = data.map(function(d) { return d[args.y_accessor]; });
if (args.least_squares) {
args.ls_line = least_squares(x,y);
}
return this;
}
MG.process_point = process_point;
?>