d3.js绘制气泡图[密集型、分散型]

实现如下效果:

气泡图-分散型

气泡图-密集型
一、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);
					}
				}

			});
		}
	}
};

以上就是图的全部实现,如果有不清楚的地方可以留言,欢迎交流

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值