簡體   English   中英

基於numpy數組中的行生成唯一值

[英]Generate unique values based on rows in a numpy array

我有一個3D numpy數組, arr ,形狀m*n*k

對於沿m軸的arr[:, 0, 0]值(例如arr[:, 0, 0] ),我想生成一個單獨的值來表示這個集合,這樣我最終可能得到一個2D矩陣, n*k 如果重復沿m軸的一組值,那么我們應該每次都生成相同的值。

即它是一個哈希問題。

我使用字典創建了問題的解決方案,但它大大降低了性能。 對於每組值,我調用此函數:

 def getCellId(self, valueSet):

     # Turn the set of values (a numpy vector) to a tuple so it can be hashed
     key = tuple(valueSet)

     # Try and simply return an existing ID for this key
     try:
       return self.attributeDict[key]
     except KeyError:

       # If the key was new (and didnt exist), try and generate a new Id by adding one to the max of all current Id's. This will fail the very first time we do this (as there will be no Id's yet), so in that case, just assign the value '1' to the newId
       try:
         newId = max(self.attributeDict.values()) +1
       except ValueError:
         newId = 1
       self.attributeDict[key] = newId
       return newId

數組本身的大小通常為30 * 256 * 256,因此一組值將具有30個值。 我有數百個這樣的陣列可以在任何時候處理。 目前,對於100個陣列的塊,執行需要完成計算哈希的所有處理需要1.3s。 包括高達75s的散列凸起。

有沒有更快的方法來生成單個代表值?

根據需要生成的新密鑰與舊密鑰的數量,很難說什么是最佳的。 但是使用你的邏輯,以下應該相當快:

import collections
import hashlib

_key = 0

def _get_new_key():
    global _key
    _key += 1
    return _key

attributes = collections.defaultdict(_get_new_key)

def get_cell_id(series):                             
    global attributes
    return attributes[hashlib.md5(series.tostring()).digest()]

編輯:

我現在更新了使用步幅根據您的問題循環所有數據系列:

In [99]: import numpy as np

In [100]: A = np.random.random((30, 256, 256))

In [101]: A_strided = np.lib.stride_tricks.as_strided(A, (A.shape[1] * A.shape[2], A.shape[0]), (A.itemsize, A.itemsize * A.shape[1] * A.shape[2]))

In [102]: %timeit tuple(get_cell_id(S) for S in A_strided)
10 loops, best of 3: 169 ms per loop

以上是每個30個元素陣列的256x256查找/分配。 當然不能保證md5哈希不會發生碰撞。 如果這應該是一個問題,你當然可以改為同一個lib中的其他哈希。

編輯2:

鑒於您似乎在3D陣列的第一軸上進行了大部分昂貴的操作,我建議您重新組織陣列:

In [254]: A2 = np.random.random((256, 256, 30))

In [255]: A2_strided = np.lib.stride_tricks.as_strided(A2, (A2.shape[0] * A2.shape[1], A2.shape[2]), (A2.itemsize * A2.shape[2], A2.itemsize))

In [256]: %timeit tuple(get_cell_id(S) for S in A2_strided)
10 loops, best of 3: 126 ms per loop

不必在內存中長距離跳轉就能實現大約25%的加速

編輯3:

如果沒有實際需要將哈希緩存到int查找,但是你只需要實際哈希,如果3D數組是int8 A2_strided ,那么給定A2A2_strided組織,時間可以減少一些。 在這15ms中是元組循環。

In [9]: from hashlib import md5

In [10]: %timeit tuple(md5(series.tostring()).digest() for series in A2_strided) 
10 loops, best of 3: 72.2 ms per loop

這可能是使用基本numpy函數的一種方法 -

import numpy as np

# Random input for demo
arr = np.random.randint(0,3,[2,5,4])

# Get dimensions for later usage
m,n,k = arr.shape

# Reshape arr to a 2D array that has each slice arr[:, n, k] in each row
arr2d = np.transpose(arr,(1,2,0)).reshape([-1,m])

# Perform lexsort & get corresponding indices and sorted array 
sorted_idx = np.lexsort(arr2d.T)
sorted_arr2d =  arr2d[sorted_idx,:]

# Differentiation along rows for sorted array
df1 = np.diff(sorted_arr2d,axis=0)

# Look for changes along df1 that represent new labels to be put there
df2 = np.append([False],np.any(df1!=0,1),0)

# Get unique labels
labels = df2.cumsum(0)

# Store those unique labels in a n x k shaped 2D array
pos_labels = np.zeros_like(labels)
pos_labels[sorted_idx] = labels
out = pos_labels.reshape([n,k])

樣品運行 -

In [216]: arr
Out[216]: 
array([[[2, 1, 2, 1],
        [1, 0, 2, 1],
        [2, 0, 1, 1],
        [0, 0, 1, 1],
        [1, 0, 0, 2]],

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

In [217]: out
Out[217]: 
array([[6, 4, 6, 5],
       [1, 0, 6, 4],
       [6, 3, 1, 1],
       [3, 0, 4, 1],
       [1, 3, 3, 2]], dtype=int32)

如果只是哈希試試這個

import numpy as np
import numpy.random

# create random data
a = numpy.random.randint(10,size=(5,3,3))

# create some identical 0-axis data
a[:,0,0] = np.arange(5)
a[:,0,1] = np.arange(5)

# create matrix with the hash values
h = np.apply_along_axis(lambda x: hash(tuple(x)),0,a)

h[0,0]==h[0,1]
# Output: True

但是,請謹慎使用它,並首先使用您的代碼測試此代碼。 ......我只能說它適用於這個簡單的例子。

此外,盡管兩個值可能具有相同的散列值,但它們可能有所不同。 這是一個總是可以使用哈希函數發生的問題,但它們不太可能發生

編輯 :為了與其他解決方案進行比較

timeit(np.apply_along_axis(lambda x: hash(tuple(x)),0,a))
# output: 1 loops, best of 3: 677 ms per loop

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM