如何在Python中重新塑造一个networkx图形?

26

所以我创建了一种非常朴素的(可能效率低下)方法来生成哈斯图。

问题:

我有4个维度...p q r s

我想以统一的方式显示它(四维立方体),但我不知道如何重新塑造它。 如何在Python中调整networkx图形的形状?

我看到有些人使用spring_layout()draw_circular()的示例,但它们的形状不是我要找的那种均匀的形状。

是否有一种方法可以重新塑造我的图形并使其均匀? (即将我的哈斯图重塑成一个四维立方体形状(最好使用nx.draw()

这是我的哈斯图的当前外观: enter image description here

这是生成N维哈斯图的代码:

#!/usr/bin/python

import networkx as nx
import matplotlib.pyplot as plt
import itertools

H = nx.DiGraph()

axis_labels = ['p','q','r','s']

D_len_node = {}

#Iterate through axis labels
for i in xrange(0,len(axis_labels)+1):
    #Create edge from empty set
    if i == 0:
        for ax in axis_labels:
            H.add_edge('O',ax)
    else:
        #Create all non-overlapping combinations
        combinations = [c for c in itertools.combinations(axis_labels,i)]
        D_len_node[i] = combinations
    #Create edge from len(i-1) to len(i) #eg. pq >>> pqr, pq >>> pqs
    if i > 1:
        for node in D_len_node[i]:
            for p_node in D_len_node[i-1]:
                #if set.intersection(set(p_node),set(node)): Oops
                if all(p in node for p in p_node) == True: #should be this!
                    H.add_edge(''.join(p_node),''.join(node))

#Show Plot
nx.draw(H,with_labels = True,node_shape = 'o')
plt.show() 

我希望将其改造成这样:enter image description here

如果有人知道更简便的制作哈斯图方式,请分享一些经验,但这不是本文的主要目的。


通过调整shell_layout中的k值,您可以获得一个不那么堆积的图形。使用0.48的值,我得到了一些可读性,但并不是非常接近您的期望。 - Cyrbil
嗨@cyrbil,我在思考是否有一种方法可以获取N维2D投影的坐标,因为这实际上就是nx.hypercube_graph(3)所发生的事情。 - O.rka
如果我的答案有任何不太清楚的地方或您需要其他帮助,请在评论中告诉我。否则,玩得开心,欢迎接受 :) - J Richard Snape
1个回答

18

这是一个实用的回答,而不仅仅是数学上的回答。

我认为你有两个问题——一个与布局有关,另一个与你的网络有关。

1. 网络

你的网络中有太多的边了,不能代表单位四维立方体。注意我不是数学专家——只是从绘图角度(matplotlib标签)来看待这个问题。如果我错了,请解释一下。

例如,你想要的投影和n=4的Hasse图示例在wolfram mathworld页面上只有4条边连接所有节点,而你的图有6条边连接到2和7条边连接到3个位节点。你的图完全连接了每个“层”,也就是具有0 1值的4-D向量连接到具有1 1值的所有向量,然后连接到具有2 1值的所有向量,以此类推。这在基于维基百科答案的投影中最为明显(如下图第二张图片所示)

2. 投影

我找不到预先编写的算法或库来自动将4D超立方体投影到2D平面上,但我找到了一些示例,例如维基百科。从这个示例中,你可以计算出适合你的坐标集,并将其传递给nx.draw()函数。

这里有一个例子——我包括了两个坐标集,一个看起来像你上面展示的投影,另一个与维基百科中的投影相匹配。

import networkx as nx
import matplotlib.pyplot as plt
import itertools

H = nx.DiGraph()

axis_labels = ['p','q','r','s']

D_len_node = {}

#Iterate through axis labels
for i in xrange(0,len(axis_labels)+1):
    #Create edge from empty set
    if i == 0:
        for ax in axis_labels:
            H.add_edge('O',ax)
    else:
        #Create all non-overlapping combinations
        combinations = [c for c in itertools.combinations(axis_labels,i)]
        D_len_node[i] = combinations
    #Create edge from len(i-1) to len(i) #eg. pq >>> pqr, pq >>> pqs
    if i > 1:
        for node in D_len_node[i]:
            for p_node in D_len_node[i-1]:
                if set.intersection(set(p_node),set(node)):
                    H.add_edge(''.join(p_node),''.join(node))

#This is manual two options to project tesseract onto 2D plane 
# - many projections are available!!
wikipedia_projection_coords = [(0.5,0),(0.85,0.25),(0.625,0.25),(0.375,0.25),
                                (0.15,0.25),(1,0.5),(0.8,0.5),(0.6,0.5),
                                (0.4,0.5),(0.2,0.5),(0,0.5),(0.85,0.75),
                                (0.625,0.75),(0.375,0.75),(0.15,0.75),(0.5,1)]

#Build the "two cubes" type example projection co-ordinates
half_coords = [(0,0.15),(0,0.6),(0.3,0.15),(0.15,0),
               (0.55,0.6),(0.3,0.6),(0.15,0.4),(0.55,1)]
#make the coords symmetric
example_projection_coords = half_coords + [(1-x,1-y) for (x,y) in half_coords][::-1]

print example_projection_coords


def powerset(s):
    ch = itertools.chain.from_iterable(itertools.combinations(s, r) for r in range(len(s)+1))
    return [''.join(t) for t in ch]

pos={}
for i,label in enumerate(powerset(axis_labels)):
    if label == '':
       label = 'O'
    pos[label]= example_projection_coords[i]

#Show Plot
nx.draw(H,pos,with_labels = True,node_shape = 'o')
plt.show() 

注意 - 除非您更改我在第1点中提到的内容,否则它们仍具有您的边缘结构,因此与Web上的示例不完全相同。 这是使用您现有的网络生成代码的样子 - 如果将其与示例进行比较,您可以看到额外的边缘(例如,我不认为pr应连接到pqs):

“两个立方体”投影

由代码生成的四维超立方体的投影

Wikimedia示例投影

由提供的代码生成的四维超立方体的另一种投影


注意

如果您想了解自己进行投影(并在数学上建立pos),您可以查看这篇研究论文


编辑:

我的好奇心战胜了我,我必须寻找一种数学方法来完成这项任务。 我发现了这个博客 - 其主要结果是投影矩阵:

四维到二维的投影矩阵

这使我开发了此函数,以投影每个标签,将包含“p”的标签视为点在“p”轴上具有值1,即我们正在处理单位超立方体。 因此:

def construct_projection(label):
    r1 = r2 = 0.5
    theta = math.pi / 6
    phi = math.pi / 3
    x = int( 'p' in label) + r1 * math.cos(theta) * int('r' in label) - r2 * math.cos(phi) * int('s' in label)
    y = int( 'q' in label) + r1 * math.sin(theta) * int('r' in label) + r2 * math.sin(phi) * int('s' in label)
    return (x,y)

给出了一个漂亮的投影,将所有点都投射到一个有区别的正八边形中。
只需在上述程序中运行此代码即可,只需替换。
 pos[label] = example_projection_coords[i]

使用

pos[label] = construct_projection(label)

这将得到以下结果: 投影到八边形上 随意尝试 r1r2thetaphi 的值 :)

1
哇,这太有帮助了 J!这就是我在寻找的东西,我无法感谢你抽出时间来帮忙。我真的认为这将为社区带来巨大的价值。 - O.rka
"您的网络边太多了" 我没听清楚!刚刚修复了错误。应该使用all()而不是set.intersect()。" - O.rka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接