使用 Networkx 高效地在图形的 matplotlib 上制作动画
Making animation on matplotlib of graph with Networkx efficiently
我正在尝试为边宽和颜色随时间变化的图表制作动画。我的代码有效,但速度非常慢。我想还有更有效的实现。
def minimal_graph(datas, pos):
frames = len(datas[0])
fig, axes = plt.subplots(1, 2)
axes = axes.flatten()
for j, dat in enumerate(datas):
G = nx.from_numpy_matrix(dat[0])
nx.draw(G, pos, ax=axes[j])
def update(it, data, pos, ax):
print(it)
for i, dat in enumerate(data):
# This is the problematic line, because I clear the axis hence
# everything has to be drawn from scratch every time.
ax[i].clear()
G = nx.from_numpy_matrix(dat[it])
edges, weights = zip(*nx.get_edge_attributes(G, 'weight').items())
nx.draw(
G,
pos,
node_color='#24FF00',
edgelist=edges,
edge_color=weights,
width=weights,
edge_vmin=-5,
edge_vmax=5,
ax=ax[i])
ani = animation.FuncAnimation(fig, update, frames=frames, fargs=(
datas, pos, axes), interval=100)
ani.save('temp/graphs.mp4')
plt.close()
dataset1 = []
dataset2 = []
for i in range(100):
arr1 = np.random.rand(400, 400)
arr2 = np.random.rand(400, 400)
dataset1.append(arr1)
dataset2.append(arr2)
datasets = [dataset1, dataset2]
G = nx.from_numpy_matrix(dataset1[0])
pos = nx.spring_layout(G)
minimal_graph(datasets, pos)
正如代码中所指出的,问题在于我在每一帧都从“头”重新绘制图形。在 matplotlib 中使用动画时,我通常会尝试创建线条并使用函数“'line.set_data()'”,我知道这要快得多。只是我不知道如何使用 networkx 为图形设置它。我在这里发现 问题,但他们也使用相同的 ax.clear
并为每一帧重绘所有内容。那么,有没有一种方法可以将线对象设置为不在每次迭代时重绘所有内容?例如,在我的例子中,节点总是相同的(颜色、位置、大小保持不变)。
nx.draw
不公开用于表示节点和边的 matplotlib 艺术家,因此您无法更改艺术家的属性 in-place。从技术上讲,如果您单独绘制边缘,您确实会得到一些艺术家的集合,但是 non-trivial 将艺术家列表映射回边缘,in particular if there are self-loops present。
如果你愿意使用其他库来制作动画,我前段时间写过netgraph。对于您的问题至关重要的是,它可以轻松地将所有艺术家暴露给索引形式,以便可以更改其属性 in-place 而无需重新绘制其他所有内容。 netgraph
接受 full-rank 矩阵和 networkx
Graph
对象作为输入,因此输入数据应该很简单。
下面是一个简单的可视化示例。如果我 运行 具有 400 个节点和 1000 个边的相同脚本,它需要 30 秒才能在我的笔记本电脑上完成。
#!/usr/bin/env python
"""
MWE for animating edges.
"""
import numpy as np
import matplotlib.pyplot as plt
from netgraph import Graph # pip install netgraph
from matplotlib.animation import FuncAnimation
total_nodes = 10
total_frames = 100
adjacency_matrix = np.random.rand(total_nodes, total_nodes) < 0.2
weight_matrix = 5 * np.random.randn(total_frames, total_nodes, total_nodes)
# precompute normed weight matrix, such that weights are on the interval [0, 1];
# weights can then be passed directly to matplotlib colormaps (which expect float on that interval)
vmin, vmax = -5, 5
weight_matrix[weight_matrix<vmin] = vmin
weight_matrix[weight_matrix>vmax] = vmax
weight_matrix -= vmin
weight_matrix /= vmax - vmin
cmap = plt.cm.RdGy
plt.ion()
fig, ax = plt.subplots()
g = Graph(adjacency_matrix, arrows=True, ax=ax)
def update(ii):
artists = []
for jj, kk in zip(*np.where(adjacency_matrix)):
w = weight_matrix[ii, jj, kk]
g.edge_artists[(jj, kk)].set_facecolor(cmap(w))
g.edge_artists[(jj, kk)].width = 0.01 * np.abs(w-0.5) # assuming large negative edges should be wide, too
g.edge_artists[(jj, kk)]._update_path()
artists.append(g.edge_artists[(jj, kk)])
return artists
animation = FuncAnimation(fig, update, frames=total_frames, interval=100, blit=True)
animation.save('edge_animation.mp4')
我正在尝试为边宽和颜色随时间变化的图表制作动画。我的代码有效,但速度非常慢。我想还有更有效的实现。
def minimal_graph(datas, pos):
frames = len(datas[0])
fig, axes = plt.subplots(1, 2)
axes = axes.flatten()
for j, dat in enumerate(datas):
G = nx.from_numpy_matrix(dat[0])
nx.draw(G, pos, ax=axes[j])
def update(it, data, pos, ax):
print(it)
for i, dat in enumerate(data):
# This is the problematic line, because I clear the axis hence
# everything has to be drawn from scratch every time.
ax[i].clear()
G = nx.from_numpy_matrix(dat[it])
edges, weights = zip(*nx.get_edge_attributes(G, 'weight').items())
nx.draw(
G,
pos,
node_color='#24FF00',
edgelist=edges,
edge_color=weights,
width=weights,
edge_vmin=-5,
edge_vmax=5,
ax=ax[i])
ani = animation.FuncAnimation(fig, update, frames=frames, fargs=(
datas, pos, axes), interval=100)
ani.save('temp/graphs.mp4')
plt.close()
dataset1 = []
dataset2 = []
for i in range(100):
arr1 = np.random.rand(400, 400)
arr2 = np.random.rand(400, 400)
dataset1.append(arr1)
dataset2.append(arr2)
datasets = [dataset1, dataset2]
G = nx.from_numpy_matrix(dataset1[0])
pos = nx.spring_layout(G)
minimal_graph(datasets, pos)
正如代码中所指出的,问题在于我在每一帧都从“头”重新绘制图形。在 matplotlib 中使用动画时,我通常会尝试创建线条并使用函数“'line.set_data()'”,我知道这要快得多。只是我不知道如何使用 networkx 为图形设置它。我在这里发现 ax.clear
并为每一帧重绘所有内容。那么,有没有一种方法可以将线对象设置为不在每次迭代时重绘所有内容?例如,在我的例子中,节点总是相同的(颜色、位置、大小保持不变)。
nx.draw
不公开用于表示节点和边的 matplotlib 艺术家,因此您无法更改艺术家的属性 in-place。从技术上讲,如果您单独绘制边缘,您确实会得到一些艺术家的集合,但是 non-trivial 将艺术家列表映射回边缘,in particular if there are self-loops present。
如果你愿意使用其他库来制作动画,我前段时间写过netgraph。对于您的问题至关重要的是,它可以轻松地将所有艺术家暴露给索引形式,以便可以更改其属性 in-place 而无需重新绘制其他所有内容。 netgraph
接受 full-rank 矩阵和 networkx
Graph
对象作为输入,因此输入数据应该很简单。
下面是一个简单的可视化示例。如果我 运行 具有 400 个节点和 1000 个边的相同脚本,它需要 30 秒才能在我的笔记本电脑上完成。
#!/usr/bin/env python
"""
MWE for animating edges.
"""
import numpy as np
import matplotlib.pyplot as plt
from netgraph import Graph # pip install netgraph
from matplotlib.animation import FuncAnimation
total_nodes = 10
total_frames = 100
adjacency_matrix = np.random.rand(total_nodes, total_nodes) < 0.2
weight_matrix = 5 * np.random.randn(total_frames, total_nodes, total_nodes)
# precompute normed weight matrix, such that weights are on the interval [0, 1];
# weights can then be passed directly to matplotlib colormaps (which expect float on that interval)
vmin, vmax = -5, 5
weight_matrix[weight_matrix<vmin] = vmin
weight_matrix[weight_matrix>vmax] = vmax
weight_matrix -= vmin
weight_matrix /= vmax - vmin
cmap = plt.cm.RdGy
plt.ion()
fig, ax = plt.subplots()
g = Graph(adjacency_matrix, arrows=True, ax=ax)
def update(ii):
artists = []
for jj, kk in zip(*np.where(adjacency_matrix)):
w = weight_matrix[ii, jj, kk]
g.edge_artists[(jj, kk)].set_facecolor(cmap(w))
g.edge_artists[(jj, kk)].width = 0.01 * np.abs(w-0.5) # assuming large negative edges should be wide, too
g.edge_artists[(jj, kk)]._update_path()
artists.append(g.edge_artists[(jj, kk)])
return artists
animation = FuncAnimation(fig, update, frames=total_frames, interval=100, blit=True)
animation.save('edge_animation.mp4')