2 回答

TA貢獻1775條經驗 獲得超11個贊
基準測試
我們將僅對各種數據集進行基準測試并從中得出結論。
計時
使用benchit
包(幾個基準測試工具打包在一起;免責聲明:我是它的作者)對建議的解決方案進行基準測試。
基準代碼:
import numpy as np
import benchit
def numpy_concatenate(a, b):
return np.concatenate((a,b),axis=1)
def numpy_hstack(a, b):
return np.hstack((a,b))
def preallocate(a, b):
m,n = a.shape[1], b.shape[1]
out = np.empty((a.shape[0],m+n), dtype=np.result_type((a.dtype, b.dtype)))
out[:,:m] = a
out[:,m:] = b
return out
funcs = [numpy_concatenate, numpy_hstack, preallocate]
R = np.random.rand
inputs = {n: (R(1000,1000), R(1000,n)) for n in [100, 200, 500, 1000, 200, 5000]}
t = benchit.timings(funcs, inputs, multivar=True, input_name='Col length of b')
t.plot(logy=False, logx=True, savepath='plot_1000rows.png')
結論:它們在時間上具有可比性。
內存分析
在內存方面,np.hstack應該類似于np.concatenate. 因此,我們將使用其中之一。
讓我們設置一個帶有大型二維數組的輸入數據集。我們將做一些內存基準測試。
設置代碼:
# Filename : memprof_npconcat_preallocate.py
import numpy as np
from memory_profiler import profile
@profile(precision=10)
def numpy_concatenate(a, b):
return np.concatenate((a,b),axis=1)
@profile(precision=10)
def preallocate(a, b):
m,n = a.shape[1], b.shape[1]
out = np.empty((a.shape[0],m+n), dtype=np.result_type((a.dtype, b.dtype)))
out[:,:m] = a
out[:,m:] = b
return out
R = np.random.rand
a,b = R(1000,1000), R(1000,1000)
if __name__ == '__main__':
numpy_concatenate(a, b)
if __name__ == '__main__':
preallocate(a, b)
所以,a是 1000x1000,對于b.
跑 :
$ python3 -m memory_profiler memprof_npconcat_preallocate.py
Filename: memprof_npconcat_preallocate.py
Line # Mem usage Increment Line Contents
================================================
9 69.3281250000 MiB 69.3281250000 MiB @profile(precision=10)
10 def numpy_concatenate(a, b):
11 84.5546875000 MiB 15.2265625000 MiB return np.concatenate((a,b),axis=1)
Filename: memprof_npconcat_preallocate.py
Line # Mem usage Increment Line Contents
================================================
13 69.3554687500 MiB 69.3554687500 MiB @profile(precision=10)
14 def preallocate(a, b):
15 69.3554687500 MiB 0.0000000000 MiB m,n = a.shape[1], b.shape[1]
16 69.3554687500 MiB 0.0000000000 MiB out = np.empty((a.shape[0],m+n), dtype=np.result_type((a.dtype, b.dtype)))
17 83.6484375000 MiB 14.2929687500 MiB out[:,:m] = a
18 84.4218750000 MiB 0.7734375000 MiB out[:,m:] = b
19 84.4218750000 MiB 0.0000000000 MiB return out
因此,對于preallocatemethod 來說,總的 mem 消耗為14.2929687500+ 0.7734375000,略小于15.2265625000.
將輸入數組的大小更改為 5000x5000a和b-
$ python3 -m memory_profiler memprof_npconcat_preallocate.py
Filename: memprof_npconcat_preallocate.py
Line # Mem usage Increment Line Contents
================================================
9 435.4101562500 MiB 435.4101562500 MiB @profile(precision=10)
10 def numpy_concatenate(a, b):
11 816.8515625000 MiB 381.4414062500 MiB return np.concatenate((a,b),axis=1)
Filename: memprof_npconcat_preallocate.py
Line # Mem usage Increment Line Contents
================================================
13 435.5351562500 MiB 435.5351562500 MiB @profile(precision=10)
14 def preallocate(a, b):
15 435.5351562500 MiB 0.0000000000 MiB m,n = a.shape[1], b.shape[1]
16 435.5351562500 MiB 0.0000000000 MiB out = np.empty((a.shape[0],m+n), dtype=np.result_type((a.dtype, b.dtype)))
17 780.3203125000 MiB 344.7851562500 MiB out[:,:m] = a
18 816.9296875000 MiB 36.6093750000 MiB out[:,m:] = b
19 816.9296875000 MiB 0.0000000000 MiB return out
同樣,預分配的總數較少。
結論:預分配方法具有稍好的內存優勢,這在某種程度上是有道理的。使用連接,我們有三個涉及 src1 + src2 -> dst 的數組,而使用預分配,只有 src 和 dst,雖然分兩步,但內存擁塞較少。

TA貢獻1794條經驗 獲得超8個贊
numpy編譯的代碼,例如concatenate通常確定它需要多大的返回數組,創建該數組,并將值復制到它。它通過 C-API 調用實現這一點的事實對內存使用沒有任何影響。concatenate不會覆蓋或重用參數使用的任何內存。
In [465]: A, B = np.ones((1000,1000)), np.zeros((1000,500))
一些時間比較:
In [466]: timeit np.concatenate((A,B), axis=1)
6.73 ms ± 338 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [467]: C = np.zeros((1000,1500))
In [468]: timeit np.concatenate((A,B), axis=1, out=C)
6.44 ms ± 174 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [469]: %%timeit
...: C = np.zeros((1000,1500))
...: np.concatenate((A,B), axis=1, out=C)
11.5 ms ± 358 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [470]: %%timeit
...: C = np.zeros((1000,1500))
...: C[:,:1000]=A; C[:,1000:]=B
11.5 ms ± 282 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [471]: %%timeit
...: C[:,:1000]=A; C[:,1000:]=B
6.29 ms ± 160 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
因此,如果目標數組已經存在,請使用它。但是,僅僅為了這個目的而創建一個似乎并沒有太大的優勢。
添加回答
舉報