有边,增加边权重和复杂度

0 投票
3 回答
627 浏览
提问于 2025-04-18 02:07

我想进行一些边的操作:检查是否有边和增加边的权重。

我试过这样做:

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 个回答

0

我非常推荐使用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
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]
3

好的,有几件事情你需要了解,才能明白为什么增加操作不奏效。

首先,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 的边属性时,如果 sourcetarget 是连接的,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

撰写回答