【问题标题】:NetworkX Minimum Spanning Tree has different cluster arrangement with the same data?NetworkX最小生成树具有相同数据的不同簇排列?
【发布时间】:2020-08-19 10:46:02
【问题描述】:

我有一个大型数据集,它将产品与相关性度量进行比较,如下所示:

product1      product2  relatedness
0101          0102      0.047619
0101          0103      0.023810
0101          0104      0.095238
0101          0105      0.214286
0101          0106      0.047619
...           ...       ...

我使用以下代码将数据输入 NetworkX 绘图工具并生成 MST 图:

import networkx as nx
import matplotlib.pyplot as plt

products = (data['product1'])
products = list(dict.fromkeys(products))
products = sorted(products)

G = nx.Graph()
G.add_nodes_from(products)
print(G.number_of_nodes())
print(G.nodes())

row = 0
for c in data['product1']:
    p = data['product2'][row]
    w = data['relatedness'][row]
    if w > 0:
        G.add_edge(c,p, weight=w, with_labels=True)
    row = row + 1

nx.draw(nx.minimum_spanning_tree(G), with_labels=True)
plt.show()

生成的图表如下所示:https://i.imgur.com/pBbcPGc.jpg

但是,当我重新运行代码时,使用相同的数据且未进行任何修改,簇的排列似乎发生了变化,因此看起来有所不同,这里的示例:https://i.imgur.com/4phvFGz.jpg,这里的第二个示例:https://i.imgur.com/f2YepVx.jpg .簇、边和权重似乎没有变化,但它们在图空间上的排列每次都在变化。

是什么导致节点的排列每次都发生变化,而代码或数据没有任何变化?我如何重新编写此代码以生成每次对相同数据具有大致相同的节点和边排列的网络图?

【问题讨论】:

    标签: python graph networkx minimum-spanning-tree


    【解决方案1】:

    nx.draw 方法默认使用spring_layout (link to the doc)。这个布局实现了Fruchterman-Reingold force-directed algorithm,它以随机初始位置开始。这就是您在重复试验中看到的这种布局效果。

    如果你想“修复”这些位置,那么你应该显式调用spring_layout 函数并在pos 参数中指定初始位置。

    【讨论】:

    • 感谢您回复此问题,我尝试添加此代码:pos=nx.spring_layout(G) nx.draw(nx.minimum_spanning_tree(G), pos=pos, with_labels=True) plt.show() 但这似乎更加扰乱了节点的位置:i.imgur.com/zlbqKN5.jpg 我可以使用哪些其他参数与spring_layout 一起使用鼓励绘图工具以大致相同的方式显示图表?
    • 尽管有 G,为什么不使用 pos=nx.spring_layout(nx.minimum_spanning_tree(G)) 和 nx.minimum_spanning_tree(G)?
    • 我更改了代码,使其以pos=nx.spring_layout(nx.minimum_spanning_tree(G)) nx.draw(nx.minimum_spanning_tree(G), pos=pos, with_labels=True) plt.show() 结尾,运行了两次,它给了我这个:i.imgur.com/PQVqDkB.jpg,然后是这个:i.imgur.com/2g8BsAx.jpg。所以它似乎仍然有同样的问题,我在这里做错了什么?
    • 每次运行指令pos=nx.spring_layout(nx.minimum_spanning_tree(G)) 都会计算新的位置。或者,您可以定义一个随机种子,例如 pos=nx.spring_layout(G = nx.minimum_spanning_tree(G), seed=1) 或任何其他布局让您满意的种子。
    • 这是一个很好的建议,每次将种子设置为相同会产生我希望的结果,谢谢您,如果您将其添加为新答案,我可以将其标记为解决方案
    【解决方案2】:

    为了清楚起见,分配G = nx.minimum_spanning_tree(G)。那么

    nx.draw(G, with_labels=True)
    

    等价于

    pos = nx.spring_layout(G)
    nx.draw(G, pos=pos, with_labels=True)
    

    由于您不希望每次运行脚本时都随机计算pos,因此保持pos 稳定的唯一方法是将其存储一次并在每次重新运行后从文件中检索。你可以把这个脚本在nx.draw(G, pos=pos, with_labels=True)之前以改进的方式计算pos

    import os, json
    
    def store(pos):
        #form of dictionary to be stored dictionary retrieved
        return {k: v.tolist() for k, v in pos.items()}
    def retrieve(pos):
        #form of dictionary to be retrieved
        return {float(k): v for k, v in pos.items()}
    
    if 'nodes.txt' in os.listdir():
        json_file = open('pos.txt').read()
        pos = retrieve(json.loads(json_file)) #retrieving dictionary from file
        print('retrieve', pos)
    else:
        with open('pos.txt', 'w') as outfile:
            pos = nx.spring_layout(new_G) #calculates pos
            print('store', pos)
            json.dump(store(pos), outfile, indent=4) #records pos dictionary into file
    

    这是一个丑陋的解决方案,因为它无条件地依赖于pos 字典中使用的数据类型。它对我有用,但您可以定义在 storeretrieve 中使用的自定义项

    【讨论】:

    • 哇,这也是一个令人印象深刻的解决方案!我有几个问题,我可以将这种方法与上面@Tone 的解决方案结合起来以保存特定种子的位置值吗?其次,我可以修改这个文件中的位置值来改变节点的固定排列吗?
    • 当然这两种情况都可以。只是不要犹豫,通过试错来进行实验。我的解决方案对您有用吗?
    • 我设法让您的解决方案适用于同一个数据集,但我有多个与连续年份数据相关联的数据集,我似乎无法以大致相同的方式排列节点不同年份,我认为我的代码每次都必须覆盖 pos.txt 文件
    • 它实际上尝试从名为“pos.txt”的文件中提取pos 值,如果未找到,则计算一个新的pos 值并将其保存在“pos.txt”中。如果您更改数据集,您应该使用不同的文件名或在运行前将其删除。这就是我称我的代码丑陋的原因:它不能以灵活的方式定制。
    • 对不同的数据集使用相同的pos 值可能会很复杂。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-24
    • 2018-12-02
    • 2013-11-13
    相关资源
    最近更新 更多