Skip to content

Box trace directory split into multiple files #158

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

Merged
merged 6 commits into from
Jan 5, 2016
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions src/traces/box/calc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
'use strict';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

paste the license header from another file here.

Better yet, maybe you add a no-header-found clause in https://github.com/plotly/plotly.js/blob/master/tasks/header.js#L50 to do it automatically.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh I thought there was a check to automatically insert it already. I'll do it manually right now, but that will be a good thing to add in.


var isNumeric = require('fast-isnumeric');

var Plotly = require('../../plotly');
var Lib = require('../../lib');

module.exports = function calc(gd, trace) {
// outlier definition based on http://www.physics.csbsju.edu/stats/box2.html
var xa = Plotly.Axes.getFromId(gd, trace.xaxis||'x'),
ya = Plotly.Axes.getFromId(gd, trace.yaxis||'y'),
orientation = trace.orientation,
cd = [],
valAxis, valLetter, val, valBinned,
posAxis, posLetter, pos, posDistinct, dPos;

// Set value (val) and position (pos) keys via orientation
if (orientation==='h') {
valAxis = xa;
valLetter = 'x';
posAxis = ya;
posLetter = 'y';
} else {
valAxis = ya;
valLetter = 'y';
posAxis = xa;
posLetter = 'x';
}

val = valAxis.makeCalcdata(trace, valLetter); // get val

// size autorange based on all source points
// position happens afterward when we know all the pos
Plotly.Axes.expand(valAxis, val, {padded: true});

// In vertical (horizontal) box plots:
// if no x (y) data, use x0 (y0), or name
// so if you want one box
// per trace, set x0 (y0) to the x (y) value or category for this trace
// (or set x (y) to a constant array matching y (x))
function getPos (gd, trace, posLetter, posAxis, val) {
var pos0;
if (posLetter in trace) pos = posAxis.makeCalcdata(trace, posLetter);
else {
if (posLetter+'0' in trace) pos0 = trace[posLetter+'0'];
else if ('name' in trace && (
posAxis.type==='category' ||
(isNumeric(trace.name) &&
['linear','log'].indexOf(posAxis.type)!==-1) ||
(Lib.isDateTime(trace.name) &&
posAxis.type==='date')
)) {
pos0 = trace.name;
}
else pos0 = gd.numboxes;
pos0 = posAxis.d2c(pos0);
pos = val.map(function(){ return pos0; });
}
return pos;
}

pos = getPos(gd, trace, posLetter, posAxis, val);

// get distinct positions and min difference
var dv = Lib.distinctVals(pos);
posDistinct = dv.vals;
dPos = dv.minDiff/2;

function binVal (cd, val, pos, posDistinct, dPos) {
var posDistinctLength = posDistinct.length,
valLength = val.length,
valBinned = [],
bins = [],
i, p, n, v;

// store distinct pos in cd, find bins, init. valBinned
for (i = 0; i < posDistinctLength; ++i) {
p = posDistinct[i];
cd[i] = {pos: p};
bins[i] = p - dPos;
valBinned[i] = [];
}
bins.push(posDistinct[posDistinctLength-1] + dPos);

// bin the values
for (i = 0; i < valLength; ++i) {
v = val[i];
if(!isNumeric(v)) continue;
n = Lib.findBin(pos[i], bins);
if(n>=0 && n<valLength) valBinned[n].push(v);
}

return valBinned;
}

valBinned = binVal(cd, val, pos, posDistinct, dPos);

// sort the bins and calculate the stats
function calculateStats (cd, valBinned) {
var v, l, cdi, i;

for (i = 0; i < valBinned.length; ++i) {
v = valBinned[i].sort(Lib.sorterAsc);
l = v.length;
cdi = cd[i];

cdi.val = v; // put all values into calcdata
cdi.min = v[0];
cdi.max = v[l-1];
cdi.mean = Lib.mean(v,l);
cdi.sd = Lib.stdev(v,l,cdi.mean);
cdi.q1 = Lib.interp(v, 0.25); // first quartile
cdi.med = Lib.interp(v, 0.5); // median
cdi.q3 = Lib.interp(v, 0.75); // third quartile
// lower and upper fences - last point inside
// 1.5 interquartile ranges from quartiles
cdi.lf = Math.min(cdi.q1, v[
Math.min(Lib.findBin(2.5*cdi.q1-1.5*cdi.q3,v,true)+1, l-1)]);
cdi.uf = Math.max(cdi.q3,v[
Math.max(Lib.findBin(2.5*cdi.q3-1.5*cdi.q1,v), 0)]);
// lower and upper outliers - 3 IQR out (don't clip to max/min,
// this is only for discriminating suspected & far outliers)
cdi.lo = 4*cdi.q1-3*cdi.q3;
cdi.uo = 4*cdi.q3-3*cdi.q1;
}
}

calculateStats(cd, valBinned);

// remove empty bins
cd = cd.filter(function(cdi){ return cdi.val && cdi.val.length; });
if(!cd.length) return [{t: {emptybox: true}}];

// add numboxes and dPos to cd
cd[0].t = {boxnum: gd.numboxes, dPos: dPos};
gd.numboxes++;
return cd;
};
59 changes: 59 additions & 0 deletions src/traces/box/defaults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'use strict';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto


var Lib = require('../../lib');
var Color = require('../../components/color');

var attributes = require('./attributes');

module.exports = function supplyDefaults(traceIn, traceOut, defaultColor) {
function coerce(attr, dflt) {
return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
}

var y = coerce('y'),
x = coerce('x'),
defaultOrientation;

if (y && y.length) {
defaultOrientation = 'v';
if (!x) coerce('x0');
} else if (x && x.length) {
defaultOrientation = 'h';
coerce('y0');
} else {
traceOut.visible = false;
return;
}

coerce('orientation', defaultOrientation);

coerce('line.color', (traceIn.marker||{}).color || defaultColor);
coerce('line.width', 2);
coerce('fillcolor', Color.addOpacity(traceOut.line.color, 0.5));

coerce('whiskerwidth');
coerce('boxmean');

var outlierColorDflt = Lib.coerce2(traceIn, traceOut, attributes, 'marker.outliercolor'),
lineoutliercolor = coerce('marker.line.outliercolor'),
boxpoints = outlierColorDflt ||
lineoutliercolor ? coerce('boxpoints', 'suspectedoutliers') :
coerce('boxpoints');

if(boxpoints) {
coerce('jitter', boxpoints==='all' ? 0.3 : 0);
coerce('pointpos', boxpoints==='all' ? -1.5 : 0);

coerce('marker.symbol');
coerce('marker.opacity');
coerce('marker.size');
coerce('marker.color', traceOut.line.color);
coerce('marker.line.color');
coerce('marker.line.width');

if(boxpoints==='suspectedoutliers') {
coerce('marker.line.outliercolor', traceOut.marker.color);
coerce('marker.line.outlierwidth');
}
}
};
97 changes: 97 additions & 0 deletions src/traces/box/hover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
'use strict';

var Plotly = require('../../plotly');
var Color = require('../../components/color');

module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
// closest mode: handicap box plots a little relative to others
var cd = pointData.cd,
trace = cd[0].trace,
t = cd[0].t,
xa = pointData.xa,
ya = pointData.ya,
closeData = [],
dx, dy, distfn, boxDelta,
posLetter, posAxis, posText,
val, valLetter, valAxis;

// adjust inbox w.r.t. to calculate box size
boxDelta = (hovermode==='closest') ? 2.5*t.bdPos : t.bdPos;

if (trace.orientation==='h') {
dx = function(di){
return Plotly.Fx.inbox(di.min - xval, di.max - xval);
};
dy = function(di){
var pos = di.pos + t.bPos - yval;
return Plotly.Fx.inbox(pos - boxDelta, pos + boxDelta);
};
posLetter = 'y';
posAxis = ya;
valLetter = 'x';
valAxis = xa;
} else {
dx = function(di){
var pos = di.pos + t.bPos - xval;
return Plotly.Fx.inbox(pos - boxDelta, pos + boxDelta);
};
dy = function(di){
return Plotly.Fx.inbox(di.min - yval, di.max - yval);
};
posLetter = 'x';
posAxis = xa;
valLetter = 'y';
valAxis = ya;
}

distfn = Plotly.Fx.getDistanceFunction(hovermode, dx, dy);
Plotly.Fx.getClosest(cd, distfn, pointData);

// skip the rest (for this trace) if we didn't find a close point
if(pointData.index===false) return;

// create the item(s) in closedata for this point

// the closest data point
var di = cd[pointData.index],
lc = trace.line.color,
mc = (trace.marker||{}).color;
if(Color.opacity(lc) && trace.line.width) pointData.color = lc;
else if(Color.opacity(mc) && trace.boxpoints) pointData.color = mc;
else pointData.color = trace.fillcolor;

pointData[posLetter+'0'] = posAxis.c2p(di.pos + t.bPos - t.bdPos, true);
pointData[posLetter+'1'] = posAxis.c2p(di.pos + t.bPos + t.bdPos, true);

posText = Plotly.Axes.tickText(posAxis, posAxis.c2l(di.pos), 'hover').text;
pointData[posLetter+'LabelVal'] = di.pos;

// box plots: each "point" gets many labels
var usedVals = {},
attrs = ['med','min','q1','q3','max'],
attr,
pointData2;
if(trace.boxmean) attrs.push('mean');
if(trace.boxpoints) [].push.apply(attrs,['lf', 'uf']);

for (var i=0; i<attrs.length; i++) {
attr = attrs[i];

if(!(attr in di) || (di[attr] in usedVals)) continue;
usedVals[di[attr]] = true;

// copy out to a new object for each value to label
val = valAxis.c2p(di[attr], true);
pointData2 = Plotly.Lib.extendFlat({}, pointData);
pointData2[valLetter+'0'] = pointData2[valLetter+'1'] = val;
pointData2[valLetter+'LabelVal'] = di[attr];
pointData2.attr = attr;

if(attr==='mean' && ('sd' in di) && trace.boxmean==='sd') {
pointData2[valLetter+'err'] = di.sd;
}
pointData.name = ''; // only keep name on the first item (median)
closeData.push(pointData2);
}
return closeData;
};
Loading