【问题标题】:Efficiently count the number of occurrences of unique subarrays in NumPy?有效计算 NumPy 中唯一子数组的出现次数?
【发布时间】:2015-09-01 23:19:42
【问题描述】:

我有一个形状为(128, 36, 8) 的数组,我想找出最后一维中长度为 8 的唯一子数组的出现次数。

我知道np.uniquenp.bincount,但它们似乎是针对元素而不是子数组的。我见过this question,但它是关于查找特定子数组的第一次出现,而不是所有唯一子数组的计数。

【问题讨论】:

  • 我想不出办法在 numpy 中做到这一点,但 trie 会不会太慢?它只需要访问每个元素一次,最后你会自动获得唯一子数组的数量以及它们的位置(如果你存储了它们)。
  • 这是一个密切相关的问题,stackoverflow.com/questions/8560440/…。基本思想是对子数组进行排序(字典排序)。一旦相似的子数组被分组,识别和计数它们就很简单了。

标签: python arrays numpy counting


【解决方案1】:

问题表明输入数组的形状为(128, 36, 8),我们有兴趣在最后一维中找到长度为8 的唯一子数组。 所以,我假设唯一性是沿着前两个维度合并在一起的。让我们假设 A 作为输入 3D 数组。

获取唯一子数组的数量

# Reshape the 3D array to a 2D array merging the first two dimensions
Ar = A.reshape(-1,A.shape[2])

# Perform lex sort and get the sorted indices and xy pairs
sorted_idx = np.lexsort(Ar.T)
sorted_Ar =  Ar[sorted_idx,:]

# Get the count of rows that have at least one TRUE value 
# indicating presence of unique subarray there
unq_out = np.any(np.diff(sorted_Ar,axis=0),1).sum()+1

示例运行 -

In [159]: A # A is (2,2,3)
Out[159]: 
array([[[0, 0, 0],
        [0, 0, 2]],

       [[0, 0, 2],
        [2, 0, 1]]])

In [160]: unq_out
Out[160]: 3

获取唯一子数组的出现次数

# Reshape the 3D array to a 2D array merging the first two dimensions
Ar = A.reshape(-1,A.shape[2])

# Perform lex sort and get the sorted indices and xy pairs
sorted_idx = np.lexsort(Ar.T)
sorted_Ar =  Ar[sorted_idx,:]

# Get IDs for each element based on their uniqueness
id = np.append([0],np.any(np.diff(sorted_Ar,axis=0),1).cumsum())

# Get counts for each ID as the final output
unq_count = np.bincount(id) 

示例运行 -

In [64]: A
Out[64]: 
array([[[0, 0, 2],
        [1, 1, 1]],

       [[1, 1, 1],
        [1, 2, 0]]])

In [65]: unq_count
Out[65]: array([1, 2, 1], dtype=int64)

【讨论】:

  • 这太棒了——我没想过要使用np.lexsort,我也不知道np.diff——但我们实际上有兴趣找到出现次数 i> 的唯一子数组,而不仅仅是唯一子数组的数量。正如@farhawa 的回答,这种方法是否可以适应返回唯一子数组及其计数?
  • 太棒了,谢谢。顺便说一句,我对您原始答案的修改似乎比您的扩展要快一些:~668 µs vs ~685 µs。
  • @Will 太好了!如果可能的话,如何在更大的数据集上测试它,比如(1000, 1000, 8)
  • 我在更大的数据集上得到了类似的结果:423ms vs 433ms。
【解决方案2】:

这里我修改了@Divakar 的非常有用的答案以返回唯一子数组的计数,以及子数组本身,以便输出与collections.Counter.most_common() 的输出相同:

# Get the array in 2D form.
arr = arr.reshape(-1, arr.shape[-1])

# Lexicographically sort
sorted_arr = arr[np.lexsort(arr.T), :]

# Get the indices where a new row appears
diff_idx = np.where(np.any(np.diff(sorted_arr, axis=0), 1))[0]

# Get the unique rows
unique_rows = [sorted_arr[i] for i in diff_idx] + [sorted_arr[-1]]

# Get the number of occurences of each unique array (the -1 is needed at
# the beginning, rather than 0, because of fencepost concerns)
counts = np.diff(
    np.append(np.insert(diff_idx, 0, -1), sorted_arr.shape[0] - 1))

# Return the (row, count) pairs sorted by count
return sorted(zip(unique_rows, counts), key=lambda x: x[1], reverse=True)

【讨论】:

    【解决方案3】:

    我不确定这是否是最有效的方法,但这应该可行。

    arr = arr.reshape(128*36,8)
    unique_ = []
    occurence_ = []
    
    for sub in arr:
        if sub.tolist() not in unique_:
            unique_.append(sub.tolist())
            occurence_.append(1)
        else:
            occurence_[unique_.index(sub.tolist())]+=1
    for index_,u in unique_:
       print u,"occurrence: %s"%occurence_[index_]
    

    【讨论】:

    • 这可行,但我希望避免使用诸如 tolistindex 之类的原生 Python 的函数,这些函数很昂贵。不过感谢您的回答。
    • 顺便说一句,对您的方法的一个明显优化是将计数保存在字典中,其中键是子数组的元组,而不是我们需要继续搜索的列表中unique_.index.
    • @Will 甚至更好,使用collections.Counter, counts = Counter(tuple(row) for row in arr) :)
    • @BiRico,太好了,不知道那个内置函数!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    • 2018-03-16
    • 2015-09-14
    • 1970-01-01
    • 2012-05-31
    相关资源
    最近更新 更多