D3 Force Graph Neighboring Without Index (Including Self) - javascript

I am basing my D3 Force Model after this example: H is for Highlights. My JSON data is formatted differently since it uses the id as target/source instead of the default index given as a node property.
Sample of my JSON:
{
"nodes": [
{"fixed":true,"data": {"id": "foo","idType":"USERNAME","visible":true },"grabbable": true,"grabbed":false},
{"fixed":true,"data": {"id": "bar","idType":"USERNAME","visible":true },"grabbable": true}
],
"links": [
{classes":null,"data":{"color":"blue"","source":"foo","target":"bar","visible":true},"grabbable":false},
{classes":null,"data":{"color":"blue"","source":"bar","target":"foo","visible":true},"grabbable":false}
]
}
So as you can see I have both the target/source within the data and it uses the id as the type. Now here I am having trouble getting the highlight to work. I have it where it highlights its neighboring nodes and links BUT for some reason it doesn't keep the node itself highlighted. I have added arrows below to what I think needs modification but I am not sure what the issue is at all.
// sets the source and target to use id instead of index
var edges = [];
root.links.forEach(function(e) {
var sourceNode = root.nodes.filter(function(n) {
return n.data.id === e.data.source;
})[0],
targetNode = root.nodes.filter(function(n) {
return n.data.id === e.data.target;
})[0];
edges.push({
source: sourceNode,
target: targetNode
});
});
// Create an array logging what is connected to what
var linkedByIndex = { };
// array algorithm for what is connected to what
for (var i = 0; i < root.nodes.length; i++) {
linkedByIndex[i + "," + i] = 1;
};
root.links.forEach(function (d) {
linkedByIndex[d.data.source + "," + d.data.target] = 1;
});
// This function looks up whether a pair are neighbors
function neighboring(a, b) {
return linkedByIndex[a.data.id + "," + b.data.id]; <<<<<<<<<<<<<
}
function connectedNodes() {
//Reduce the opacity of all but the neighboring nodes
d = d3.select(this).node().__data__;
node.style("opacity", function (o) {
return neighboring(d, o) | neighboring(o, d) ? 1 : 0.1;
});
link.style("opacity", function (o) {
return d.index==o.source.index| d.index==o.target.index ? 1 : 0.1;
});
}
I have played around with it and I am not sure what is wrong with my algorithm.

Figured it out. Add the following line which modifies the node selected to the end of the connected node function
Code added
return d3.select(this).style("opacity",1);
So now it looks like...
function connectedNodes() {
// Reduce the opacity of all but the neighbouring nodes
// Ternary operator. condition | condition ? value if true : value if false
// the | means or
d = d3.select(this).node().__data__;
node.style("opacity", function (o) {
return neighboring(d, o) | neighboring(o, d) ? 1 : 0.1;
});
link.style("opacity", function (o) {
return d.index==o.source.index | d.index==o.target.index ? 1 : 0.1;
});
// Maintains opacity of selected node.
return d3.select(this).style("opacity",1);
}

Related

Sum of all nodes in a binary tree giving incorrect output in JavaScript

I am trying to solve the following problem.
Problem statement: Given a binary tree, return the sum of the depth of all the nodes. For example, this is a binary tree.
1
/ \
2 3
/ \ / \
4 5 6 7
/ \
8 9
The depth of the node with value 2 is 1.
Similarly, the Depth of node with value 4 is 2 etc...
The total depth of all nodes is 16.
I have written the following recursive solution which is giving me the incorrect output.
My pseudocode is as follows :
If the current node has no left and right child, then return;
Check if the current node has any left child. If it has, then:
Add 1 to depthSoFar variable
Add the depthSoFar value to totalDepth variable
Then, recursively call the function with the left child as the current node
If the current node has the right child, then:
Add 1 to depthSoFar variable
Add the depthSoFar value to totalDepth variable
Then, recursively call the function with the right child as the current node
The code is as follows;
var nodeDepths = function(root) {
var totalDepth = 0;
function depth(root, depthSoFar) {
if (root.left === null && root.right === null) {
return;
}
// checking if it has left child
if(root.left) {
depthSoFar = depthSoFar + 1;
// Add it to total
totalDepth = totalDepth + depthSoFar;
depth(root.left, depthSoFar);
}
if(root.right) {
depthSoFar = depthSoFar + 1;
totalDepth = totalDepth + depthSoFar;
depth(root.right, depthSoFar);
}
}
depth(root, 0);
return totalDepth;
};
My output is 22 but the correct output is 16.
var nodeDepths = function(root) {
var totalDepth = 0;
function depth(root, depthSoFar) {
if (root.left === null && root.right === null) {
return;
}
// checking if it has left child
if(root.left) {
depthSoFar = depthSoFar + 1;
// Add it to total
totalDepth = totalDepth + depthSoFar;
depth(root.left, depthSoFar);
}
if(root.right) {
depthSoFar = depthSoFar + 1;
totalDepth = totalDepth + depthSoFar;
depth(root.right, depthSoFar);
}
}
depth(root, 0);
return totalDepth;
};
class BinaryTree {
constructor(value) {
this.value = value;
this.left = null;
this.right = null;
}
}
var root = new BinaryTree(1);
root.left = new BinaryTree(2);
root.right = new BinaryTree(3);
root.left.left = new BinaryTree(4);
root.left.right = new BinaryTree(5);
root.right.left = new BinaryTree(6);
root.right.right = new BinaryTree(7);
root.left.left.left = new BinaryTree(8);
root.left.left.right = new BinaryTree(9);
console.log(nodeDepths(root))
I am not understanding where I am getting wrong. Any help in understanding the problem, would be appreciated. Thanks in advance.
sumDepths has a simple inductive definition
if the input tree, t, is empty, return the empty sum, zero.
(inductive) the tree has at least one node. return the current depth, d, plus the sum of the recursive sub-problems, sumDepths(t.left, d + 1) plus sumDepths(t.right, d + 1)
function sumDepths (t, d = 0) {
if (t == null)
return 0 // #1
else
return d + sumDepths(t.left, d + 1) + sumDepths(t.right, d + 1) // #2
}
function node (value, left, right) {
return { value, left, right }
}
const tree =
node
( 1
, node
( 2
, node(4, node(8), node(9))
, node(5)
)
, node(3, node(6), node(7))
)
console.log(sumDepths(tree)) // 16
Another way to think about the problem is write a depth traverse generator and sum each depth -
function* traverse (t, d = 0) {
if (t == null) return
yield [d, t.value]
yield *traverse(t.left, d + 1)
yield *traverse(t.right, d + 1)
}
function node (value, left, right) {
return { value, left, right }
}
const tree =
node
( 1
, node
( 2
, node(4, node(8), node(9))
, node(5)
)
, node(3, node(6), node(7))
)
const sum =
Array
.from(traverse(tree), ([ depth, value ]) => depth)
.reduce((r, d) => r + d, 0)
console.log(sum) // 16

How to insert a property into the leaf nodes of a nested array?

This is my data : https://api.myjson.com/bins/pmzf0
I retrieve all the leaf nodes of that data by using this function :
function getLeafNodes(nodes, result = []) {
for (var i = 0, length = nodes.length; i < length; i++) {
if (!nodes[i].children) {
result.push(nodes[i]);
} else {
result = getLeafNodes(nodes[i].children, result);
}
}
return result;
}
This is the returned result :https://api.myjson.com/bins/12qiq4
Then I calculated the percentage info of each element by getting its max value then multiplying it by 1.2 to obtain my target Value.
var maxVal = Math.max.apply(Math, leafs.map(function (o) { return o.value; }))
var targetVal = maxVal * 1.2;
Then I obtain my array of percentages for each element by using the following code :
var percentageData = leafs.map(function (o) {
return { percentage: (o.value / targetVal * 100).toFixed(2) };
})
This returns an array of percentage data : https://api.myjson.com/bins/mnte4
Now how do I insert this back into my original data ?
Or is my approach wrong and I can straight away calculate the percentage info for each element in my original data without all this steps.
Instead of saving the percentage values in a separate array like,
var percentageData = leafs.map(function (o) {
return { percentage: (o.value / targetVal * 100).toFixed(2) };
})
You can add a new percentage field to leafs using foreach like,
leafs.forEach(function (o) {
o['percentage'] = (o.value / targetVal * 100).toFixed(2);
})

javascript: Fast way to find connected components in array (looking for speed up / alternative way)

I have an matrix (1d array, x&y-axe) like this:
var matrix=[
'00000000000000000000000000000000000000000000000000000000000000000000111111111',
'11111100000000000000000000001000000000000000000000000000000000000000000001111',
'00000000000000000000000000000000001000000000000000000000000000000000000000011',
'11111000000000000000000000000000000000000001111110000000000000000000000000001',
'11111000000000000111000000000000000000111111000000000000000000000000000000000',
'00000000000111111111111100000000000000111111000000000000000000000000000000000',
'00000000000000000110000000000000000000111111000000000000000000010000000000000',
'00000000000000000000000000000001111111000000000000000000000000000000000000000',
'00000100000000000000000000000000000000000000000000000000000000000010000000000',
'00000010010000000000000000000000000000111111110000000000000000000000000000000',
'00000000000000000000000000000000000000000000000000000000000000000000000000000',
'00000000000000000000000000000000000000000000000000000000000000100000000000000',
'00000000000000000000000000000000000000000000000000000100000000000000000000000'
]
and I'D like to find all connected groups of '1'-characters and extract all of their positions to get something like this as result:
[{"0": [[68,0],[69,0],[70,0],[71,0],[72,0],[73,0],[73,1],[74,1],[75,1],[75,2],[76,2],[76,3],[76,1],[76,0],[75,0],[74,0]],"1": [[0,1],[1,1],[2,1],[3,1],[4,1],[5,1]],"2": [[28,1]],"3": [[34,2]],"4": [[0,3],[0,4],[1,4],[2,4],[3,4],[4,4],[4,3],[3,3],[2,3],[1,3]],"5": [[43,3],[43,4],[43,5],[43,6],[42,6],[42,5],[42,4],[41,4],[41,5],[41,6],[40,6],[40,5],[40,4],[39,4],[39,5],[39,6],[38,6],[38,5],[38,4],[44,3],[45,3],[46,3],[47,3],[48,3]],"6": [[17,4],[17,5],[17,6],[18,6],[18,5],[19,5],[20,5],[21,5],[22,5],[23,5],[19,4],[18,4],[16,5],[15,5],[14,5],[13,5],[12,5],[11,5]],"7": [[63,6]],"8": [[31,7],[32,7],[33,7],[34,7],[35,7],[36,7],[37,7]],"9": [[5,8]],"10": [[66,8]],"11": [[6,9]],"12": [[9,9]],"13": [[38,9],[39,9],[40,9],[41,9],[42,9],[43,9],[44,9],[45,9]],"14": [[62,11]],"15": [[53,12]]}]
I have already developed some kind of a flood fill algorithm that is working quiet well with the matrix above.
But there must be a way more efficient & fast way to find connected components in a bigger matrix (e.g 10 or even 100 times bigger).
--> My idea was maybe this result could be also achieved with some kind of regex expression combined with javascript code, but I'm absolutely not sure how to code this, so I hope somebody has an good idea to fast up my little algorithm below so that I can avoid Overflow Errors :
var matrix=[
'00000000000000000000000000000000000000000000000000000000000000000000111111111',
'11111100000000000000000000001000000000000000000000000000000000000000000001111',
'00000000000000000000000000000000001000000000000000000000000000000000000000011',
'11111000000000000000000000000000000000000001111110000000000000000000000000001',
'11111000000000000111000000000000000000111111000000000000000000000000000000000',
'00000000000111111111111100000000000000111111000000000000000000000000000000000',
'00000000000000000110000000000000000000111111000000000000000000010000000000000',
'00000000000000000000000000000001111111000000000000000000000000000000000000000',
'00000100000000000000000000000000000000000000000000000000000000000010000000000',
'00000010010000000000000000000000000000111111110000000000000000000000000000000',
'00000000000000000000000000000000000000000000000000000000000000000000000000000',
'00000000000000000000000000000000000000000000000000000000000000100000000000000',
'00000000000000000000000000000000000000000000000000000100000000000000000000000'
]
Array.prototype.extract_components_positions = function(offset) {
var array = this.map(item => item.split('')).map(str => Array.from(str, Number)),
default_value = 0,
result_object = {}
function test_connection(array, i, j) {
if (array[i] && array[i][j] === -1) {
if (!result_object[default_value]) result_object[default_value] = [];
result_object[default_value].push([j, i]);
array[i][j] = 1;
for (var k = offset; k > 0; k--) {
test_connection(array, i + k, j); // left - right
test_connection(array, i, j + k); // top - bottom
test_connection(array, i - k, j); // right - left
test_connection(array, i, j - k); // bottom - top
}
return true
}
}
array.forEach(function(a) {
a.forEach(function(b, i, bb) {
bb[i] = -b
})
});
array.forEach(function(a, i, aa) {
a.forEach(function(b, j, bb) {
test_connection(aa, i, j) && default_value++
})
})
return [result_object];
}
var result = matrix.extract_components_positions(1);
console.log((result))
.as-console-wrapper { max-height: 100% !important; top: 0; }
Edit 1.0 BTW It would be also not that bad if the algorithm is just able to connect bigger components (e.g minimum group of 5 connected characters)
Code
This is the method I figured out. I'm sure it can be optimized, but I feel it's at least a big step in the right direction.
I've also taken information from the following answers and applied it.
Finding all indexes of a specified character within a string
Check whether an array exists in an array of arrays
var matrix=[
'00000000000000000000000000000000000000000000000000000000000000000000111111111',
'11111100000000000000000000001000000000000000000000000000000000000000000001111',
'00000000000000000000000000000000001000000000000000000000000000000000000000011',
'11111000000000000000000000000000000000000001111110000000000000000000000000001',
'11111000000000000111000000000000000000111111000000000000000000000000000000000',
'00000000000111111111111100000000000000111111000000000000000000000000000000000',
'00000000000000000110000000000000000000111111000000000000000000010000000000000',
'00000000000000000000000000000001111111000000000000000000000000000000000000000',
'00000100000000000000000000000000000000000000000000000000000000000010000000000',
'00000010010000000000000000000000000000111111110000000000000000000000000000000',
'00000000000000000000000000000000000000000000000000000000000000000000000000000',
'00000000000000000000000000000000000000000000000000000000000000100000000000000',
'00000000000000000000000000000000000000000000000000000100000000000000000000000'
]
var results = [];
function charPos(str, char) {
return str
.split("")
.map(function (c, i) { if (c == char) return i; })
.filter(function (v) { return v >= 0; });
}
matrix.forEach(function(s, i) {
var p = charPos(s, '1');
results[i] = [];
matrix.forEach(function(s2, i2) {
var p2;
if((p2 = charPos(s2, '1').filter((n) => p.includes(n))).length) {
p2.forEach(function(v) {
if(JSON.stringify(results).indexOf(JSON.stringify([v, i2])) === -1)
results[i].push([v, i2]);
});
}
});
});
console.log(results.filter(v => v.length > 0));

How to create a stacked bar chart using dc.js?

I'd like to create a stacked bar chart using DC.JS.
I've tried to utilize the documentation from DC.JS (graph,source code) to no avail - Below is my attempt (here is my attempt in jsfiddle) and my most recent attempt in CodePen.
I'd like the 'Name' as the X axis and 'Type' as the stacks.
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
<script src="https://rawgithub.com/NickQiZhu/dc.js/master/web/js/crossfilter.js"></script>
<script src="https://cdnjs.site44.com/dc2.js"></script>
<div id="chart"></div>
Javascript
var data = [ {"Name":"Abby","Type":"Apple"}, {"Name":"Abby","Type":"Banana"}, {"Name":"Bob","Type":"Apple"} ]
data.forEach(function(x) {
x.Speed = +x.Type;
});
var ndx = crossfilter(data)
var xdim = ndx.dimension(function (d) {return d.Name;});
function root_function(dim,stack_name) {
return dim.group().reduce(
function(p, v) {
p[v[stack_name]] = (p[v[stack_name]] || 0) + v.Speed;
return p;},
function(p, v) {
p[v[stack_name]] = (p[v[stack_name]] || 0) - v.Speed;
return p;},
function() {
return {};
});}
var ydim = root_function(xdim,'Type')
function sel_stack(i) {
return function(d) {
return d.value[i];
};}
var chart = dc.barChart("#chart");
chart
.x(d3.scale.ordinal().domain(xdim))
.dimension(xdim)
.group(ydim, "1", sel_stack('1'))
.xUnits(dc.units.ordinal);
for(var i = 2; i<6; ++i)
chart.stack(ydim, ''+i, sel_stack(i));
chart.render();
I've been fiddling with this for some time and I have some additional findings:
When I replace the data array with the following it works
data = [ {"Name":"Abby","Type":"1"}, {"Name":"Abby","Type":"2"}, {"Name":"Bob","Type":"1"} ]
But it only works when I swapped out dc 1.7.5 (https://cdnjs.cloudflare.com/ajax/libs/dc/1.7.5/dc.min.js) with dc 2.1.0-dev (https://github.com/dc-js/dc.js/blob/develop/web/js/dc.js)
However when I replace the data array with the following it doesn't work:
data = [ {"Name":"Abby","Type":"3"}, {"Name":"Abby","Type":"4"}, {"Name":"Bob","Type":"2"} ]
I believe the root issue lies in the root_function.
v.Speed is always NaN in your current example. Because +x.Type attempts to convert a string like "Apple" into a number and fails. If you just want to count, then add or subtract 1 in your reducer, rather than v.Speed. Then you need to update your sel_stack code and chart code to handle this change, of course.
Here's a working example for the 2 types in your data. You'll have to update it to handle arbitrary numbers of types, probably by building an array of all your types up front and then looping through it to add stacks to the chart: http://codepen.io/anon/pen/GjjyOv?editors=1010
var data = [ {"Name":"Abby","Type":"Apple"}, {"Name":"Abby","Type":"Banana"}, {"Name":"Bob","Type":"Apple"} ]
var ndx = crossfilter(data)
var xdim = ndx.dimension(function (d) {return d.Name;});
In the reducer, just add and subtract 1 to count:
var ydim = xdim.group().reduce(
function(p, v) {
p[v.Type] = (p[v.Type] || 0) + 1;
return p;},
function(p, v) {
p[v.Type] = (p[v.Type] || 0) - 1;
return p;},
function() {
return {};
});
sel_stack no longer takes a number, but a key:
function sel_stack(valueKey) {
return function(d) {
return d.value[valueKey];
};}
var chart = dc.barChart("#chart");
Here we hard-code the stack key, for the purpose of the example:
chart
.x(d3.scale.ordinal().domain(xdim))
.dimension(xdim)
.group(ydim, "Apple", sel_stack('Apple'))
.xUnits(dc.units.ordinal);
Again, the other hard-coded stack key. You'll need to recreate the loop after creating some sort of data structure that holds all of your stack values.
//for(var i = 2; i<6; ++i)
chart.stack(ydim, 'Banana', sel_stack('Banana'));
chart.render();

Using Crossfilter, is it possible to track max/min when grouping?

When using Crossfilter (https://github.com/square/crossfilter), I specify functions to use when adding and removing data from a group. It's fairly trivial to keep track of a running average (using CoffeeScript):
reduceAdd = (p, v) ->
++p.count;
p.sum += v.digit;
p
reduceRemove = (p, v) ->
--p.count;
p.sum -= v.digit;
p
reduceInitial = ->
{
count: 0
sum: 0
average: ->
return 0 if this.count == 0
return this.sum / this.count
}
Is it possible to keep track of the max and min of each group? I can't figure out a way short of keeping all elements in a huge array and doing a d3.min / d3.max. It seems that adding/removing data would be extremely inefficient.
I also looked for a way to tell Crossfilter to completely rebuild the group from scratch, rather than removing items from an existing group. If a filter is applied, the group is reset and rebuilt. Nothing obvious.
You can use dimension.top(1) and dimension.bottom(1) to retrieve the current min and max. These methods respect any filters that may be active on the crossfilter.
The best solution I came up with, was to keep track of all values in an ordered list and to add elements with a simple quicksort-style insertion function (cp. how to insert a number into a sorted array) and to remove them using indexOf.
Common functions:
function insertElement(element, array) {
array.splice(locationOfElement(element, array) + 1, 0, element);
return array;
}
function removeElement(element, array) {
var index = array.indexOf(element);
if (index >= 0) array.splice(index, 1);
return array;
}
function locationOfElement(element, array, start, end) {
start = start || 0;
end = end || array.length;
var pivot = parseInt(start + (end - start) / 2, 10);
if (array[pivot] === element) return pivot;
if (end - start <= 1)
return array[pivot] > element ? pivot - 1 : pivot;
if (array[pivot] < element) {
return locationOfElement(element, array, pivot, end);
} else {
return locationOfElement(element, array, start, pivot);
}
}
function maxElement(array) {
return (array.length > 0) ?
array[array.length - 1] : null;
}
function minElement(array) {
return (array.length > 0) ?
array[0] : null;
}
Functions to use when adding and removing data from a group to track min / max:
minMaxDimension = cf.dimension(function (d) {
return d.key;
});
var reduceAdd = function(p, v) {
insertElement(v.value, p.elements);
return p;
};
var reduceRemove = function(p, v) {
removeElement(v.value, p.elements);
return p;
};
var reduceInitial = function() {
return {
elements: [],
max: function() { return maxElement(elements); },
min: function() { return minElement(elements); }
}
}
minMaxGroup = minMaxDimension
.group()
.reduce(reduceAdd, reduceRemove, reduceInitial)
.orderNatural()
.top(Infinity);
After playing around with this for a bit, you can rebuild the group by just calling the group method again.

Categories

Resources