While following numerous D3 examples, data usually gets formatted in the format given in flare.json:
{
"name": "flare",
"children": [
{
"name": "analytics",
"children": [
{
"name": "cluster",
"children": [
{"name": "AgglomerativeCluster", "size": 3938},
:
I have an adjacency list as follows:
A1 A2
A2 A3
A2 A4
which I want to convert to the above format. Currently, I am doing this on the server-side but is there a way to achieve this using d3's functions? I found one here, but the approach seems to require modification of the d3 core library which I am not in favor due to maintainability. Any suggestions?
There's no prescribed format, as you can usually redefine your data through various accessor functions (such as hierarchy.children) and array.map. But the format you quoted is probably the most convenient representation for trees because it works with the default accessors.
The first question is whether you intend to display a graph or a tree. For graphs, the data structure is defined in terms of nodes and links. For trees, the input to the layout is the root node, which may have an array of child nodes, and whose leaf nodes have an associated value.
If you want to display a graph, and all you have is a list of edges, then you'll want to iterate over the edges in order to produce an array of nodes and an array of links. Say you had a file called "graph.csv":
source,target
A1,A2
A2,A3
A2,A4
You could load this file using d3.csv and then produce an array of nodes and links:
d3.csv("graph.csv", function(links) {
var nodesByName = {};
// Create nodes for each unique source and target.
links.forEach(function(link) {
link.source = nodeByName(link.source);
link.target = nodeByName(link.target);
});
// Extract the array of nodes from the map by name.
var nodes = d3.values(nodeByName);
function nodeByName(name) {
return nodesByName[name] || (nodesByName[name] = {name: name});
}
});
You can then pass these nodes and links to the force layout to visualize the graph:
http://bl.ocks.org/2949937
If you want to produce a tree instead, then you'll need to do a slightly different form of data transformation to accumulate the child nodes for each parent.
d3.csv("graph.csv", function(links) {
var nodesByName = {};
// Create nodes for each unique source and target.
links.forEach(function(link) {
var parent = link.source = nodeByName(link.source),
child = link.target = nodeByName(link.target);
if (parent.children) parent.children.push(child);
else parent.children = [child];
});
// Extract the root node.
var root = links[0].source;
function nodeByName(name) {
return nodesByName[name] || (nodesByName[name] = {name: name});
}
});
Like so:
http://bl.ocks.org/2949981
D3 doesn't require a specific format. It all depends on your application. You can certainly convert an adjacency list to the format used in flare.json, but this again would be application-specific code. In general, you can't do that as adjacency lists as such don't have "head" or "root" elements you would need to build a tree. In addition, you would need to handle cycles, orphans etc. separately.
Given that you're currently doing the conversion on the server side, I'd be tempted to say that "if it ain't broken, don't fix it" ;)
Related
So I'm wanting to dynamically build a GraphLinksModel in GoJS using addNodeData(nodedata) and addLinkData(linkdata). My issue is that I don't know what the fields of nodedata and linkdata are, other than the fields that are in example code. I can build the Node object and Link object for each of my nodes and links, but addNodeData and addLinkData don't actually want those, they want "nodedata" and "linkdata". When I try to addNodeData(Node) then I get an error.
Here are the descriptions of addNodeData(nodedata) and addLinkData(linkdata). As you can see, it just defines the parameter as an Object, with no hints, other that in the examples about what's supposed to be in it. Any direction or explanation would be appreciated.
Node data is a JavaScript Object with minimum of the below formats. There can be additional property added to it based on type of object that you use.
{ "id": 1, "text": "TextToDisplay1" }
{ "id": 2, "text": "TextToDisplay2" }
The link data is again a JavaScript Object with the below format
{ "from": 1, "to": 2, "text": "Link above 1 and 2", "curviness": 20 }
'From' attribute of this object will have the From node's Id value and the same is teh case with 'to' attribute, it is 'to' nodes id.
There are no "all", the Node data can contain anything, arbitrarily, but must contain a key. If no key is defined, the GoJS model will automatically assign one. In other words, if you add { } as your node data and look at it, you'll find it is:
{ __gohashid: 1975, key: -5 }
__gohashid is internal to GoJS and should not be modified or used. They key is unique.
You can change the uniqueness function of the model, and you can even change the keyword for key.
You can read more in the Model overview in the API.
Other than there are a few built in data properties:
category, which specifies which Node or Link template to use, but the default is "" and its not necessary to specify.
In TreeModel there's also parent, which specifies the key of the parent Node.
Any other properties you find on the Model don't mean anything unless the Diagram's Node and Link templates have data-bindings to those names. For example this template has two data bindings:
// define a simple Node template
myDiagram.nodeTemplate =
$(go.Node, "Auto", // the Shape will go around the TextBlock
$(go.Shape, "RoundedRectangle", { strokeWidth: 0},
// Shape.fill is bound to Node.data.color
new go.Binding("fill", "color")),
$(go.TextBlock,
{ margin: 8 }, // some room around the text
// TextBlock.text is bound to Node.data.key
new go.Binding("text", "key"))
);
If color exists in the Node data, it will be used as the Shape.fill in this Node template's Shape.
If key exists (it has to) in the Node data, it will be used as the TextBlock.text.
You can see that in action here: http://codepen.io/simonsarris/pen/ORwoLA?editors=1010
Let me know if you think there ought to be additional clarifications and I'll update the answer.
I am currently working with the Treemap visualization of D3.js and was hoping to understand how the flare.json used in the example has been organized. Does the format of the json input file need to be in the exact same structure as used in the example. I have an input file from a web crawler with a list of URLs and their respective parent URLs. I tried using something like the following but it won't work and am not sure if it's just the structure that's different or something else.
listURLs.json:
{
"name": "flare",
"children": [
{"children":"http:\/\/a.wholelottanothing.org","name":"http:\/\/buzz.blogger.com"},
{"children":"http:\/\/www.bitworking.org","name":"http:\/\/buzz.blogger.com"},
{"children":"http:\/\/blog.computationalcomplexity.org","name":"http:\/\/buzz.blogger.com"},
{"children":"http:\/\/www.blogactionday.org","name":"http:\/\/buzz.blogger.com"},
{"children":"http:\/\/www.wikipaintings.org","name":"http:\/\/littlegreeniguana.blogspot.com"}
]
}
I know this looks very different from the flare.json used in the example but can this work? Also, the input that I am using doesn't include the 'size' parameter which is also probably why the output is blank. How do I use the size here? Can it be dynamically adjusted later in the code? Any help will be most appreciated, I am a D3 novie!
The hierarchical data format expected by tree, pack and other D3 hierarchical layouts is expecting "children" to be an array of objects, and traverses that hierarchical data in preparation for formatting your objects for display using layouts. So, you don't want to use "children" to store a single link, instead, you want it to store an array of objects formatted just like the parent object (even if there is only one thing in that array). It's a bit hard to grasp what you're trying to display in your dataset, but my guess is all those websites are under buzz.blogger.com, except the last one, in which case properly formatted hierarchical data would look like this (Note that everything is nested in a root node, which you can name whatever you want):
{
"name": "root node",
"children": [
{"name":"http:\/\/buzz.blogger.com", "children": [
{"name": "http:\/\/www.bitworking.org"},
{"name": "http:\/\/blog.computationalcomplexity.org"},
{"name": "http:\/\/www.blogactionday.org"}
]
},
{"name":"http:\/\/littlegreeniguana.blogspot.com", "children": [
{"name": "http:\/\/www.wikipaintings.org"}
]
}
]
}
I am developing a forced network based on http://bl.ocks.org/mbostock/4062045#index.html but using a JSON file of the following format:
{"organizations":[
{"member":
{"name":"Green 13",
"target":"TCAN",
"category":"community group",
"source":"G13"}
}, ....
As you can see the root object is "organizations" and the top level child object is "member". (The JSON file has to be in this format because I generate it from a Drupal module.)
I extract the member array using this code:
var links=members.organizations.map(function(members) {
return members.member;
});
The network works correctly but I am unable to use the category attribute to set the colour of the circles.
How do I access "category" to use in "fill" and "name" for "text"? (At present, "text" uses the "source" value.)
The problem is that you're not putting the category into the nodes you're generating. To do so, modify the code that generates the nodes like this:
links.forEach(function(link) {
link.source = nodes[link.source] || (nodes[link.source] = {name: link.name, category: link.category});
link.target = nodes[link.target] || (nodes[link.target] = {name: link.name, category: link.category});
});
This will allow you to color the nodes depending on the category and also adds the correct text. Complete example here.
The example doesn't quite work -- the names of the nodes and the source/target strings don't match up and that messes things up. For example, source "G13" doesn't exist (should that be "Green 13"?) and neither does "TRSG".
I have a large object, mixed with arrays of data (it's a treeview of folders and images - I have no control over what is outputted here.
For example:
var test = {
Folders: [{
Folders:[{
Folders:[{
Folders:[
{value:1},{value:2}
]
}]
}]
},{}
]
}
Value 1 (which in my case is an image) can be found here:
test.Folders[0].Folders[0].Folders[0].Folders[0].value
My end users are using a drop down to select their folder, I need to somehow pass the location via the drop down.
I've tried adding the "path" to the data-attr attribute of the drop down:
<option value="folder6" data-attr="[0].Folders[0].Folders[0].Folders[0]">Folder6</option>
However attempting to use that like this:
var myLocation = $('#element').find('option:selected').attr('data-attr');
//myLocation now is a string "Folders[0].Folders[0].Folders[0].Folders[0].value"
console.log(test[myLocation]
Doesn't work (it's down to the Arrays and numbers as passing a string as an object location in this fashion normally works).
I'm quietly confident I'm going about this the wrong way fullstop. I'm open to ideas on how better to do this in general, or how to get this horrible fudge to work.
There are many ways to solve this issue and it mostly depends on your needs and global architecture.
#1 The Evil Way (using eval)
var path = '[0].Folders[0].Folders[0].Folders[0]';
eval('test.Folders' + path); //Object {value: 1}
#2 Use an object as a map to index every folder
var foldersMap = {};
//loop over your tree and build the index
foldersMap[path] = folder;
//then retrieve it later
foldersMap[path];
#3 Store the object on the option directly
//while building the option
optionEl.folder = folder;
//then later retrieve it from the selected option
yourSelect.options[yourSelect.selectedIndex].folder;
#4 Create your own keypath function that can traverse an object structure based on a string keypath rather than using eval. I will provide an implementation as soon as I have more time.
There are probably many other ways, but these are just ideas.
Conceptually a tree can be n-levels deep. Baking in the path like that may not be a good ideas unless you know how "deep" the structure will be ahead of time.
It looks like you might be trying to show each potential folder in a drop down list (or maybe just the ones with images).
To do this, I would create a function to recursively loop through your json structure. The result of this function would either directly build the drop down menu or flatten the values into an array which could then be bound to the drop down menu. I would use a front-end templating library like handlebar.js, but you could also manipulate a dropdown control via JQuery.
Try storing it as a JSON array in data-attr. jQuery will pull it out as an array. Then you just need to find a way to loop it. Here's my first crack at it (with no validation and exception handling)...
HTML:
<select>
<option data-attr="[0,0,0,0]">Foo</option>
</select>
JavaScript:
var test = {
Folders: [{
Folders: [{
Folders: [{
Folders: [{
value: 1
}, {
value: 2
}]
}]
}]
}, {}]
};
var folderIndexes = $("option").data("attr"); // an array
alert(getValue(test, folderIndexes));
function getValue(folders, folderIndexes) {
for (var i = 0; i < folderIndexes.length; i++) {
folders = folders.Folders[folderIndexes[i]];
}
return folders.value;
}
Fiddle... http://jsfiddle.net/atn22/
I have a string that I need to parse into a graph (DAG) data structure using javascript. Included in the data structure are a few attributes I should store, such as the node's id, name, and a label that is given to the link if one exists to another node. So, an example would be
Node1 (id: 1, name: 'first') --('link name')--> Node2 (id:....)
and so forth. Once the data structure is created I do not need to do any more operations on it other than read it (I will later use it to render a visualization with d3). The amount of nodes will not be very many, as several of them are shared.
I am imagining an adjacency list but am not sure how I would encode that in javascript. For instance, I know a json object can have a "field" : "value" structure but can I do that with Object : [list of adjacent Objects]?
you can use lists (arrays) in json. E.g. I could represent a simple directed graph as
{
"NodeA": {"name": "NodeA", "adjacentTo": ["NodeB", "NodeC"]},
"NodeB": {"name": "NodeB", "adjacentTo": ["NodeC", "NodeD"]},
"NodeC": {"name": "NodeC", "adjacentTo": ["NodeA"]},
"NodeD": {"name": "NodeD", "adjacentTo": []}
}
This would be the graph:
C
^^
| \
| \
A -> B -> D
The name field really isn't needed, but you can associate any attributes you want with a node that way.
JavaScript objects must have string keys, but can store any type of value. Of course, the entire point in an id is to allow you to represent a complex type wirh a simple one.
var adjacentTo = {};
adjacentTo[node1.id] = [node2, node3]