簡體   English   中英

向量化pythonic方法來獲取大於當前元素的元素數

[英]Vectorized pythonic way to get count of elements greater than current element

我想將數組b中的每個條目與其相應的列進行比較,以找出(來自該列的)條目大於引用的條目。 我的代碼似乎令人尷尬地慢,我懷疑這是由於for循環而不是矢量運算引起的。

我們可以將以下代碼“向量化”嗎?

import numpy as np

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

for i in range(0,n): #rows
   for j in range(0,m):  # cols
     r = b[i,j]  
     p[i,j] = ( ( b[:,j] > r).sum() ) / (n) 

經過一番思考之后,我認為對每一列進行排序將使后面的比較更快,從而改善整體運行時間。

經過更多搜索后,我相信我希望按列顯示percentileofscore( http://lagrange.univ-lyon1.fr/docs/scipy/0.17.1/generation/scipy.stats.percentileofscore.html

它只需要進行更深入的研究就可以確定,我們可以在每列中簡單地使用argsort()索引來獲取每次迭代中大於當前元素的計數。

方法1

牢記這一理論,一種解決方案就是簡單地使用兩個argsort -ing來獲取計數-

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

方法#2

考慮到argsort索引是唯一數字,我們可以進一步優化它。 因此,第二個argsort步驟可以使用一些array-assignment + 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

將會增加一些空間上的復雜性,但是要達到這個目的,一種方法是擴展b來啟用廣播比較,並使您完全擺脫(Python)循環:

# 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],
       # ...

完整代碼:

import numpy as np

np.random.seed(444)

def loop(b):
    # Could also use for (i, j), val in np.ndenumerate(b)
    p = np.zeros_like(b)
    for i in range(0, n):
        for j in range(0, m):
            r = b[i, j]
            p[i, j] = ((b[:, j] > r).sum())
    return p / n

def noloop(b):
    n = b.shape[0]
    return np.sum(b > b[:, None], axis=-2) / n

n = 10
m = 2
b = np.random.rand(n, m)

assert np.allclose(loop(b), noloop(b))
# True

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

%timeit loop(b)
# 1.59 s ± 50.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit -r 7 -n 1 noloop(b)
# 443 ms ± 18.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

assert np.allclose(loop(b), noloop(b))
# True

我認為以下解決方案要快得多:

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個)

暫無
暫無

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

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