Статьи

Визуализация графиков и Neo4j — часть третья

Как я и обещал в моем предыдущем посте , я хотел бы сделать кое — что на D3.js .

Мы собираемся взять один из их примеров визуализации и визуализировать следующий график.

Чтобы создать наш график, мы возьмем имена 20 человек: создадим для них узлы, добавим их в индекс и случайным образом свяжем их вместе.

Обратите внимание, что мы используем команду Neography Batch для одновременного создания всего графика.

01	def create_graph
02	  neo = Neography::Rest.new
03	  graph_exists = neo.get_node_properties(1)
04	  return if graph_exists && graph_exists['name']
05	 
06	  names = %w[Max Agam Lester Musannif Adel Andrey Ryan James Bruce Tim Pinaki Mark Peter Anne Helene Corey Ben Rob Pramod Prasanna]
07	 
08	  commands = names.map{ |n| [:create_node, {"name" => n}]}
09	  names.each_index do |x|
10	    commands << [:add_node_to_index, "nodes_index", "type", "User", "{#{x}}"]
11	    follows = names.size.times.map{|y| y}
12	    follows.delete_at(x)
13	    follows.sample(1 + rand(5)).each do |f|
14	      commands << [:create_relationship, "follows", "{#{x}}", "{#{f}}"]   
15	    end
16	  end
17	 
18	  batch_result = neo.batch *commands
19	end

Мы не будем делать ошибку, оставляя метод create_graph снова общедоступным, поэтому создадим для него задачу Rake.

1	require 'neography/tasks'
2	require './d3.rb'
3	 
4	namespace :neo4j do
5	  task :create do
6	    create_graph
7	  end
8	end

Мы будем использовать Cypher для создания матрицы последователей, которую мы будем использовать для заполнения нашего скрипта D3.

1	def follower_matrix
2	  neo = Neography::Rest.new
3	  cypher_query =  " START a = node:nodes_index(type='User')"
4	  cypher_query << " MATCH a-[:follows]->b"
5	  cypher_query << " RETURN a.name, collect(b.name)"
6	  neo.execute_query(cypher_query)["data"]
7	end 

Функция collect возвращает строку с массивом внутри нее, поэтому нам нужно немного перебрать строки, чтобы превратить ее в правильный массив, а затем преобразовать все в JSON.

1	get '/follows' do
2	  follower_matrix.map{|fm| {"name" => fm[0], "follows" => fm[1][1..(fm[1].size - 2)].split(", ")} }.to_json
3	end

Наша функция D3 — небольшая вариация на примере аккордового аккорда в репозитории github D3:

01	var r1 = 960 / 2,
02	    r0 = r1 - 120;
03	 
04	var fill = d3.scale.category20c();
05	 
06	var chord = d3.layout.chord()
07	    .padding(.04)
08	    .sortSubgroups(d3.descending)
09	    .sortChords(d3.descending);
10	 
11	var arc = d3.svg.arc()
12	    .innerRadius(r0)
13	    .outerRadius(r0 + 20);
14	 
15	var svg = d3.select("body").append("svg")
16	    .attr("width", r1 * 2)
17	    .attr("height", r1 * 2)
18	  .append("g")
19	    .attr("transform", "translate(" + r1 + "," + r1 + ")");
20	 
21	d3.json("follows", function(follows) {
22	  var indexByName = {},
23	      nameByIndex = {},
24	      matrix = [],
25	      n = 0;
26	 
27	  function name(name) {
28	    return name
29	  }
30	 
31	  // Compute a unique index for each name.
32	  follows.forEach(function(d) {
33	    d = name(d.name);
34	    if (!(d in indexByName)) {
35	      nameByIndex[n] = d;
36	      indexByName[d] = n++;
37	    }
38	  });
39	 
40	  // Construct a square matrix counting relationships.
41	  follows.forEach(function(d) {
42	    var source = indexByName[name(d.name)],
43	        row = matrix[source];
44	    if (!row) {
45	     row = matrix[source] = [];
46	     for (var i = -1; ++i < n;) row[i] = 0;
47	    }
48	    d.follows.forEach(function(d) { row[indexByName[name(d)]]++; });
49	  });
50	 
51	  chord.matrix(matrix);
52	 
53	  var g = svg.selectAll("g.group")
54	      .data(chord.groups)
55	    .enter().append("g")
56	      .attr("class", "group");
57	 
58	  g.append("path")
59	      .style("fill", function(d) { return fill(d.index); })
60	      .style("stroke", function(d) { return fill(d.index); })
61	      .attr("d", arc);
62	 
63	  g.append("text")
64	      .each(function(d) { d.angle = (d.startAngle + d.endAngle) / 2; })
65	      .attr("dy", ".35em")
66	      .attr("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
67	      .attr("transform", function(d) {
68	        return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
69	            + "translate(" + (r0 + 26) + ")"
70	            + (d.angle > Math.PI ? "rotate(180)" : "");
71	      })
72	      .text(function(d) { return nameByIndex[d.index]; });
73	 
74	  svg.selectAll("path.chord")
75	      .data(chord.chords)
76	    .enter().append("path")
77	      .attr("class", "chord")
78	      .style("stroke", function(d) { return d3.rgb(fill(d.source.index)).darker(); })
79	      .style("fill", function(d) { return fill(d.source.index); })
80	      .attr("d", d3.svg.chord().radius(r0));
81	 
82	});

Весь код доступен на Github .
Наконец, мы разместим все это на Heroku, как я уже показывал:

1	git clone git@github.com:maxdemarzi/d3_js_intro.git
2	cd d3_js_intro
3	bundle install
4	heroku create --stack cedar
5	heroku addons:add neo4j
6	git push heroku master
7	heroku run rake neo4j:create

   

Довольно, не правда ли?

Источник:
http://maxdemarzi.com/2012/02/02/graph-visualization-and-neo4j-part-three/