私は純粋なPythonで、次のコードを書いて、それが何をするかの説明はドキュメンテーション文字列である:Cython numpyの配列インデクサ速度向上
import numpy as np
from scipy.ndimage.measurements import find_objects
import itertools
def alt_indexer(arr):
"""
Returns a dictionary with the elements of arr as key
and the corresponding slice as value.
Note:
This function assumes arr is sorted.
Example:
>>> arr = [0,0,3,2,1,2,3]
>>> loc = _indexer(arr)
>>> loc
{0: (slice(0L, 2L, None),),
1: (slice(2L, 3L, None),),
2: (slice(3L, 5L, None),),
3: (slice(5L, 7L, None),)}
>>> arr = sorted(arr)
>>> arr[loc[3][0]]
[3, 3]
>>> arr[loc[2][0]]
[2, 2]
"""
unique, counts = np.unique(arr, return_counts=True)
labels = np.arange(1,len(unique)+1)
labels = np.repeat(labels,counts)
slicearr = find_objects(labels)
index_dict = dict(itertools.izip(unique,slicearr))
return index_dict
私は非常に大きな配列のインデックスを作成されますので、私はスピードアップしたかったですここでは、cythonを使っての操作は同等の実装です::私はそれが両方の操作を完了するのに要した時間を比較した
import numpy as np
cimport numpy as np
def _indexer(arr):
cdef tuple unique_counts = np.unique(arr, return_counts=True)
cdef np.ndarray[np.int32_t,ndim=1] unique = unique_counts[0]
cdef np.ndarray[np.int32_t,ndim=1] counts = unique_counts[1].astype(int)
cdef int start=0
cdef int end
cdef int i
cdef dict d ={}
for i in xrange(len(counts)):
if i>0:
start = counts[i-1]+start
end=counts[i]+start
d[unique[i]]=slice(start,end)
return d
ベンチマーク
In [26]: import numpy as np
In [27]: rr=np.random.randint(0,1000,1000000)
In [28]: %timeit _indexer(rr)
10 loops, best of 3: 40.5 ms per loop
In [29]: %timeit alt_indexer(rr) #pure python
10 loops, best of 3: 51.4 ms per loop
速度向上は最小限に抑えられています。私はnumpyを使用して以来、私のコードはすでに部分的に最適化されていたことを認識しています。
私が気づいていないボトルネックはありますか? np.unique
を使用せず、代わりに自分の実装を書いてください。
ありがとうございました。
def unique_counts(arr):
counts = np.bincount(arr)
mask = counts!=0
unique = np.nonzero(mask)[0]
return unique, counts[mask]
ランタイムテスト
ケース - 非負、非常に大きく、多くのない繰り返しint
番号を持つarr
で
'cython'ループは、純粋な' C'に変換できれば高速です。あなたの場合、ループは 'numpy.unique'とPython辞書とスライスオブジェクトを使います。 – hpaulj