-
-
Save yelper/dbf7c22635b56aaf26963756931d4fd3 to your computer and use it in GitHub Desktop.
Hierarchical Clusterings of Dashboards
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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