python从数组中随机选择一些元素_numpy.random随机选择数组元素如何更高效
最近在看代碼庫rlkit時,發現一句有意思的代碼和注釋(如下所示),大意是從列表中隨機選擇一個元素時使用np.random.randint比np.random.choice更加高效,相關的解釋是np.random.choice會進行一些不必要的復制操作,使得效率相較于randint低一些。
possible_future_obs_idxs = self._idx_to_future_obs_idx[i]
# This is generally faster than random.choice.
# Makes you wonder what random.choice is doing
num_options = len(possible_future_obs_idxs)
next_obs_i = int(np.random.randint(0, num_options))
future_obs_idxs.append(possible_future_obs_idxs[next_obs_i])
我做了相關的實驗進行對比,發現使用np.random.random的實現比np.random.randint更快,并且是否使用參數size=1對結果的影響也很大,文末進行了總結。
1. random, randint, choice
生成長度為1e6的隨機array,從中隨機選擇1個數和1000個數,比較兩種情況下運行速度,測試代碼如下:
import numpy as np
import time
N = 1000000
array = np.random.random(N)
def random(N=N): # 使用random隨機選擇1個數
return array[int(np.random.random() * N)]
def random_size1(N=N):# 使用random(size=1)隨機選擇1個數
return array[int(np.random.random(size=1) * N)]
def random_size_n(n, N=N): # 使用random隨機選擇n個數
return array[(np.random.random(n) * N).astype(np.int)]
def randint(N=N):# 使用randint隨機選擇1個數
return array[np.random.randint(N)]
def randint_size1(N=N): # 使用randint(size=1)隨機選擇1個數
return array[np.random.randint(N, size=1)]
def randint_size_n(n, N=N): # 使用randint隨機選擇n個數
return array[np.random.randint(N,size=n)]
def choice(array=array): # 使用choice隨機選擇1個數
return np.random.choice(array)
def choice_size1(array=array): # 使用choice(size=1)隨機選擇1個數
return np.random.choice(array, size=1)
def choice_size_n(n, array=array): # 使用choice隨機選擇n個數
return np.random.choice(array, size=n)
test_funs = [random, random_size1, randint, randint_size1, choice, choice_size1]
test_randn_funs = [random_size_n, randint_size_n, choice_size_n]
def test_main(mode, times=1000000):
test_fun = test_funs[mode]
start = time.time()
for _ in range(times):
test_fun()
end = time.time()
print('test {} {} times using time {} s'.format(test_fun, times, end - start))
def test_randn_main(mode,n=1000, times=1000000):
test_fun = test_randn_funs[mode]
start = time.time()
for _ in range(times):
test_fun(n)
end = time.time()
print('test {} {} times using time {} s'.format(test_fun, times, end - start))
if __name__ == "__main__":
for i in range(6):
test_main(i)
for i in range(3):
test_randn_main(i)
進行1e6次的實驗結果(單位:s):
更直觀的使用ipython的%timeit的結果:
import numpy as np
N = 1000
%timeit int(np.random.random() * N)
446 ns ± 3.21 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit np.random.randint(N)
908 ns ± 4.11 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit np.random.randint(N, size=1)
1.29 μs ± 5.42 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
以上實驗結果在不同環境中跑會有些許誤差,但是應該能得到以下結論:隨機選擇一個數時,random最快,速度是randint的2倍;
隨機選擇一個數時,不指定size=1的參數更快,以random為例,速度差異能到5倍左右;
隨機選擇n個數時,指定size=n,randint最快。
以下是我的猜測和解釋,如有問題歡迎指出:choice會有內存申請和復制array的操作,通常是最慢的;
指定size會有內存申請的操作,并且會轉化為ndarray類型,因此產生一個隨機數時不指定size=1更快;
random的輸出是python的float類型,指定size后是ndarray類型,乘以N時,float類型和N都直接進行python的float類型運算,而ndarray乘以N多進行了數據轉化和傳遞操作(轉化為numpy的類型并傳入底層進行運算,可以參考這個問題https://www.zhihu.com/question/24789359/answer/55643155),randint內部實現其實是一樣的,但是乘以N的操作本身是在numpy的類型中進行的,減少了兩次數據轉化和傳遞,速度更快。
>>> x=np.random.random()
>>> type(x)
>>> y=np.random.random(size=1)
>>> type(y)
為了讓大家體會到數據格式轉化的耗時,補充一個實驗,注意上面是np.random.random()乘以的是python的常數,均為python內置數據類型的運算,而如果乘以的是numpy數據類型,結果可能不一樣:
import numpy as np
N = 1000
L = np.random.randint(1, N, size=N)
%timeit [np.random.randint(x) for x in L]
1.12 ms ± 87.9 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit [int(np.random.random() * x) for x in L]
2.68 ms ± 92.4 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
## 可以看到這里使用np.random.random 反而比randint慢了,這是因為x其實是numpy.int64的格式
# np.random.random()是python數據類型,又涉及到numpy的數據格式轉換和數據傳遞。
>>>type(L[0])
numpy.int64
>>>type(np.random.random())
float
# 而當我們將x轉換為int型后,再次得到了我們之前的結論
%timeit [int(np.random.random() * int(x)) for x in L]
638 μs ± 12.8 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
因為涉及到數據格式轉化和向底層代碼的數據傳遞,簡單的python內置運算可能比numpy更快,當運算較多時,numpy并行的優勢才能顯示出來。
2. 案例優化
我們以rlkit的這段代碼為例進行優化,該段代碼的目的是從不等長的列表組里對每個列表隨機選擇1個數據。這個案例特殊的地方在于列表組里有N個不等長的子列表,還要考慮循環處理子列表的時間,使用numpy并行進行向量對應位相乘速度更快,選擇random_idx的過程能夠加速80倍左右。
import numpy as np
N = 1000
L = np.random.randint(1, N, size=N) # 子列表的長度
%timeit random_idx = (np.random.random(N) * L).astype(np.int)
13 μs ± 419 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit random_idx = [np.random.randint(x) for x in array_len]
1e+03 μs ± 22.1 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
3. Take Awaynumpy的運算會涉及到數據格式轉化和向底層代碼的數據傳遞,當運算比較少時python內置運算可能比numpy更快,所以要注意運算量的類型,numpy的優勢在于大規模的并行計算;
隨機選擇一個數時,盡量避免設置size=1,不設置size的運行速度從快到慢為:random > randint > choice;
隨機選擇n個數時,由于randint(size=n)內置了random(size=n) * N的操作,比外部實現的random(size=n) * N少了兩次數據轉化而更快,速度從快到慢為:randint > random > choice。
總結
以上是生活随笔為你收集整理的python从数组中随机选择一些元素_numpy.random随机选择数组元素如何更高效的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JavaWeb学习心得总结
- 下一篇: Python利用qrcode生成二维码并