Python 具有非通用函数的向量化列表理解

Python 具有非通用函数的向量化列表理解,python,python-3.x,numpy,optimization,vectorization,Python,Python 3.x,Numpy,Optimization,Vectorization,我正试图成熟地优化类似于这个mwe代码的东西。我正在使用列表理解,但我相信我应该能够以某种方式将其矢量化 A = numpy.arange(20000) B = numpy.arange(20000, 50000) C = [bin(i^j).count('1') for i in A for j in B].count(1) (这是搜索组a中与组B中成员之间的汉明距离为1的所有成员)大小顺序正确,但我将重复整个序列大约100次。C的平均尺寸预计约为10k 我没有成功地为bin(I^j)创建一

我正试图成熟地优化类似于这个mwe代码的东西。我正在使用列表理解,但我相信我应该能够以某种方式将其矢量化

A = numpy.arange(20000)
B = numpy.arange(20000, 50000)
C = [bin(i^j).count('1') for i in A for j in B].count(1)
(这是搜索组
a
中与组
B
中成员之间的汉明距离为1的所有成员)大小顺序正确,但我将重复整个序列大约100次。
C
的平均尺寸预计约为10k

我没有成功地为
bin(I^j)创建一个通用函数
uhamming
。count('1')
numpy.frompyfunc
;我要走了

module 'numpy' has no attribute 'uhamming'
我很乐意让
C
成为一个数组。谢谢你的关注

仅供参考,以下是我使用(2000)和(20005000)对最小化版本的评测输出:

12000007函数调用只需5.442秒
订购人:内部时间
ncalls tottime percall cumtime percall文件名:lineno(函数)
1    2.528    2.528    5.342    5.342 :4()
6000000 1.527 0.000 1.527 0.000{“str”对象的方法“计数”}
6000000 1.287 0.000 1.287 0.000{内置方法内置.bin}
1 0.089 0.089 0.089 0.089{“列表”对象的方法“计数”}
1    0.011    0.011    5.442    5.442 :2()
1 0.000 0.000 5.442 5.442{内置方法builtins.exec}
2 0.000 0.000 0.000 0.000{内置方法numpy.core.multiarray.arange}
1 0.000 0.000 0.000 0.000{方法'disable'的''lsprof.Profiler'对象}

错误提示您正在使用的某个位置

numpy.unhamming
快速而肮脏地使用
frompyfunc
将是:

In [126]: def unhamming(i,j):
     ...:     return bin(i^j).count('1')
     ...: 
In [127]: f = np.frompyfunc(unhamming, 2,1)
该函数接受2个输入,并返回1

对于较小的阵列:

In [124]: A = np.arange(200)
In [125]: B = np.arange(200,500)
In [128]: C = [bin(i^j).count('1') for i in A for j in B].count(1)
In [131]: C
Out[131]: 336
使用“矢量化”功能:

In [129]: f(A,B[:,None])
Out[129]: 
array([[3, 4, 4, ..., 3, 3, 4],
       [4, 3, 5, ..., 2, 4, 3],
       [4, 5, 3, ..., 4, 2, 3],
       ..., 
       [6, 5, 7, ..., 4, 6, 5],
       [6, 7, 5, ..., 6, 4, 5],
       [7, 6, 6, ..., 5, 5, 4]], dtype=object)
要知道有多少个1,要么把它变成一个列表,要么用一个整数和

In [130]: _.ravel().tolist().count(1)
Out[130]: 336
In [132]: (f(A,B[:,None])==1).sum()
Out[132]: 336
速度基本相同

In [133]: timeit C = [bin(i^j).count('1') for i in A for j in B].count(1)
45 ms ± 380 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [134]: timeit (f(A,B[:,None])==1).sum()
46.1 ms ± 97.4 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
有时,与直接迭代相比,
frompyfunc
的速度提高了2倍。但是它仍然需要多次调用
取消设置
函数。这不是真正的“矢量化”(在将计算移到C级代码的意义上)

我怀疑有一种方法可以对numpy表达式进行同样的计算,即对
B[:,None]
广播
a
。但我会留到另一个时间或海报


这是你工作的一部分。但是我还没有找到一个能在数组上工作的
bin
np.binary\u repr
没有帮助)

只是小小的改进


搜索[numpy]和hamming产生了这个问题:

下面是对@Divaker的一个答案的改编:

def foo(A, B):
    x = np.bitwise_xor(A,B[:,None])
    r = (1 << np.arange(15))[:,None]
    xr = (r[:,None]&x)
    xrc = (xr>0).sum(axis=0)
    return (xrc==1).sum()
In [280]: foo(A,B)
Out[280]: 336
def foo(A,B):
x=np.按位异或(A,B[:,无])
r=(10).和(轴=0)
返回值(xrc==1).sum()
In[280]:foo(A,B)
Out[280]:336

可以对其进行调整,例如调整
r
的大小,更改广播和形状。但是最终的和是匹配的。

如果A的一个元素与B的多个元素的汉明距离为1,那么这应该算一次还是多次?好问题!多次。谢谢@hpaulj,我想函数调用会增加一些开销,我会用它来处理更大的数字。是的,我使用的是
numpy.uhamming.outer(A,B)
。如果PyFunc中的
不能将我的函数转换成一个通用函数,然后我就可以用于广播,那它有什么用呢?它不能将它转换成
ufunc
(使用
outer
reduce
等方法)。但它确实会广播输入数组,如[32]
中的
所示,与
A*B[:,None]
np相同。添加(A,B[:,None])
。再次感谢@hpaulj。我仍然感到惊讶的是,列表公司的效率并没有低很多。在汉明函数上的Numba有点帮助。也许会有帮助。
C = A ^ B[:,None]
In [160]: f1 = np.frompyfunc(lambda x: bin(x).count('1'),1,1)
In [161]: f1(A^B[:,None])
Out[161]: 
array([[3, 4, 4, ..., 3, 3, 4],
       [4, 3, 5, ..., 2, 4, 3],
       [4, 5, 3, ..., 4, 2, 3],
       ..., 
       [6, 5, 7, ..., 4, 6, 5],
       [6, 7, 5, ..., 6, 4, 5],
       [7, 6, 6, ..., 5, 5, 4]], dtype=object)
In [162]: (f1(A^B[:,None])==1).sum()
Out[162]: 336
In [163]: timeit (f1(A^B[:,None])==1).sum()
37 ms ± 295 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
def foo(A, B):
    x = np.bitwise_xor(A,B[:,None])
    r = (1 << np.arange(15))[:,None]
    xr = (r[:,None]&x)
    xrc = (xr>0).sum(axis=0)
    return (xrc==1).sum()
In [280]: foo(A,B)
Out[280]: 336