簡短介紹
我有兩個成對的 2D numpy 陣列串列(見下文) - 在陣列 1 中的索引 0 對應于陣列 2 中的索引 0 的意義上配對。對于每一對,我想獲得 2D numpy 陣列中所有行的所有組合,就像Divakar 在這里回答的那樣。
陣列示例
arr1 = [
np.vstack([[1,6,3,9], [8,5,6,7]]),
np.vstack([[1,6,3,9]]),
np.vstack([[1,6,3,9], [8,5,6,7],[8,5,6,7]])
]
arr2 = [
np.vstack([[8,8,8,8]]),
np.vstack([[8,8,8,8]]),
np.vstack([[1,6,3,9], [8,5,6,7],[8,5,6,7]])
]
作業代碼
請注意,與鏈接的答案不同,我的列是固定的(總是4),因此我使用形狀替換為硬編碼值 4(或 np.zeros 中的 8)。
def merge(a1, a2):
# From: https://stackoverflow.com/questions/47143712/combination-of-all-rows-in-two-numpy-arrays
m1 = a1.shape[0]
m2 = a2.shape[0]
out = np.zeros((m1, m2, 8), dtype=int)
out[:, :, :4] = a1[:, None, :]
out[:, :, 4:] = a2
out.shape = (m1 * m2, -1)
return out
total = np.concatenate([merge(arr1[i], arr2[i]) for i in range(len(arr1))])
print(total)
問題
雖然上述作業正常,但在我看來效率低下,因為它:
- 涉及遍歷陣列
- “追加”(在串列串列理解中)到
total陣列,要求它每次分配記憶體 - 創建多個
zero陣列(在合并函式中),而我可以在開始時創建一個空陣列?與上述點有關
我在具有數百萬個元素的陣列上執行了數千次此操作,那么有關如何將此代碼轉換為更有效的內容的任何建議?
uj5u.com熱心網友回復:
老實說,這似乎很難優化。回圈中的每個步驟都有不同的大小,因此可能沒有任何純矢量化的方式來做這些事情。您可以嘗試預先分配記憶體并就地寫入,而不是分配許多塊并最終連接結果,但我敢打賭這對您沒有多大幫助(除非您處于這樣的限制條件下,否則您沒有當然,足夠的 RAM 可以將所有內容存盤兩次)。
隨意在較大的資料上嘗試以下方法,但是如果您獲得任何顯著的加速(或者甚至沒有獲得更慢的結果!),我會感到驚訝。
# Use scalar product to get the final size
result = np.zeros((np.dot([len(x) for x in arr1], [len(x) for x in arr2]), 8), dtype=int)
start = 0
for a1, a2 in zip(arr1, arr2):
end = start len(a1) * len(a2)
result[start:end, :4] = np.repeat(a1, len(a2), axis=0)
result[start:end, 4:] = np.tile(a2, (len(a1), 1))
start = end
uj5u.com熱心網友回復:
這就是我想看到的 - 串列和合并結果:
In [60]: arr1
Out[60]:
[array([[1, 6, 3, 9],
[8, 5, 6, 7]]),
array([[1, 6, 3, 9]]),
array([[1, 6, 3, 9],
[8, 5, 6, 7],
[8, 5, 6, 7]])]
In [61]: arr2
Out[61]:
[array([[8, 8, 8, 8]]),
array([[8, 8, 8, 8]]),
array([[1, 6, 3, 9],
[8, 5, 6, 7],
[8, 5, 6, 7]])]
In [63]: merge(arr1[0],arr2[0]) # a (2,4) with (1,4) => (2,8)
Out[63]:
array([[1, 6, 3, 9, 8, 8, 8, 8],
[8, 5, 6, 7, 8, 8, 8, 8]])
In [64]: merge(arr1[1],arr2[1]) # a (1,4) with (1,4) => (1,8)
Out[64]: array([[1, 6, 3, 9, 8, 8, 8, 8]])
In [65]: merge(arr1[2],arr2[2]) # a (3,4) with (3,4) => (9,8)
Out[65]:
array([[1, 6, 3, 9, 1, 6, 3, 9],
[1, 6, 3, 9, 8, 5, 6, 7],
[1, 6, 3, 9, 8, 5, 6, 7],
[8, 5, 6, 7, 1, 6, 3, 9],
[8, 5, 6, 7, 8, 5, 6, 7],
[8, 5, 6, 7, 8, 5, 6, 7],
[8, 5, 6, 7, 1, 6, 3, 9],
[8, 5, 6, 7, 8, 5, 6, 7],
[8, 5, 6, 7, 8, 5, 6, 7]])
并且total是(12,8),梳理所有“行”。
串列理解更清楚地說:
[merge(a,b) for a,b in zip(arr1,arr2)]
串列雖然長度相同,但具有不同行數的陣列,合并也不同。
人們經常詢問迭代地制作陣列,我們一貫說,將結果收集在一個串列中,并concatenate在最后進行一次(類似)構造。等效回圈為:
In [70]: alist = []
...: for a,b in zip(arr1,arr2):
...: alist.append(merge(a,b))
這通常與預定義total陣列和分配行具有競爭力。在您的情況下,要獲得最終形狀,total您必須遍歷串列并記錄行數等。
除非計算是微不足道的,否則迭代機制只是總時間的一小部分。我很確定在這里,它呼叫了merge3 次,這占用了大部分時間。對于這樣的任務,我不會太擔心記憶體使用,包括創建zeros. 您必須以一種方式或其他方式使用記憶體來獲得 (12,8) 最終結果。從 (2,8)、(1,8) 和 (9,8) 構建它并不是什么大問題。
帶連接和不帶連接的串列理解:
In [72]: timeit total = np.concatenate([merge(a,b) for a,b in zip(arr1,arr2)])
22.4 μs ± 29.8 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [73]: timeit [merge(a,b) for a,b in zip(arr1,arr2)]
16.3 μs ± 25.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
merge用任何對呼叫3 次大約需要相同的時間。
哦,另一件事,不要試圖out在merge呼叫之間“重用”陣列。在串列中累積這樣的結果時,重用陣列是危險的。每個merge呼叫都必須回傳它自己的陣列,而不是“回收”的陣列。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/403487.html
標籤:
