实现如下效果:
一、json数据:
{
"data": [
[{
"name": "GO:0051560",
"x": 0.5,
"y": 9.8563728170021,
"size": 5,
"desc": "mitochondrial calcium ion homeostasis"
}, {
"name": "GO:0051561",
"x": 0.57142857142857,
"y": 9.0049471920302,
"size": 4,
"desc": "positive regulation of mitochondrial calcium ion concentration"
}, {
"name": "GO:0030239",
"x": 0.75,
"y": 8.4532838414318,
"size": 3,
"desc": "myofibril assembly"
}, {
"name": "GO:0032101",
"x": 0.15277777777778,
"y": 8.2530711398424,
"size": 33,
"desc": "regulation of response to external stimulus"
}, {
"name": "GO:1902579",
"x": 0.22916666666667,
"y": 8.2296343948674,
"size": 11,
"desc": "multi-organism localization"
}, {
"name": "GO:0044766",
"x": 0.22916666666667,
"y": 8.2296343948674,
"size": 11,
"desc": "multi-organism transport"
}, {
"name": "GO:2001244",
"x": 0.31578947368421,
"y": 7.5033794951248,
"size": 6,
"desc": "positive regulation of intrinsic apoptotic signaling pathway"
}, {
"name": "GO:0048008",
"x": 0.44444444444444,
"y": 7.3720590625498,
"size": 4,
"desc": "platelet-derived growth factor receptor signaling pathway"
}, {
"name": "GO:0006921",
"x": 0.44444444444444,
"y": 7.3720590625498,
"size": 4,
"desc": "cellular component disassembly involved in execution phase of apoptosis"
}, {
"name": "GO:0051258",
"x": 0.35714285714286,
"y": 7.3124290040172,
"size": 5,
"desc": "protein polymerization"
}, {
"name": "GO:1902583",
"x": 0.22222222222222,
"y": 7.293490998686,
"size": 10,
"desc": "multi-organism intracellular transport"
}, {
"name": "GO:0075733",
"x": 0.22222222222222,
"y": 7.293490998686,
"size": 10,
"desc": "intracellular transport of virus"
}, {
"name": "GO:0032071",
"x": 0.6,
"y": 7.2328721660712,
"size": 3,
"desc": "regulation of endodeoxyribonuclease activity"
}, {
"name": "GO:0071447",
"x": 1,
"y": 6.8972551025776,
"size": 2,
"desc": "cellular response to hydroperoxide"
}, {
"name": "GO:0051490",
"x": 1,
"y": 6.8972551025776,
"size": 2,
"desc": "negative regulation of filopodium assembly"
}, {
"name": "GO:0039692",
"x": 1,
"y": 6.8972551025776,
"size": 2,
"desc": "single stranded viral RNA replication via double stranded DNA intermediate"
}],
[{
"name": "GO:0031674",
"x": 0.8,
"y": 11.595760208652,
"size": 4,
"desc": "I band"
}, {
"name": "GO:0033116",
"x": 0.27586206896552,
"y": 8.1395994248895,
"size": 8,
"desc": "endoplasmic reticulum-Golgi intermediate compartment membrane"
}],
[{
"name": "GO:0046961",
"x": 0.38461538461538,
"y": 7.8387786335014,
"size": 5,
"desc": "proton-transporting ATPase activity, rotational mechanism"
}, {
"name": "GO:0036442",
"x": 0.35714285714286,
"y": 7.3124290040172,
"size": 5,
"desc": "hydrogen-exporting ATPase activity"
}]
],
"size": {
"width": 750,
"height": 550
},
"legend": [{
"name": "BP",
"desc": "Biological Process"
}, {
"name": "CC",
"desc": "Cellular Component"
}, {
"name": "MF",
"desc": "Molecular Function"
}],
"params": {
"title": "GO enrichment analysis(Model_vs_Con_all)",
"x_label": "Rich Factor",
"y_label": "-log10(Pvalue)",
"disperse": true,
"show_grid": false,
"bubble_labels": [],
"show_label": false,
"format": "e",
"colors": ["#F44336", "#FF00FF", "#388E3C"]
}
}
参数说明:
- 用途:气泡图(不需要指定的属性可以去掉)
- *data[name:标签,x:x轴,y:y轴, size:大小(可以不写)]
- *legend:图例名称
- title:标题
- disperse:[true:分散型, false:密集型],默认是密集型
- show_grid:显示网格,针对密集型才有
- bubble_labels:哪些散点的标签显示
- show_label:false,true :是否显示标签
- format:e(科学计数法),如果没有特殊的格式要求可以不写
以下为toolip的样式:
<style>
.bubble_tooltip{
font-family:simsun;
font-size:16px;
width:120;
height:auto;
position:absolute;
text-align:center;
border-style:solid;
border-width:1px;
background-color:white;
border-radius:5px;
text-align:left;
font-size:12px;
}
</style>
二、用到的js插件
#jquery插件
<script src="jquery-1.8.3.min.js"></script>
#d3 v3版本插件
<script src="d3-3.min.js"></script>
#图的实现文件
<script src="bubble.js"></script>
三、图形的调用
var content = $.parseJSON($('textarea').val()); //从textarea里面取出图的json数据并转化为object
graph.bubble("container", content); //把生成的图放在#container里面
四、以下为图的代码实现(D3.js实现)
/* globals jQuery: true, d3: true */
var graph = {
/**
* 气泡图
*
**/
bubble:function(container, content)
{
d3.select('#'+container).select('svg').remove();
var colors = ["#388E3C", "#F44336", "#0288D1", "#FF9800", "#727272", "#E91E63", "#673AB7", "#8BC34A", "#2196F3", "#D32F2F", "#FFC107", "#BDBDBD", "#F8BBD0", "#3F51B5", "#CDDC39", "#009688", "#C2185B", "#FFEB3B", "#212121", "#FFCCBC", "#BBDEFB", "#0099CC", "#FFcc99"];
var width = content.size.width;
var height = content.size.height;
var padding = {
left:90,
top:70,
right:80,
bottom:70,
}
var grid_width = width - padding.left - padding.right;
var grid_height = height - padding.top - padding.bottom;
var max_x, max_y = 0, min_x, min_y;
var max_xs = new Array();
var min_xs = new Array();
// 根据密集型和分散型区别计算x轴的最大值最小值, disperse
for (var i in content.data) {
if (typeof(content.params.disperse) != 'undefined' && content.params.disperse == true) {
min_x = max_x = 0;
}
for (var j in content.data[i]){
max_x = max_x >= content.data[i][j]['x'] ? max_x : content.data[i][j]['x'];
max_y = max_y >= content.data[i][j]['y'] ? max_y : content.data[i][j]['y'];
min_x = min_x < content.data[i][j]['x'] ? min_x : content.data[i][j]['x'];
min_y = min_y < content.data[i][j]['y'] ? min_y : content.data[i][j]['y'];
}
if (typeof(content.params.disperse) != 'undefined' && content.params.disperse == true) {
max_xs.push(max_x);
min_xs.push(min_x);
}
}
if (typeof(content.params.disperse) == 'undefined' || content.params.disperse == false) {
max_xs.push(max_x);
min_xs.push(min_x);
}
for (var i in max_xs){
max_xs[i] += max_xs[i]/5;
}
max_y += max_y/5;
for (var i in min_xs) {
if (min_xs[i] != 0){
min_xs[i] -= min_xs[i] /5;
}
}
if (min_y != 0) {
min_y -= min_y/5;
}
var draw_bg_padding = 10;
var draw_bg_scale = draw_bg_padding/4;
var draw_bg_width = grid_width / content.data.length - (content.data.length -1)* draw_bg_scale;
// 定义比例尺及刻度函数
var y_scale = d3.scale.linear()
.domain([min_y, max_y])
.range([grid_height, 0]);
var yaxis = d3.svg.axis()
.scale(y_scale)
.orient('left');
if (typeof(content.params.format) != 'undefined' && content.params.format == 'e') {
yaxis.tickFormat(function(d) {
return d.toExponential(1);
});
}
var svg = d3.select('#'+container)
.append('svg')
.attr('version', '1.1')
.attr('style', 'font-family:arial')
.attr('xmlns', 'http://www.w3.org/2000/svg')
.attr('width', width)
.attr('height', height);
// 标题
var title = typeof(content.params.title) != 'undefined' ? content.params.title : '';
var title_width = 20;
if (title != '') {
var title_x = width/2 - title.length *title_width/2;
svg.append('text').text(title).attr('transform', 'translate('+title_x+','+padding.top/2+')');
}
var x_label = typeof(content.params.x_label) != 'undefined' ? content.params.x_label : 'x轴坐标测试';
if (x_label != '') {
var xlabel_x = width/2 - x_label.length *title_width/2;
svg.append('text').text(x_label)
.attr('transform', 'translate('+xlabel_x+','+(height-padding.bottom /2)+')')
.attr('style', 'font-size:12px');
}
var y_label = typeof(content.params.y_label) != 'undefined' ? content.params.y_label : 'y轴坐标测试';
if (y_label != '') {
var ylabel_y = height/2 - y_label.length *title_width/6;
svg.append('text').text(y_label)
.attr('transform', 'translate('+padding.left/3+', '+ylabel_y+')rotate(-90)')
.attr('style', 'font-size:12px');
}
var main = svg.append('g')
.attr('transform', 'translate('+padding.left+','+padding.top+')');
var yaxis_obj = main.append('g')
.attr('class', 'yaxis')
.attr('style', 'font-size:11px')
.attr('text-anchor', 'start')
.call(yaxis);
// 设置y坐标轴的样式
yaxis_obj.select('.domain')
.attr('stroke', '#000')
.attr('fill','none');
yaxis_obj.selectAll('line')
.attr('stroke', '#000');
var data = new Array();
var circle_size_default = 8;
var text_width = 3.7;
var fill_opacity = 0.3, stroke_opacity = 0.8;
var tooltip = d3.select("body").append("div")
.attr("class","bubble_tooltip") //用于css设置类样式
.style({"opacity":0.0, "z-index": 1000});
// 画圆
function drawCircle(data, i)
{
main_draw.selectAll('.circles'+'_'+i)
.data(data)
.enter()
.append('circle')
.attr('class', 'circles'+'_'+i)
.attr('select', 1)
.attr('name', function(d) {
return d.name;
})
.attr('cx', function(d) {
return x_scale(d['x']);
})
.attr('cy', function(d) {
return y_scale(d['y']);
})
.attr('r', function(d, i) {
return typeof(d.size) != 'undefined' ? d.size : circle_size_default;
})
.attr('fill-opacity', fill_opacity)
.attr('fill', function(d, ii) {
return typeof(content.params.colors) != 'undefined' ? content.params.colors[i] : colors[i%colors.length];
})
.attr('stroke', function(d, ii) {
return typeof(content.params.colors) != 'undefined' ? content.params.colors[i] : colors[i%colors.length];
})
.attr('stroke-opacity', stroke_opacity)
.attr('style', 'cursor:pointer')
.on('click', function() {
var self = d3.select(this);
var name = self.attr('name');
var select = self.attr('select');
if (select == '1'){
var text_self = d3.select('.'+name);
if (text_self.attr('select') == '1') {
text_self.attr('select', 0);
text_self.attr('opacity', 0.0);
} else {
text_self.attr('select', 1);
text_self.attr('opacity', 1.0);
}
}
})
.on('mouseover', function(d) {
var self = d3.select(this);
var select = self.attr('select');
if (select == 0) {
return;
}
var page_x = d3.event.pageX;
var page_y = d3.event.pageY+20;
var html = '';
if (typeof(d.name) != 'undefined') {
html += '<b>ID:</b>'+d.name+'<br/>';
}
if (typeof(d.desc) != 'undefined') {
html += '<b>Description:</b> '+d.desc+'<br />';
}
if (typeof(d.size) != 'undefined') {
html += '<b>Size:</b> '+d.size+'<br/>';
}
html += '<b>X:</b> '+d.x+'<br/><b>Y:</b> '+d.y+'<br />';
tooltip.html(html)
.style("left",page_x+"px")
.style("top",page_y+"px")
.style("opacity",0.9)
.style('padding', '5px');
})
.on('mouseout', function() {
tooltip.style('opacity', 0.0);
});
main_draw.selectAll('circle_texts_'+i)
.data(data)
.enter()
.append('text')
.attr('class', function(d){
return 'circle_texts_'+i+' '+d.name;
})
.text(function(d,i) {
return typeof(d.name) != 'undefined' ? d.name : 'NaN';
})
.attr('x', function(d) {
return x_scale(d['x']) - (text_width * d['name'].length)/2;
})
.attr('y', function(d) {
return y_scale(d['y'])- (typeof(d.size)!= 'undefined' ? d.size : circle_size_default)-2;
})
.style('font-size','11')
.attr('select', function(d) {
if (typeof(content.params.show_label) != 'undefined' && content.params.show_label == true){
return 1;
} else if (typeof(content.params.bubble_labels) != 'undefined'){
var bubble_string = '|'+content.params.bubble_labels.join('|')+'|';
if (bubble_string.indexOf(d.name) != -1) {
return 1;
}
} else {
return 0;
}
})
.attr('opacity', function(d) {
if (typeof(content.params.bubble_labels) != 'undefined'){
var bubble_string = '|'+content.params.bubble_labels.join('|')+'|';
}
if (typeof(content.params.show_label) != 'undefined' && content.params.show_label == true){
return 1.0;
} else if (typeof(content.params.bubble_labels) != 'undefined' && bubble_string.indexOf(d.name) != -1){
return 1.0;
} else {
return 0.0;
}
});
}
// 分散型的气泡图
if (typeof(content.params.disperse) != 'undefined' && content.params.disperse == true) {
for (var i in content.data){
var draw_bg_x = i * draw_bg_width+draw_bg_padding*i;
data = content.data[i];
var main_draw = main.append('g')
.attr('class', 'main_draw')
.attr('transform', 'translate('+draw_bg_x+')');
var x_scale = d3.scale.linear()
.domain([min_xs[i], max_xs[i]])
.range([0, draw_bg_width]);
var xaxis = d3.svg.axis()
.scale(x_scale)
.orient('bottom');
var xaxis_obj = main_draw.append('g')
.attr('class', 'xaxis')
.attr('style', 'font-size:11px')
.attr('transform', "translate(0, "+grid_height+")")
.attr('text-anchor', 'start')
.call(xaxis);
main_draw.append('rect')
.attr('class', 'draw_bg')
.attr('width', draw_bg_width)
.attr('height', grid_height)
.attr('opacity', 0.1)
.attr('fill', colors[i%colors.length]);
drawCircle(data, i);
}
} else {
// 密集型的气泡图
var main_draw = main.append('g')
.attr('class', 'main_draw');
var x_scale = d3.scale.linear()
.domain([min_xs[0], max_xs[0]])
.range([0, grid_width]);
if (typeof(content.params.show_grid) != 'undefined' && content.params.show_grid == true) {
// 获取x轴的刻度
var x_ticks = x_scale.ticks();
var y_ticks = y_scale.ticks();
x_ticks.shift();
console.log(y_ticks);
var grid_line = main_draw.append('g')
// 画垂直辅助线
grid_line.selectAll('.grid_xlines')
.data(x_ticks)
.enter()
.append('line')
.attr('class', 'grid_xlines')
.attr('x1', function(d,i) {
return x_scale(d);
})
.attr('y1', 0)
.attr('x2', function(d) {
return x_scale(d);
})
.attr('y2', y_scale(min_y))
.attr('stroke', '#ccc');
// 画水平辅助线
grid_line.selectAll('.grid_ylines')
.data(y_ticks)
.enter()
.append('line')
.attr('class', 'grid_ylines')
.attr('x1', 0)
.attr('y1', function(d,i) {
return y_scale(d);
})
.attr('x2', x_scale(max_xs[0]))
.attr('y2', function(d) {
return y_scale(d);
})
.attr('stroke', '#ccc');
}
var xaxis = d3.svg.axis()
.scale(x_scale)
.orient('bottom');
var xaxis_obj = main_draw.append('g')
.attr('class', 'xaxis')
.attr('style', 'font-size:11px')
.attr('transform', "translate(0, "+grid_height+")")
.attr('text-anchor', 'start')
.call(xaxis);
for (var i in content.data){
data = content.data[i];
drawCircle(data, i);
}
}
// 设置x坐标轴的样式
main.selectAll('.xaxis').selectAll('.domain')
.attr('stroke', '#000')
.attr('fill', 'none');
main.selectAll('.xaxis').selectAll('line')
.attr('stroke', '#000');
// 画图例
var legend_obj = svg.append('g').attr('transform', 'translate('+(width-padding.right+15)+',10)');
legend_obj.selectAll('.legend_'+i)
.data(content.legend)
.enter()
.append('circle')
.attr('class', function(d,i) {
return 'legend_'+i;
})
.attr('cx', 0)
.attr('cy', function(d, i) {
return 20 * i;
})
.attr('r', 5)
.attr('fill', function(d, i) {
return typeof(content.params.colors) != 'undefined' ? content.params.colors[i] : colors[i%colors.length];
})
var legend_text_obj = legend_obj.selectAll('.legend_text_'+i)
.data(content.legend)
.enter()
.append('text')
.attr('class', function(d, i) {
return 'legend_text_'+i;
})
.text(function(d) {
return d.name;
})
.attr('x', 10)
.attr('y', function(d, i) {
return i * 20+4;
})
.attr('select', 1)
.attr('style', "font-size:11px;cursor:pointer")
.attr('text-anchor', 'start')
.on('mouseover', function(d) {
var page_x = d3.event.pageX;
var page_y = d3.event.pageY+20;
var html = d.desc;
tooltip.html(html)
.style("left",page_x+"px")
.style("top",page_y+"px")
.style("opacity",0.9)
.style('padding', '5px');
})
.on('mouseout', function() {
tooltip.style('opacity',0.0);
});
if (typeof(content.params.disperse) == 'undefined' || content.params.disperse == false) {
legend_text_obj.on('click', function() {
var obj = d3.select(this);
var group_ids = obj.attr('class').split('_');
var group_id = group_ids[2];
var is_select = obj.attr('select');
if (is_select == '1') {
obj.attr('select', 0);
obj.attr('fill', '#ccc');
legend_obj.select('.legend_'+group_id)
.attr('fill', '#ccc');
d3.selectAll('.circles_'+group_id)
.attr('fill-opacity', 0.0)
.attr('stroke-opacity', 0.0)
.attr('style', 'z-index:-1;cursor:pointer')
.attr('select', 0);
d3.selectAll('.circle_texts_'+group_id)
.attr('opacity', 0.0)
.attr('select', 0);
} else {
obj.attr('select', 1);
obj.attr('fill', '#000');
legend_obj.select('.legend_'+group_id)
.attr('fill', typeof(content.params.colors) != 'undefined' ? content.params.colors[group_id] : colors[group_id%colors.length]);
d3.selectAll('.circles_'+group_id)
.attr('fill-opacity', fill_opacity)
.attr('stroke-opacity', stroke_opacity)
.attr('select', 1);
if (typeof(content.params.show_label) == 'undefined' || content.params.show_label != false){
d3.selectAll('.circle_texts_'+group_id)
.attr('opacity', 1.0)
.attr('select', 1);
}
}
});
}
}
};
以上就是图的全部实现,如果有不清楚的地方可以留言,欢迎交流