绘制数据点的外部轮廓而不失去分辨率

2 投票
1 回答
49 浏览
提问于 2025-04-14 17:47

我有一组数据点(黑色的散点),我想画出它们的外部轮廓。我尝试过计算这些点的凸包(下面的代码),但是这样会失去太多细节,形状也变得不那么准确了。

# load usual stuff
from __future__ import print_function
import sys, os
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from matplotlib import colors
from scipy.spatial import ConvexHull

# read input file
cbm_contour = sys.argv[1]


def parse_pdb_coords(file):
    f = open(file, "r")

    coords = X = np.empty(shape=[0, 3])

    while True:
        line = f.readline()
        if not line: break

        if line.split()[0] == "ATOM" or line.split()[0] == "HETATM":
            Xcoord = float(line[30:38])
            Ycoord = float(line[38:46])
            Zcoord = float(line[46:54])

            coords = np.append(coords, [[Xcoord, Ycoord, Zcoord]], axis=0)

    return coords

##########################################################################

plt.figure(figsize=(11, 10))

# parse input file
cbm_coords = parse_pdb_coords(cbm_contour)

# consider only x- and y-axis coordinates
flattened_points = cbm_coords[:, :2]
x = cbm_coords[:,0]
y = cbm_coords[:,1]

# Find the convex hull of the flattened points
hull = ConvexHull(flattened_points)

for simplex in hull.simplices:
    plt.plot(flattened_points[simplex, 0], flattened_points[simplex, 1], color='red', lw=2)

plt.scatter(cbm_coords[:,0], cbm_coords[:,1], s=1, c='black')

plt.xlabel('X-axis coordinate ($\mathrm{\AA} $)', size=16)
plt.ylabel('Y-axis distance ($\mathrm{\AA} $)', size=16)

plt.yticks(np.arange(-20, 24, 4),size=16)
plt.xticks(np.arange(-20, 24, 4),size=16)

plt.savefig("example.png", dpi=300, transparent=False)
plt.show()

需要注意的是,由于数据点的复杂性,这个问题不能简化成一个“最小可工作示例”,但我的数据集可以在这里下载。我的想法是希望能找到一个适用于其他数据集的通用解决方案。

有没有人有什么建议呢?

在这里输入图片描述

1 个回答

1

你可以用一个叫做 Alpha Shape 的东西来代替你的凸包,举个例子,你可以使用 alphashape 这个库。

你需要尝试不同的 alpha 值,找到一个适合你需求的值。在这里,alpha=1 的效果不错。

# pip install alphashape
# pip install descartes

import matplotlib.pyplot as plt
from alphashape import alphashape
from descartes import PolygonPatch

ax = plt.subplot()
ax.scatter(arr[:, 0], arr[:, 1], s=1, color='k')
ax.set_aspect(1)

ashape = alphashape(arr[:, :2].tolist(), alpha=1)

ax.add_patch(PolygonPatch(ashape, fc='none', ec='red'))

输出结果:

pandas alpha shape

假设输入是 arr (X, Y, Z):

array([[-18.1  ,   1.418,   4.103],
       [-17.712,   2.202,   5.819],
       [-17.58 ,   1.998,   5.527],
       ...,
       [  9.472,  -1.901,  11.157],
       [  9.472,  -1.295,  11.615],
       [  9.472,  -0.895,  12.261]])

注意

如果你遇到以下错误:

IndexError: too many indices for array: array is 0-dimensional, but 2 were indexed

那么你的 shapely 版本太新了,不兼容 descartes。你可以按照 这里 的说明来解决这个问题。

撰写回答