示例#1
0
def draw(G, pos=None, **kwargs):
    """
    Draw the graph G using hvPlot.

    Draw the graph with hvPlot with options for node positions,
    labeling, titles, and many other drawing features.

    Parameters
    ----------
    G : graph
       A networkx graph
    pos : dictionary, optional
       A dictionary with nodes as keys and positions as values.
       If not specified a spring layout positioning will be computed.
       See :py:mod:`networkx.drawing.layout` for functions that
       compute node positions.
    arrows : bool, optional (default=True)
       For directed graphs, if True draw arrowheads.
       Note: Arrows will be the same color as edges.
    arrowhead_length : float, optional (default=0.025)
       The length of the arrows as fraction of the overall extent of
       the graph
    with_labels :  bool, optional (default=True)
       Set to True to draw labels on the nodes.
    nodelist : list, optional (default G.nodes())
       Draw only specified nodes
    edgelist : list, optional (default=G.edges())
       Draw only specified edges
    node_size : scalar or array, optional (default=300)
       Size of nodes.  If an array is specified it must be the
       same length as nodelist.
    node_color : color string, node attribute, or array of floats, (default='r')
       Can be a single color, the name of an attribute on the nodes or
       sequence of colors with the same length as nodelist.  If the
       node_color references an attribute on the nodes or is a list of
       values they will be colormapped using the cmap and vmin, vmax
       parameters.
    node_shape :  string, optional (default='o')
       The shape of the node. Specification is as valid bokeh marker.
    alpha : float, optional (default=1.0)
       The node and edge transparency
    cmap : Colormap, optional (default=None)
       Colormap for mapping intensities of nodes
    vmin,vmax : float, optional (default=None)
       Minimum and maximum for node colormap scaling
    linewidths : [None | scalar | sequence]
       Line width of symbol border (default =1.0)
    edge_width : float, optional (default=1.0)
       Line width of edges
    edge_color : color string, or array of floats (default='r')
       Can be a single color, the name of an attribute on the edges or
       sequence of colors with the same length as the edges.  If the
       edge_color references an attribute on the edges or is a list of
       values they will be colormapped using the edge_cmap and
       edge_vmin, edge_vmax parameters.
    edge_cmap : Matplotlib colormap, optional (default=None)
       Colormap for mapping intensities of edges
    edge_vmin,edge_vmax : floats, optional (default=None)
       Minimum and maximum for edge colormap scaling
    style : string, optional (default='solid')
       Edge line style (solid|dashed|dotted,dashdot)
    labels : dictionary or string, optional (default=None)
       Node labels in a dictionary keyed by node of text labels or
       a string referencing a node attribute
    font_size : int, optional (default=12)
       Font size for text labels
    font_color : string, optional (default='black')
       Font color string
    font_family : string, optional (default='sans-serif')
       Font family
    label : string, optional
       Label for graph legend
    selection_policy : string, optional (default='nodes')
       Whether to select 'nodes', 'edges' or None on tap and selection
       events.
    inspection_policy : string, optional (default='nodes')
       Whether to select 'nodes', 'edges' or None on tap and selection
       events.
    geo : boolean, optional (default=False)
       Whether to return a GeoViews graph
    crs : cartopy.crs.CRS
       A cartopy coordinate reference system (enables a geographic plot)
    height : int, optional (default=400)
       The height of the plot in pixels
    width : int, optional (default=400)
       The width of the plot in pixels
    """
    if pos is None:
        pos = nx.drawing.spring_layout

    if not isinstance(pos, dict):
        pos = pos(G, **kwargs.get('layout_kwargs', {}))

    params, label_params = {}, {}
    label_element = Labels
    if kwargs.get('geo', False) or 'crs' in kwargs:
        try:
            import geoviews
        except ImportError:
            raise ImportError('In order to use geo-related features '
                              'the geoviews library must be available. '
                              'It can be installed with:\n  conda '
                              'install -c pyviz geoviews')
        crs = process_crs(kwargs.get('crs'))
        label_element = geoviews.Labels
        params['cls'] = geoviews.Graph
        params['crs'] = crs
        label_params['crs'] = crs

    # Construct Graph object
    g = _from_networkx(G, pos, **params)

    if 'nodelist' in kwargs:
        g.nodes.data = g.nodes.data.iloc[list(kwargs['nodelist'])]

    if 'edgelist' in kwargs:
        edges = g.array([0, 1])
        comparisons = []
        for edge in kwargs['edgelist']:
            comparisons.append(edges == edge)
        if len(comparisons):
            selector = np.logical_and(*np.logical_or.reduce(comparisons).T)
            g = g.iloc[selector]
        else:
            g = g.iloc[:0]

    # Compute options
    inspection_policy = kwargs.pop('inspection_policy', 'nodes')
    opts = dict(
        axiswise=True,
        arrowhead_length=kwargs.get('arrowhead_length', 0.025),
        directed=kwargs.pop('arrows', isinstance(G, nx.DiGraph)),
        colorbar=kwargs.pop('colorbar', False),
        padding=kwargs.get('padding', 0.1),
        width=kwargs.pop('width', 400),
        height=kwargs.pop('height', 400),
        selection_policy=kwargs.pop('selection_policy', 'nodes'),
        inspection_policy=inspection_policy,
        node_fill_color='red')

    if '_axis_defaults':
        opts.update(xaxis=None, yaxis=None, show_frame=False)

    opts.update({k: kwargs.pop(k) for k in list(kwargs) if k in GraphPlot.style_opts})
    if 'node_size' in opts:
        if isinstance(opts['node_size'], str):
            opts['node_size'] = dim(opts['node_size'])
        opts['node_size'] = np.sqrt(opts['node_size'])
    if 'node_color' in opts:
        opts['node_fill_color'] = opts.pop('node_color')
    if 'edge_color' in opts:
        opts['edge_line_color'] = opts.pop('edge_color')
    if 'node_shape' in kwargs:
        marker = kwargs.pop('node_shape')
        if marker in markers:
            marker_opts = markers[marker]
            marker = marker_opts['marker']
            if 'angle' in marker_opts:
                Store.add_style_opts(Graph, ['node_angle'], 'bokeh')
                opts['node_angle'] = marker_opts['angle']
        opts['node_marker'] = marker
    if 'alpha' in kwargs:
        alpha = kwargs.pop('alpha')
        opts['node_alpha'] = alpha
        opts['edge_alpha'] = alpha
    if 'linewidths' in kwargs:
        opts['node_line_width'] = kwargs.pop('linewidths')
    if 'edge_width' in kwargs:
        opts['edge_line_width'] = kwargs.pop('edge_width')
    if 'style' in kwargs:
        opts['edge_line_dash'] = kwargs.pop('style')

    node_styles = ('node_fill_color', 'node_size', 'node_alpha', 'node_line_width')
    for node_style in node_styles:
        if isinstance(opts.get(node_style), (np.ndarray, list, range)):
            g = g.clone((g.data, g.nodes.add_dimension(node_style, len(g.nodes.vdims), opts[node_style], True)))
            opts[node_style] = node_style

    edge_styles = ('edge_line_color', 'edge_line_alpha', 'edge_alpha', 'edge_line_width')
    for edge_style in edge_styles:
        if isinstance(opts.get(edge_style), (np.ndarray, list, range)):
            g = g.add_dimension(edge_style, len(g.vdims), opts[edge_style], True)
            opts[edge_style] = edge_style

    if opts.get('node_fill_color') in g.nodes.dimensions():
        lims = (kwargs.get('vmin', None), kwargs.get('vmax', None))
        if lims != (None, None):
            dimension = g.nodes.get_dimension(opts.get('node_fill_color'))
            dimension.range = lims

    if opts.get('edge_line_color') in g.dimensions():
        lims = (kwargs.get('edge_vmin', None), kwargs.get('edge_vmax', None))
        if lims != (None, None):
            dimension = g.get_dimension(opts.get('edge_line_color'))
            dimension.range = lims

    if inspection_policy == 'nodes':
        tooltip_dims = [(d.label, 'index_hover' if d in g.nodes.kdims else d.name)
                        for d in g.nodes.kdims[2:] + g.nodes.vdims]
    else:
        tooltip_dims = [(d.label, d.name+'_values' if d in g.kdims else d.name)
                        for d in g.kdims + g.vdims]
    tooltips = [(label, '@{%s}' % dimension_sanitizer(name))
                for label, name in tooltip_dims if name not in node_styles + edge_styles]
    opts['tools'] = [HoverTool(tooltips=tooltips), 'tap']

    g.opts(**opts)

    # Construct Labels
    if kwargs.get('with_labels', kwargs.get('labels', False)):
        label_opts = {k: kwargs.pop(k) for k in list(kwargs) if k in LabelsPlot.style_opts}
        if 'xoffset' in kwargs:
            label_opts['xoffset'] = kwargs.pop('xoffset')
        if 'yoffset' in kwargs:
            label_opts['yoffset'] = kwargs.pop('yoffset')
        if 'font_size' in kwargs:
            label_opts['text_font_size'] = kwargs.pop('font_size')
        if 'font_color' in kwargs:
            label_opts['text_color'] = kwargs.pop('font_color')
        if 'font_family' in kwargs:
            label_opts['text_font'] = kwargs.pop('font_family')
        labels = kwargs.get('labels', g.nodes.kdims[2])
        if isinstance(labels, dict):
            values = g.nodes.array(g.nodes.kdims)
            data = [(x, y, labels[i]) for (x, y, i) in values if i in labels]
            labels = label_element(data, g.nodes.kdims[:2], 'text', **label_params)
        else:
            labels = label_element(g.nodes, g.nodes.kdims[:2], labels, **label_params)
        g = g * labels.opts(**label_opts)

    # Apply label
    if 'label' in kwargs:
        g = g.relabel(kwargs.pop('label'))

    return g