lib/rgraph/graph.rb in rgraph-0.0.6 vs lib/rgraph/graph.rb in rgraph-0.0.8

- old
+ new

@@ -1,32 +1,30 @@ +#encoding: utf-8 require 'csv' require_relative '../../lib/rgraph/link' require_relative '../../lib/rgraph/node' - class Graph - attr_accessor :nodes, :links + attr_accessor :nodes, :links, :infinity def initialize(csv) @nodes = [] @links = [] + @distance = nil + @infinity = 100_000 raise Exception.new("the file must be a .csv") unless File.extname(csv) == ".csv" CSV.foreach(csv, headers: true) do |row| #last because CSV#delete returns [column,value] source_id = row.delete('source').last target_id = row.delete('target').last - unless source = get_node_by_id(source_id) - source = Node.new(id: source_id) - @nodes << source - end - unless target = get_node_by_id(target_id) - target = Node.new(id: target_id) - @nodes << target - end + source = get_node_by_id(source_id) || Node.new(id: source_id) + target = get_node_by_id(target_id) || Node.new(id: target_id) + maybe_add_to_nodes(source, target) + @links << Link.new(source: source, target: target, weight: row['weight'], year: row['year']) end end def each_node(&block) @@ -45,19 +43,116 @@ degrees.inject(:+) / @nodes.size.to_f end def cumulative_degree cached_degrees = degrees - cum = [] + out = [] 0.upto(degrees.max - 1) do |i| - cum[i] = cached_degrees.select{|degree| degree > i}.count + out[i] = cached_degrees.select{|degree| degree > i}.count end - cum.map{|a| a / cum.max.to_f} + out.map{|a| a / out.max.to_f} end + def idistance + dist = Array.new(@nodes.size) { Array.new(@nodes.size, 0) } + + @nodes.sort{|a,b| a.id <=> b.id}.each_with_index do |n1, i| + @nodes.sort{|a,b| a.id <=> b.id}.each_with_index do |n2, j| + if i != j + dist[i][j] = n1.neighbours.include?(n2) ? @links.select{ |link| (link.source == n1 || link.source == n2) && (link.target == n1 || link.target == n2) }.first.weight.to_i : @infinity + end + end + end + dist + end + + def distances + @path = Array.new(@nodes.size) { Array.new(@nodes.size, nil) } + + @distance = idistance + @distance.each_index do |k| + @distance.each_index do |i| + @distance.each_index do |j| + new_dist = @distance[i][k] + @distance[k][j] + if @distance[i][j] > new_dist + @distance[i][j] = new_dist + @path[i][j] = k + end + end + end + end + @distance + end + + def shortest_paths + tmp = [] + + distances unless @my_shortest_paths + + 0.upto(@nodes.size - 1).each do |i| + i.upto(@nodes.size - 1).each do |j| + next if i == j + sp = shortest_path(i, j) + tmp << sp if sp + end + end + + @shortest_paths = tmp + end + + def shortest_path(i, j) + + return [] if @distance[i][j] == @infinity + + k = @path[i][j] + + case k + when @infinity + [] + when nil + [i,j] + else + #We need to do this or k will appear three times + shortest_path(i, k)[0..-2] + [k] + shortest_path(k, j)[1..-1] + end + end + + def between(i) + shortest_paths unless @shortest_paths + + n = @shortest_paths.select{|c| c[1..-2].include?(i)}.size.to_f + m = @shortest_paths.size.to_f + n / m + end + + def betweenness(normalized = false) + bts = 0.upto(@nodes.size - 1).map { |i| between(i) } + + if normalized + max = bts.max + min = bts.min + + bts.map{|bt| (bt - min) / (max - min)} + else + bts + end + end + + def diameter + distances unless @distance + + (distances.flatten - [@infinity]).max + end + private def get_node_by_id(node_id) @nodes.select{|n| n.id == node_id}.first + end + + def maybe_add_to_nodes(*nodes) + nodes.each do |node| + @nodes << node unless get_node_by_id(node.id) + end end end