将仅包含0
和1
s的1d Numpy数组转换为唯一整数的最快方法是什么?
到目前为止,我提出的最好的方法是使用Cython并将数组视为镜像二进制数*。
@cython.boundscheck(False)
@cython.wraparound(False)
def _map_binary(np.ndarray[np.int64_t, ndim=1] x):
cdef int tot = 0
cdef int i
cdef int n = x.shape[0]
for i in xrange(n):
if x[i]:
tot += 2**i
return tot
由于这仍然是我的算法的瓶颈,我想知道是否有更智能,更快捷的方法。
*显然这种映射只对于具有相同长度的数组是一对一的(因为对数组附加零不会改变结果整数),但这对我来说是好的。
答案 0 :(得分:3)
整理评论和我自己的一些想法
一些纯Python(+ numpy)选项:
import numpy as np
def _map_binary_str_and_back(x):
# from comment by @NickA
return int("".join([str(c) for c in x]),2)
def _map_binary_np_dot(x):
# from question http://stackoverflow.com/questions/41069825/convert-binary-01-numpy-to-integer-or-binary-string
return np.dot(x,1 << np.arange(x.size))
def _map_binary_np_pack(x):
# uses built in numpy function packbits, but unfortunately needs a bit of manipulation
# afterwards
x = np.packbits(x) # as np.int8
x.resize((8,),refcheck=False)
return x.view(dtype=np.int64)
一些Cython选项(请注意,我已将输出更改为64位整数,以便它适用于最多64个元素的数组):
cimport cython
cimport numpy as np
def _map_binary_original(np.ndarray[np.int64_t, ndim=1] x):
cdef np.uint64_t tot = 0
cdef np.uint64_t i
cdef int n = x.shape[0]
for i in xrange(n):
if x[i]:
tot += 2**i
return tot
@cython.boundscheck(False)
@cython.wraparound(False)
def _map_binary_contig(np.ndarray[np.int64_t, ndim=1, mode="c"] x):
cdef np.uint64_t tot = 0
cdef np.uint64_t i
cdef int n = x.shape[0]
for i in xrange(n):
if x[i]:
tot += 2**i
return tot
@cython.boundscheck(False)
@cython.wraparound(False)
def _map_binary_shift(np.ndarray[np.int64_t, ndim=1, mode="c"] x):
cdef np.uint64_t tot = 0
cdef np.uint64_t i
cdef int n = x.shape[0]
for i in xrange(n):
if x[i]:
tot += 1<<i
return tot
@cython.boundscheck(False)
@cython.wraparound(False)
def _map_binary_times2(np.ndarray[np.int64_t, ndim=1, mode="c"] x):
# @FranciscoCouzo
cdef np.uint64_t tot = 0
cdef np.uint64_t i
cdef int n = x.shape[0]
for i in xrange(n):
tot *= 2
if x[i]:
tot +=1
return tot
@cython.boundscheck(False)
@cython.wraparound(False)
def _map_binary_times2_as_shift(np.ndarray[np.int64_t, ndim=1, mode="c"] x):
cdef np.uint64_t tot = 0
cdef np.uint64_t i
cdef int n = x.shape[0]
for i in xrange(n):
tot *= 2
if x[i]:
tot +=1
return tot
和(供参考)一些时序代码
from map_binary import (_map_binary_original,_map_binary_contig,
_map_binary_shift,_map_binary_times2,
_map_binary_times2_as_shift)
test_array = np.random.randint(2,size=(60,)).astype(dtype=np.int64)
def time_function(name):
from timeit import timeit
num = 10000
timed = timeit("f(x)","from __main__ import {} as f, test_array as x".format(name),number=num)/num
print(name, timed)
for n in list(globals().keys()):
if n.startswith('_map_binary'):
time_function(n)
结果(为了清晰起见,略微重新格式化):
_map_binary_str_and_back 9.774386967484043e-05
_map_binary_np_dot 7.402434574531678e-06
_map_binary_np_pack 1.5813756692768855e-06
_map_binary_original 7.462656716457738e-07
_map_binary_contig 7.208434833198e-07
_map_binary_shift 5.84043665719558e-07
_map_binary_times2 6.467991376011505e-07
_map_binary_times2_as_shift 6.412435894529889e-07
总结:
np.packbits
的版本是最快的选择(但显然比Cython版本差)。然而,无Cython版本需要进一步的工作,以确保他们给出相同的答案(我认为点是整数溢出。packbits
翻转字节序,所以给出一个有效但不同的答案)答案 1 :(得分:0)
我不确定您的解决方案是否是算法视角下的最佳方式,但为了编写更优化的Cython代码,我建议遵循以下更改:
shape
而不是使用size()
而不是使用xrange
和索引,而不是使用i
,并且仅在数组上循环索引并增加变量range()
在每次迭代中(或者至少只使用xrange
),因为pow
是一个生成器,需要更多工作才能转换为C. form ibc.math cimport pow
@cython.boundscheck(False)
@cython.wraparound(False)
cdef int _map_binary(np.int32_t[:] x):
cdef int tot = 0
cdef int i = 0
cdef int n = x.size
cdef int item
for item in x:
if item:
tot = tot + pow(2, i)
i = i + 1
return tot
dplyr