python - Numpy:在另一个二维数组中查找二维数组的对应关系,而不考虑顺序
问题描述
我有两个形状不同的 numpy 矩阵:
vertices = np.array([[ 100, 101, 102, 103, -1],
[ 200, 201, 202, 203, 204],
[ 300, 301, 302, 303, 104],
[ 505, 506, 507, -1, -1]])
faces = np.array([[ 104, 102, 100],
[1202, 203, 2000],
[ 303, 505, 104],
[ 101, 102, 104]])
我想将每行的索引链接faces
到行,vertices
以便计算每个顶点的对应区域。面积计算不在这篇文章中,因为它无关紧要。
每个顶点可以对应于任意数量的面。每个面只能附加到一个顶点。
链接规则:对于 中的每一行vertices
,如果一行中存在 2 个或多个元素faces
,则这些行是链接的。
预期结果是一个字典,其中的键是 的索引vertices
,值是相应顶点的链接面的计数。
预期结果:
{0: 2.0, 2: 1.0}
我写了一个工作算法,但我正在寻找一个性能更高的实现:
def area(faces, corresponding_vertices_id):
skeleton_node_corresponding_area = defaultdict(lambda: 0.)
for face in faces:
for skeleton_node, vectex_id in enumerate(corresponding_vertices_id):
if np.count_nonzero(np.in1d(face, vectice_id)) >=2 :
area = 1 # Mock action. real area will be compute later
skeleton_node_corresponding_area[skeleton_node] = skeleton_node_corresponding_area[skeleton_node] + area
break
return skeleton_node_corresponding_area
area(faces, vertices)
defaultdict(<function __main__.area.<locals>.<lambda>()>, {0: 2.0, 2: 1.0})
解决方案
in1d(a, b)
本质上是这样做的(但可能没有中间阵列和一些短路):
(a == b[:, None]).any(axis=0)
您可以通过对数组进行预排序来加快速度。只要您只关心两个数字匹配,这应该是有效的:
vertices.sort(axis=1)
现在你可以做类似的事情
result = defaultdict(float)
for face in faces:
for i, vertex in enumerate(vertices):
n = vertex[np.searchsorted(vertex, face) % vertices.shape[1]] == face).sum()
if n > 2:
result[i] += 1.
break
faces
这是通过对的每一行中的每个元素进行二进制搜索来工作的vertices
。由于searchsorted
返回插入索引,因此您必须检查哪些位置实际匹配该值。模运算符确保插入索引超过数组末尾的元素不会导致IndexError
. 处理这个问题的正确方法是有条件的,但是将它们的索引设置为零会更快,并且效果很好,因为它们都不匹配。break
由于您提到每个面只能属于一个顶点,因此加快了速度。
这仍然不是非常快。假设一对数组的形状(M, N)
,你的算法是O(M^2 * N^2)
。这个执行O(M * N * log(N))
排序,然后是一个嵌套循环,即O(M * M * N * log(N))
(每次搜索都是O(log(N))
,但必须针对行的每个元素进行),总共O(M^2 * N * log(N))
.
一种更高效的方法(一旦你超过了额外开销很重要的点)是使用 python set
s,因为搜索单个元素是O(1)
:
vertices = [set(vertex) for vertex in vertices]
faces = [set(face) for face in faces]
result = defaultdict(float)
for face in faces:
for i, vertex in enumerate(vertices):
if len(face & vertex) > 2:
result[face] += 1
break
如果area
取决于转换为 之前face
和之前的原始值,请修改您的循环以仅在索引上运行,并通过索引访问您需要的内容。vertex
set
这是O(M * M * N)
因为这face & vertex
是一个O(N)
操作。如果你有一个非随机分布的值,你可能会做一些事情来降低外部循环的复杂性,例如,通过对行进行排序。
推荐阅读
- jmeter - 我们能否在运行时重命名 Jmeter 中的文件名,该文件名将上传到脚本中
- laravel - 在 laravel 6 中更新数据
- python - Twilio 不工作 - 与数据库 [on pythonanywhere] 连接后
- python - Pandas - 比较两个不同大小的数据帧中的列表
- python - Python - Pandas,将变体长度列表聚合成一个整洁的数据集
- reactjs - Material-ui ButtonBase 显示灰色背景而不是图像
- c++ - 如何构造具有“unique_ptr”作为成员变量的对象的“std::vector”?
- c++ - 初始大小显式设置为 0 的 std::vector
- vue.js - (JEST/VueJS) 不能在模块外使用 import 语句
- html - 用 CSS 适配 HTML 表格