Skip to content

Instantly share code, notes, and snippets.

@1Cr18Ni9
Last active March 16, 2018 08:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 1Cr18Ni9/49f0348aa918a780a9ec2f295528e5ed to your computer and use it in GitHub Desktop.
Save 1Cr18Ni9/49f0348aa918a780a9ec2f295528e5ed to your computer and use it in GitHub Desktop.
chrod
license: mit
{
"title": "Chord",
"size": [500, 600],
"table": {
"header": [["cellular.component.organization.or.biogenesis", 8.0], ["metabolic.process", 7.0], ["multicellular.organismal.process", 4.0], ["immune.system.process", 4.0], ["multi-organism.process", 2.00], ["detoxification", 2.00]],
"body": [
["QW06727", 1, 1, 1, 1, 0, 1, 1.53474591],
["ER8493", 0, 0, 1, 0, 0, 0, 2.513523301],
["OPIWZ3", 0, 0, 0, 1, 0, 0, 1.80352426],
["GG24R6N9", 1, 0, 1, 0, 0, 0, 2.041762452],
["V9GYM3", 1, 1, 1, 0, 1, 0, 1.68518518519],
["B2R657", 1, 1, 0, 0, 1, 0, 1.65060240964],
["B4DUV1", 1, 0, 0, 0, 0, 0, 1.6106493506],
["V9HW62", 0, 1, 0, 0, 0, 0, 1.6025572196],
["LH0A024R3E3", 1, 1, 0, 0, 0, 0, 1.6009270965],
["KHJBTZM4", 1, 1, 1, 1, 0, 1, 2.57423657424],
["Q5U000", 1, 1, 1, 1, 0, 0, 1.54274937133],
["P13804", 0, 1, 0, 0, 0, 0, 1.53814884303],
["P23142", 1, 1, 0, 0, 1, 0, 2.5827158],
["P32189", 0, 1, 0, 0, 0, 0, 1.516545218],
["A8K6C1", 1, 1, 1, 0, 0, 0, 1.510312256],
["Q8TDB2", 0, 1, 0, 0, 0, 0, 1.5023535],
["B2R8Y9", 0, 0, 1, 0, 0, 0, 0.663498836307],
["OPHBJH0A0S2Z471", 0, 1, 0, 0, 0, 0, 0.654721658778],
["A0A0C4DH39", 1, 1, 0, 1, 1, 0, 0.6501820886],
["KHGHBQ7Z333", 1, 1, 0, 0, 0, 0, 0.6291174213],
["P02768", 1, 1, 1, 0, 1, 1, 0.5503859826],
["P03951", 0, 1, 1, 0, 0, 0, 0.526632067741],
["Q02127", 0, 1, 1, 0, 1, 0, 2.006145136],
["A8K8P1", 1, 0, 0, 0, 0, 0, 1.987159438],
["P00738", 0, 0, 0, 1, 1, 1, 1.97685185185],
["B0YIW2", 1, 1, 1, 0, 0, 0, 1.92465753425],
["A0A0K0K1L1", 0, 1, 0, 0, 0, 0, 1.87334656085],
["B4DI23", 0, 1, 0, 0, 0, 0, 1.74512836375],
["A0A024RCW6", 0, 1, 1, 0, 0, 0, 0.524739583333],
["A0A0F7G8J1", 0, 1, 1, 0, 1, 0, 0.5155267911],
["B2RCQ6", 0, 1, 0, 0, 0, 0, 0.5101132095],
["KGYGGVKHUUHU22", 1, 1, 0, 0, 0, 0, 0.3308482805]
]
},
"gradient": ["#0000ff", "#ffeeff", "#ff0000"],
"gradientTitle": "FC",
"legendTitle": "Iterms:"
}
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
</style>
</head>
<body>
<div id="container"></div>
<script>
d3.svg.goChord = function(id, options){
var opt = {
title : "",
size : [600, 500],
gradient : ["#0e0ee8", "#f8f7f7", "#e81a1a"],
gradientTitle : "",
legendTitle : "",
margin : {top: 60, right: 30, bottom: 30, left: 30}
};
_.extend(opt, options);
// data processing
var cData = (function(dt){
dt.table.body.sort(function(a, b){ return _.last(a) - _.last(b); });
dt.table.row = dt.table.body.map(function(d){
return [d.shift(), d.pop()];
});
var cd = {};
// closure values
var cSquareMatrix = function(n){
var repeat = String.prototype.repeat, s;
if (!repeat) {
// "repeat" function is not available in IE
// Make sure compatibility with IE
repeat = function(str, m){
return _.range(0, m).map(function(){ return str; }).join("");
};
s = "[" + repeat(",0", n).slice(1) + "]";
s = repeat("," + s, n).slice(1);
} else {
s = "[" + ",0".repeat(n).slice(1) + "]";
s = ("," + s).repeat(n).slice(1);
}
return JSON.parse("[" + s + "]");
};
var fcDomain = d3.extent(dt.table.row.map(function(d){ return d[1]; }));
var lScale = d3.scale.linear()
.domain([fcDomain[0], 0.5 * (fcDomain[0] + fcDomain[1]), fcDomain[1]])
.range(dt.gradient);
var rScale = d3.scale.linear()
.domain(d3.range(0, dt.table.header.length))
.range([].concat(d3.scale.category20().range(),
d3.scale.category20b().range()));
// initial values
cd._padding = 0;
cd._innerRadius = 100;
cd._outerRadius = 155;
cd._groups = null;
cd._chords = null;
//cd.chd = d3.layout.chord().sortSubgroups(d3.descending);
cd.chd = d3.layout.chord();
cd.chord = d3.svg.chord().radius((cd._innerRadius + cd._outerRadius) * 0.5);
cd.leftItems = dt.table.row.map(function(d){ return d[0]; });
cd.rightItems = dt.table.header.map(function(d){ return d[0]; });
cd.items = [].concat(cd.rightItems, cd.leftItems);
cd.fcDomain = fcDomain;
cd.rScale = rScale;
cd.lScale = lScale;
// right arc generator
cd.rArc = d3.svg.arc()
.innerRadius(cd._innerRadius)
.outerRadius(cd._outerRadius);
// left arc generator
cd.lArc = d3.svg.arc()
.innerRadius((cd._innerRadius + cd._outerRadius) * 0.5)
.outerRadius(cd._outerRadius);
cd.getMatrix = function(){
if(this.matrix){ return this._matrix; }
var matrix = cSquareMatrix(cd.items.length);
dt.table.body.forEach(function(row, index){
var columIndex, rowIndex;
rowIndex = index + dt.table.header.length;
_.each(row, function(val, ii){
columIndex = ii;
matrix[rowIndex][columIndex] = val;
matrix[columIndex][rowIndex] = val;
});
});
this.matrix = matrix;
return matrix;
};
cd.chd.matrix(cd.getMatrix());
// get-Function and set-Function
cd.padding = function(val){
if(!arguments.length){ return this._padding; }
this._padding = val;
this.chd.padding(val);
return this;
};
cd.innerRadius = function(val){
if(!arguments.length){ return this._innerRadius; }
var half = 0.5 * (val + this._outerRadius);
this._innerRadius = val;
this.lArc.innerRadius(half);
this.rArc.innerRadius(val);
this.chord.radius(val);
this._groups = null;
this._chords = null;
return this;
};
cd.outerRadius = function(val){
if(!arguments.length){ return this._outerRadius; }
var half = 0.5 * (val + this._innerRadius);
this._outerRadius = val;
this.rArc.outerRadius(val);
this.lArc.outerRadius(val);
this.lArc.innerRadius(half);
this._groups = null;
this._chords = null;
return this;
};
cd.check = function(){
return this._outerRadius > this._innerRadius;
};
cd.getGroups = function(){
if(this._groups){ return this._groups; }
if(!this.check()){ alert("Bad data!"); return; }
var groups = this.chd.groups(),
right = dt.table.header.length;
_.each(groups, function(dd, ii){
if (ii < right) {
_.extend(dd, {
color : rScale(ii),
zscore : dt.table.header[ii][1],
opacity : 0.7,
name : cd.items[dd.index]
});
} else {
_.extend(dd, {
color : lScale(dt.table.row[ii - right][1]),
fc : dt.table.row[ii - right][1],
opacity : 1,
name : cd.items[dd.index]
});
}
});
// cache this value avoiding intensive calculation
this._groups = groups;
return groups;
};
cd.getChords = function(){
if(this._chords){ return this._chords; }
if(!this.check()){ alert("Bad data!"); return; }
var chords = this.chd.chords().map(function(dd){
dd.color = rScale(dd.source.index);
dd.opacity = 1;
return dd;
});
// cache this value avoiding intensive calculation
this._chords = chords;
return chords;
};
return cd;
})(opt);
var svg = d3.select("#" + id)
.html("")
.append("svg")
.attr({
width : 100,
height: 100,
xmlns : "http://www.w3.org/2000/svg"
})
.style("font", "12px arial, sans-serif");
var dim = (function(svg, cd, opt){
var dd = {};
dd.outerRadius = 0;
dd.outerTextLength = 0;
dd.lgdHeight = 0;
dd.lgdWidth = 0;
dd.lgdBoxSize = 12;
dd.grdWidth = 100;
dd.grdHeight = 85;
dd.smallGap = 10;
dd.largeGap = 15;
dd.svgSize = [0, 0];
dd.getTextBox = function(text){
var box, node;
try {
node = svg.append("text").text(text);
box = node.node().getBBox();
node.remove();
} catch (err) {
box = { width: text.length * 8.2, height: 15, x: null, y: null };
}
return box;
};
dd.outerRadius = (function(self){
var longText = _.last(_.sortBy(cd.leftItems.slice(), "length"));
var box = self.getTextBox(longText);
// The bigger value of "factor",
// the rarer collison chances on the side texts
var factor = 15;
var outerRadius = Math.ceil((box.height + factor) * cd.leftItems.length / Math.PI);
if (outerRadius < 100){ outerRadius = 100; }
self.outerTextLength = Math.ceil(box.width + 10);
return outerRadius;
})(dd);
dd.lgdWidth = (function(self){
var longText = _.last(_.sortBy(cd.rightItems.slice(), "length"));
var box = self.getTextBox(longText);
return Math.ceil(self.lgdBoxSize + box.width + 5);
})(dd);
dd.lgdHeight = (cd.rightItems.length + 1) * (dd.lgdBoxSize + 4);
// recalculate svg dimension
dd.svgSize[0] = opt.margin.left + opt.margin.right +
dd.outerRadius * 2 + dd.outerTextLength + dd.largeGap +
Math.max(dd.lgdWidth, dd.grdWidth);
if(dd.svgSize[0] < opt.size[0]){
dd.outerRadius += (opt.size[0] - dd.svgSize[0]) * 0.5;
dd.svgSize[0] = opt.size[0];
}
dd.svgSize[1] = opt.margin.top + opt.margin.bottom +
Math.max((dd.outerRadius + dd.outerTextLength) * 2,
(dd.grdHeight + dd.lgdHeight));
return dd;
})(svg, cData, opt); // anonymous function END
// resize SVG canvas
svg.attr({ width: dim.svgSize[0], height: dim.svgSize[1] });
// defs
var gradientID = "BWR-" + (new Date()).getTime();
svg.append("defs")
.append("linearGradient")
.attr({
id: gradientID,
x1: 0, y1: 0, x2: 1, y2: 0
})
.selectAll("stop")
.data(["0%", "50%", "100%"])
.enter()
.append("stop")
.each(function(d, i){
d3.select(this).attr({ offset: d, "stop-color": opt.gradient[i] });
});
var gMain = svg.append("g")
.attr("class", "gMain")
.attr("transform", "translate(" + [opt.margin.left, opt.margin.top] + ")");
var gChordPie = gMain.append("g")
.attr("class", "gChordPie")
.attr("transform", "translate(" + [dim.outerRadius + dim.outerTextLength,
dim.outerRadius + dim.outerTextLength] + ")");
var gOther = gMain.append("g")
.attr("class", "gOther")
.attr("transform", "translate(" +
(dim.outerRadius * 2 + dim.outerTextLength + dim.largeGap) + ",0)");
cData.outerRadius(dim.outerRadius).innerRadius(dim.outerRadius - 30);
gChordPie.append("g")
.attr("class", "gChrods")
.selectAll("path")
.data(cData.getChords())
.enter()
.append("path")
.on("mouseenter", chordsOnMouseEnter)
.on("mouseleave", chordsOnMouseLeave)
.attr({
d : cData.chord,
stroke : "black",
"stroke-width": 0.5,
fill : function(d){ return d.color; },
opacity : function(d){ return d.opacity; }
});
gChordPie.selectAll("g.gGroups")
.data(cData.getGroups())
.enter()
.append("g")
.attr("class", "gGroups")
.each(function(d){
var self = d3.select(this);
self.append("path")
.on("mouseenter", groupsOnMouseEnter)
.on("mouseleave", groupsOnMouseLeave)
.attr({
d : ("zscore" in d) ? cData.rArc(d) : cData.lArc(d),
stroke : "black",
"stroke-width": 0.5,
fill : d.color,
opacity : d.opacity
})
.append("title")
.text(function(){
var title = "name: " + d.name;
if("zscore" in d){
title += "\nzscore: " + d.zscore;
}else{
title += "\n" + opt.gradientTitle + ": " + d.fc;
}
return title;
});
self.append("g")
.attr("class", "gName")
.attr("transform", function(d){
return "rotate(" +
((d.startAngle + d.endAngle) * 0.5 * 180 / Math.PI - 90) + ")" +
"translate(" + (cData.outerRadius() + 5) + ",0)";
})
.append("text")
.text(d.name)
.attr({ transform: "rotate(180)", "text-anchor": "end", y: 5 })
.style("display", ("zscore" in d) ? "none" : "block");
});
// Events
function chordsOnMouseEnter(d){
gChordPie.selectAll(".gChrods path").attr("opacity", 0.1);
d3.select(this).attr("opacity", 1);
gChordPie.selectAll(".gGroups").attr("opacity", function(j){
return ((j.index === d.source.index) || (j.index === d.target.index)) ? 1 : 0.3;
});
}
function chordsOnMouseLeave(d){
gChordPie.selectAll(".gChrods path").attr("opacity", 1);
gChordPie.selectAll(".gGroups").attr("opacity", 1);
}
function groupsOnMouseEnter(d){
var indexs = [];
gChordPie.selectAll(".gChrods path")
.each(function(j){
if ((d.index === j.source.index) ||
(d.index === j.target.index)) {
indexs.push(j.source.index);
indexs.push(j.target.index);
d3.select(this).attr({ "stroke-width": 1, opacity: 1 });
} else {
d3.select(this).attr({ "stroke-width": 0, opacity: 0.2 });
}
});
gChordPie.selectAll(".gGroups").attr("opacity", function(j){
return _.contains(indexs, j.index) ? 1 : 0.2;
});
}
function groupsOnMouseLeave(d){
gChordPie.selectAll(".gGroups").attr("opacity", 1);
gChordPie.selectAll(".gChrods path")
.attr("opacity", 1)
.attr("stroke-width", 0.5);
}
gOther.append("g")
.attr("class", "gGradient")
.attr("transform", "translate(0, 20)")
.call(function(s){
var axis = d3.svg.axis()
.ticks(3)
.orient("bottom")
.scale(d3.scale.linear().range([0, dim.grdWidth]).domain(cData.fcDomain));
s.append("rect").attr({
width : dim.grdWidth,
height: 20,
fill : "url(#" + gradientID + ")"
});
s.append("g")
.attr("transform", "translate(0,20)")
.call(axis)
.call(function(t){
t.select("path").attr({ fill: "none", stroke: "none" });
t.selectAll("line").attr({ stroke: "black", "stroke-width": 1 });
});
s.append("text")
.text(("gradientTitle" in opt) ? opt.gradientTitle : "")
.attr({
"text-anchor": "middle",
transform: "translate(" + (dim.grdWidth * 0.5) + ",-5)",
});
});
gOther.append("g")
.attr("class", "gLegend")
.attr("transform", "translate(0," + dim.grdHeight + ")")
.call(function(self){
self.append("text").text(("legendTitle" in opt) ? opt.legendTitle : "");
self.selectAll("g")
.data(cData.rightItems)
.enter()
.append("g")
.each(function(dd, ii){
var yoo = d3.select(this);
yoo.attr("transform", "translate(0," + ((ii + 1) * 19) + ")");
yoo.append("rect").attr({
width : dim.lgdBoxSize,
height: dim.lgdBoxSize,
fill : cData.rScale(ii)
});
yoo.append("text")
.text(dd)
.attr({
transform: "translate(" + (dim.lgdBoxSize + 5) + ",10)"
});
});
});
// main title
gMain.append("text")
.text(("title" in opt) ? opt.title : "")
.attr({
"text-anchor": "middle",
"transform": "translate(" + [dim.outerRadius + dim.outerTextLength, -25] + ")"
})
.style("font", "25px arial, sans-serif");
}; // goChord END
d3.json("CHROD.json", function(error, data){
if(error){ return console.log(error); }
d3.svg.goChord("container", data);
});
d3.select(self.frameElement)
.style({
width: 950 + "px",
height: 750 + "px",
overflow: "auto"
});
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment