Published
Edited
Jun 10, 2019
2 stars
Insert cell
Insert cell
flumpert = {
const flumpert = new Node("Flumpert", "cat", {cute: true})
flumpert.set_edge({id: 'ball', type: 'toy'});
flumpert.set_edge({id: 'mouse', type: 'toy'});
flumpert.set_edge({id: 'ball', type: 'toy'});

return flumpert;
}
Insert cell
flumpert.edges
Insert cell
flumpert.degree
Insert cell
flumpert.get_random_edge()
Insert cell
class Node {
constructor(id, type){
// Unique identifier for this node
this.id = id;
// Type of the node of the two possible
this.type = type;
// Map containing all the nodes connected to this node
this._edges = new Map();
// How many total edges this node has
this.degree = 0;
// Cluster membership of this node
this._cluster = null;
}
// Takes node's edge map and a new edge node
// and either increments the edge count up if node is already
// connected, or adds a new edge if it's the first time.
set_edge(edge_node){
const new_edge_id = edge_node.id;
const existing_edge = this._edges.get(new_edge_id);
if(existing_edge){
existing_edge.count++;
} else {
this._edges.set(new_edge_id, {node: edge_node, count: 1});
}
// Increment the degree
this.degree += 1;
}
get cluster(){
return this._cluster;
}
set cluster(clust_id){
this._cluster = clust_id;
}
// Takes two nodes and adds an edge between the two of them.
static new_edge(a_node, b_node){
a_node.set_edge(b_node);
b_node.set_edge(a_node);
}
get edges(){
return Array.from(this._edges.values());
}
// Get a random neighbor of node
get_random_edge(){
const random_connection_id = sample(Array.from(this._edges.keys()));
return this._edges.get(random_connection_id).node;
}

}
Insert cell
md `## BiGraph class

This is the main class. It is initiated with two types provided as strings. These correspond to the two node types.
`
Insert cell
party_graph = {
const party_graph = BiGraph.create('person', 'party');
party_graph
.add_nodes([
{id: 'Sarah', type: 'person'},
{id: 'Nick', type: 'person'},
{id: 'Movies', type: 'party'},
])
.add_edges([
{person: 'Flumpus', party: 'Birfday'},
{person: 'Sarah', party: 'Birfday'},
{person: 'Flumpus', party: 'Birfday'},
{person: 'Nick', party: 'Movies'},
]);
return party_graph;
}
Insert cell
party_graph.edges
Insert cell
party_graph.num_nodes
Insert cell
party_graph.get_nodes('person')
Insert cell
party_graph.all_nodes
Insert cell
class BiGraph {
constructor(a_type, b_type){
this.a_type = a_type;
this.b_type = b_type;

// Object that holds the node maps for each type
this.nodes = {
[a_type]: new Map(),
[b_type]: new Map(),
};
}

// Add a single node to the graph
add_node({id, type, ...other_props}){
// Initialize a new node object and
// add to its node type's node map
this.nodes[type].set(
id,
new Node(id, type, other_props)
);
return this;
};

// Add multiple nodes at once
add_nodes(node_arr){
node_arr.forEach(node => this.add_node(node));
return this;
};
// Add an edge {type_a: a_id, type_b: b_id} to the graph
add_edge(edge){
// Check if the nodes in edge are already in the node map, if they arent, add them.
const [a_node, b_node] = [this.a_type, this.b_type].map(type => {
// Get node type's edge id
const id = edge[type];
const node_does_not_exist = !this.nodes[type].has(id);

if(node_does_not_exist){
this.add_node({id, type});
}

// Get node object
return this.nodes[type].get(id);
});

// Add each node as a edge for its connection
Node.new_edge(a_node, b_node);

return this;
};
// Add multiple edges at once
add_edges(edge_arr){
edge_arr.forEach(edge => this.add_edge(edge));
return this;
}
// Grabs an array of all the edges in terms of node object to node object and count
get edges(){
let all_edges = [];
this.nodes[this.a_type].forEach(a_node =>
a_node.edges.forEach(({node: b_node, count}) =>
all_edges.push({
[`${this.a_type}_id`]: a_node.id,
[`${this.b_type}_id`]: b_node.id,
[this.a_type]: a_node,
[this.b_type]: b_node,
count: count,
})
)
);
return all_edges
}
// Return all nodes of a given type in array
get_nodes(type){
return Array.from(this.nodes[type].values());
}
// Return all nodes of both types
get all_nodes(){
return [...this.get_nodes(this.a_type), ...this.get_nodes(this.b_type)];
}

get num_nodes(){
const n_a_nodes = this.nodes[this.a_type].size;
const n_b_nodes = this.nodes[this.b_type].size
return {
[this.a_type]: n_a_nodes,
[this.b_type]: n_b_nodes,
all: n_a_nodes + n_b_nodes,
}
}
static create(a_type, b_type){
return new this(a_type, b_type)
}


}
Insert cell
sample = arr => arr[gen_discrete_unif(0,arr.length-1)]
Insert cell
import {gen_discrete_unif} from "@nstrayer/javascript-statistics-snippets@84"
Insert cell
md`## Old code

Old non-class version of the Graph
`
Insert cell
function build_graph(a_type, b_type){
const types = [a_type, b_type];
// Object that holds the node maps for each type
const nodes = {
[a_type]: new Map(),
[b_type]: new Map(),
};
// Add a single node to the graph
const add_node = function({id, type, ...other_props}){
// Initialize a new node object and
// add to its node type's node map
nodes[type].set(
id,
new Node(id, type, other_props)
);
return this;
};
// Add multiple nodes at once
const add_nodes = function(node_arr){
node_arr.forEach(add_node);
return this;
};
// Add an edge {type_a: a_id, type_b: b_id} to the graph
const add_edge = function(edge){
// Check if the nodes in edge are already in the node map, if they arent, add them.
const [a_node, b_node] = types.map(type => {
// Get node type's edge id
const id = edge[type];
const node_does_not_exist = !nodes[type].has(id);
if(node_does_not_exist){
add_node({id, type});
}

// Get node object
return nodes[type].get(id);
});

// Add each node as a edge for its connection
Node.new_edge(a_node, b_node);

return this;
};

// Add multiple edges at once
const add_edges = function(edge_arr){
edge_arr.forEach(add_edge);
return this;
}
// Grabs an array of all the edges in terms of node object to node object and count
const get_edges = () => {
let all_edges = [];
nodes[a_type].forEach(a_node =>
a_node.edges.forEach(({node: b_node, count}) =>
all_edges.push({
[`${a_type}_id`]: a_node.id,
[`${b_type}_id`]: b_node.id,
[a_type]: a_node,
[b_type]: b_node,
count: count,
})
)
);
return all_edges
}
const get_node = (type, id) => nodes[type].get(id);
return {
nodes,
add_node,
add_nodes,
add_edge,
add_edges,
get_node,
get_edges,
};
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more