zoukankan      html  css  js  c++  java
  • 简单图模板 Graph

    仿写 networkx 的功能

    # -*- coding: cp936 -*-
    '''
                                简单图 Graph:
    
    要求:
        关于节点:
            功能1.add_node:
                通过 add_node 一次性加一个节点
                字符串。数字,不论什么能够被哈希的 python 对象都能够当做节点
                包含自己定义的类型或者还有一个图( 构成超图 )
            格式:
                >>> G.add_node( 1 )
        
            功能2.add_nodes_from:
                通过其它容器增加节点,比方list, dict,set,文件或者还有一个图
            格式:
                >>> G.add_nodes_from( [2, 3] )
                >>> H = Graph()
                >>> H.add_path( range( 10 ) )
                >>> G.add_nodes_from( H ) # 构成超图
                
        关于边:
            功能1.add_edge:
                增加一条边
            格式:
                >>> G.add_edge( 1, 2 ) # 在节点 1 和节点 2 之间加一条边
                
            功能2.add_edges_from:
                增加边集
            格式:
                >>> G.add_edges_from( [ ( 2, 4 ), ( 1, 3 ) ] ) # 通过边集来加边
                >>> G.add_edges_from( H.edges() ) # 通过还有一个容器来加边
            注:
                当加边操作的两个节点不存在时。会自己主动创建出来
    
        关于属性:
            每一个图,边。节点都能够拥有 '键值对' 属性
            初始时都为空,可是同意被动态增加和改动属性
            能够通过 add_node, add_edge 操作。或者直接对节点,边。图的字典进行改动
            功能1.节点属性:
                >>> G = Graph( name = 'scheme' )
                >>> G.graph
                { 'name': 'scheme' }
                
                >>> G.add_node( 1, city = 'Nanjing' )
                >>> G.nodes[1]
                { 'city': 'Nanjing' }
                
                >>> G.node
                { 1: {'city': 'Nanjing'} }
                
                >>> G.add_node( 1 ) # 可是再增加同样的节点时,属性不会消失掉
                >>> G.node
                {1: {'city': 'Nanjing'}}
    
                >>> G.add_node( 1, nowtime = '8:00' ) # 增加同样的节点,带上新的属性时,原来的属性会被更新
                >>> G.node
                {1: { 'city': 'Nanjing', 'nowtime': '8:00' } }
    
                >>> G.add_node( 1, nowtime = '8:10' )
                >>> G.node
                {1: { 'city': 'Nanjing', 'nowtime': '8:10' } }
                
    
                >>> G.nodes[1]['nowtime'] = '10:00' # 可是当节点 1 不存在时。这样写属性须要报错
                >>> del G.nodes[1]['nowtime']
    
                >>> G.add_nodes_from( range( 7, 9 ), city = 'shanghai' )
                注:
                    pass
                    
            功能2.边的属性:
                >>> G.add_edge( 1, 2 weight = 100 )
                >>> G.edge
                { 1: { 2: { 'weight': 100 } }, 2: { 1: { 'weight': 100 } } }
                
                >>> G.add_edges_from( [ ( 1, 3 ), ( 2, 3 ) ], weight = 111 )
                >>> G.edge
                { 1: { 2: { 'weight': 100 }, 3: { 'weight': 111 } },
                2: { 1: { 'weight': 100 }, 3: { 'weight': 111 } },
                3: { 1: { 'weight': 111 }, 2: { 'weight': 111 } } }
                
                >>> G.add_edges_from( [ ( 1, 3, { 'weight': 1 } ), ( 3, 4, { 'weight': 2 } ) ] )
                >>> G.edge
                { 1: { 2: { 'weight': 100 }, 3: { 'weight': 1 } },
                2: { 1: { 'weight': 100 }, 3: { 'weight': 111 } },
                3: { 1: { 'weight': 1 }, 2: { 'weight': 111 },
                4: { 'weight': 2 } }, 4: { 3: { 'weight': 2 } } }
    
                >>> G.edge[1][2]['weight'] = 111111 # 同意直接操作
                或者
                >>> G[1][2]['weight'] = 1111
    
        利用 python 的特性提供快捷操作:
            比方:
                >>> 1 in G
                True
                >>> [ n for n in G if n < 3 ] # 节点迭代
                [1, 2]
                >>> len(G) # 节点个数
                5
                >>> G[1] # 与该点相邻的全部点的属性
                { 2: { 'weight': 100 }, 3: { 'weight': 1 } }
    
            提供 adjacency_iter 来遍历边:
                >>> for node, nbrsdict in G.adjacency_iter():
                        for nbr, attr in nbrsdict.items():
                            if 'weight' in attr:
                                ( node, nbr, attr['weight'] )
    
    			
                (1, 2, 100)
                (1, 3, 1)
                (2, 1, 100)
                (2, 3, 111)
                (3, 1, 1)
                (3, 2, 111)
                (3, 4, 2)
                (4, 3, 2)
    
                >>> [ ( start, end, attr['weight']) 
                for start, end, attr in G.edges( data = True ) if 'weight' in attr ]
                [(1, 2, 100), (1, 3, 1), (2, 3, 111), (3, 4, 2)]
    
       其它一些功能:
           pass
    '''
    
    class Graph( object ):
    
    
        def __init__( self, data = None, **attr ):
            self.graph = {}
            self.node  = {}
            self.adj   = {}
            if data is not None:
                pass
            self.graph.update( attr )
            self.edge = self.adj
    
    
        @property
        def name( self ):
            return self.graph.get( 'name', '' )
    
    
        @name.setter
        def name( self, newname ):
            self.graph['name'] = newname
    
    
        def __str__( self ):
            return self.name
    
    
        def __iter__( self ):
            return iter( self.node )
    
    
        def __contains__( self, node ):
            try:
                return node in self.node
            except TypeError:
                return False
    
    
        def __len__( self ):
            return len( self.node )
    
    
        def __getitem__( self, node ):
            return self.adj[node]
    
    
        def add_node( self, node, attr_dict = None, **attr ):
            if attr_dict is None:
                attr_dict = attr
            else:
                try:
                    attr_dict.update( attr )
                except AttributeError:
                    raise Exception( "The attr_dict argument must be a dictionary." )
            if node not in self.node:
                self.adj[node] = {}
                self.node[node] = attr_dict
            else:
                self.node[node].update( attr_dict )
            
    
        def add_nodes_from( self, nodes, **attr ):
            for node in nodes:
                try:
                    newnode = node not in self.node
                except TypeError:
                    node_, node_dict = node
                    if node_ not in self.node:
                        self.adj[node_] = {}
                        newdict = attr.copy()
                        newdict.update( node_dict )
                        self.node[node_] = newdict
                    else:
                        olddict = self.node[node_]
                        olddict.update( attr )
                        olddict.update( node_dict )
                    continue
                if newnode:
                    self.adj[node] = {}
                    self.node[node] = attr.copy()
                else:
                    self.node[node].update( attr )
                    
                
        def remove_node( self, start ):
            try:
                nbrs = list( adj[start].keys() )
                del self.node[start]
            except KeyError:
                raise Exception( "The node %s is not in the graph."%( start ) )
    
            for end in nbrs:
                del self.adj[start][end]
            del self.adj[start]
            
    
        def remove_nodes_from( self, nodes ):
            for start in nodes:
                try:
                    del self.node[start]
                    for end in list( self.adj[start].keys() ):
                        del self.adj[end][start]
                    del self.adj[start]
                except KeyError:
                    pass
    
    
        def nodes( self, show_info = False ):
    
            def nodes_iter( show_info = False ):
                if show_info:
                    return iter( self.node.item() )
                return iter( self.node )
            
            return list( nodes_iter( show_info = show_info ) )
    
    
        def number_of_nodes( self ):
            return len( self.node )
    
    
        def order( self ):
            return len( self.node )
    
        
        def has_node( self, node ):
            try:
                return node in self.node
            except TypeError:
                return False
    
    
        def add_edge( self, start, end, attr_dict = None, **attr ):
            if attr_dict is None:
                attr_dict = attr
            else:
                try:
                    attr_dict.update( attr )
                except AttributeError:
                    raise Exception( "The attr_dict argument must be a dictionary." )
    
            if start not in self.node:
                self.adj[start]  = {}
                self.node[start] = {}
                
            if end not in self.node:
                self.adj[end]  = {}
                self.node[end] = {}
    
            data_dict = self.adj[start].get( end, {} )
            data_dict.update( attr_dict )
            self.adj[start][end] = data_dict
            self.adj[end][start] = data_dict
            
    
        def add_edges_from( self, edges, attr_dict = None, **attr ):
            if attr_dict is None:
                attr_dict = attr
            else:
                try:
                    attr_dict.update( attr )
                except AttributeError:
                    raise Exception( "The attr_dict argument must be a dictionary." )
    
            for edge in edges:
                elem_num = len( edge )
                if elem_num == 3:
                    start, end, start_end_dict = edge
                elif elem_num == 2:
                    start, end = edge
                else:
                    raise Exception( "Edge tuple %s must be a 2-tuple or 3-tuple."%( edge ) )
    
                attr_dict.update( start_end_dict )
                self.add_edge( start, end, attr_dict )
                    
    
        def remove_edge( self, start, end ):
            try:
                del self.adj[start][end]
                if start != end:
                    del adj[end][start]
            except KeyError:
                raise Exception( "The edge %s-%s is not in the graph"%( start,end ) )
    
        def remove_edges_from( self, edges ):
            for edge in edges:
                start, end = edge[:2]
                if start in self.adj and end in self.adj[start]:
                    del self.adj[start][end]
                    if start != end:
                        del self.adj[end][start]
    
        def has_edge( self, start, end ):
            try:
                return end in self.adj[start]
            except KeyError:
                return False
            
    
        def neighbors( self, node ):
            try:
                return list( self.adj[node] )
            except KeyError:
                raise Exception( "The node %s is not in the graph."%( node,) )
            
    
        def neighbors_iter( self, node ):
            try:
                return iter( self.adj[n] )
            except KeyError:
                raise Exception( "The node %s is not in the graph."%( node,) )
            
    
        def edges( self, nodes = None, show_info = False ):
    
            def edges_iter( nodes, show_info = False ):
                seen = {}
                if nodes is None:
                    nodes_nbrs = self.adj.items()
                else:
                    nodes_nbrs = ( ( node, self.adj[node] ) for node in self.nodes_container( nodes ) )
                    
                if show_info:
                    for node, nbrs in nodes_nbrs:
                        for nbr, data in nbrs.items():
                            if nbr not in seen:
                                yield ( node, nbr, data )
                        seen[node] = 1
                else:
                    for node, nbrs in nodes_nbrs:
                        for nbr, data in nbrs.items():
                            if nbr not in seen:
                                yield ( node, nbr )
                        seen[node] = 1
                del seen
            
            return list( edges_iter( nodes, show_info = show_info ) )
                
    
        def get_edge_data( self, start, end, default = None ):
            try:
                return self.adj[start][end]
            except KeyError:
                return default
            
    
        def adjacency_list( self ):
            return list( map( list, iter( self.adj.values() ) ) )
        
    
        def adjacency_iter( self ):
            return iter( self.adj.items() )
            
    
        def degree( self, nodes_container = None, weight = None ):
            '''
                功能:
                    返回一个节点或者一系列节点的度( degree )
                    
                參数:
                    nodes_container: 能够被迭代的容器,可选,默认值为全部节点
                    weight: 字符串或者为空,可选。默觉得空。
                            若是为None,则全部的边的权重都设为 1,
                            degree 则是全部与节点相邻的 weight 权重的边的个数之和
                            
                返回值:
                    字典或者数字
                    字典: 一系列节点与它们的键值对
                    数字: 一个指定节点的度
                    
                比如:
                    >>> G = nx.Graph()   # or DiGraph, MultiGraph, MultiDiGraph, etc
                    >>> G.add_path( [ 0, 1, 2, 3 ] )
                    >>> G.degree(0)
                    1
                    >>> G.degree( [ 0, 1 ] )
                    { 0: 1, 1: 2 }
                    >>> list( G.degree( [ 0, 1 ] ).values() )
                    [ 1, 2 ]
            '''
            if nodes_container in self:
                return next( self.degree_iter( nodes_container, weight ) )[1]
            else:
                return dict( self.degree_iter( nodes_container, weight ) )
            
    
        def degree_iter( self, nodes_container = None, weight = None ):
            '''
                功能:
                    返回一个( node, degree ) 的迭代对象
                    
                參数:
                    nodes_container: 能够被迭代的容器。可选。默认值为全部节点
                    weight: 字符串或者为空。可选,默觉得空,
                            若是为None,则全部的边的权重都设为 1。
                            degree 则是全部与节点相邻的 weight 权重的边的个数之和
                返回值:
                    返回一个( node, degree ) 的迭代对象
            '''
            if nodes_container is None:
                nodes_nbrs = self.adj.items()
            else:
                nodes_nbrs = ( ( node, self.adj[node] ) 
                               for node in self.nodes_container_iter(
                                   nodes_container ) )
            if weight is None:
                for node, nbrs in nodes_nbrs:
                    yield ( node, len( nbrs ) + ( node in nbrs ) )
            else:
                for node, nbrs in nodes_nbrs:
                    yield (node, sum( ( nbrs[nbr].get( weight, 1 ) for nbr in nbrs ) ) +
                                  ( node in nbrs and nbrs[node].get( weight, 1 ) ) )
            
    
            
        def clear( self ):
            self.name = ''
            self.adj.clear()
            self.node.clear()
            self.graph.clear()
            
    
        def copy( self ):
            from copy import deepcopy
            return deepcopy( self )
        
    
        def is_multigraph( self ):
            return False
        
    
        def is_directed( self ):
            return False
    
        
        def subgraph( self, nodes ):
            
            from copy import deepcopy
            
            nodes = self.nodes_container_iter( nodes )
            H = self.__class__()
            H.graph = deepcopy( self.graph )
    
            for node in nodes:
                H.node[node] = deepcopy( self.node[node] )
    
            for node in H.node:
                H_nbrs = {}
                H.adj[node] = H_nbrs
                for nbr, attr in self.adj[node].items():
                    if nbr in H.adj:
                        H_nbrs[nbr] = attr
                        H.adj[nbr][node] = attr
            return H
    
    
        def nodes_with_selfloops( self ):
            return [ node for node, nbrs in self.adj.items() if node in nbrs ]
    
    
        def selfloop_edges( self, show_info = False ):
            if show_info:
                return [ ( node, node, nbrs[node] )
                         for node, nbrs in self.adj.items() if node in nbrs ]
            else:
                return [ ( node, node )
                         for node, nbrs in self.adj.items() if node in nbrs ]
    
        
        def number_of_selfloops( self ):
            return len( self.selfloop_edges() )
        
    
        def size( self, weight = None ):
            s = sum( self.degree( weight = weight ).values() ) / 2
            if weight is None:
                return int( s )
            else:
                return float( s )
    
    
        def number_of_edges( self, start = None, end = None ):
            if start is None:
                return int( self.size() )
            if end in self.adj[start]:
                return 1
            else:
                return 0
    
    
        def add_path( self, nodes, **attr ):
            node_list = list( nodes )
            edges = zip( node_list[:-1], node_list[1:] )
            self.add_edges_from( edges, **attr )
            
    
        def add_cycle( self, nodes, **attr ):
            node_list = list( nodes )
            edges = zip( node_list, node_list[1:]  + [node_list[0]] )
            self.add_edges_from( edges, **attr )
            
    
        def nodes_container_iter( self, nodes_container = None ):
            '''
                功能:
                    返回存在图中且在 nodes_container 中的节点的迭代对象
    
                參数:
                    nodes_container: 能够被迭代的容器,可选。默认值为全部节点
    
                返回值:
                    nodes_iter: iterator
                    
            '''
            if nodes_container is None:
                container = iter( self.adj.keys() )
                
            elif nodes_container in self:
                container = iter( [nodes_container] )
                
            else:
                def container_iter( node_list, adj ):
                    try:
                        for node in node_list:
                            if node in adj:
                                yield node
                    except TypeError as e:
                        message = e.args[0]
                        import sys
                        sys.stdout.write( message )
                        if 'iter' in message:
                            raise Exception( "nodes_container is not a node or a sequence of nodes." )
                        elif 'hashable' in message:
                            raise Exception( "Node %s in the sequence nbunch is not a valid node."%n )
                        else:
                            raise
                container = container_iter( nodes_container, self.adj )
            return container
    


  • 相关阅读:
    Android BitmapFactory.Options
    Android TabHost
    Android 黑色样式menu
    Android Tab与TabHost
    Android Theme
    Activity 四种launchMode
    IOS xcode安装
    BaseActivity合集
    自定义BaseActivity
    Fragment 底部菜单栏
  • 原文地址:https://www.cnblogs.com/wgwyanfs/p/6941818.html
Copyright © 2011-2022 走看看