【问题标题】:How to create ternary contour plot in Python?如何在 Python 中创建三元等值线图?
【发布时间】:2015-04-08 10:24:05
【问题描述】:

我有一个数据集如下(在 Python 中):

import numpy as np
A = np.array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0, 0.1, 0.2, 0.3, 0.4, 0.2, 0.2, 0.05, 0.1])
B = np.array([0.9, 0.7, 0.5, 0.3, 0.1, 0.2, 0.1, 0.15, 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
C = np.array([0, 0.1, 0.2, 0.3, 0.4, 0.2, 0.2, 0.05, 0.1, 0.9, 0.7, 0.5, 0.3, 0.1, 0.2, 0.1, 0.15, 0])
D = np.array([1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2])

我正在尝试使用 matplotlib 创建三元图,如图 (source) 所示。轴是 A、B、C 和 D 值应该用轮廓表示,并且需要像图中那样标记点。

可以在 matplotlib 或 Python 中创建这样的图吗?

【问题讨论】:

  • 简短的回答是“是”,但工作量很大...而且我不确定您是否得到了这些倾斜的刻度标签...

标签: python graph matplotlib plot contour


【解决方案1】:

是的,他们可以;至少有几个软件包可以提供帮助。

我曾经试图在一篇博文中收集它们,Ternary diagrams。请务必查看各种链接和 cmets。

2019 年 9 月 11 日更新:我写了一篇关于同一主题的更新、更实用的博文:x lines of Python: Ternary diagrams。它使用之前引用的python-ternary 库。

这些似乎是 Python 的最佳选择:

在另一个 SO 问题中也有一些建议:Library/tool for drawing ternary/triangle plots [closed]

【讨论】:

    【解决方案2】:

    你可以试试这样的:

    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib.tri as tri
    
    
    
    
    # first load some data:  format x1,x2,x3,value
    test_data = np.array([[0,0,1,0],
                          [0,1,0,0],
                          [1,0,0,0],
                          [0.25,0.25,0.5,1],
                          [0.25,0.5,0.25,1],
                          [0.5,0.25,0.25,1]])
    
    # barycentric coords: (a,b,c)
    a=test_data[:,0]
    b=test_data[:,1]
    c=test_data[:,2]
    
    # values is stored in the last column
    v = test_data[:,-1]
    
    # translate the data to cartesian corrds
    x = 0.5 * ( 2.*b+c ) / ( a+b+c )
    y = 0.5*np.sqrt(3) * c / (a+b+c)
    
    
    # create a triangulation out of these points
    T = tri.Triangulation(x,y)
    
    # plot the contour
    plt.tricontourf(x,y,T.triangles,v)
    
    
    # create the grid
    corners = np.array([[0, 0], [1, 0], [0.5,  np.sqrt(3)*0.5]])
    triangle = tri.Triangulation(corners[:, 0], corners[:, 1])
    
    # creating the grid
    refiner = tri.UniformTriRefiner(triangle)
    trimesh = refiner.refine_triangulation(subdiv=4)
    
    #plotting the mesh
    plt.triplot(trimesh,'k--')
    
    
    plt.show()
    

    请注意,您可以通过以下方式移除 x,y 轴:

    plt.axis('off')
    

    但是,对于三角轴+标签和刻度,我还不知道,但如果有人有解决方案,我会接受它;)

    最好的,

    朱利安

    【讨论】:

    • 部分代码灵感来自 [blog.bogatron.net/blog/2014/02/02/…
    • 这段代码对我很有用!我想知道如何调整网格中的线数;在上面的示例中,有 16 行,但理想情况下您希望是 5 的倍数(例如,5、10 或 20 行)。我尝试在 refiner.refine_triangulation(subdiv=x) 中更改细分数,但我只能得到 4 的倍数(例如 4、8、16 等)。有没有一个简单的解决方案?谢谢!
    【解决方案3】:

    您可以尝试以下受以下启发的代码: https://matplotlib.org/gallery/images_contours_and_fields/tricontour_smooth_user.html#sphx-glr-gallery-images-contours-and-fields-tricontour-smooth-user-py

    from matplotlib.tri import Triangulation, TriAnalyzer, UniformTriRefiner
    import matplotlib.pyplot as plt
    import matplotlib.cm as cm
    import numpy as np
    from lineticks import LineTicks
    
    
    
    #-----------------------------------------------------------------------------
    # Analytical test function
    #-----------------------------------------------------------------------------
    def experiment_res(x, y):
        """ An analytic function representing experiment results """
        x = 2.*x
        r1 = np.sqrt((0.5 - x)**2 + (0.5 - y)**2)
        theta1 = np.arctan2(0.5 - x, 0.5 - y)
        r2 = np.sqrt((-x - 0.2)**2 + (-y - 0.2)**2)
        theta2 = np.arctan2(-x - 0.2, -y - 0.2)
        z = (4*(np.exp((r1/10)**2) - 1)*30. * np.cos(3*theta1) +
             (np.exp((r2/10)**2) - 1)*30. * np.cos(5*theta2) +
             2*(x**2 + y**2))
        return (np.max(z) - z)/(np.max(z) - np.min(z))
    
    #-----------------------------------------------------------------------------
    # Generating the initial data test points and triangulation for the demo
    #-----------------------------------------------------------------------------
    # User parameters for data test points
    n_test = 200  # Number of test data points, tested from 3 to 5000 for subdiv=3
    
    subdiv = 3  # Number of recursive subdivisions of the initial mesh for smooth
                # plots. Values >3 might result in a very high number of triangles
                # for the refine mesh: new triangles numbering = (4**subdiv)*ntri
    
    init_mask_frac = 0.0    # Float > 0. adjusting the proportion of
                            # (invalid) initial triangles which will be masked
                            # out. Enter 0 for no mask.
    
    min_circle_ratio = .01  # Minimum circle ratio - border triangles with circle
                            # ratio below this will be masked if they touch a
                            # border. Suggested value 0.01 ; Use -1 to keep
                            # all triangles.
    
    # Random points
    random_gen = np.random.mtrand.RandomState(seed=1000)
    #x_test = random_gen.uniform(-1., 1., size=n_test)
    
    x_test=np.array([0, 0.25, 0.5, 0.75, 1, 0.125, 0.375, 0.625,     0.875, 0.25, 0.5, 0.75, 0.375, 0.625, 0.5])
    y_test=np.array([0, 0, 0, 0, 0, 0.216506406, 0.216506406, 0.216506406, 0.216506406, 0.433012812, 0.433012812,0.433012812, 0.649519219, 0.649519219, 0.866025625
    ])
    
    #y_test = random_gen.uniform(-1., 1., size=n_test)
    z_test = experiment_res(x_test, y_test)
    
    # meshing with Delaunay triangulation
    tri = Triangulation(x_test, y_test)
    ntri = tri.triangles.shape[0]
    
    # Some invalid data are masked out
    mask_init = np.zeros(ntri, dtype=np.bool)
    masked_tri = random_gen.randint(0, ntri, int(ntri*init_mask_frac))
    mask_init[masked_tri] = True
    tri.set_mask(mask_init)
    
    
    #-----------------------------------------------------------------------------
    # Improving the triangulation before high-res plots: removing flat triangles
    #-----------------------------------------------------------------------------
    # masking badly shaped triangles at the border of the triangular mesh.
    mask = TriAnalyzer(tri).get_flat_tri_mask(min_circle_ratio)
    tri.set_mask(mask)
    
    # refining the data
    refiner = UniformTriRefiner(tri)
    tri_refi, z_test_refi = refiner.refine_field(z_test, subdiv=subdiv)
    
    # analytical 'results' for comparison
    z_expected = experiment_res(tri_refi.x, tri_refi.y)
    
    # for the demo: loading the 'flat' triangles for plot
    flat_tri = Triangulation(x_test, y_test)
    flat_tri.set_mask(~mask)
    
    
    #-----------------------------------------------------------------------------
    # Now the plots
    #-----------------------------------------------------------------------------
    # User options for plots
    plot_tri = True          # plot of base triangulation
    plot_masked_tri = True   # plot of excessively flat excluded triangles
    plot_refi_tri = False    # plot of refined triangulation
    plot_expected = False    # plot of analytical function values for comparison
    
    
    # Graphical options for tricontouring
    levels = np.arange(0., 1., 0.025)
    #cmap = cm.get_cmap(name='Blues', lut=None)
    cmap = cm.get_cmap(name='terrain', lut=None)
    
    
    f=-0.2
    e=-0.2
    ##############################################################################
    ##############################################################################
    
    
    t = np.linspace(0, 1, 100)
    xx = t/2
    yy = t*0.8660254037
    
    plt.subplots(facecolor='w')
    ax = plt.axes([-0.2, -0.2, 1.2, 1.2])
    
    traj, = ax.plot(xx, yy, c='red', lw=4)
    
    
    ax.plot(e, f)
    
    ax.set_xlim(-0.5,1.2)
    ax.set_ylim(-0.5,1.2)
    # Add major ticks every 10th time point and minor ticks every 4th;
    # label the major ticks with the corresponding time in secs.
    major_ticks = LineTicks(traj, range(0, n, 10), 10, lw=2,
                            label=['{:.2f}'.format(tt) for tt in t[::10]])
    minor_ticks = LineTicks(traj, range(0,n), 4, lw=1)
    
    
    xg=xx+0.5
    yg=np.fliplr([yy])[0]
    
    ax1 = plt.axes([-0.2, -0.2, 1.2, 1.2])
    
    
    
    traj1, = ax1.plot(xg, yg, c='Blue', lw=4)
    
    major_ticks1 = LineTicks(traj1, range(0, n, 10), 10, lw=2,
                            label=['{:.2f}'.format(tt) for tt in t[::10]])
    minor_ticks1 = LineTicks(traj1, range(0,n), 4, lw=1)
    #ax.set_xlim(-0.2,t[-1]+0.2)
    
    ax1.plot(e, f)
    ax1.set_xlim(-0.5,1.2)
    ax1.set_ylim(-0.5,1.2)
    
    
    xgg=1-t
    ygg=yy*0
    
    ax3 = plt.axes([-0.2, -0.2, 1.2, 1.2])
    
    
    
    traj2, = ax3.plot(xgg, ygg, c='green', lw=4)
    
    major_ticks2 = LineTicks(traj2, range(0, n, 10), 10, lw=2,
                            label=['{:.2f}'.format(tt) for tt in t[::10]])
    minor_ticks2 = LineTicks(traj2, range(0,n), 4, lw=1)
    #ax.set_xlim(-0.2,t[-1]+0.2)
    
    ax1.plot(e, f)
    ax1.set_xlim(-0.5,1.2)
    ax1.set_ylim(-0.5,1.2)
    
    ##############################################################################
    ##############################################################################
    
    
    ax4 = plt.axes([-0.2, -0.2, 1.2, 1.2])
    #plt.figure()
    #plt.gca().set_aspect('equal')
    plt.title("Filtering a Delaunay mesh\n" +
              "(application to high-resolution tricontouring)")
    
    # 1) plot of the refined (computed) data contours:
    
    ax4.axes.tricontour(tri_refi, z_test_refi, levels=levels,
                   colors=['0.25', '0.5', '0.5', '0.5', '0.5'],
                   linewidths=[1.0, 0.5, 0.5, 0.5, 0.5])              
    ax4.axes.tricontourf(tri_refi, z_test_refi, levels=levels, cmap=cmap)
    
    
    ax4.plot(e, f)
    
    
    #ax4.set_xlim(-0.2,1.2)
    #ax4.set_ylim(-0.2,1.2)
    
    
    # 2) plot of the expected (analytical) data contours (dashed):
    if plot_expected:
        plt.tricontour(tri_refi, z_expected, levels=levels, cmap=cmap,
                       linestyles='--')
    # 3) plot of the fine mesh on which interpolation was done:
    if plot_refi_tri:
        plt.triplot(tri_refi, color='0.97')
    # 4) plot of the initial 'coarse' mesh:
    if plot_tri:
        plt.triplot(tri, color='0.7')
    # 4) plot of the unvalidated triangles from naive Delaunay Triangulation:
    if plot_masked_tri:
        plt.triplot(flat_tri, color='red')
    
    
    ##################################################################
    ###################################################################
    ax4.annotate('Oil', xy=(0.0, -0.15), xytext=(1, -0.15),
                arrowprops=dict(facecolor='green', shrink=0.05),
                )
    
    plt.show()
    
    enter code here
    

    ternary plot

    【讨论】:

    • 点击代码下方的链接“三元图”看图。
    【解决方案4】:

    只是添加另一个选项(虽然可能为时已晚,无法帮助 OP,但也许是其他人)。您可以使用pip install samternary 进行 pip 安装。 github链接是https://github.com/samueljmcameron/samternary

    对于原始帖子,您可以非常密切地遵循源代码中的示例examples/flatdata.py,即

    import matplotlib.pyplot as plt
    import numpy as np
    
    from samternary.ternary import Ternary
    
    # OP's data                                                             
    A = np.array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0,
                  0.1, 0.2, 0.3, 0.4, 0.2, 0.2, 0.05, 0.1])
    B = np.array([0.9, 0.7, 0.5, 0.3, 0.1, 0.2, 0.1, 0.15, 0, 0.1,
                  0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
    C = np.array([0, 0.1, 0.2, 0.3, 0.4, 0.2, 0.2, 0.05, 0.1, 0.9,
                  0.7, 0.5, 0.3, 0.1, 0.2, 0.1, 0.15, 0])
    D = np.array([1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1, 0,
                  1, 2])
    # note that the array C above is not necessary since A+B+C=1            
    
    
    # plot the data in two ways, in cartesian coordinates (ax_norm)         
    # and in ternary-plot coordinates (ax_trans)                            
    
    # create the figure and the two sets of axes                            
    fig, (ax_norm,ax_trans) = plt.subplots(1,2,
                                           figsize=[5,2.8])
    
    
    # plot data in normal way first using tricontourf                       
    ax_norm.tricontourf(A,B,D)
    ax_norm.set_xlabel(r'$\phi_1$')
    ax_norm.set_ylabel(r'$\phi_2$')
    
    # transform ax_trans to ternary-plot style, which includes              
    # building axes and labeling the axes                                   
    cob = Ternary(ax_trans, bottom_ax = 'bottom', left_ax = 'left',
                  right_ax = 'right',labelpad=20)
    
    # use change of bases method within Ternary() to                        
    points = cob.B1_to_B2(A,B)
    
    # affine transform x,y points to ternary-plot basis                     
    cs = ax_trans.tricontourf(points[0],points[1],D)
    
    
    ax_norm.set_title("Cartesian "
                      "(basis " + r"$\mathcal{B}_1$" + ")")
    ax_trans.set_title("flattened-grid "
                       "(basis " + r"$\mathcal{B}_2$" + ")")
    
    cbar = fig.colorbar(cs,ax=ax_trans,shrink=0.6)
    fig.subplots_adjust(bottom=0.2,hspace=0.01)
    plt.show()
    

    结果是(空白是由于来自 OP 的数据的稀疏性):

    image of data in cartesian coordinates vs ternary plot

    【讨论】:

      【解决方案5】:

      我认为截至 2021 年最强大的软件包是 Plottly,它有 contour ternary plotsscatter ternary plots

      【讨论】:

        猜你喜欢
        • 2022-10-15
        • 1970-01-01
        • 2021-07-21
        • 1970-01-01
        • 2021-12-12
        • 1970-01-01
        • 1970-01-01
        • 2019-11-02
        • 2016-01-31
        相关资源
        最近更新 更多