2016-09-30 3 views
1

Я в процессе создания плагина mpld3 для преобразования графика NetworkX в силовой макет. У меня возникли проблемы с пониманием того, как масштабирование осей работает в mpld3 и как я могу заставить его перевести на график компоновки сил.NetworkX D3 Force Layout Plugin для mpld3

import matplotlib 
import matplotlib.pyplot as plt 
import numpy as np 
import mpld3 
from mpld3 import plugins, utils 

from networkx.readwrite.json_graph import node_link_data 

class NetworkXD3ForceLayoutView(plugins.PluginBase): 
    """A simple plugin showing how multiple axes can be linked""" 

    JAVASCRIPT = """ 
    mpld3.register_plugin("networkxd3forcelayoutview", NetworkXD3ForceLayoutViewPlugin); 
    NetworkXD3ForceLayoutViewPlugin.prototype = Object.create(mpld3.Plugin.prototype); 
    NetworkXD3ForceLayoutViewPlugin.prototype.constructor = NetworkXD3ForceLayoutViewPlugin; 
    NetworkXD3ForceLayoutViewPlugin.prototype.requiredProps = ["graph", "charge", "linkDistance", "gravity"]; 

    function NetworkXD3ForceLayoutViewPlugin(fig, props){ 
     mpld3.Plugin.call(this, fig, props); 
    }; 

    var color = d3.scale.category20(); 


    NetworkXD3ForceLayoutViewPlugin.prototype.draw = function(){ 

     var zoom = d3.behavior.zoom(); 

     var height = this.fig.height 
     var width = this.fig.width 

     var graph = this.props.graph  
     var gravity = this.props.gravity.toFixed() 
     var charge = this.props.charge.toFixed() 
     var linkDistance = this.props.linkDistance.toFixed() 

     console.log(graph) 
     var ax = this.fig.axes[0] // axis required for zoomx and zoomy presumably? 

     var g = d3.select('.mpld3-axes').append('g') // This is right? 

     var vis = g.append('svg') 
      .attr('width', this.width) 
      .attr('height', this.height); 

     force = d3.layout.force() 
     .gravity(gravity) 
     .charge(charge) 
     .linkDistance(linkDistance) 
     .nodes(graph.nodes) 
     .links(graph.links) 
     .size([width, height]) 
     .start() 

     var link = vis.selectAll("line.link") 
      .data(graph.links) 
     .enter().append("svg:line") 
      .attr("class", "link") 
      .attr("stroke", "black") 
      .style("stroke-width", function(d) { return Math.sqrt(d.value); }) 
      .attr("x1", function(d) { return d.source.x; }) 
      .attr("y1", function(d) { return d.source.y; }) 
      .attr("x2", function(d) { return d.target.x; }) 
      .attr("y2", function(d) { return d.target.y; }); 

     var node = vis.selectAll("circle.node") 
      .data(graph.nodes) 
     .enter().append("svg:circle") 
      .attr("class", "node") 
      .attr("cx", function(d) { return d.x; }) 
      .attr("cy", function(d) { return d.y; }) 
      .attr("r", 5) 
      .style("fill", function(d) { return d.color; }) 
      .call(force.drag); 

     node.append("svg:title") 
      .text(function(d) { return d.name; }); 

     vis.style("opacity", 1e-6) 
     .transition() 
      .duration(1000) 
      .style("opacity", 1); 
     force.on("tick", function() { 
     link.attr("x1", function(d) { return d.source.x; }) 
      .attr("y1", function(d) { return d.source.y; }) 
      .attr("x2", function(d) { return d.target.x; }) 
      .attr("y2", function(d) { return d.target.y; }); 

     node.attr("cx", function(d) { return d.x; }) 
      .attr("cy", function(d) { return d.y; }); 
     }); 

     zoom.on("zoom", function() { 
      g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); 
     }) 

     g.call(zoom) 

    }; 
    """ 

    def __init__(self, G, gravity=0.5, link_distance=20, charge=-10): 
     self.dict_ = {"type": "networkxd3forcelayoutview", 
         "graph": node_link_data(G), 
         "gravity": gravity, 
         "charge": charge, 
         "linkDistance": link_distance} 


fig, ax = plt.subplots(1, 1) 

# scatter periods and amplitudes 

np.random.seed(0) 

import networkx as nx 
G=nx.Graph() 
G.add_node(1, color='red') 
G.add_edge(1,2) 

plugins.connect(fig, NetworkXD3ForceLayoutView(G)) 

mpld3.display() 

Вышеупомянутый минимальный рабочий пример, который я смог запустить в ноутбуке. Я добавил обратный вызов масштабирования к элементу группы, который содержит график в настоящее время, поэтому график будет увеличиваться, если мышь, если над узлом. Как заставить его работать, когда я использую масштабирование на пользовательской панели инструментов. Правильно ли это подходит для создания плагина силового макета? Я также опубликовал here, но может быть, что SO - лучшее место для этого вопроса.

+0

Ох, круто, я всегда этого хотел! Объекты 'zoom' действительно сложны, и я не помню, как они работают. Вы можете найти их в своем объекте 'ax', как' ax.zoom', 'ax.zoom_x' и' ax.zoom_y'. Хотелось бы, чтобы я лучше помнил, что они делают, но чтобы узнать, я бы начал копать через код js здесь: https://github.com/mpld3/mpld3/blob/73473c9ffd8ea36a1912244e664fdb7ce391fd8b/src/core/axes.js#L171 –

+0

Спасибо за ответ! Я начинаю копаться в коде, и я нажимаю на некоторые стены. Я прокомментирую здесь, как только у меня появится лучшее представление о том, что происходит. Будет хорошо иметь это в mpld3 :) – kdheepak

+0

Хорошо, у меня есть версия! :) Еще несколько перегибов, чтобы сгладить, и должно быть хорошо идти. Считаете ли вы, что это должно делать в основном репо? Я думаю, это будет хорошо, если мы создадим пользовательский репозиторий плагинов в организации. Мысли? – kdheepak

ответ

0

Я опубликовал рабочий пример here для тех, кто ищет что-то подобное.

Смежные вопросы