有边,增加边权重和复杂度
我想进行一些边的操作:检查是否有边和增加边的权重。
我试过这样做:
from igraph import *
g = Nexus.get("karate")
if g.es.select(_from=0, _target=1):
g.es.select(_from=0, _target=1)['weight'] += 1
else:
g.add_edge(0, 1, weight=1)
但是我遇到了一些问题:增加边权重的操作:
g.es.select(_from=0, _target=1)['weight'] += 1 # not work
这个操作不管用,不过在处理添加新边权重时,这个是有效的。例如:
g.es.select(_from=0, _target=1)['weight'] = 200000 # this is ok
另外,我希望能有更高效或更优雅的方法。比如,在IGRAPH这个库里,有没有更高效的操作或函数来完成这个操作?
3 个回答
我非常推荐使用networkx来实现这个功能。下面是相同的代码。
import networkx as nx
G = nx.karate_club_graph()
#you can assign all with a starting weight
for x,y in G.edges_iter():
G.add_edge(x,y,weight=1)
# then add more as you come across them.
for x,y in SomeIterableAffectingTheGraph:
G.edge[x][y]['weight']+=1
为了简单地工作,(既不高效也不优雅)
>>> from igraph import *
>>> g = Nexus.get('karate')
>>> print g.es.select(_from=0, _target=1)['weight'] # to see the value
[4.0]
>>> if g.es.select(_from=0, _target=1):
... buff = g.es.select(_from=0, _target=1)['weight']
... buff[0] += 1
... g.es.select(_from=0, _target=1)['weight'] = buff
... else:
... g.add_edge(0, 1, weight=1)
>>> print g.es.select(_from=0, _target=1)['weight'] # to see the value
[5.0]
好的,有几件事情你需要了解,才能明白为什么增加操作不奏效。
首先,g.es.select()
返回的是一个 EdgeSeq
对象,也就是边的序列。在你的情况下,如果边存在,它会返回一个长度为 1 的边序列,否则返回长度为 0 的边序列:
>>> edges = g.es.select(_from=0, _to=1)
>>> edges
<igraph.EdgeSeq at 0x10ce62350>
>>> len(edges)
1
其次,在 EdgeSeq
上使用 ['weight']
操作会返回一个 列表,这个列表包含了每条边的权重属性:
>>> edges["weight"]
[42]
第三,x += 1
实际上等同于 x = x + 1
,所以当你尝试用 +=
来增加权重时,你实际上是在尝试将 1 添加 到列表中(因为 x
是一个列表,参见我的第二点),这不会成功,因为你只能将另一个列表添加到列表中。此外,你得到的列表只是边属性的一个 副本,所以修改它并不会改变边的属性:
>>> edges["weight"] += 1
TypeError: 'int' object is not iterable
>>> edges["weight"] += [1]
>>> edges["weight"]
[42]
最简单的解决办法是从 EdgeSeq
中获取一个单独的 Edge
对象并修改它,因为在 Edge
上使用 ['weight']
操作会返回权重作为单个元素,你可以用 +=
操作符轻松地增加并写回到边上:
>>> edge = edges[0]
>>> edge
igraph.Edge(<igraph.Graph object at 0x10d788148>, 0, {'weight': 42})
>>> edge["weight"] += 1
>>> edge
igraph.Edge(<igraph.Graph object at 0x10d788148>, 0, {'weight': 43})
不过,我建议在这种情况下避免使用 g.es.select
,因为它会评估 g.es
中的每一条边,以找到满足你条件的边。与其这样,我会使用 g.get_eid()
来获取我想要的边的 ID,然后用 g.es[edge_id]
找到合适的边:
eid = g.get_eid(0, 1, error=False)
if eid >= 0:
g.es[eid]["weight"] += 1
else:
g.add_edge(0, 1, weight=1)
另一种方法是认识到 igraph 对象可以被视为邻接矩阵。当图中有一个名为 weight
的边属性时,调用 g[source, target]
会返回给定源点和目标点之间的边的权重,这当然也可以用来设置权重。当图中没有名为 weight
的边属性时,如果 source
和 target
是连接的,g[source, target]
就是 1,否则就是 0。所以,你也可以这样做:
g = Nexus.get('karate')
g.es["weight"] = 1 # to ensure that existing edges have weight=1
g[0,1] += 1