Skip to content

Instantly share code, notes, and snippets.

@yelper
Forked from mcorrell/data.csv
Created March 21, 2018 03:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save yelper/dbf7c22635b56aaf26963756931d4fd3 to your computer and use it in GitHub Desktop.
Save yelper/dbf7c22635b56aaf26963756931d4fd3 to your computer and use it in GitHub Desktop.
Hierarchical Clusterings of Dashboards
DB String
DB001 YYNNOMNNNNNYNYY
DB002 YYNNOLNNYNNNYYN
DB003 NNNYPMYNYNNNYYN
DB004 YNNYPMNNNNNNNYN
DB005 YNNYPMNNYNNNNYY
DB006 NNNYPMNNYNNNNYN
DB007 NNNYPMNNYNNNYNY
DB008 YYNNOMNNYNNNYYN
DB009 NYNNPLNNYNNNNYN
DB010 NNNYPHNNYNNNNNN
DB011 YNNNOHYNYNNNYYN
DB012 NNNYPHNNNNNNNYN
DB013 NYYNSMNNYNNNNYN
DB014 YNNYSLNNYNNNNYY
DB017 NYYNIHNNYNYYYYN
DB019 NNYNIMNYYNNNNYN
DB020 NNYNILNNYNNYNYN
DB021 NYYNIHNNYYYNYYN
DB022 YYYNIMNYYNYYYYN
DB023 YYYNIMNNYNYNYYN
DB024 YYNNILNNYYYYNYY
DB025 YYYNSMNYYNNYYYN
DB026 NYYNOMNNYNNYNYN
DB027 NYYNIMNNYNNYYYN
DB028 YYYNSMNYYYNYYYN
DB029 YYNNILNNYNNYYYY
DB030 YYYNIHYNYNYYYYN
DB031 NYYNILNNYYYYYYN
DB032 NYNNOLYYYNNNNYN
DB034 NNYNOMYNNNNYNYN
DB036 NYYNOHYYYNYYYYY
DB037 NNYNOMYNNNNYNYN
DB040 YNNYPMNNNNNYYYN
DB041 YNNYPHNNNNNYYYN
DB042 NYNYPHNNYNNYNYN
DB043 NYYNOLNNYNNYYYN
DB044 YYYYPLNNNNNYNYN
DB045 NYNYSHYNNNNNNYN
DB046 NYYNOMYYYNNYYYN
DB047 YYNNOHYYYNNYYYN
DB048 YNNYPHNNYNNNNYN
DB049 NNYNILNNNNYNNYN
DB050 NNYNOHYNYYYYYYN
DB051 NYNNOHNYYNNYNYN
DB052 YYNNOHYYYNNNYYN
DB053 YYYNIMYNYNYYYYN
DB054 YYNNILYNNNNNNYN
DB055 NYYNOLYNYNNYYYN
DB057 NNYNOLYYYYNYNYN
DB056 NNYNOLYYYNYYYYN
DB058 NNYNOHYYYNYYYYN
DB059 NNYNOLNNNNYYNYN
DB060 NNYNOHYYYNNYYYN
DB061 NNYNOLYNNNYYNYN
DB062 YYNNOHNNYNNYYYN
DB063 NNYNIHNYNNNYNYN
DB064 YYYNOMNYYNNNYYN
DB065 NYYNOLNYYNNYNYN
DB066 YYYNOLNNNNNYNYN
DB100 NNYNOHYNYYNNNYN
DB101 NYNNOLNNNNNYNYY
DB102 NNYNOLYYNNNYNYN
DB103 NNYNPLYYYYNNYYY
DB104 NYYNIHNYYNYNYYN
DB105 NNYNILNNNNYYNYN
DB106 NYNNILNNNNNYNYN
DB107 NYYNIHNNYNNYYYY
DB108 YNNNOMNNNNNYNYN
DB109 NYYNOLYNNNNNNYN
DB110 YYNNOMYNYNNNNYN
DB111 NYYNILNNNNNNNYN
DB112 NYYNIHNYYNNNNYN
DB113 YNNNOMYNNNNNNYN
DB114 YNNNOMYNNNNNNYN
DB115 NYYNIHYNYNNNYYN
DB116 NNYNSHNNNNNNNNN
DB117 YNNYPMNNNNNNNYN
<!DOCTYPE html>
<meta charset="utf-8">
<title>Dashboard Clusters</title>
<style>
.node circle {
fill: #999;
}
.node text {
font: 10px sans-serif;
}
.node--internal circle {
fill: #555;
}
.node--internal text {
text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff;
}
.link {
fill: none;
stroke: #555;
stroke-opacity: 0.4;
stroke-width: 1.5px;
}
</style>
<svg width="1500" height="1060"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
//Hierarchical Clustering
let hCluster = function(data){
// a hierarchical cluster is a root object with a "name" and an array of "children"
let matrix = makeDistMatrix(data);
let root;
let node;
let clusName;
let closest;
// while we've still got data points to add:
// find the closest two points/clusters i,j
// create a cluster of those two points
// remove i and j from the matrix
// add entry for new cluster to the matrix
//n^3, but who cases.
while(matrix.length>1){
closest = findClosestWithDiff(matrix, data);
matrix = addCluster(matrix,closest.i,closest.j,closest.diffs);
}
return matrix[0].node;
};
let hamming = function(a,b){
let cA = a.split('');
let cB = b.split('');
let dist = 0;
for(let i = 0;i<cA.length;i++){
dist += cA[i]!=cB[i];
}
return dist + Math.abs(cA.length - cB.length);
};
let makeDistMatrix = function(data){
//create initial distance matrix
let matrix = [];
for(let i = 0;i<data.length;i++){
matrix.push([]);
matrix[i].node = {"name": data[i].DB, "children": null};
for(let j=0;j<data.length;j++){
matrix[i][j] = hamming(data[i].String,data[j].String);
}
}
return matrix;
};
let findClosestWithDiff = function(distMatrix, data) {
let closestPair = findClosest(distMatrix);
// find what the difference is between these two
let diffs = [];
let iData = data[closestPair.i].String, jData = data[closestPair.j].String;
for (let n = 0; n < theMeaning.length; n++) {
if (iData[n] !== jData[n]) {
diffs.push({ name: theMeaning[n].name, values: [getValues(n, iData[n]), getValues(n, jData[n])] });
}
}
closestPair.diffs = diffs;
return closestPair;
}
let getValues = function(n, val) {
let dim = theMeaning[n];
return dim.values ? dim.values[val] : defaultValues[val];
}
let findClosest = function(distMatrix){
//find two closest points in a distance matrix
let minI = 0
let minJ = 0;
let minVal = -1;
for(let i = 0;i<distMatrix.length;i++){
for(let j = 0;j<distMatrix[i].length;j++){
if(i!=j && (minVal==-1 || distMatrix[i][j]<minVal)){
minI = i;
minJ = j;
minVal = distMatrix[i][j];
}
}
}
return {"i": minI, "j": minJ, "d": minVal};
}
let addCluster = function(distMatrix,i,j,diffs){
//remove the entries for i and j and replace with a cluster ij.
//add entry to the matrix that's min( d(x,i), d(x,j) )
if(i>j){
let t = i;
i = j;
j = t;
}
let newClust = [];
let dist;
let clusName = distMatrix[i].node.name + distMatrix[j].node.name;
newClust.node = {"name": clusName, "children": [distMatrix[i].node, distMatrix[j].node], "diffs": diffs};
for(let k = 0;k < distMatrix.length;k++){
dist = Math.min(distMatrix[k][i],distMatrix[k][j])
distMatrix[k].push(dist);
newClust.push(dist);
distMatrix[k].splice(j,1);
distMatrix[k].splice(i,1);
}
newClust.push(0);
newClust.splice(j,1);
newClust.splice(i,1);
distMatrix.push(newClust);
distMatrix.splice(j,1);
distMatrix.splice(i,1);
return distMatrix;
}
//D3 Tree Layout, from our boy MB https://bl.ocks.org/mbostock/4339184
let theMeaning = [
{ name: "Strategic" },
{ name: "Tactical" },
{ name: "Operational" },
{ name: "Learning" },
{ name: "Audience", values: { I: "Individual", O: "Org", S: "Social", P: "General Public" } },
{ name: "Vis Literacy", values: { L: "Low", M: "Medium", H: "High" } },
{ name: "Domain Knowledge" },
{ name: "Constructability" },
{ name: "Interactivity" },
{ name: "Modify Data/World" },
{ name: "Alerting" },
{ name: "Benchmarks" },
{ name: "Multipage" },
{ name: "Updatable" },
{ name: "Highlighting" },
];
let defaultValues = { Y: "Yes", N: "No" };
let theTree;
let theData;
let svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(40,0)");
let tree = d3.tree()
.size([height, width - 160]);
d3.csv("data.csv", function(error, data) {
if (error) throw error;
theData = data;
theTree = hCluster(data);
let root = tree(d3.hierarchy(theTree));//tree(hCluster(data));
let link = g.selectAll(".link")
.data(tree(root).links())
.enter().append("path")
.attr("class", "link")
.attr("d", d3.linkHorizontal()
.x(function(d) { return d.y; })
.y(function(d) { return d.x; }));
let node = g.selectAll(".node")
.data(root.descendants())
.enter().append("g")
.attr("class", function(d) { return "node" + (d.children ? " node--internal" : " node--leaf"); })
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
node.append("circle")
.attr("r", 2.5);
/*
iffn ya want images
let imgsize = 25;
let imgsizeBig = 200;
node.filter(function(d){ return !d.children;}).append("image")
.attr("xlink:href", function(d){
return "images/"+d.data.name+".jpg";
})
.attr("width", imgsize)
.attr("height", imgsize)
.attr("x",8)
.attr("y",-0.5*imgsize)
.on("mouseover",function(d){
d3.select(this)
.attr("width",imgsizeBig)
.attr("height",imgsizeBig)
.attr("y",-0.5*imgsizeBig);
})
.on("mouseout",function(d){
d3.select(this)
.attr("width",imgsize)
.attr("height",imgsize)
.attr("y",-0.5*imgsize);
})
*/
node.append("text")
.attr("dy", 3)
.attr("x", function(d) { return d.children ? -8 : 8; })
.style("text-anchor", function(d) { return d.children ? "end" : "start"; })
.text(function(d) {
return !d.children ? d.data.name : ""; });
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment