矢量化的pythonic方式来获取比当前元素更大的元素数

时间:2018-04-17 14:35:17

标签: python performance numpy vectorization

我想比较数组b中的每个条目及其各自的列,以查找有多少条目(来自该列)大于引用。我的代码似乎令人尴尬地缓慢,我怀疑它是由于循环而不是向量操作。

我们可以“矢量化”以下代码吗?

/Volumes/SSD/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.10.0/7ef0f1d95bf4c0b3ba30bbae25e0e562b05cf75e/okhttp-3.10.0.jar: D8: Type `org.conscrypt.Conscrypt` was not found, it is required for default or static interface methods desugaring of `okhttp3.internal.platform.Platform okhttp3.internal.platform.ConscryptPlatform.buildIfSupported()`

经过一番思考后,我认为对每个列进行排序可以通过以后的比较更快地改善整体运行时间。

经过一些搜索后,我相信我想要逐列感知(http://lagrange.univ-lyon1.fr/docs/scipy/0.17.1/generated/scipy.stats.percentileofscore.html

3 个答案:

答案 0 :(得分:3)

它只需要进行一些深入的研究,以确定我们可以简单地在每列上使用argsort()索引来获得大于每次迭代时当前元素的计数。

方法#1

考虑到这个理论,一个解决方案就是使用两个argsort来获取计数 -

p = len(b)-1-b.argsort(0).argsort(0)

方法#2

我们可以进一步优化它,因为argsort索引是唯一数字。所以,第二个argsort步骤可以使用一些数组赋值+ advanced-indexing,就像这样 -

def count_occ(b):
    n,m = b.shape     
    out = np.zeros((n,m),dtype=int)
    idx = b.argsort(0)
    out[idx, np.arange(m)] = n-1-np.arange(n)[:,None]
    return out

最后,如问题中所述,除以n两种方法。

基准

到目前为止发布的所有方法的时间安排 -

In [174]: np.random.seed(0)
     ...: n = 1000
     ...: m = 200
     ...: b = np.random.rand(n,m)

In [175]: %timeit (len(b)-1-b.argsort(0).argsort(0))/float(n)
100 loops, best of 3: 17.6 ms per loop

In [176]: %timeit count_occ(b)/float(n)
100 loops, best of 3: 12.1 ms per loop

# @Brad Solomon's soln
In [177]: %timeit np.sum(b > b[:, None], axis=-2) / float(n)
1 loop, best of 3: 349 ms per loop

# @marco romelli's loopy soln
In [178]: %timeit loopy_soln(b)/float(n)
10 loops, best of 3: 139 ms per loop

答案 1 :(得分:1)

会有一些额外的空间复杂性,但实现这一目标的一种方法是扩展if (Fname !=null) students=students.Where(x => x.FirstName==Fname) if (Lname!=null) students=students.Where(x => x.LastName==Lname) if (ClassDate!=null) students=students.Where(x => x.classdate==ClassDate) 以启用广播比较,并使您能够完全摆脱(Python)循环:

b

完整代码:

# for n = 10; m = 2; np.random.seed(444)
>>> np.sum(b > b[:, None], axis=-2) / n
array([[0.7, 0.1],
       [0.3, 0.6],
       [0. , 0.2],
       [0.4, 0.8],
       # ...

答案 2 :(得分:1)

我认为以下解决方案要快得多:

import numpy as np

n = 1000
m = 200
b = np.random.rand(n,m)
p = np.zeros((n,m))

for i in range(m):
    col = []
    for j in range(n):
        col.append((j, b[j,i]))
    a = sorted(col, key=lambda x: x[1], reverse=True)
    for j in range(n):
        p[a[j][0], i] = j

它逐列工作,并且基于排序,所以快速猜测我会说O(nmlogn)

修改

基准测试结果

原始代码:每循环1.46 s±8.28 ms(7次运行的平均值±标准差,每次10次循环)

这个答案:每循环178 ms±295μs(平均值±标准偏差,7次运行,每次循环10次)