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>
  • 有没有一种方法可以比我目前所做的更有效地为3D曲线图添加颜色(例如,避免循环)
  • 有没有一种方法可以在不重新绘制图形的情况下更新图形的颜色
  • 有没有一种方法可以更新一个或多个坐标向量而无需重新绘制
  • 说明:

    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>