【问题标题】:Fastest way to count identical sub-arrays in a nd-array?计算 nd 数组中相同子数组的最快方法?
【发布时间】:2014-10-15 14:10:55
【问题描述】:

让我们考虑一个二维数组A

2   3   5   7
2   3   5   7
1   7   1   4
5   8   6   0
2   3   5   7

第一行、第二行和最后一行是相同的。我正在寻找的算法应该返回每个不同行的相同行数(=每个元素的重复数)。如果脚本可以很容易地修改为也计算相同列的数量,那就太好了。

我使用了一种低效的幼稚算法来做到这一点:

import numpy
A=numpy.array([[2,  3,  5,  7],[2,  3,  5,  7],[1,  7,  1,  4],[5,  8,  6,  0],[2,  3,  5,  7]])
i=0
end = len(A)
while i<end:
    print i,
    j=i+1
    numberID = 1
    while j<end:
        print j
        if numpy.array_equal(A[i,:] ,A[j,:]):
            numberID+=1
        j+=1
    i+=1
print A, len(A)

预期结果:

array([3,1,1]) # number identical arrays per line

我的算法看起来像是在 numpy 中使用原生 python,因此效率低下。感谢您的帮助。

【问题讨论】:

  • 考虑到 Jaime 的不那么简单的答案(如果有人有时间,希望得到更详细的解释?),对于像我这样的新手来说,不要认为这个问题是重复的;-)

标签: python arrays numpy


【解决方案1】:

在 unumpy >= 1.9.0 中,np.unique 有一个 return_counts 关键字参数,您可以结合解决方案 here 来获得计数:

b = np.ascontiguousarray(A).view(np.dtype((np.void, A.dtype.itemsize * A.shape[1])))
unq_a, unq_cnt = np.unique(b, return_counts=True)
unq_a = unq_a.view(A.dtype).reshape(-1, A.shape[1])

>>> unq_a
array([[1, 7, 1, 4],
       [2, 3, 5, 7],
       [5, 8, 6, 0]])

>>> unq_cnt
array([1, 3, 1])

在较旧的 numpy 中,您可以复制 np.unique does 的内容,如下所示:

a_view = np.array(A, copy=True)
a_view = a_view.view(np.dtype((np.void,
                               a_view.dtype.itemsize*a_view.shape[1]))).ravel()
a_view.sort()
a_flag = np.concatenate(([True], a_view[1:] != a_view[:-1]))
a_unq = A[a_flag]
a_idx = np.concatenate(np.nonzero(a_flag) + ([a_view.size],))
a_cnt = np.diff(a_idx)

>>> a_unq
array([[1, 7, 1, 4],
       [2, 3, 5, 7],
       [5, 8, 6, 0]])

>>> a_cnt
array([1, 3, 1])

【讨论】:

  • 谢谢,考虑到单行形式化,真的很难理解算法和逻辑,但我会一直努力直到理解;-)
  • 你能解释一下在申请 numpy.unique() 之前你做了什么以及为什么要得到 b 吗?
  • 在链接问题的 cmets 中有解释。基本上,它创建np.void 类型数据的视图,并调整整行中的字节数。因此,每一行似乎都 numpy 为一个单独的项目。
  • 好的,一些测试和处理有助于我理解。 TY 又是 Jaime!
【解决方案2】:

您可以对行条目进行 lexsort,这将为您提供按排序顺序遍历行的索引,从而使搜索 O(n) 而不是 O(n^2)。请注意,默认情况下,最后一列中的元素排在最后,即行从右到左而不是从左到右“按字母顺序排列”。

In [9]: a
Out[9]: 
array([[2, 3, 5, 7],
       [2, 3, 5, 7],
       [1, 7, 1, 4],
       [5, 8, 6, 0],
       [2, 3, 5, 7]])

In [10]: lexsort(a.T)
Out[10]: array([3, 2, 0, 1, 4])

In [11]: a[lexsort(a.T)]
Out[11]: 
array([[5, 8, 6, 0],
       [1, 7, 1, 4],
       [2, 3, 5, 7],
       [2, 3, 5, 7],
       [2, 3, 5, 7]])

【讨论】:

  • 您的提案对子数组进行了排序,但不计算它们;-)
【解决方案3】:

您可以为此使用 collections 模块中的 Counter 类。

它是这样工作的:

x = [2, 2, 1, 5, 2]
from collections import Counter
c=Counter(x)
print c

输出:计数器({2: 3, 1: 1, 5: 1})

您将面临的唯一问题是您的情况,因为 x 的每个值本身都是一个列表,它是一个不可散列的数据结构。 如果您可以转换元组中 x 的每个值,它应该作为:

x = [(2,  3,  5,  7),(2,  3,  5,  7),(1,  7,  1,  4),(5,  8,  6,  0),(2,  3,  5,  7)]
from collections import Counter
c=Counter(x)
print c

输出:计数器({(2, 3, 5, 7): 3, (5, 8, 6, 0): 1, (1, 7, 1, 4): 1})

【讨论】:

  • Counter 不适用于 numpy 对象(可惜),因此我们需要转换为列表。对我来说,将 numpy nd-array 转换为 nd-list 会很耗时,但感谢 pythonic 而不是 numpic 方法。
猜你喜欢
  • 2020-01-17
  • 2023-03-14
  • 2016-02-11
  • 2016-12-20
  • 1970-01-01
  • 2019-02-27
  • 2016-02-09
  • 2018-01-10
  • 2017-12-28
相关资源
最近更新 更多