简单图模板 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

简单图模板 Graph

时间: 2024-11-14 11:28:10

简单图模板 Graph的相关文章

打造自己的Cacti模板

1.新建数据模板(Data Template) 在console选项卡下左侧菜单栏中点击Data Templates连接,打开新建数据模板窗口. 在新建数据模板窗口点击Add按钮添加"Data Templates". 填写以下内容: 1).Name 第一个Name是模板的名称,可自定义设置 ,再此填写"Windows - CPU Usage": 2).Name 第二个Name是数据源的名字,再此填写"Windows - CPU Usage": 3

cacti监控工具之数据收集方法、模板介绍及使用

目录 1.cacti数据收集方法.三种模板介绍 2.模板的使用介绍 1.cacti数据收集方法.三种模板介绍 在上一博文中我们部署好了cacti环境,并让cacti运行起来了.今天在这里介绍一下在cacti的"consolle"控制台中的"Collection Methods"."Templates"."Import/Export"三个部件,即在下图中标明的三个部件. 在"Collection Methods&quo

cacti监控一个web上的多个tomcat

Cacti监控一个web上多个tomcat 第一部分:监控单个tomcat 1.首先下载监控tomcat的模板 TomcatStats-0.1.zip 下载之后,修改tomcatstats.pl, a.注释第三行左右的 use Data::Dumper; b.将 第19行左右的my $xml = `GET $url`; 改为my $xml = `wget -qO - $url`; c.在第24行左右添加此行   print "  "; d.注释第23行左右的print Dumper($

Centos 6.7 安张nagios和cacti 并添加监控主机

wget http://mcshell.org/nrpe-2.8.tar.gz wget http://mcshell.org/nagios-plugins-1.4.13.tar.gz 一.环境Centos 6.7 ip:192.168.3.150 nagios 和cacti  监控主机 192.168.3.150/152   被检控主机 二.安装准备 mount yum  –y install httpd php php-mysql mysql mysql-server php  net-sn

十一、图论

11.1 图的基本概念 图是一种网状的数据结构,其中的结点之间的关系是任意的,即图中任何两个结点之间都可能直接相关. 顶点:图中的数据元素.设它的集合用V(Vertex)表示. 边:顶点之间的关系的集合用E(edge)来表示: 顶点的度:连接顶点的边的数量称为该顶点的度.顶点的度在有向图和无向图中具有不同的表示. 对于无向图,一个顶点V的度比较简单,其是连接该顶点的边的数量,记为D(V). 对于有向图要稍复杂些,根据连接顶点V的边的方向性,一个顶点的度有入度和出度之分. 入度是以该顶点为端点的入

HDU 2454 Degree Sequence of Graph G(Havel定理 判断简单图的存在)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2454 Problem Description Wang Haiyang is a strong and optimistic Chinese youngster. Although born and brought up in the northern inland city Harbin, he has deep love and yearns for the boundless oceans.

HDU 2454 Degree Sequence of Graph G (可简单图化的判定 havel定理)

题目链接:HDU 2454 Degree Sequence of Graph G 题意:给出N个点的度(简单图),问能能否画出个图,(其实就是给出一个串非负的序列是否有对应的图存在) 没见过这个定理 题意真的难懂. havel定理就是一个给出一串非负的序列,存在一个无向图使得图中各点的度与此序列一一对应,则称此序列可图化.简单图的话就是,可简单图化. 可简单图化的判定(Havel定理):把序列排成不增序,即d1>=d2>=-->=dn,则d可简单图化当且仅当d'={d2-1,d3-1,-

Graph Coloring(最大独立集模板题)

Graph Coloring POJ - 1419 You are to write a program that tries to find an optimal coloring for a given graph. Colors are applied to the nodes of the graph and the only available colors are black and white. The coloring of the graph is called optimal

Easy Graph Problem?(一道简单图论题?) bfs + 优先队列

Easy Graph Problem? 题目抽象:给你一个n * m 的矩阵.每个格子是数字或者障碍物'*'. 第一问:从起点到终点的最短路.第二问:要求相同的方向不能连续走两次,也就是每到达一个格子,需要改变方向.问最短路.  不过不存在就输出-1. 分析:可以直接bfs搜索.第一问有一个minv[MS][MS]数组记录到达(i,j)的最短距离.第二问用flag[MS][MS][4]及记录(i,j)格子k方向是否走过. 由于2<=n,m<=500,范围较大.所以需要优先队列优化.  不然超时