[英]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.