Skip to content

Instantly share code, notes, and snippets.

@1Cr18Ni9
Last active February 6, 2018 02:22
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/5fa4f75e4161b0ea38f5ab38a4f69a27 to your computer and use it in GitHub Desktop.
Save 1Cr18Ni9/5fa4f75e4161b0ea38f5ab38a4f69a27 to your computer and use it in GitHub Desktop.
Protein Mutation
license: mit
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-polyfills/0.1.41/polyfill.min.js"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
.extra-tip{
position: fixed;
max-width: 200px;
left: -9999px;
background-color: white;
border: 1px solid gray;
border-radius: 5px;
font: 12px;
overflow-wrap: break-word;
word-wrap: break-word;
}
.extra-tip > .extra-tip-content{
padding: 5px;
}
.extra-tip .extra-tip-content a{
text-decoration: none;
}
.extra-tip > .extra-tip-arrow{
position: absolute;
height: 15px;
width: 100%;
top: 100%;
/* indicate area when needed */
background-color: rgba(200,100,0,0);
}
.extra-tip .extra-tip-wrapper{
position: absolute;
left: 50%;
}
.extra-tip .extra-tip-wrapper:before,
.extra-tip .extra-tip-wrapper:after{
content: " ";
width: 0;
height: 0;
top: 100%;
position: absolute;
border: solid transparent;
}
.extra-tip .extra-tip-wrapper:before{
border-width: 10px;
border-top-color: gray;
left: -8px;
}
.extra-tip .extra-tip-wrapper:after{
border-width: 8px;
border-top-color: white;
left: -6px;
}
table{ border-collapse: collapse; }
table td{ border: 1px solid #000; padding: 5px; }
.highlight{ background-color: #fac; }
</style>
</head>
<body>
<table id="mutated">
<thead></thead>
<tbody>
<tr>
<td>TCGA-C5-A2LT-01A</td>
<td>dolor sit amet</td>
<td>consectetur adipisicing </td>
</tr>
<tr>
<td>TCGA-FU-A3HZ-01A</td>
<td>architecto beatae cum</td>
<td>tenetur quisquam</td>
</tr>
<tr>
<td>TCGA-FU-A23L-01A</td>
<td>nam sunt repellendus</td>
<td>hic inventore nisi</td>
</tr>
<tr>
<td>TCGA-EK-A2PK-01A</td>
<td>Obcaecati ducimus</td>
<td>ab molestias sit</td>
</tr>
<tr>
<td>TCGA-VS-AA62-01A</td>
<td>Obcaecati ducimus</td>
<td>ab molestias sit</td>
</tr>
</tbody>
</table>
<div id="container"></div>
<script>
d3.svg.mutate = function(id, rData, tableID){
var svgWidth = 650,
svgHeight = 180,
margin = {top: 20, right: 70, bottom: 20, left: 50},
width = svgWidth - margin.left - margin.right,
// height = svgHeight - margin.top - margin.bottom,
pointPlotHeight = 90,
barPlotHeight = 30,
pointRadius = 4,
barHeight1 = 12,
barHeight2 = barHeight1 + 10,
widthPerLetter,
tooltip,
mutateTypeColors = {
"inframe" : "#8b4513",
"others" : "#8b00c9",
"splice site" : "#393b79",
"frameshift" : "#000000",
"missense" : "#008000"
},
pfamColors = [
"#6b6ecf", "#9c9ede", "#637939",
"#8ca252", "#b5cf6b", "#cedb9c", "#8c6d31", "#bd9e39",
"#e7ba52", "#e7cb94", "#843c39", "#ad494a", "#d6616b",
"#e7969c", "#7b4173", "#a55194", "#ce6dbd", "#de9ed6",
"#3182bd", "#6baed6", "#9ecae1", "#c6dbef", "#e6550d",
"#fd8d3c", "#fdae6b", "#fdd0a2", "#31a354", "#74c476",
"#a1d99b", "#c7e9c0", "#756bb1", "#9e9ac8", "#bcbddc",
"#393b79", "#5254a3"
];
// auto calculate letter width, if fail assign it with 11
try{ widthPerLetter = averageLetterWith(); }catch(err){ widthPerLetter = 11; }
var table = document.getElementById(tableID);
var xScale = d3.scale.linear().range([0, width]),
yScale = d3.scale.linear().range([0, pointPlotHeight]),
xAxis = d3.svg.axis().orient("bottom").scale(xScale),
yAxis = d3.svg.axis().orient("left");
var svg = d3.select("#" + id)
.append("svg")
.attr({
width : svgWidth,
height : svgHeight,
version : 1.1,
"shape-rendering" : "crispEdges",
xmlns : "http://www.w3.org/2000/svg"
})
.style("font", "12px arial, sans-serif");
var gPlotMain = svg.append("g").attr({
class: "gPlotMain",
transform: "translate(" + [margin.left, margin.top] + ")"
}),
gYAxis = gPlotMain.append("g").attr({
class: "axis gYAxis",
transform: "translate(" + -pointRadius +",0)"
}),
gPoints = gPlotMain.append("g").attr({
class: "gPoints",
transform: "translate(0," + pointPlotHeight + ")"
}),
gBars = gPlotMain.append("g").attr({
class: "gBars",
transform: "translate(0," + pointPlotHeight + ")"
}),
gXAxis = gPlotMain.append("g").attr({
class: "axis gXAxis",
transform: "translate(0," + (pointPlotHeight + barPlotHeight) + ")"
});
// data processing
var yMax = (rData.point && rData.point.length > 0) ?
d3.max(rData.point, function(d){ return d.y; }) : 1,
xMax = d3.max(rData.domain, function(d){ return d.end; });
// Forcing all search sample strings to upper case format
// before comparing with table cell textContent
try{ // in case there is no point at all: rData.point === undefined
rData.point.forEach(function(d){
d.sample = d.sample.map(function(e){ return e.toUpperCase(); });
});
}catch(error){
console.log("rData.point: " + error.message + " :-)");
}
xScale.domain([0, xMax]);
yScale.domain([0, yMax]);
yAxis.scale(yScale.copy().range([pointPlotHeight, 0]));
gXAxis.call(xAxis).call(styleAxis).call(xAxisMaxLabel);
if(yMax <= 9){ yAxis.ticks(yMax); }else{ yAxis.ticks(5); }
gYAxis.call(yAxis).call(styleAxis)
.append("text")
.attr({
x : -pointPlotHeight * 0.5,
y : -25,
transform : "rotate(-90)",
"text-anchor" : "middle"
})
.text("# Mutations");
// begin to draw
if(rData.point && rData.point.length > 0){
gPoints
.selectAll("g")
.data(rData.point)
.enter()
.append("g")
.each(function(d){
var g = d3.select(this);
g.attr("transform",
"translate(" + [xScale(d.x), -yScale(d.y)] + ")");
g.append("line").attr({
y2 : yScale(d.y) + 0.5 * barPlotHeight,
stroke : "#000",
"stroke-width": 1
});
g.append("circle").attr({
r : pointRadius,
fill : mutateTypeColors[d.mutation.toLowerCase()] || "red",
"shape-rendering" : "auto"
})
.on("mouseenter", pointOnMouseEnter)
.on("mouseleave", pointOnMouseLeave);
});
} // END IF :-)
var longBanner, otherBannners = [], otherBannerType = [];
rData.domain.forEach(function(d){
if(d.end === xMax && d.start === 0){
longBanner = d;
}else{ otherBannners.push(d); }
});
otherBannners
.sort(function(a, b){ return a.start - b.start; })
.forEach(function(d){
if(otherBannerType.indexOf(d.pfam) === -1){
otherBannerType.push(d.pfam);
}
});
// domain and other protein banners:
gBars
.append("g")
.datum(longBanner)
.on("mouseenter", barOnMouseEnter)
.on("mouseleave", barOnMouseLeave)
.attr("class", "domain")
.attr("transform", "translate(" +
[0, (barPlotHeight - barHeight1) * 0.5 ] + ")")
.append("rect")
.attr({
width : xScale(xMax),
height : barHeight1,
fill : "#BABDB6",
stroke : "#000",
"stroke-width": 0
});
gBars
.append("g")
.selectAll("g")
.data(otherBannners)
.enter()
.append("g")
.on("mouseenter", barOnMouseEnter)
.on("mouseleave", barOnMouseLeave)
.attr("class", "protein")
.each(function(d, i){
var g = d3.select(this), barWidth = xScale(d.end - d.start);
g.attr("transform", "translate(" +
[xScale(d.start), (barPlotHeight - barHeight2) * 0.5] + ")");
g.append("rect")
.attr({
width : barWidth,
height : barHeight2,
fill : pfamColors[otherBannerType.indexOf(d.pfam)],
stroke : "#000",
"stroke-width": 0
});
g.append("text")
.text(getLabel(barWidth, d.desc, widthPerLetter))
.attr({
x : 0.5 * barWidth,
y : 0.5 * barHeight2,
fill : "#fff",
"text-anchor" : "middle",
"alignment-baseline": "central",
"pointer-events" : "none"
});
});
tooltip = document.getElementById("extra-tip");
if(!tooltip){
tooltip = d3.select("body")
.append("div")
.attr({id: "extra-tip", class: "extra-tip"})
.html('<div class="extra-tip-content"></div><div class="extra-tip-arrow"><div class="extra-tip-wrapper"></div></div>')
.on("mouseleave", function(){ this.style.left = "-9999px"; })
.node();
}else{
d3.select(tooltip)
.on("mouseleave", function(){ this.style.left = "-9999px"; });
}
function pointOnMouseEnter(d){
var x = d3.event.clientX, y = d3.event.clientY;
var html = "<strong>" + d.y + " mutation</strong></br>" +
"AA change: " + d.change;
d3.select(tooltip).select("div:first-child").html(html);
d3.select(this).transition().attr("r", pointRadius * 1.5);
tooltip.style.left = x - 0.5 * tooltip.offsetWidth + "px";
tooltip.style.top = y - tooltip.offsetHeight - 20 + "px";
// highlight table row if matched
if(table && d.sample && d.sample.length){
d3.select(table)
.selectAll("tr > td:first-child")
.each(function(){
if(d.sample.indexOf(this.textContent.trim().toUpperCase()) !== -1){
this.parentNode.classList.add("highlight");
}
});
}
}
function pointOnMouseLeave(d){
d3.select(this).transition().attr("r", pointRadius);
tooltip.style.left = "-99999px";
// remove highlight class from all table row
if(table){
d3.select(table)
.selectAll("tr")
.classed("highlight", false);
}
}
function barOnMouseEnter(d){
var x = d3.event.clientX,
y = d3.event.clientY,
rCoordinate = d3.mouse(this);
d3.select(tooltip)
.select("div:first-child")
.html(d.desc + " (" + d.start + " - " + d.end + ") <br />");
d.link.forEach(function(e){
d3.select(tooltip)
.select("div:first-child")
.append("a")
.text(" " + e.name + " ")
.attr({ href: e.address, target: "_blank" })
.style({ display: "inline-block", margin: "2px" });
});
d3.select(this).select("rect").attr("stroke-width", 1);
if(this.classList.contains("domain")){
tooltip.style.left = x - (0.5 * tooltip.offsetWidth) + "px";
}else{
tooltip.style.left = (x - rCoordinate[0]) +
(0.5 * xScale(d.end - d.start)) -
(0.5 * tooltip.offsetWidth) - 2 + "px";
}
tooltip.style.top = (y - rCoordinate[1]) -
tooltip.offsetHeight - 10 + "px";
}
function barOnMouseLeave(d){
d3.select(this).select("rect").attr("stroke-width", 0);
if(d3.event.toElement.tagName !== "DIV"){
tooltip.style.left = "-99999px";
}
}
function styleAxis(s){
s.select(".domain")
.attr({ fill: "none", stroke: "#000", "stroke-width": 1});
s.selectAll(".tick line")
.attr({ stroke: "#000", "stroke-width": 1 });
}
function xAxisMaxLabel(s){
var lastTick = s.select("path").node().previousElementSibling,
lastTickNumber = xScale.ticks().slice().pop();
// Remove the last tick if the max-label will collision with it.
if(xScale(xMax - lastTickNumber) < (widthPerLetter * lastTickNumber.toString().length / 2)){
d3.select(lastTick).select("text").remove();
}
// minic the normal tick layout
s.insert("g", "path")
.attr({
class: "tick",
transform: "translate(" + xScale.range()[1] + ",0)"
})
.append("text")
.text(xMax + "aa")
.attr({
y : yAxis.outerTickSize() + yAxis.tickPadding(),
dy: ".71em",
// "text-anchor": "middle"
});
}
function averageLetterWith(){
var str = "WWWWWWMMMMMMEUMM",
text = svg.append("text").html(str),
width = Math.ceil(text.node().getBBox().width / str.length);
text.remove();
return width;
}
function getLabel(barWidth, str, letterWidth, maxTextLength){
if(!maxTextLength){ maxTextLength = 11; }
if(barWidth < letterWidth * 1.5){ return ""; }
else{
var length = Math.floor(barWidth / letterWidth);
return str.slice(0, Math.min(length, maxTextLength));
}
}
}; // mutate END
d3.json("protein_seq.json", function(error, rData){
if(error){ throw error; }
d3.svg.mutate("container", rData, "mutated");
});
</script>
</body>
{
"point": [
{
"x": 996.0,
"y": 3.0,
"change": "Ser996Ile",
"mutation": "Inframe",
"sample": [
"TCGA-C5-A2LT-01A"
]
},
{
"x": 2033.0,
"y": 2.0,
"change": "Asp2033Gly",
"mutation": "Splice site",
"sample": [
"TCGA-FU-A3HZ-01A"
]
},
{
"x": 1137.0,
"y": 4.0,
"change": "Asp1137His",
"mutation": "Frameshift",
"sample": [
"TCGA-FU-A23L-01A"
]
},
{
"x": 459.0,
"y": 1.0,
"change": "Asp459His",
"mutation": "Missense",
"sample": [
"TCGA-EK-A2PK-01A"
]
},
{
"x": 299.0,
"y": 3.0,
"change": "Ser299Cys",
"mutation": "Frameshift",
"sample": [
"TCGA-VS-AA62-01A"
]
},
{
"x": 918.0,
"y": 2.0,
"change": "Glu918Lys",
"mutation": "Inframe",
"sample": [
"TCGA-VS-A94X-01A"
]
},
{
"x": 2527.0,
"y": 3.0,
"change": "Asp2527Asn",
"mutation": "Frameshift",
"sample": [
"TCGA-C5-A1BQ-01C"
]
},
{
"x": 1482.0,
"y": 1.0,
"change": "Leu1482Pro",
"mutation": "Others",
"sample": [
"TCGA-DS-A1OC-01A"
]
},
{
"x": 1483.0,
"y": 2.0,
"change": "Pro1483Thr",
"mutation": "Missense",
"sample": [
"TCGA-DS-A1OC-01A"
]
},
{
"x": 1137.0,
"y": 1.0,
"change": "Asp1137Asn",
"mutation": "Missense",
"sample": [
"TCGA-IR-A3LK-01A"
]
},
{
"x": 2439.0,
"y": 1.0,
"change": "Arg2439Cys",
"mutation": "Missense",
"sample": [
"TCGA-2W-A8YY-01A"
]
},
{
"x": 1544.0,
"y": 1.0,
"change": "Glu1544Gln",
"mutation": "Missense",
"sample": [
"TCGA-FU-A23K-01A"
]
}
],
"domain": [
{
"start": 0,
"end": 2843.0,
"desc": "",
"link": [
{
"name": "P25054",
"address": "http://www.uniprot.org/uniprot/P25054"
}
],
"pfam": ""
},
{
"start": 732.0,
"end": 1019.0,
"desc": "Arm_APC_u3, Armadillo-associated region on APC",
"pfam": "PF16629",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF16629"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF16629"
}
]
},
{
"start": 2670.0,
"end": 2843.0,
"desc": "Voluptas aut, velit harum amet",
"pfam": "PF05937",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF05937"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF05937"
}
]
},
{
"start": 4.0,
"end": 55.0,
"desc": "APC_N_CC, Coiled-coil N-terminus of APC, dimerisation domain",
"pfam": "PF16689",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF16689"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF16689"
}
]
},
{
"start": 1662.0,
"end": 1715.0,
"desc": "",
"pfam": "PF16634",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF16634"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF16634"
}
]
},
{
"start": 1372.0,
"end": 1393.0,
"desc": "APC_crr, APC cysteine-rich region",
"pfam": "PF05923",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF05923"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF05923"
}
]
},
{
"start": 2008.0,
"end": 2030.0,
"desc": "APC_crr, APC cysteine-rich region",
"pfam": "PF05923",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF05923"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF05923"
}
]
},
{
"start": 1259.0,
"end": 1280.0,
"desc": "APC_crr, APC cysteine-rich region",
"pfam": "PF05923",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF05923"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF05923"
}
]
},
{
"start": 1637.0,
"end": 1660.0,
"desc": "APC_crr, APC cysteine-rich region",
"pfam": "PF05923",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF05923"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF05923"
}
]
},
{
"start": 1841.0,
"end": 1865.0,
"desc": "APC_crr, APC cysteine-rich region",
"pfam": "PF05923",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF05923"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF05923"
}
]
},
{
"start": 1950.0,
"end": 1972.0,
"desc": "APC_crr, APC cysteine-rich region",
"pfam": "PF05923",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF05923"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF05923"
}
]
},
{
"start": 1486.0,
"end": 1509.0,
"desc": "APC_crr, APC cysteine-rich region",
"pfam": "PF05923",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF05923"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF05923"
}
]
},
{
"start": 1746.0,
"end": 1839.0,
"desc": "",
"pfam": "PF16635",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF16635"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF16635"
}
]
},
{
"start": 2224.0,
"end": 2575.0,
"desc": "Lorem ipsum dolor sit amet",
"pfam": "PF05956",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF05956"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF05956"
}
]
},
{
"start": 2031.0,
"end": 2051.0,
"desc": "SAMP, SAMP Motif",
"pfam": "PF05924",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF05924"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF05924"
}
]
},
{
"start": 1567.0,
"end": 1588.0,
"desc": "SAMP, SAMP Motif",
"pfam": "PF05924",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF05924"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF05924"
}
]
},
{
"start": 1717.0,
"end": 1737.0,
"desc": "SAMP, SAMP Motif",
"pfam": "PF05924",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF05924"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF05924"
}
]
},
{
"start": 1282.0,
"end": 1368.0,
"desc": "",
"pfam": "PF16633",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF16633"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF16633"
}
]
},
{
"start": 1867.0,
"end": 1947.0,
"desc": "",
"pfam": "PF16636",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF16636"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF16636"
}
]
},
{
"start": 649.0,
"end": 689.0,
"desc": "Arm, Armadillo/beta-catenin-like repeat",
"pfam": "PF00514",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF00514"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF00514"
}
]
},
{
"start": 518.0,
"end": 552.0,
"desc": "Arm, Armadillo/beta-catenin-like repeat",
"pfam": "PF00514",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF00514"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF00514"
}
]
},
{
"start": 1155.0,
"end": 1169.0,
"desc": "APC_15aa, APC 15 residue motif",
"pfam": "PF05972",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF05972"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF05972"
}
]
},
{
"start": 1020.0,
"end": 1034.0,
"desc": "APC_15aa, APC 15 residue motif",
"pfam": "PF05972",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF05972"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF05972"
}
]
},
{
"start": 1172.0,
"end": 1186.0,
"desc": "APC_15aa, APC 15 residue motif",
"pfam": "PF05972",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF05972"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF05972"
}
]
},
{
"start": 1036.0,
"end": 1135.0,
"desc": "",
"pfam": "PF16630",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF16630"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF16630"
}
]
},
{
"start": 131.0,
"end": 206.0,
"desc": "Suppressor_APC, Adenomatous polyposis coli tumour suppressor protein",
"pfam": "PF11414",
"link": [
{
"name": "PFAM",
"address": "http://pfam.xfam.org/family/PF11414"
},
{
"name": "Mutation Aligner",
"address": "http://mutationaligner.org/domains/PF11414"
}
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment