Python 3.x 查找相等numpy 2D行的索引

Python 3.x 查找相等numpy 2D行的索引,python-3.x,numpy,Python 3.x,Numpy,我有一个2D numpy数组的Python列表(所有数组都具有相同的形状),我想提取相等数组的索引。我想到了这个: a = np.array([[1, 2], [3, 4]]) b = np.array([[1, 2], [3, 4]]) c = np.array([[3, 4], [1, 2]]) d = np.array([[3, 4], [1, 2]]) e = np.array([[3, 4], [1, 2]]) f = np.array([[1, 2], [3, 4]]) g = np

我有一个2D numpy数组的Python列表(所有数组都具有相同的形状),我想提取相等数组的索引。我想到了这个:

a = np.array([[1, 2], [3, 4]])
b = np.array([[1, 2], [3, 4]])
c = np.array([[3, 4], [1, 2]])
d = np.array([[3, 4], [1, 2]])
e = np.array([[3, 4], [1, 2]])
f = np.array([[1, 2], [3, 4]])
g = np.array([[9, 9], [3, 4]])

li = [a, b, c, d, e, f, g]

indexes = list(range(len(li)))
equals = []
for i, a_i in enumerate(indexes):
    a_equals = []
    for j, b_i in enumerate(indexes[i+1:]):
        if np.array_equal(li[a_i], li[b_i]):
            del indexes[j]
            a_equals.append(b_i)
    if a_equals:
        equals.append((a_i, *a_equals))

print(equals)
# [(0, 1, 5), (2, 3, 4)]

它可以工作(您可以假设所有二维阵列都不是空的),但是解决方案很笨重而且可能很慢。有没有办法用Numpy更优雅地实现这一点?

也许你可以试试
itertools

import itertools
from collections import defaultdict

equals=defaultdict(list)
visited=[]
for a, b in itertools.combinations(enumerate(li), 2):
  if not b[0] in visited and np.array_equal(a[1], b[1]) :
    equals[a[0]].append(b[0])
    visited += (a[0],b[0])

print equals
# defaultdict(<type 'list'>, {0: [1, 5], 2: [3, 4]})
导入itertools
从集合导入defaultdict
等于=默认dict(列表)
已访问=[]
对于itertools.组合中的a、b(枚举(li),2):
如果访问的数组中没有b[0],且np.数组_等于(a[1],b[1]):
等于[a[0]]。追加(b[0])
访问次数+=(a[0],b[0])
打印等于
#defaultdict(,{0:[1,5],2:[3,4]})

鉴于列表中的输入数组具有相同的形状,您可以将数组列表连接到单个2D数组中,每行表示输入列表的每个元素。这使得进一步的计算更容易,并且便于矢量化操作。实现看起来像这样-

# Concatenate all elements into a 2D array
all_arr = np.concatenate(li).reshape(-1,li[0].size)

# Reduce each row with IDs such that each they represent indexing tuple 
ids = np.ravel_multi_index(all_arr.T,all_arr.max(0)+1)

# Tag each such IDs based on uniqueness against other IDs
_,unqids,C = np.unique(ids,return_inverse=True,return_counts=True)

# Sort the unique IDs and split into groups for final output
sidx = unqids.argsort()

# Mask corresponding to unqids that has ID counts > 1
mask = np.in1d(unqids,np.where(C>1)[0])

# Split masked sorted indices at places corresponding to cumsum-ed counts
out = np.split(sidx[mask[sidx]],C[C>1].cumsum())[:-1]
注意:如果串联的输入数组
all\u arr
中有大量的列,您可能希望使用手动获取索引
id
,如下所示-

ids = all_arr.dot(np.append(1,(all_arr.max(0)+1)[::-1][:-1].cumprod())[::-1])

使用该软件包可以优雅地解决此问题(免责声明:我是其作者):

我怀疑找到这些索引很可能不是你的最终目标,如果你在numpy_索引上玩一玩,你可能会发现一条通往最终目标的更直接的路线存在


实际上,删除单计数索引可能最好作为后处理步骤,尽管您也可以使用npi.multiplicity>1作为预处理步骤

所有这些2D数组的形状都相同吗?是的。总是相同的形状。输出行的顺序重要吗,即,如果我们得到
[(2,3,4)(0,1,5)]
会怎么样?顺序并不重要,因为结果可以很容易地按照元组的第一个索引进行排序,比较散列,然后作为最后一步检查数组这当然是一个全numpy解决方案,速度非常快(对于我的解决方案为0.002秒,对于大输入的@gdlmx解决方案为2秒),但结果包括频率为1的数组索引。最简单的解决方案是从
out
中删除所有长度为1的数组,但是有更好的方法吗?@tsorn需要做更多的工作。刚刚添加了更新版本以满足该要求,请查看。这是对ravel_multi_索引的巧妙使用,用于编码唯一的子阵列;虽然我认为如果数组变得更高维或其最大值增加,可能会遇到溢出问题。@eelcoogendoorn是的,
max
value是OP必须记住的一件事。这对OP来说是个好消息!谢谢。
ids
是如何计算的?我看到它是2D数组值及其顺序的某种散列,但我不理解ravel_multi_索引的文档
import numpy_indexed as npi
print(npi.group_by(npi.as_index(li).inverse).split(np.arange(len(li))))