//## ScatterPlot ## $.fn.scatterPlotCluster = function(data, inSettings) { var plot = scatterPlotCluster($(this).attr('id'), data, inSettings); $(this).data('plot', plot); return plot; } function scatterPlotCluster(id, data, inSettings) { var plot = {}; var extraDefaults = { xTickFormat: ".2s", xNumMaxTicks: 10, xNumMinTicks: 4, yNumMaxTicks: 10, yNumMinTicks: 4, transitionTime: 1000, radius:2, removeTooltip:false }; var settings = addDefaultPlotSettings(inSettings, extraDefaults); plot.settings = settings; // Inner dimension var w = plot.w = settings.dimension.w - settings.padding.l - settings.padding.r; // inner width var h = plot.h = settings.dimension.h - settings.padding.t - settings.padding.b; // inner height var xMin = settings.xMin; var xMax = settings.xMax; var yMin = settings.yMin; var yMax = settings.yMax; //plot.data = data; plot.xMin = xMin; plot.xMax = xMax; plot.yMin = yMin; plot.yMax = yMax; // X axis var xScale = plot.x = d3.scaleLinear() .domain([xMin, xMax]) .range([0, w]); // Y axis var yScale = plot.y = d3.scaleLinear() .domain([yMin, yMax]) .range([h, 0]); var xAxis = d3.axisBottom(xScale) .tickFormat(d3.format(settings.xTickFormat)) .ticks(Math.min(Math.max(Math.ceil(xMax), settings.xNumMinTicks), settings.xNumMaxTicks)); var yAxis = d3.axisLeft(yScale) .tickFormat(d3.format(settings.yTickFormat)) .ticks(Math.min(Math.max(Math.ceil(yMax), settings.yNumMinTicks), settings.yNumMaxTicks)); if (settings.grid) yAxis.tickSizeInner(-w); // Visualisation selection var vis = plot.vis = d3.select('#'+id) .append("svg") .attr("width", settings.dimension.w) .attr("height", settings.dimension.h) .attr("class", "chart scatterplot") .append("g") .attr("transform", "translate(" + settings.padding.l + "," + settings.padding.t + ")"); // Plot area background vis.append("rect") .attr("class", "plotBG") .attr("width", xScale(xMax)) .attr("height", yScale(0)); var group = vis.selectAll(".group") .data(data) .enter().append("g") .attr("class", function(d, i) { return "group group"+i; }) .each(function(d) { d3.select(this).selectAll('.dot') .data(d.data) .enter() .append("circle") .attr("r", settings.radius) .each(function(d) { var el = d3.select(this); el.attr("class", d.class); el.attr('cx', xScale(d.x)); el.attr('cy', yScale(d.y)); if (!settings.removeTooltip) { el.attr("title", d.tooltip); } }); }); // Y axis if (settings.yAxisLabel) { vis.append("g") .attr("class", "y axis") .call(yAxis) .append("text") .attr("transform", "rotate(-90)") .attr("y", 10) .attr("x", -2) .attr("class", "label") .style("text-anchor", "end") .text(settings.yAxisLabel) //.attr('url', data[i].url).style('cursor', 'pointer'); } // X axis if (settings.xAxisLabel) { vis.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + h + ")") .call(xAxis) .append("text") .attr("y",-2) .attr("x", w-2) .attr("class", "label") .style("text-anchor", "end") .text(settings.xAxisLabel); } // Legend if (settings.legend !== '') { var legend = vis.selectAll(".legend") .data(data) .enter().append("g") .attr("class", function(d, i) { return "legend legend"+i; }) .attr("name", function(d, i) { return "legend"+i; }) .attr("transform", function(d, i) { return "translate("+settings.legend.x+"," + (settings.legend.y + i * (settings.legend.size+2)) + ")"; }); legend.append("rect") .attr("width", settings.legend.size) .attr("height", settings.legend.size) .style("fill", function(d, i) { return d.color? d.color : color(i); }); legend.append("text") .attr("x", settings.legend.size+2) .attr("y", settings.legend.size/2) .attr("dy", ".35em") .text(function(d) { return d.name; }); } return plot; } $.fn.bubble_plot = function(data,inSettings) { var plot = {}; var extraDefaults = { xTickFormat: ".2s", xNumMaxTicks: 10, xNumMinTicks: 4, yNumMaxTicks: 10, yNumMinTicks: 4, transitionTime: 1000, radius:2, removeTooltip:false }; var settings = addDefaultPlotSettings(inSettings, extraDefaults); plot.settings = settings; // Inner dimension var w = plot.w = settings.dimension.w - settings.padding.l - settings.padding.r; // inner width var h = plot.h = settings.dimension.h - settings.padding.t - settings.padding.b; // inner height // append the svg object to the body of the page var svg = d3.select($(this).get(0)) .append("svg") .attr("width", settings.dimension.w) .attr("height", settings.dimension.h) .append("g") .attr("transform", "translate(" + settings.padding.l + "," + settings.padding.t + ")"); // Build X scales and axis: var x = d3.scaleBand() .range([ 0, w]) .domain(data.map(function(d) {return d.tissue;})) .padding(0.01); var xAxis = d3.axisBottom(x); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + h + ")") .call(xAxis) .selectAll(".tick text") .attr("class", "barchartlabel") .attr("y", 0) .attr("x", -9) .attr("transform", "rotate(-55)") .style("text-anchor", "end") .each(function(d, i) { if (data[i].tooltip) { d3.select(this).attr('title', d).style('cursor', 'default'); } }); var y = d3.scaleBand() .range([ h, 0 ]) .domain(data.map(function(d) {return d.cell_type; })) .padding(0.01); svg.append("g") .call(d3.axisLeft(y)) .selectAll(".tick text") .each(function(d, i) { if (data[i].tooltip) { d3.select(this).attr('title', data[i].cell_type); } d3.select(this).attr('url',data[i].url).style('cursor', 'pointer'); }); // Build color scale var myColor = d3.scaleLinear() .range(["white", "#69b3a2"]) .domain([1,100]) svg.selectAll() .data(data, function(d) {return d.tissue+':'+d.cell_type;}) .enter() .append("rect") .attr("x", function(d) { return x(d.tissue) }) .attr("y", function(d) { return y(d.cell_type) }) .attr("width", x.bandwidth()) .attr("height", y.bandwidth()) //.attr("width", x.bandwidth() ) //.attr("height", y.bandwidth() ) .style("fill", function(d) { return (d.value == -1)?'white':'white'} ) svg.selectAll() .data(data) .enter() .append("line") .style("stroke", "#E0E0E0") .style("stroke-width", .25) .attr("x1", function(d) { return x(d.tissue) + x.bandwidth()/2 }) .attr("y1", function(d) { return h}) .attr("x2", function(d) { return x(d.tissue) + x.bandwidth()/2 }) .attr("y2", function(d) { return 0 }) svg.selectAll() .data(data) .enter() .append("line") .style("stroke", "#E0E0E0") .style("stroke-width", .25) .attr("x1",0) .attr("y1", function(d) { return y(d.cell_type) + y.bandwidth()/2}) .attr("x2", w) .attr("y2", function(d) { return y(d.cell_type) + y.bandwidth()/2}) //.attr("width", x.bandwidth() ) //.attr("height", y.bandwidth() ) svg.selectAll() .data(data, function(d) {return d.tissue+':'+d.cell_type;}) .enter() .append("circle") .attr("cx", function(d) { return x.bandwidth()/2 + x(d.tissue) }) .attr("cy", function(d) { return y.bandwidth()/2 +y(d.cell_type) }) .attr("r", function(d) { if(d.value >= 0.1) {return d.value*5+3} else if (d.value >= 0) {return 3} else {return 0}}) // //.attr("width", x.bandwidth() ) //.attr("height", y.bandwidth() ) .style("fill", function(d) { return ((d.value==0)?'lightgrey':d.color)} ) .each(function(d, i) { if (data[i].tooltip) { d3.select(this).attr('title', data[i].tooltip); } if (data[i].url) { d3.select(this).attr('url', data[i].url).style('cursor', 'pointer'); } }); $(this).on('click', '[url]', function() { document.location.href = $(this).attr('url'); }); } $.fn.sc_heatmap = function(id,data,col_ct,inSettings) { var plot = {}; var settings = { dimension: {w: 400, h: 200}, padding: {t: 150, r: 80, b: 20, l: 280}, xAxisLabel:'umap', yAxisLabel:'umap', yTickFormat: "", xTickFormat: "0.5", legend: {txtX: 22, txtAnchor: 'start', size:10} } plot.settings = settings; var itemSize = 15; var cellSize = itemSize - 1; var y_elements = Object.keys(data); var cell_groups = {}; var cell_types = {}; var x_elements = []; var max_tpm = {}; var sum_tpm = {}; var cnt_tpm = {}; var mean_tpm = {}; var sd_tpm = {}; var first = true; var last_cell_type = ''; var last_cell_group = ''; $.each(data, function(i, d) { max_tpm[i] = sum_tpm[i] = cnt_tpm[i] = sd_tpm[i] = 0; $.each(d.data, function(i2, d2) { d2.logValue = 1*d2.value==0?0:Math.log(d2.value); if (d2.label=='All cells') return; if (first) x_elements.push(d2.label); max_tpm[i] = Math.max(max_tpm[i], d2.value); sum_tpm[i] = sum_tpm[i] + d2.logValue*1; cnt_tpm[i]++; }); mean_tpm[i] = sum_tpm[i]/cnt_tpm[i]; $.each(d.data, function(i2, d2) { sd_tpm[i] = sd_tpm[i] + Math.pow(d2.logValue*1-mean_tpm[i],2); }); sd_tpm[i] = Math.sqrt(sd_tpm[i] / (cnt_tpm[i] - 1)); if (!cell_types[d.cell_type_name]) cell_types[d.cell_type_name] = {'minY':i, 'maxY':i, 'cell_type_name':d.cell_type_name.split('|')[0],'group_name':d.group_name.split('|')[0]}; else cell_types[d.cell_type_name].maxY = i; if (!cell_groups[d.group_name]) cell_groups[d.group_name] = {'minY':i, 'maxY':i, 'group_name':d.group_name.split('|')[0]}; else cell_groups[d.group_name].maxY = i; first = false; }); var width = plot.w = x_elements.length*itemSize; var height = plot.h = y_elements.length*itemSize; var xScale = plot.x = d3.scaleBand() .domain(x_elements) .range([0, x_elements.length * itemSize]); var xAxis = d3.axisTop() .scale(xScale) .tickFormat(function (d) { return d; }); var yScale = plot.y = d3.scaleBand() .domain(y_elements) .range([0, height]); var yAxis = d3.axisLeft() .scale(yScale) .tickFormat(function (d) { return d; }); var thresholds = [0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95,1]; var colorScale = d3.scaleThreshold() .domain(thresholds) .range(['#FCFFA4', '#F1ED6F', '#F7D340', '#FBB91F', '#FCA007', '#F8870E', '#F17020', '#E55C30', '#D64B40', '#C43C4E', '#B1325A', '#9C2964', '#87216B', '#711A6E', '#5C126E', '#460B6A', '#300A5B', '#190C3E', '#08051E', '#000004']); var vis = plot.vis = d3.select('#'+id) .append("svg") .attr("width", width + settings.padding.l + settings.padding.r) .attr("height", height + settings.padding.t + settings.padding.b) .append("g") .attr("transform", "translate(" + settings.padding.l + "," + settings.padding.t + ")"); // Heatmap var cells = vis.selectAll('rect') .data(y_elements) .enter().append('g') .each(function(yd) { var el = d3.select(this); el.attr('class', yd); $.each(data[yd]['data'], function(i, xd) { var zScore = (xd.value<1 || sd_tpm[yd]==0)? 0 : (xd.logValue-mean_tpm[yd])/sd_tpm[yd]; el.append('rect') .attr('class', 'heatmapCell col' + i) .attr('width', cellSize) .attr('height', cellSize) .attr('y', yScale(yd)) .attr('x', xScale(xd.label)) .attr('fill', colorScale(xd.value<1?0 : xd.value/max_tpm[yd])) .attr('maxNorm', colorScale(xd.value<1?0 : xd.value/max_tpm[yd])) .attr('zScore', d3.interpolateRdBu(1-(zScore+4)/8)) .attr('title', xd.value+' <?= $unit?><br>zScore '+zScore.toFixed(2)); }); }); // Axis vis.append("g") .attr("class", "y axis") .call(yAxis) .selectAll('text') .attr('font-weight', 'normal') .each(function (d, i) { var node = d3.select(this); var txt = node.text(); node.text(''); node.append('a').attr("xlink:href", data[d]['url']).text(txt).attr('style', (i<1?'font-weight:bold':'')); }); vis.append("g") .attr("class", "x axis") .call(xAxis) .selectAll('text') .attr('font-weight', 'normal') .style("text-anchor", "start") .attr("dx", ".8em") .attr("dy", ".5em") .attr("transform", function(d) { return "rotate(-65)"; }); // Cell types var cellTypes = vis.selectAll(".cellTypes") .data(Object.values(cell_types)) .enter().append("g") .attr("title", function(d, i) {return (d.cell_type_name+'<br>'+d.group_name.replace('Current gene','')) }) .attr("class", "markerLabel cellTypes") .attr("transform", function(d, i) { return "translate(" + (-120) + ", " + yScale(d.minY) + ")"; }); cellTypes.append("rect") .attr("width", cellSize/1.5) .attr("height", function(d, i) {return yScale(d.maxY)-yScale(d.minY)+cellSize; }) .style("fill", function(d, i) {return col_ct[d.group_name.trim()][4]}) .style("stroke", '#D9D9D9'); cellTypes.append("text") .attr("transform", function(d, i) {return "translate(-12, "+(yScale(d.maxY)-yScale(d.minY)+cellSize+6)/2+") rotate(0)"; }) .attr("text-anchor", "end") .style('font-size', '10px') .text(function(d, i) {return d.cell_type_name; }); cellTypes.append("line") .style("stroke","#D9D9D9") .attr("stroke-width",1) .attr("x1",0) .attr("x2",120) .attr("y1",function(d) {return (yScale(d.maxY)-yScale(d.minY)+cellSize+0.5)}) .attr("y2",function(d) {return (yScale(d.maxY)-yScale(d.minY)+cellSize+0.5)}); // Hide texts longer than boxes vis.selectAll('.markerLabel').each(function(d, i) { var node = d3.select(this); /*if (node.select('text').node().getBBox().width > node.selectAll('rect').attr('height')) { node.select('text').text('...'); }*/ }); // Legends var maxNorm = vis.selectAll(".maxNorm") .data(thresholds) .enter().append("g") .attr("class", "legend maxNorm") .attr("transform", function(d, i) { return "translate(0," + i * itemSize + ")"; }); maxNorm.append("rect") .attr("x", width + 10) .attr("y", 10) .attr("width", cellSize) .attr("height", cellSize) .style("fill", function(d, i) {return colorScale(d); }); maxNorm.append("text") .attr("x", width + 10+settings.legend.txtX) .attr("y", 10 + 9) .attr("dy", ".35em") .style("text-anchor", settings.legend.txtAnchor) .text(function(d,i) { if (i%5==0) {return d; } else return ''}); var zScore = vis.selectAll(".zScore") .data([-4, -3.6, -3.2, -2.8, -2.4, -2, -1.6, -1.2, -0.8, -0.4, 0, 0.4, 0.8, 1.2, 1.6, 2, 2.4, 2.8, 3.2, 3.6, 4]) .enter().append("g") .attr("class", "legend zScore hidden") .attr("transform", function(d, i) { return "translate(0," + i * itemSize + ")"; }); zScore.append("rect") .attr("x", width + 10) .attr("y", 10) .attr("width", cellSize) .attr("height", cellSize) .style("fill", function(d, i) {return d3.interpolateRdBu(1-(d+4)/8); }); zScore.append("text") .attr("x", width + 10+settings.legend.txtX) .attr("y", 10 + 9) .attr("dy", ".35em") .style("text-anchor", settings.legend.txtAnchor) .text(function(d,i) { if (i%5==0) {return d; } else return ''}); $('.heatmapCell').on('mouseover', function(e) { var color = '#73ff96'; var color = 'black'; var col_class = $(this).attr('class').split(' '); $('.'+col_class[1]).css('stroke', color); var row_class = $(this).closest('g').attr('class'); $('.'+row_class + ' rect').css('stroke', color); }).on('mouseout', function(e) { var col_class = $(this).attr('class').split(' '); $('.'+col_class[1]).css('stroke', ''); var row_class = $(this).closest('g').attr('class'); $('.'+row_class + ' rect').css('stroke', ''); }); };