从子数组中的 numpy 二维数组中提取相交数组的索引
Extract indices of intersecting array from numpy 2D array in subarray
我有两个 2D numpy 方形数组,A 和 B。B 是从 A 中提取的数组,其中一定数量的列和行(具有相同的索引)已被剥离。它们都是对称的。例如,A 和 B 可以是:
A = np.array([[1,2,3,4,5],
[2,7,8,9,10],
[3,8,13,14,15],
[4,9,14,19,20],
[5,10,15,20,25]])
B = np.array([[1,3,5],
[3,13,15],
[5,15,25]])
使得缺失索引为 [1,3],相交索引为 [0,2,4]。
是否有 "smart" 方法提取 A 中与 B 中存在的 rows/columns 相对应的索引,涉及高级索引等?我所能想到的是:
import numpy as np
index = np.array([],dtype=int)
n,m = len(A),len(B)
for j in range(n):
k = 0
while set(np.intersect1d(B[j],A[k])) != set(B[j]) and k<m:
k+=1
np.append(index,k)
据我所知,在处理大型数组时速度很慢且很耗资源。
谢谢!
编辑:
我确实找到了一个更聪明的方法。我从两个数组中提取对角线,并通过简单的相等性检查对其执行上述循环:
index = []
a = np.diag(A)
b = np.diag(B)
for j in range(len(b)):
k = 0
while a[j+k] != b[j] and k<n:
k+=1
index.append(k+j)
虽然它仍然没有使用高级索引并且仍然迭代一个可能很长的列表,但这个部分解决方案看起来更清晰,我暂时会坚持使用它。
考虑所有值都不同的简单情况:
A = np.arange(25).reshape(5,5)
ans = [1,3,4]
B = A[np.ix_(ans, ans)]
In [287]: A
Out[287]:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]])
In [288]: B
Out[288]:
array([[ 6, 8, 9],
[16, 18, 19],
[21, 23, 24]])
如果我们用 A 的每一行测试 B 的第一行,我们最终会得出
[6, 8, 9]
与 [5, 6, 7, 8, 9]
的比较,我们可以从中收集
索引的候选解 [1, 3, 4]
.
我们可以通过先配对 来生成一组所有可能的候选解决方案
B 的行 与 A 的 行
如果只有一个候选人,那么我们就完成了,因为我们得到了 B 是 a
A 的子矩阵,因此总有一个解。
如果有不止一个候选人,那么我们可以对
B的第二行,并取候选解的交集 -- 之后
所有,一个解决方案必须是B的每一行的解决方案。
因此我们可以遍历 B 的行和 short-circuit 一旦我们找到那里
只有一个候选人。同样,我们假设 B 始终是 A 的子矩阵。
下面的find_idx
函数实现了上述想法:
import itertools as IT
import numpy as np
def find_idx_1d(rowA, rowB):
result = []
if np.in1d(rowB, rowA).all():
result = [tuple(sorted(idx))
for idx in IT.product(*[np.where(rowA==b)[0] for b in rowB])]
return result
def find_idx(A, B):
candidates = set([idx for row in A for idx in find_idx_1d(row, B[0])])
for Bi in B[1:]:
if len(candidates) == 1:
# stop when there is a unique candidate
return candidates.pop()
new = [idx for row in A for idx in find_idx_1d(row, Bi)]
candidates = candidates.intersection(new)
if candidates:
return candidates.pop()
raise ValueError('no solution found')
正确性:您提出的两个解决方案可能并不总是return正确的结果,尤其是当存在重复值时。例如,
def is_solution(A, B, idx):
return np.allclose(A[np.ix_(idx, idx)], B)
def find_idx_orig(A, B):
index = []
for j in range(len(B)):
k = 0
while k<len(A) and set(np.intersect1d(B[j],A[k])) != set(B[j]):
k+=1
index.append(k)
return index
def find_idx_diag(A, B):
index = []
a = np.diag(A)
b = np.diag(B)
for j in range(len(b)):
k = 0
while a[j+k] != b[j] and k<len(A):
k+=1
index.append(k+j)
return index
def counterexample():
"""
Show find_idx_diag, find_idx_orig may not return the correct result
"""
A = np.array([[1,2,0],
[2,1,0],
[0,0,1]])
ans = [0,1]
B = A[np.ix_(ans, ans)]
assert not is_solution(A, B, find_idx_orig(A, B))
assert is_solution(A, B, find_idx(A, B))
A = np.array([[1,2,0],
[2,1,0],
[0,0,1]])
ans = [1,2]
B = A[np.ix_(ans, ans)]
assert not is_solution(A, B, find_idx_diag(A, B))
assert is_solution(A, B, find_idx(A, B))
counterexample()
Benchmark:出于好奇而忽略正确性问题,后果自负
让我们根据速度比较这些功能。
def make_AB(n, m):
A = symmetrize(np.random.random((n, n)))
ans = np.sort(np.random.choice(n, m, replace=False))
B = A[np.ix_(ans, ans)]
return A, B
def symmetrize(a):
" (EOL)"
return a + a.T - np.diag(a.diagonal())
if __name__ == '__main__':
counterexample()
A, B = make_AB(500, 450)
assert is_solution(A, B, find_idx(A, B))
In [283]: %timeit find_idx(A, B)
10 loops, best of 3: 74 ms per loop
In [284]: %timeit find_idx_orig(A, B)
1 loops, best of 3: 14.5 s per loop
In [285]: %timeit find_idx_diag(A, B)
100 loops, best of 3: 2.93 ms per loop
所以find_idx
比find_idx_orig
快很多,但不如find_idx_orig
快
find_idx_diag
.
我有两个 2D numpy 方形数组,A 和 B。B 是从 A 中提取的数组,其中一定数量的列和行(具有相同的索引)已被剥离。它们都是对称的。例如,A 和 B 可以是:
A = np.array([[1,2,3,4,5],
[2,7,8,9,10],
[3,8,13,14,15],
[4,9,14,19,20],
[5,10,15,20,25]])
B = np.array([[1,3,5],
[3,13,15],
[5,15,25]])
使得缺失索引为 [1,3],相交索引为 [0,2,4]。
是否有 "smart" 方法提取 A 中与 B 中存在的 rows/columns 相对应的索引,涉及高级索引等?我所能想到的是:
import numpy as np
index = np.array([],dtype=int)
n,m = len(A),len(B)
for j in range(n):
k = 0
while set(np.intersect1d(B[j],A[k])) != set(B[j]) and k<m:
k+=1
np.append(index,k)
据我所知,在处理大型数组时速度很慢且很耗资源。
谢谢!
编辑: 我确实找到了一个更聪明的方法。我从两个数组中提取对角线,并通过简单的相等性检查对其执行上述循环:
index = []
a = np.diag(A)
b = np.diag(B)
for j in range(len(b)):
k = 0
while a[j+k] != b[j] and k<n:
k+=1
index.append(k+j)
虽然它仍然没有使用高级索引并且仍然迭代一个可能很长的列表,但这个部分解决方案看起来更清晰,我暂时会坚持使用它。
考虑所有值都不同的简单情况:
A = np.arange(25).reshape(5,5)
ans = [1,3,4]
B = A[np.ix_(ans, ans)]
In [287]: A
Out[287]:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]])
In [288]: B
Out[288]:
array([[ 6, 8, 9],
[16, 18, 19],
[21, 23, 24]])
如果我们用 A 的每一行测试 B 的第一行,我们最终会得出
[6, 8, 9]
与 [5, 6, 7, 8, 9]
的比较,我们可以从中收集
索引的候选解 [1, 3, 4]
.
我们可以通过先配对 来生成一组所有可能的候选解决方案 B 的行 与 A 的 行
如果只有一个候选人,那么我们就完成了,因为我们得到了 B 是 a A 的子矩阵,因此总有一个解。
如果有不止一个候选人,那么我们可以对 B的第二行,并取候选解的交集 -- 之后 所有,一个解决方案必须是B的每一行的解决方案。
因此我们可以遍历 B 的行和 short-circuit 一旦我们找到那里 只有一个候选人。同样,我们假设 B 始终是 A 的子矩阵。
下面的find_idx
函数实现了上述想法:
import itertools as IT
import numpy as np
def find_idx_1d(rowA, rowB):
result = []
if np.in1d(rowB, rowA).all():
result = [tuple(sorted(idx))
for idx in IT.product(*[np.where(rowA==b)[0] for b in rowB])]
return result
def find_idx(A, B):
candidates = set([idx for row in A for idx in find_idx_1d(row, B[0])])
for Bi in B[1:]:
if len(candidates) == 1:
# stop when there is a unique candidate
return candidates.pop()
new = [idx for row in A for idx in find_idx_1d(row, Bi)]
candidates = candidates.intersection(new)
if candidates:
return candidates.pop()
raise ValueError('no solution found')
正确性:您提出的两个解决方案可能并不总是return正确的结果,尤其是当存在重复值时。例如,
def is_solution(A, B, idx):
return np.allclose(A[np.ix_(idx, idx)], B)
def find_idx_orig(A, B):
index = []
for j in range(len(B)):
k = 0
while k<len(A) and set(np.intersect1d(B[j],A[k])) != set(B[j]):
k+=1
index.append(k)
return index
def find_idx_diag(A, B):
index = []
a = np.diag(A)
b = np.diag(B)
for j in range(len(b)):
k = 0
while a[j+k] != b[j] and k<len(A):
k+=1
index.append(k+j)
return index
def counterexample():
"""
Show find_idx_diag, find_idx_orig may not return the correct result
"""
A = np.array([[1,2,0],
[2,1,0],
[0,0,1]])
ans = [0,1]
B = A[np.ix_(ans, ans)]
assert not is_solution(A, B, find_idx_orig(A, B))
assert is_solution(A, B, find_idx(A, B))
A = np.array([[1,2,0],
[2,1,0],
[0,0,1]])
ans = [1,2]
B = A[np.ix_(ans, ans)]
assert not is_solution(A, B, find_idx_diag(A, B))
assert is_solution(A, B, find_idx(A, B))
counterexample()
Benchmark:出于好奇而忽略正确性问题,后果自负 让我们根据速度比较这些功能。
def make_AB(n, m):
A = symmetrize(np.random.random((n, n)))
ans = np.sort(np.random.choice(n, m, replace=False))
B = A[np.ix_(ans, ans)]
return A, B
def symmetrize(a):
" (EOL)"
return a + a.T - np.diag(a.diagonal())
if __name__ == '__main__':
counterexample()
A, B = make_AB(500, 450)
assert is_solution(A, B, find_idx(A, B))
In [283]: %timeit find_idx(A, B)
10 loops, best of 3: 74 ms per loop
In [284]: %timeit find_idx_orig(A, B)
1 loops, best of 3: 14.5 s per loop
In [285]: %timeit find_idx_diag(A, B)
100 loops, best of 3: 2.93 ms per loop
所以find_idx
比find_idx_orig
快很多,但不如find_idx_orig
快
find_idx_diag
.