Pen*_*uin 7 python networkx plotly
networkx我正在和中从事可视化项目plotly。有没有一种方法可以创建一个类似于人脑的 3D 图形,networkx然后将其可视化plotly(因此它将是交互式的)?

这个想法是将节点放在外面(或者如果更容易的话只显示节点)并对一组节点进行不同的着色,如上图所示
首先,这段代码大量借鉴了 Matteo Mancini 的代码,他在此处对此进行了描述,并已根据 MIT 许可证发布。
在原始代码中,未使用networkx,因此很明显您实际上并不需要networkx来实现您的目标。如果这不是严格的要求,我会考虑使用他的原始代码并对其进行修改以适合您的输入数据。
由于您将 networkx 列为一项要求,我只是修改了他的代码,以获取Graph具有某些节点属性的 networkx 对象,例如'color'和'coord'用于最终绘图散点中的那些标记特征。我只是选择数据集中的前十个点将其涂成红色,这就是它们没有分组的原因。
完整的可复制粘贴代码如下。这里的屏幕截图显然不是交互式的,但您可以在 Google Colab 上尝试演示。
要在 Linux/Mac 上的 Jupyter 笔记本中下载文件:
!wget https://github.com/matteomancini/neurosnippets/raw/master/brainviz/interactive-network/lh.pial.obj
!wget https://github.com/matteomancini/neurosnippets/raw/master/brainviz/interactive-network/icbm_fiber_mat.txt
!wget https://github.com/matteomancini/neurosnippets/raw/master/brainviz/interactive-network/fs_region_centers_68_sort.txt
!wget https://github.com/matteomancini/neurosnippets/raw/master/brainviz/interactive-network/freesurfer_regions_68_sort_full.txt
Run Code Online (Sandbox Code Playgroud)
代码:
import numpy as np
import plotly.graph_objects as go
import networkx as nx # New dependency
def obj_data_to_mesh3d(odata):
# odata is the string read from an obj file
vertices = []
faces = []
lines = odata.splitlines()
for line in lines:
slist = line.split()
if slist:
if slist[0] == 'v':
vertex = np.array(slist[1:], dtype=float)
vertices.append(vertex)
elif slist[0] == 'f':
face = []
for k in range(1, len(slist)):
face.append([int(s) for s in slist[k].replace('//','/').split('/')])
if len(face) > 3: # triangulate the n-polyonal face, n>3
faces.extend([[face[0][0]-1, face[k][0]-1, face[k+1][0]-1] for k in range(1, len(face)-1)])
else:
faces.append([face[j][0]-1 for j in range(len(face))])
else: pass
return np.array(vertices), np.array(faces)
with open("lh.pial.obj", "r") as f:
obj_data = f.read()
[vertices, faces] = obj_data_to_mesh3d(obj_data)
vert_x, vert_y, vert_z = vertices[:,:3].T
face_i, face_j, face_k = faces.T
cmat = np.loadtxt('icbm_fiber_mat.txt')
nodes = np.loadtxt('fs_region_centers_68_sort.txt')
labels=[]
with open("freesurfer_regions_68_sort_full.txt", "r") as f:
for line in f:
labels.append(line.strip('\n'))
# Instantiate Graph and add nodes (with their coordinates)
G = nx.Graph()
for idx, node in enumerate(nodes):
G.add_node(idx, coord=node)
# Add made-up colors for the nodes as node attribute
colors_data = {node: ('gray' if node > 10 else 'red') for node in G.nodes}
nx.set_node_attributes(G, colors_data, name="color")
# Add edges
[source, target] = np.nonzero(np.triu(cmat)>0.01)
edges = list(zip(source, target))
G.add_edges_from(edges)
# Get node coordinates from node attribute
nodes_x = [data['coord'][0] for node, data in G.nodes(data=True)]
nodes_y = [data['coord'][1] for node, data in G.nodes(data=True)]
nodes_z = [data['coord'][2] for node, data in G.nodes(data=True)]
edge_x = []
edge_y = []
edge_z = []
for s, t in edges:
edge_x += [nodes_x[s], nodes_x[t]]
edge_y += [nodes_y[s], nodes_y[t]]
edge_z += [nodes_z[s], nodes_z[t]]
# Get node colors from node attribute
node_colors = [data['color'] for node, data in G.nodes(data=True)]
fig = go.Figure()
# Changed color and opacity kwargs
fig.add_trace(go.Mesh3d(x=vert_x, y=vert_y, z=vert_z, i=face_i, j=face_j, k=face_k,
color='gray', opacity=0.1, name='', showscale=False, hoverinfo='none'))
fig.add_trace(go.Scatter3d(x=nodes_x, y=nodes_y, z=nodes_z, text=labels,
mode='markers', hoverinfo='text', name='Nodes',
marker=dict(
size=5, # Changed node size...
color=node_colors # ...and color
)
))
fig.add_trace(go.Scatter3d(x=edge_x, y=edge_y, z=edge_z,
mode='lines', hoverinfo='none', name='Edges',
opacity=0.3, # Added opacity kwarg
line=dict(color='pink') # Added line color
))
fig.update_layout(
scene=dict(
xaxis=dict(showticklabels=False, visible=False),
yaxis=dict(showticklabels=False, visible=False),
zaxis=dict(showticklabels=False, visible=False),
),
width=800, height=600
)
fig.show()
Run Code Online (Sandbox Code Playgroud)
根据明确的要求,我采取了新的方法:
有一个明确描绘的点可以添加不同的图形数据以及更改决定节点颜色的逻辑。在引入新的图形数据后,要使事情看起来不错的关键参数是:
scale_factor:这会改变原始 Kamada-Kuwai 计算的坐标在被捕捉回其表面之前从脑网格中心径向平移的程度。较大的值将使更多的节点捕捉到大脑的外表面。较小的值将在两个半球之间的表面上留下更多的节点。opacity边缘轨迹中的线条:具有更多边缘的图形会很快使视野变得混乱,并使整个大脑形状不那么明显。这说明了我对这种整体方法最大的不满——出现在网格表面之外的边缘使得更难看到网格的整体形状,尤其是在颞叶之间。我在这里的另一个最大的警告是,没有尝试检查位于大脑表面的任何节点是否恰好重合或具有任何相等的间距。
这是屏幕截图和Colab 上的现场演示。下面是完整的可复制粘贴代码。
这里可以讨论一大堆旁白,但为了简洁起见,我只指出两个:
import plotly.graph_objects as go
import numpy as np
import networkx as nx
import math
def mesh_properties(mesh_coords):
"""Calculate center and radius of sphere minimally containing a 3-D mesh
Parameters
----------
mesh_coords : tuple
3-tuple with x-, y-, and z-coordinates (respectively) of 3-D mesh vertices
"""
radii = []
center = []
for coords in mesh_coords:
c_max = max(c for c in coords)
c_min = min(c for c in coords)
center.append((c_max + c_min) / 2)
radius = (c_max - c_min) / 2
radii.append(radius)
return(center, max(radii))
# Download and prepare dataset from BrainNet repo
coords = np.loadtxt(np.DataSource().open('https://raw.githubusercontent.com/mingruixia/BrainNet-Viewer/master/Data/SurfTemplate/BrainMesh_Ch2_smoothed.nv'), skiprows=1, max_rows=53469)
x, y, z = coords.T
triangles = np.loadtxt(np.DataSource().open('https://raw.githubusercontent.com/mingruixia/BrainNet-Viewer/master/Data/SurfTemplate/BrainMesh_Ch2_smoothed.nv'), skiprows=53471, dtype=int)
triangles_zero_offset = triangles - 1
i, j, k = triangles_zero_offset.T
# Generate 3D mesh. Simply replace with 'fig = go.Figure()' or turn opacity to zero if seeing brain mesh is not desired.
fig = go.Figure(data=[go.Mesh3d(x=x, y=y, z=z,
i=i, j=j, k=k,
color='lightpink', opacity=0.5, name='', showscale=False, hoverinfo='none')])
# Generate networkx graph and initial 3-D positions using Kamada-Kawai path-length cost-function inside sphere containing brain mesh
G = nx.gnp_random_graph(200, 0.02, seed=42) # Replace G with desired graph here
mesh_coords = (x, y, z)
mesh_center, mesh_radius = mesh_properties(mesh_coords)
scale_factor = 5 # Tune this value by hand to have more/fewer points between the brain hemispheres.
pos_3d = nx.kamada_kawai_layout(G, dim=3, center=mesh_center, scale=scale_factor*mesh_radius)
# Calculate final node positions on brain surface
pos_brain = {}
for node, position in pos_3d.items():
squared_dist_matrix = np.sum((coords - position) ** 2, axis=1)
pos_brain[node] = coords[np.argmin(squared_dist_matrix)]
# Prepare networkx graph positions for plotly node and edge traces
nodes_x = [position[0] for position in pos_brain.values()]
nodes_y = [position[1] for position in pos_brain.values()]
nodes_z = [position[2] for position in pos_brain.values()]
edge_x = []
edge_y = []
edge_z = []
for s, t in G.edges():
edge_x += [nodes_x[s], nodes_x[t]]
edge_y += [nodes_y[s], nodes_y[t]]
edge_z += [nodes_z[s], nodes_z[t]]
# Decide some more meaningful logic for coloring certain nodes. Currently the squared distance from the mesh point at index 42.
node_colors = []
for node in G.nodes():
if np.sum((pos_brain[node] - coords[42]) ** 2) < 1000:
node_colors.append('red')
else:
node_colors.append('gray')
# Add node plotly trace
fig.add_trace(go.Scatter3d(x=nodes_x, y=nodes_y, z=nodes_z,
#text=labels,
mode='markers',
#hoverinfo='text',
name='Nodes',
marker=dict(
size=5,
color=node_colors
)
))
# Add edge plotly trace. Comment out or turn opacity to zero if not desired.
fig.add_trace(go.Scatter3d(x=edge_x, y=edge_y, z=edge_z,
mode='lines',
hoverinfo='none',
name='Edges',
opacity=0.1,
line=dict(color='gray')
))
# Make axes invisible
fig.update_scenes(xaxis_visible=False,
yaxis_visible=False,
zaxis_visible=False)
# Manually adjust size of figure
fig.update_layout(autosize=False,
width=800,
height=800)
fig.show()
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1850 次 |
| 最近记录: |