ChartJS - line graph, position tooltip - charts

I have the following graph with 3 datasets. Everything works fine except for one small bug. I want to tooltip to be placed only on the first dataset, as it currently is placed on the average position of all datasets.
Here is a screenshot:
I know that ChartJS has the positioners function (the one below), but I can't figure out how to ignore the other 2 datasets so the tooltip is sticked only to the first line graph
Chart.Tooltip.positioners.top = function (elements, eventPosition) {
const tooltip = this;
return ;
};

You can also define a custom position function for tooltips. like this,
Chart.Tooltip.positioners.custom = function(elements, eventPosition) {
var x = eventPosition.x;
var y = eventPosition.y;
var minDistance = Number.POSITIVE_INFINITY;
var i, len, nearestElement;
var top_element = elements[0];
for (i = 0, len = elements.length; i < len; ++i) {
console.log(elements[i].tooltipPosition().y);
if (elements[i].tooltipPosition().y < top_element.tooltipPosition().y) {
top_element = elements[i];
}
}
var tp = top_element.tooltipPosition();
x = tp.x;
y = tp.y;
return {
x: x,
y: y
};
};
Once done, you can specify it in tooltips.options.
More information :
https://www.chartjs.org/docs/latest/configuration/tooltip.html#position-modes
Hope it helps!

Related

Forward pass neural network output values always converge to a mean

I thought the best way to learn neural networks would be to create one from scratch in JavaScript using the MNIST database of images, each image displaying a single handwritten number from 0 to 9. Each image is 784 pixels, each pixel value being either 1 (white) or 0 (black).
Now before I start posting any code, I wanted to first keep things abstract and talk out my problem to see if I'm just missing something very basic and embarrassing.
NN basic specs are:
784 input values 2 hidden layers
each containing 50 neurons 10
output neurons representing labels 0 to 9
beta = 0 in every layer
learning rate = 0.01 initial weights set using xavier
activation function = sigmoid throughout for now
When I run this NN over a 1000 images or so, all the outputs slowly descend to around 0.1 and then basically all hover there forever. When I watch a single image flow through the NN and then backprop, this actually makes sense. Most of the time, in fact 90% of the time, the true value for a single output will be zero. Therefore, for a single output, if I run 10 images through, 9 of those images will want the output to move towards truth=zero, while only 1 images will want it to move up to truth=one. I actually see happen in the numbers....you can see a single output value slowly descend towards truth=zero a bunch of times, then get a temporary bump up when truth=one.
I hope this makes sense....basically my NN would rather reduce my total error by moving all my outputs closer to truth=zero. It's almost as if I need to adjust my learning rate to be 10x greater when adjusting my weights towards truth=one...but then I suspect this would just adjust all my outputs to 0.5.
I've been through my code so many times, and I believe my math is good, but of course I could be missing something. That said...I thought I'd start here first and see if anyone else has seen this.
EDIT....raw code containing NN classes. Sorry, bit messy. Basically you call a new network class, then run network.addlayer to populate layers, neurons, and set weights. From there you can start training by call network.inputTrainingData, which accepts the binary array of 784 pixels, and the truth value for the image. And finally, you can network backPropogate after every image.
class network {
layers = [];
learning_rate = 0.01;
inputCnt = 784;
labels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
xValues = [];
truth = [];
constructor() {
}
addLayer(neuronCnt, isOutputLayer) {
//note...we're not adding an "input layer....why do that when you can just feed the inputs into the first hidden layer?
if (isOutputLayer) {
this.layers.push(new layer(this.labels.length, this.layers[this.layers.length - 1].neurons.length, isOutputLayer));
} else if(this.layers.length) {
this.layers.push(new layer(neuronCnt, this.layers[this.layers.length - 1].neurons.length, isOutputLayer));
} else {
this.layers.push(new layer(neuronCnt, this.inputCnt, isOutputLayer));
}
}
get numberOfLayers() {
return this.layers.length;
}
inputTrainingData(data, truth) {
this.xValues = data;
this.xValues.forEach(function (x, i) {
if (x == 0) { x = 0.0001; };
})
this.truth = truth;
var $this = this;
this.layers.forEach(function (layer, index) {
layer.input($this.xValues);
$this.xValues = layer.output();
});
}
backPropogate() {
var $this = this;
var prev_layer = 0;
var t = this.getTruth();
for (var i = this.layers.length-1; i >= 0; i--) {
this.layers[i].backPropogate(t, $this.learning_rate, prev_layer);
prev_layer = this.layers[i];
}
}
getTruth() {
var truth = [];
var $this = this;
this.labels.forEach(function (label, index) {
if ($this.truth == label) {
truth.push(1);
} else {
truth.push(0);
}
})
return truth;
}
}
class layer {
neurons = [];
inputs = [];
outputs = [];
final_deriv = [];
hidden_deriv = [];
beta = 0; //Math.random();
isOutputLayer = false;
constructor(neuronCnt, prevLayerNeuronCnt, _isOutputLayer) {
this.isOutputLayer = _isOutputLayer;
var i;
for (i = 0; i < neuronCnt; i++) {
var newNeuron = new neuron(neuronCnt, prevLayerNeuronCnt);
this.neurons.push(newNeuron);
}
}
input(xValues) {
this.inputs = xValues;
var $this = this;
this.neurons.forEach(function (neuron, index) {
neuron.input(xValues, $this.beta);
});
}
output() {
this.outputs = [];
var $this = this;
this.neurons.forEach(function (neuron, index) {
$this.outputs.push(neuron.output($this.isOutputLayer));
})
return this.outputs;
}
backPropogate(truth, learning_rate, prev_layer) {
var $this = this;
this.neurons.forEach(function (neuron, index) {
neuron.backPropogateWeights(truth[index], learning_rate, prev_layer, index);
//neuron.backPropogateBeta(truth[index], prev_layer);
});
}
}
class neuron {
xValues = 0;
b = 0;
weights = [];
h = 0;
neuron_deriv = 1;
weights_deriv = [];
constructor(neuronCnt, prevLayerNeuronCnt) {
var i;
var xavier = 1 / prevLayerNeuronCnt;
for (i = 0; i < prevLayerNeuronCnt; i++) {
var rnd = (Math.random() - 0.5) * xavier;
//rnd = ((Math.random() * 2) - 1) * xavier;
this.weights.push(rnd);
}
}
input(xValues, beta) {
this.xValues = xValues;
this.b = beta;
}
output(isOutputLayer) {
var func = 0;
var $this = this;
this.xValues.forEach(function (x, index) {
func += x * $this.weights[index];
});
func += this.b;
var sigmoid = 1 / (1 + Math.exp(-func));
$this.h = sigmoid;
$this.neuron_deriv = (sigmoid * (1 - sigmoid));
return this.h;
}
backPropogateWeights(truth, learning_rate, prev_layer, cnt) {
var $this = this;
var prevSumError = 1;
if (prev_layer == 0) {
//var SqError = Math.pow((this.h - truth), 2)/2;
var dEdH = (this.h - truth);
this.neuron_deriv = this.neuron_deriv * dEdH;
} else {
prevSumError = 0;
prev_layer.neurons.forEach(function (neuron, index) {
prevSumError += neuron.weights_deriv[cnt];
});
}
this.weights.forEach(function (weight, index) {
$this.weights_deriv.push($this.neuron_deriv * weight * prevSumError);
});
for (var i = 0; i < this.weights.length; i++) {
var slope = $this.neuron_deriv * $this.xValues[i] * prevSumError;
var weightAdjust = learning_rate * slope;
$this.weights[i] = $this.weights[i] - weightAdjust;
}
}
}
This took a very long time to debug. It was the weights. I was using an xavier function to randomize all the weights, but when I opened up the weight range between -2.5 to 2.5, everything just started worked.
Here was my debug process if it helps anyone else:
- Repeated all my code calculations by hand to ensure code was running correctly
- Reduced entire neural network to bare minimal number of layers and neurons.
- Trained on a single image, then two, then three.
- First breakthrough was when I trained on just two images and eliminated ALL hidden layer....it actually learned!
- From here I slowly added in a layer and a few more images.
- It's at this point I started to expand my weight range....and I could see it was starting to learn.
- Finally I blew out my weight range, went back to my original setup, and boom! worked!
It's still not learning as fast I it should, based on the same implementation by Andrej Kaparthy in his ConvNetJS....but at least I'm now in a position to start tweaking.

How target a movieClip in animate cc in this drag drop code

is there a way to modify this code for animate cc to make object in the stage and interact with it ?
it is a bit of pain to make drag and drop in createjs for animate cc
there is nothing in the web that describe how to do it for animate cc or flash cc even the documentation has nothing to tell about drag and drop in the canvas
//Stage
var stage = new createjs.Stage("demoCanvas");
//VARIABLES
//Drag Object Size
dragRadius = 40;
//Destination Size
destHeight = 100;
destWidth = 100;
//Circle Creation
var label = new createjs.Text("DRAG ME", "14px Lato", "#fff");
label.textAlign="center";
label.y -= 7;
var circle = new createjs.Shape();
circle.graphics.setStrokeStyle(2).beginStroke("black")
.beginFill("red").drawCircle(0,0, dragRadius);
//Drag Object Creation
//Placed inside a container to hold both label and shape
var dragger = new createjs.Container();
dragger.x = dragger.y = 100;
dragger.addChild(circle, label);
dragger.setBounds(100, 100, dragRadius*2, dragRadius*2);
//DragRadius * 2 because 2*r = width of the bounding box
var label2 = new createjs.Text("HERE", "bold 14px Lato", "#000");
label2.textAlign = "center";
label2.x += 50;
label2.y += 40;
var box = new createjs.Shape();
box.graphics.setStrokeStyle(2).beginStroke("black").rect(0, 0, destHeight, destWidth);
var destination = new createjs.Container();
destination.x = 350;
destination.y = 50;
destination.setBounds(350, 50, destHeight, destWidth);
destination.addChild(label2, box);
//DRAG FUNCTIONALITY =====================
dragger.on("pressmove", function(evt){
evt.currentTarget.x = evt.stageX;
evt.currentTarget.y = evt.stageY;
stage.update(); //much smoother because it refreshes the screen every pixel movement instead of the FPS set on the Ticker
if(intersect(evt.currentTarget, destination)){
evt.currentTarget.alpha=0.2;
box.graphics.clear();
box.graphics.setStrokeStyle(3)
.beginStroke("#0066A4")
.rect(0, 0, destHeight, destWidth);
}else{
evt.currentTarget.alpha=1;
box.graphics.clear(); box.graphics.setStrokeStyle(2).beginStroke("black").rect(0, 0, destHeight, destWidth);
}
});
//Mouse UP and SNAP====================
dragger.on("pressup", function(evt) {
if(intersect(evt.currentTarget, destination)){
dragger.x = destination.x + destWidth/2;
dragger.y = destination.y + destHeight/2;
dragger.alpha = 1;
box.graphics.clear();
box.graphics.setStrokeStyle(2).beginStroke("black").rect(0, 0, destHeight, destWidth);
stage.update(evt);
}
});
//Tests if two objects are intersecting
//Sees if obj1 passes through the first and last line of its
//bounding box in the x and y sectors
//Utilizes globalToLocal to get the x and y of obj1 in relation
//to obj2
//PRE: Must have bounds set for each object
//Post: Returns true or false
function intersect(obj1, obj2){
var objBounds1 = obj1.getBounds().clone();
var objBounds2 = obj2.getBounds().clone();
var pt = obj1.globalToLocal(objBounds2.x, objBounds2.y);
var h1 = -(objBounds1.height / 2 + objBounds2.height);
var h2 = objBounds2.width / 2;
var w1 = -(objBounds1.width / 2 + objBounds2.width);
var w2 = objBounds2.width / 2;
if(pt.x > w2 || pt.x < w1) return false;
if(pt.y > h2 || pt.y < h1) return false;
return true;
}
//Adds the object into stage
stage.addChild(destination, dragger);
stage.mouseMoveOutside = true;
stage.update();
thanks
I am not exactly sure what you are asking. The demo you showed works fine (looks like it came from this codepen), and it is not clear what you are trying to add. This demo was made directly in code, not with Animate CC - which is really good for building assets, animations, and display list structure, but you should write application code around what gets exported.
There are plenty of documentation and examples online for Drag and Drop, in the EaselJS GitHub, and EaselJS docs:
DragAndDrop demo in GitHub
Live demo on EaselJS demos page
Documentation on pressMove
Tutorial on Mouse Events which includes Drag and Drop
I recommend narrowing down what you are trying to do, show what code or approaches you have tried so far, and posting specific questions here.
Lastly, here is the first part of an ongoing series for working with Animate CC: http://blog.gskinner.com/archives/2015/04/introduction-to-the-flash-cc-html5-canvas-document.html
Cheers.

Using Bing Maps Quadkeys as Openlayers 3 Tile source

I have a number of tile sources which utilise Bing Maps' Quadkey system in an old Silverlight application and I would like to use them in a new Openlayers 3 map.
I have found several examples of functions which will convert these sources for Leaflet.js but the syntax is somewhat different for OL3 and reading through the API docs indicates that there is an ol.Tile.coord class but if I understand correctly this is an experimental feature and might require a custom build from the source code.
There is reference to such functionality on the GitHub pages but I don't know if I have to compile a build with this source:
https://github.com/openlayers/ol3/blob/5c5364bbb7e8df76f18242ad665c87ca08a76e76/src/ol/source/bingmapssource.js
Can anyone provide an example of this type of conversion or indeed does anyone know if the latest (3.8.2) version of OL3 supports the quadkey method?
This is the leaflet example:
var BingLayer = L.TileLayer.extend({
getTileUrl: function (tilePoint) {
this._adjustTilePoint(tilePoint);
return L.Util.template(this._url, {
s: this._getSubdomain(tilePoint),
q: this._quadKey(tilePoint.x, tilePoint.y, this._getZoomForUrl())
});
},
_quadKey: function (x, y, z) {
var quadKey = [];
for (var i = z; i > 0; i--) {
var digit = '0';
var mask = 1 << (i - 1);
if ((x & mask) != 0) {
digit++;
}
if ((y & mask) != 0) {
digit++;
digit++;
}
quadKey.push(digit);
}
return quadKey.join('');
}
});
And this is the exisiting Silverlight code:
public override Uri GetUri(int x, int y, int zoomLevel, bool getPrintLink)
{
Uri uri = null;
if (this.Covers(x, y, zoomLevel))
{
QuadKey qk = new QuadKey(x, y, zoomLevel);
if (getPrintLink)
{
uri = new Uri(this.CurrentHostURL + "/tiles/NL/" + zoomLevel.ToString() + "/" + qk.Key + ".ipic", UriKind.RelativeOrAbsolute);
}
else
{
uri = new Uri("http://tileserver.satmap.com/NL/" + zoomLevel.ToString() + "/" + qk.Key + ".ipic", UriKind.RelativeOrAbsolute);
}
}
return uri;
}
Any insight would be appreciated as I've trawled many forums and countless pages of search results without finding a solution.
As far as I can see, your _quadKey function is correct. The issue that might arise is understanding the ol.TileCoord provided to the URL function.
In OpenLayers 3.7 and later, all TileCoords are calculated with the top left corner as their origin. Furthermore, both the X and Y coordinates increase naturally, so that the X and Y coordinates of a TileCoord corresponds to the normal concepts of a two-dimensional axis.
The top left tile of a given zoom level will always have X=0 and Y=-1. The tile below that will have X=0 and Y=-2; Y will always be negative.
Some mapping applications, such as Bing, also use the top left corner as the tile origin, but lets the Y coordinate increase downwards. The top left tile would the be X=0 and Y=0, while the tile below would be X=0 and Y=1.
In order to calculate the quadkeys, the Y coordinate has to be inverted and adjusted by one. This should work:
// this is unchanged from the question
var quadkey = function (x, y, z) {
var quadKey = [];
for (var i = z; i > 0; i--) {
var digit = '0';
var mask = 1 << (i - 1);
if ((x & mask) != 0) {
digit++;
}
if ((y & mask) != 0) {
digit++;
digit++;
}
quadKey.push(digit);
}
return quadKey.join('');
};
var quadKeyLayer = new ol.layer.Tile({
source: new ol.source.XYZ({
maxZoom: 19,
tileUrlFunction: function (tileCoord, pixelRatio, projection) {
var z = tileCoord[0];
var x = tileCoord[1];
var y = -tileCoord[2] - 1;
return "//example.com/r" + quadkey(x, y, z);
}
})
});

How can I determine the screen size using addon SDK?

How can I get the screen size using with addon SDK ?
var w = screen.width/2;
gives me an error : Message: ReferenceError: screen is not defined
You can use the window you have associated to your add-on; it's probably safer, because it will work even if the last visible window is closed but firefox is still opened (e.g. on OS X):
const { window: { screen }} = require("sdk/addon/window");
console.log(screen.width);
This will work:
var screen = require('sdk/window/utils').getMostRecentBrowserWindow().screen;
console.log(screen.width);
If you want multi monitor support I have a script but you have to understand it. It uses XPCOM, and it needs a range. This script only checks along the x axis, you should also check along the y axis.
So this is the script here that will detect all monitors in the x plane IF it falls in the y plane of 0-20 coordintates of primary screen, I don't recommend this method.
var sm = Cc['#mozilla.org/gfx/screenmanager;1'].getService(Ci.nsIScreenManager);
function getScreens() {
var screen = null;
var screens = [];
var screenManager = sm;
var min = 0;
var max = 0;
for (x = 0; x < 15000; x += 600) {
var s = screenManager.screenForRect(x, 20, 10, 10);
if (s != screen) {
screen = s;
var left = {},
top = {},
width = {},
height = {};
screenManager.primaryScreen.GetRect(left, top, width, height);
screens.push({
width: width.value,
height: height.value,
min: min,
max: min + width.value
});
min += width.value;
}
}
return screens;
}
var screens = getScreens();
console.log('screens:', screens);
This is the method I recommend
I needed to detect all monitor dimensons and had to resort to jsctypes, if you need that its here: https://github.com/Noitidart/NativeShot/blob/master/modules/workers/MainWorker.js#L853-L1523
That code is extremely long, thats because its getting all monitors and the taking screenshots of them. So you will want to extract just the monitors part. If you need help with it I can do it for you.

Eliminate sudden additions/deletions in D3 line chart transition

You can see this code in action here: http://bl.ocks.org/2626142
This code draws a line chart, then transitions between 3 sample data sets. When moving from a small data set to a larger one, the extra data points suddenly appear instead of smoothly unfolding from the existing line.
When moving from a larger data set to a smaller one, the line is suddenly truncated before transitioning to fill the whole chart.
With this code there are sudden additions and deletions to the line and gridlines. How do I eliminate those?
var data = [
[0,2,3,2,8],
[2,4,1,5,3],
];
var data2 = [
[0,1,2,3,4,5],
[9,8,7,6,5,6],
];
var data3 = [
[1,3,2],
[0,8,5],
];
var w = 300,
h = 100;
var chart = d3.select('body').append('div')
.attr('class', 'chart')
.append('svg:svg')
.attr('width', w)
.attr('height', h);
var color = d3.scale.category10();
function drawdata(data, chart) {
var num = data[0].length-1;
var x = d3.scale.linear().domain([0, num]).range([0,w]);
var y = d3.scale.linear().domain([0, 10]).range([h, 0]);
var line = d3.svg.line()
.x(function(d, i) { return x(i); })
.y(function(d) { return y(d); });
var flat = d3.svg.line()
.x(function(d, i) { return x(i); })
.y(y(-1));
var lines = chart.selectAll('.line')
.data(data);
lines.enter().append('path')
.attr('class', 'line')
.style('stroke', function(d,i) { return color(i); })
.attr('d', line);
lines.transition()
.ease('linear')
.duration(500)
.attr('d', line);
lines.exit().remove();
// legend
var ticks = chart.selectAll('line')
.data(x.ticks(num));
ticks.enter().append('line')
.attr('x1', x)
.attr('x2', x)
.attr('y1', 0)
.attr('y2', h)
.attr('class', 'rule');
ticks.transition()
.ease('linear')
.duration(500)
.attr('x1', x)
.attr('x2', x)
.attr('y1', 0)
.attr('y2', h);
ticks.exit().remove();
}
var dats = [data, data2, data3];
function next() {
var it = dats.shift();
dats.push(it);
drawdata(it, chart);
}
setInterval(next, 2000);
next();
I faced a similar problem recently, and solved it using a custom interpolator for paths:
// Add path interpolator to d3
d3.interpolators.push(function(a, b) {
var isPath, isArea, interpolator, ac, bc, an, bn;
// Create a new array of a given length and fill it with the given value
function fill(value, length) {
return d3.range(length)
.map(function() {
return value;
});
}
// Extract an array of coordinates from the path string
function extractCoordinates(path) {
return path.substr(1, path.length - (isArea ? 2 : 1)).split('L');
}
// Create a path from an array of coordinates
function makePath(coordinates) {
return 'M' + coordinates.join('L') + (isArea ? 'Z' : '');
}
// Buffer the smaller path with coordinates at the same position
function bufferPath(p1, p2) {
var d = p2.length - p1.length;
// Paths created by d3.svg.area() wrap around such that the 'end'
// of the path is in the middle of the list of coordinates
if (isArea) {
return fill(p1[0], d/2).concat(p1, fill(p1[p1.length - 1], d/2));
} else {
return fill(p1[0], d).concat(p1);
}
}
// Regex for matching the 'd' attribute of SVG paths
isPath = /M-?\d*\.?\d*,-?\d*\.?\d*(L-?\d*\.?\d*,-?\d*\.?\d*)*Z?/;
if (isPath.test(a) && isPath.test(b)) {
// A path is considered an area if it closes itself, indicated by a trailing 'Z'
isArea = a[a.length - 1] === 'Z';
ac = extractCoordinates(a);
bc = extractCoordinates(b);
an = ac.length;
bn = bc.length;
// Buffer the ending path if it is smaller than the first
if (an > bn) {
bc = bufferPath(bc, ac);
}
// Or, buffer the starting path if the reverse is true
if (bn > an) {
ac = bufferPath(ac, bc);
}
// Create an interpolater with the buffered paths (if both paths are of the same length,
// the function will end up being the default string interpolator)
interpolator = d3.interpolateString(bn > an ? makePath(ac) : a, an > bn ? makePath(bc) : b);
// If the ending value changed, make sure the final interpolated value is correct
return bn > an ? interpolator : function(t) {
return t === 1 ? b : interpolator(t);
};
}
});
Here's what the original gist looks like with the new interpolator: http://bl.ocks.org/4535474.
Its approach is to 'buffer' the smaller dataset's path by inserting zero-length line segments at the beginning. The effect is that new segments expand out of a single point at the start of the line, and unused segments similarly collapse down to a single point.
Transitioning between datasets of different sizes (apparently) isn't a common problem, and doesn't have a universal solution. Because I was visualizing time-series data and transitioning between daily/weekly/monthly intervals, I needed the segments towards the end of the path to maintain visual continuity. I can imagine a case in which you'd want to do the same for the beginning of the path, or perhaps expand/contract the path by uniformly buffering segments throughout. Either way the same approach will work.