Python 3.x 如何有效地添加或更新三维曲线的颜色(无for loop)?
问题:Python 3.x 如何有效地添加或更新三维曲线的颜色(无for loop)?,python-3.x,matplotlib,curve,colormap,Python 3.x,Matplotlib,Curve,Colormap,问题: In [2]: fig = plt.figure() ...: ax = plt.axes(projection='3d') ...: n = 100 ...: x = np.arange(n)/n ...: y = np.arange(n)/n ...: z = np.arange(n)/n ...: colors = np.arange(n)/n ...: points = np.array([x, y, z]).T.resha
In [2]: fig = plt.figure()
...: ax = plt.axes(projection='3d')
...: n = 100
...: x = np.arange(n)/n
...: y = np.arange(n)/n
...: z = np.arange(n)/n
...: colors = np.arange(n)/n
...: points = np.array([x, y, z]).T.reshape(-1, 1, 3)
...: segs = np.concatenate([points[:-1], points[1:]], axis=1)
...: lc = Line3DCollection(segs, cmap='plasma', linewidth=4)
...: ax.add_collection(lc)
Out[2]: <mpl_toolkits.mplot3d.art3d.Line3DCollection at 0x7f0528cdec10>
In [2]: fig = plt.figure()
...: ax = plt.axes(projection='3d')
...: n = 100
...: x = np.arange(n)/n
...: y = np.arange(n)/n
...: z = np.arange(n)/n
...: colors = np.arange(n)/n
...: points = np.array([x, y, z]).T.reshape(-1, 1, 3)
...: segs = np.concatenate([points[:-1], points[1:]], axis=1)
...: lc = Line3DCollection(segs, cmap='plasma', linewidth=4)
...: ax.add_collection(lc)
Out[2]: <mpl_toolkits.mplot3d.art3d.Line3DCollection at 0x7f0528cdec10>
我正在绘制3D曲线图,其颜色与下面示例中的颜色相似。我的实际图表中的点数很高,比如n>10000
,这使得这种方法非常缓慢
In [2]: fig = plt.figure()
...: ax = plt.axes(projection='3d')
...: n = 100
...: x = np.arange(n)/n
...: y = np.arange(n)/n
...: z = np.arange(n)/n
...: colors = np.arange(n)/n
...: points = np.array([x, y, z]).T.reshape(-1, 1, 3)
...: segs = np.concatenate([points[:-1], points[1:]], axis=1)
...: lc = Line3DCollection(segs, cmap='plasma', linewidth=4)
...: ax.add_collection(lc)
Out[2]: <mpl_toolkits.mplot3d.art3d.Line3DCollection at 0x7f0528cdec10>
此外,坐标和颜色与固定数量的点相关联,这使得曲线取决于另一个变量(即时间)。因此,我希望绘制一次点,然后更新一个或所有向量(x
,y
,z
,c
),以加快每个时间步的绘制。
编辑:在Matlab中,可以执行以下操作来更新已绘制顶点/点的坐标。该示例使用面片而不是线段,但概念相同:
v = [2 4; 2 8; 8 4; 5 0; 5 2; 8 0];
f = [1 2 3; 4 5 6];
col = [0; 1];
figure
p = patch('Faces',f,'Vertices',v,'FaceVertexCData',col,'FaceColor','flat');
colorbar
pause(1)
v = [1 3; 1 9;9 1; 5 0; 5 2; 8 0];
p.Vertices = v; % this updates the point coordinates which I would like to do for the line segments in my python plot.
drawnow
In [2]: fig = plt.figure()
...: ax = plt.axes(projection='3d')
...: n = 100
...: x = np.arange(n)/n
...: y = np.arange(n)/n
...: z = np.arange(n)/n
...: colors = np.arange(n)/n
...: points = np.array([x, y, z]).T.reshape(-1, 1, 3)
...: segs = np.concatenate([points[:-1], points[1:]], axis=1)
...: lc = Line3DCollection(segs, cmap='plasma', linewidth=4)
...: ax.add_collection(lc)
Out[2]: <mpl_toolkits.mplot3d.art3d.Line3DCollection at 0x7f0528cdec10>
使用的代码:
import numpy as np
from mpl_toolkits import mplot3d
import matplotlib.pyplot as plt
n = 100
x = np.arange(n)
y = np.arange(n)
z = np.arange(n)
colors = np.arange(n)/n
fig = plt.figure()
ax = fig.gca(projection='3d')
for i in range(1,len(x)-1):
ax.plot(x[i-1:i+1], \
y[i-1:i+1], \
z[i-1:i+1], \
c = plt.cm.jet(colors[i]))
plt.show()
In [2]: fig = plt.figure()
...: ax = plt.axes(projection='3d')
...: n = 100
...: x = np.arange(n)/n
...: y = np.arange(n)/n
...: z = np.arange(n)/n
...: colors = np.arange(n)/n
...: points = np.array([x, y, z]).T.reshape(-1, 1, 3)
...: segs = np.concatenate([points[:-1], points[1:]], axis=1)
...: lc = Line3DCollection(segs, cmap='plasma', linewidth=4)
...: ax.add_collection(lc)
Out[2]: <mpl_toolkits.mplot3d.art3d.Line3DCollection at 0x7f0528cdec10>
编辑:
关于我的问题1:根据GBOFI的建议,我研究了matplotlib.collections.LineCollection,现在可以更快地绘制线段。我也用了这个答案
In [2]: fig = plt.figure()
...: ax = plt.axes(projection='3d')
...: n = 100
...: x = np.arange(n)/n
...: y = np.arange(n)/n
...: z = np.arange(n)/n
...: colors = np.arange(n)/n
...: points = np.array([x, y, z]).T.reshape(-1, 1, 3)
...: segs = np.concatenate([points[:-1], points[1:]], axis=1)
...: lc = Line3DCollection(segs, cmap='plasma', linewidth=4)
...: ax.add_collection(lc)
Out[2]: <mpl_toolkits.mplot3d.art3d.Line3DCollection at 0x7f0528cdec10>
编辑:按照中的建议,使用线条集合。它们甚至比下面的
scatter
方法更快
In [2]: fig = plt.figure()
...: ax = plt.axes(projection='3d')
...: n = 100
...: x = np.arange(n)/n
...: y = np.arange(n)/n
...: z = np.arange(n)/n
...: colors = np.arange(n)/n
...: points = np.array([x, y, z]).T.reshape(-1, 1, 3)
...: segs = np.concatenate([points[:-1], points[1:]], axis=1)
...: lc = Line3DCollection(segs, cmap='plasma', linewidth=4)
...: ax.add_collection(lc)
Out[2]: <mpl_toolkits.mplot3d.art3d.Line3DCollection at 0x7f0528cdec10>
如果不需要线段,则可以使用
散点
批量绘制所有数据点,这允许每个数据点使用不同的颜色,而且速度非常快
In [2]: fig = plt.figure()
...: ax = plt.axes(projection='3d')
...: n = 100
...: x = np.arange(n)/n
...: y = np.arange(n)/n
...: z = np.arange(n)/n
...: colors = np.arange(n)/n
...: points = np.array([x, y, z]).T.reshape(-1, 1, 3)
...: segs = np.concatenate([points[:-1], points[1:]], axis=1)
...: lc = Line3DCollection(segs, cmap='plasma', linewidth=4)
...: ax.add_collection(lc)
Out[2]: <mpl_toolkits.mplot3d.art3d.Line3DCollection at 0x7f0528cdec10>
下面是一个小演示:
In [2]: fig = plt.figure()
...: ax = plt.axes(projection='3d')
...: n = 100
...: x = np.arange(n)/n
...: y = np.arange(n)/n
...: z = np.arange(n)/n
...: colors = np.arange(n)/n
...: points = np.array([x, y, z]).T.reshape(-1, 1, 3)
...: segs = np.concatenate([points[:-1], points[1:]], axis=1)
...: lc = Line3DCollection(segs, cmap='plasma', linewidth=4)
...: ax.add_collection(lc)
Out[2]: <mpl_toolkits.mplot3d.art3d.Line3DCollection at 0x7f0528cdec10>
将numpy导入为np
从mpl_工具包导入mplot3d
将matplotlib.pyplot作为plt导入
n=100
x=np.arange(n)
y=np.arange(n)
z=np.arange(n)
颜色=np.arange(n)/n
图=plt.图(图尺寸=(12,5))
ax=图添加_子图(1,2,1,投影='3d')
对于范围(1,len(x)-1)内的i:
ax.绘图(x[i-1:i+1],y[i-1:i+1],z[i-1:i+1],c=plt.cm.jet(颜色[i]))
ax.set\uxlim(0,n),ax.set\uylim(0,n),ax.set\uzlim(0,n)
ax=图添加_子图(1,2,2,投影='3d')
最大散射(x,y,z,s=1,c=colors,cmap='jet')
ax.set\uxlim(0,n),ax.set\uylim(0,n),ax.set\uzlim(0,n)
plt.show()
我用n=100
和n=10000
做了一个小测试,并对两次运行进行了计时:
In [2]: fig = plt.figure()
...: ax = plt.axes(projection='3d')
...: n = 100
...: x = np.arange(n)/n
...: y = np.arange(n)/n
...: z = np.arange(n)/n
...: colors = np.arange(n)/n
...: points = np.array([x, y, z]).T.reshape(-1, 1, 3)
...: segs = np.concatenate([points[:-1], points[1:]], axis=1)
...: lc = Line3DCollection(segs, cmap='plasma', linewidth=4)
...: ax.add_collection(lc)
Out[2]: <mpl_toolkits.mplot3d.art3d.Line3DCollection at 0x7f0528cdec10>
In [2]: fig = plt.figure()
...: ax = plt.axes(projection='3d')
...: n = 100
...: x = np.arange(n)/n
...: y = np.arange(n)/n
...: z = np.arange(n)/n
...: colors = np.arange(n)/n
...: points = np.array([x, y, z]).T.reshape(-1, 1, 3)
...: segs = np.concatenate([points[:-1], points[1:]], axis=1)
...: lc = Line3DCollection(segs, cmap='plasma', linewidth=4)
...: ax.add_collection(lc)
Out[2]: <mpl_toolkits.mplot3d.art3d.Line3DCollection at 0x7f0528cdec10>
左侧是代码绘制线段,右侧是散布版。由您决定是否需要线段来可视化希望用绘图表达的所需效果。至少对于n=10000
,我个人几乎看不出两个版本之间有什么区别
In [2]: fig = plt.figure()
...: ax = plt.axes(projection='3d')
...: n = 100
...: x = np.arange(n)/n
...: y = np.arange(n)/n
...: z = np.arange(n)/n
...: colors = np.arange(n)/n
...: points = np.array([x, y, z]).T.reshape(-1, 1, 3)
...: segs = np.concatenate([points[:-1], points[1:]], axis=1)
...: lc = Line3DCollection(segs, cmap='plasma', linewidth=4)
...: ax.add_collection(lc)
Out[2]: <mpl_toolkits.mplot3d.art3d.Line3DCollection at 0x7f0528cdec10>
但是,显而易见的是计算时间的差异!您现在甚至可以在更改x
、y
、z
、c
中的任何一项时重新绘制所有数据
In [2]: fig = plt.figure()
...: ax = plt.axes(projection='3d')
...: n = 100
...: x = np.arange(n)/n
...: y = np.arange(n)/n
...: z = np.arange(n)/n
...: colors = np.arange(n)/n
...: points = np.array([x, y, z]).T.reshape(-1, 1, 3)
...: segs = np.concatenate([points[:-1], points[1:]], axis=1)
...: lc = Line3DCollection(segs, cmap='plasma', linewidth=4)
...: ax.add_collection(lc)
Out[2]: <mpl_toolkits.mplot3d.art3d.Line3DCollection at 0x7f0528cdec10>
希望有帮助 我从您上次的编辑开始,包括所需的导入
In [1]: import numpy as np
...: import matplotlib.pyplot as plt
...: from mpl_toolkits import mplot3d
...: from mpl_toolkits.mplot3d.art3d import Line3DCollection
In [2]: fig = plt.figure()
...: ax = plt.axes(projection='3d')
...: n = 100
...: x = np.arange(n)/n
...: y = np.arange(n)/n
...: z = np.arange(n)/n
...: colors = np.arange(n)/n
...: points = np.array([x, y, z]).T.reshape(-1, 1, 3)
...: segs = np.concatenate([points[:-1], points[1:]], axis=1)
...: lc = Line3DCollection(segs, cmap='plasma', linewidth=4)
...: ax.add_collection(lc)
Out[2]: <mpl_toolkits.mplot3d.art3d.Line3DCollection at 0x7f0528cdec10>
接下来,准备LineCollection
——注意,我在调用中指定了一个colormap,并保留了数据结构的句柄(与您的代码不同)
In [2]: fig = plt.figure()
...: ax = plt.axes(projection='3d')
...: n = 100
...: x = np.arange(n)/n
...: y = np.arange(n)/n
...: z = np.arange(n)/n
...: colors = np.arange(n)/n
...: points = np.array([x, y, z]).T.reshape(-1, 1, 3)
...: segs = np.concatenate([points[:-1], points[1:]], axis=1)
...: lc = Line3DCollection(segs, cmap='plasma', linewidth=4)
...: ax.add_collection(lc)
Out[2]: <mpl_toolkits.mplot3d.art3d.Line3DCollection at 0x7f0528cdec10>
In [2]: fig = plt.figure()
...: ax = plt.axes(projection='3d')
...: n = 100
...: x = np.arange(n)/n
...: y = np.arange(n)/n
...: z = np.arange(n)/n
...: colors = np.arange(n)/n
...: points = np.array([x, y, z]).T.reshape(-1, 1, 3)
...: segs = np.concatenate([points[:-1], points[1:]], axis=1)
...: lc = Line3DCollection(segs, cmap='plasma', linewidth=4)
...: ax.add_collection(lc)
Out[2]: <mpl_toolkits.mplot3d.art3d.Line3DCollection at 0x7f0528cdec10>
当然,只要重新绘制lc
,您就可以根据需要更改颜色映射数组
In [2]: fig = plt.figure()
...: ax = plt.axes(projection='3d')
...: n = 100
...: x = np.arange(n)/n
...: y = np.arange(n)/n
...: z = np.arange(n)/n
...: colors = np.arange(n)/n
...: points = np.array([x, y, z]).T.reshape(-1, 1, 3)
...: segs = np.concatenate([points[:-1], points[1:]], axis=1)
...: lc = Line3DCollection(segs, cmap='plasma', linewidth=4)
...: ax.add_collection(lc)
Out[2]: <mpl_toolkits.mplot3d.art3d.Line3DCollection at 0x7f0528cdec10>
In [3]: lc.set_array(colors) ; ax.draw_artist(lc)
在重新更改xyz
数据时,您可以使用适当的setter(即lc.set_segments
)来更改段,但我将在回答中忽略这一点
In [2]: fig = plt.figure()
...: ax = plt.axes(projection='3d')
...: n = 100
...: x = np.arange(n)/n
...: y = np.arange(n)/n
...: z = np.arange(n)/n
...: colors = np.arange(n)/n
...: points = np.array([x, y, z]).T.reshape(-1, 1, 3)
...: segs = np.concatenate([points[:-1], points[1:]], axis=1)
...: lc = Line3DCollection(segs, cmap='plasma', linewidth=4)
...: ax.add_collection(lc)
Out[2]: <mpl_toolkits.mplot3d.art3d.Line3DCollection at 0x7f0528cdec10>
最后,您可以在plt.colorbar(lc)
`中添加一个颜色条,您可以使用@gboffi,这是我第一个问题的一个很好的解决方案。我已经用一些代码编辑了我的问题,这些代码是关于我现在如何使用LineCollection进行编辑的。关于另外两个问题,您是否知道“段”和“颜色”是否是我可以访问并设置为不同值的属性,因此我只需更新绘图,而不需要制作新绘图?在解释器内播放(我建议使用IPython shell)我可以看到LineCollection
对象具有set\u color
和set\u segments
方法。我不知道下一步该做什么,但(归结起来就是ax.draw\u artist(我的lc)
)似乎很有希望。感谢您抽出时间为我的问题提供答案。我使用了GBOFI建议的LineCollection。这是一个很好的答案,可以解决OP问题(尽管不是以最佳方式),代码清晰,基准:+1
In [2]: fig = plt.figure()
...: ax = plt.axes(projection='3d')
...: n = 100
...: x = np.arange(n)/n
...: y = np.arange(n)/n
...: z = np.arange(n)/n
...: colors = np.arange(n)/n
...: points = np.array([x, y, z]).T.reshape(-1, 1, 3)
...: segs = np.concatenate([points[:-1], points[1:]], axis=1)
...: lc = Line3DCollection(segs, cmap='plasma', linewidth=4)
...: ax.add_collection(lc)
Out[2]: <mpl_toolkits.mplot3d.art3d.Line3DCollection at 0x7f0528cdec10>