簡體   English   中英

如何將二維數組復制到第三維,N 次?

[英]How to copy a 2D array into a 3rd dimension, N times?

我想將一個 numpy 二維數組復制到第三維中。 例如,給定 2D numpy 數組:

import numpy as np

arr = np.array([[1, 2], [1, 2]])
# arr.shape = (2, 2)

將其轉換為在新維度中具有 N 個此類副本的 3D 矩陣。 作用於N=3 arr ,輸出應為:

new_arr = np.array([[[1, 2], [1,2]], 
                    [[1, 2], [1, 2]], 
                    [[1, 2], [1, 2]]])
# new_arr.shape = (3, 2, 2)

可能最干凈的方法是使用np.repeat

a = np.array([[1, 2], [1, 2]])
print(a.shape)
# (2,  2)

# indexing with np.newaxis inserts a new 3rd dimension, which we then repeat the
# array along, (you can achieve the same effect by indexing with None, see below)
b = np.repeat(a[:, :, np.newaxis], 3, axis=2)

print(b.shape)
# (2, 2, 3)

print(b[:, :, 0])
# [[1 2]
#  [1 2]]

print(b[:, :, 1])
# [[1 2]
#  [1 2]]

print(b[:, :, 2])
# [[1 2]
#  [1 2]]

話雖如此,您通常可以通過使用 廣播來避免完全重復數組。 例如,假設我想添加一個(3,)向量:

c = np.array([1, 2, 3])

a 我可以在第三維中復制a的內容 3 次,然后在第一維和第二維中復制c的內容兩次,這樣我的兩個數組都是(2, 2, 3) ,然后計算它們的總和。 但是,這樣做更簡單、更快捷:

d = a[..., None] + c[None, None, :]

這里, a[..., None]形狀為(2, 2, 1)c[None, None, :]形狀為(1, 1, 3) *。 當我計算總和時,結果沿着大小 1 的維度“廣播”出來,給我形狀(2, 2, 3)

print(d.shape)
# (2,  2, 3)

print(d[..., 0])    # a + c[0]
# [[2 3]
#  [2 3]]

print(d[..., 1])    # a + c[1]
# [[3 4]
#  [3 4]]

print(d[..., 2])    # a + c[2]
# [[4 5]
#  [4 5]]

廣播是一種非常強大的技術,因為它避免了在內存中創建輸入數組的重復副本所涉及的額外開銷。


*雖然我包括他們清楚, None指數為c實際上不是必要的-你也可以做a[..., None] + c ,即廣播(2, 2, 1)對一個數組(3,)數組。 這是因為如果其中一個數組的維度比另一個少,那么只有兩個數組的尾隨維度需要兼容。 舉一個更復雜的例子:

a = np.ones((6, 1, 4, 3, 1))  # 6 x 1 x 4 x 3 x 1
b = np.ones((5, 1, 3, 2))     #     5 x 1 x 3 x 2
result = a + b                # 6 x 5 x 4 x 3 x 2

另一種方法是使用numpy.dstack 假設你要重復的矩陣a num_repeats時間:

import numpy as np
b = np.dstack([a]*num_repeats)

訣竅是將矩陣a包裝到單個元素的列表中,然后使用*運算符將列表中的元素復制num_repeats次。

例如,如果:

a = np.array([[1, 2], [1, 2]])
num_repeats = 5

這重復了數組[1 2; 1 2] [1 2; 1 2]在第三維中 5 次。 驗證(在 IPython 中):

In [110]: import numpy as np

In [111]: num_repeats = 5

In [112]: a = np.array([[1, 2], [1, 2]])

In [113]: b = np.dstack([a]*num_repeats)

In [114]: b[:,:,0]
Out[114]: 
array([[1, 2],
       [1, 2]])

In [115]: b[:,:,1]
Out[115]: 
array([[1, 2],
       [1, 2]])

In [116]: b[:,:,2]
Out[116]: 
array([[1, 2],
       [1, 2]])

In [117]: b[:,:,3]
Out[117]: 
array([[1, 2],
       [1, 2]])

In [118]: b[:,:,4]
Out[118]: 
array([[1, 2],
       [1, 2]])

In [119]: b.shape
Out[119]: (2, 2, 5)

最后我們可以看到矩陣的形狀是2 x 2 ,在第三維中有 5 個切片。

使用視圖並獲得免費運行時間! 將通用n-dim數組擴展到n+1-dim

NumPy 1.10.0引入,我們可以利用numpy.broadcast_to簡單地將3D視圖生成到2D輸入數組中。 好處是沒有額外的內存開銷和幾乎免費的運行時間。 在數組很大並且我們可以使用視圖的情況下,這將是必不可少的。 此外,這適用於通用的n-dim情況。

我會使用單詞stack代替copy ,因為讀者可能會將它與創建內存副本的數組復制混淆。

沿第一軸堆疊

如果我們想沿第一個軸堆疊輸入arr ,使用np.broadcast_to創建3D視圖的解決方案是 -

np.broadcast_to(arr,(3,)+arr.shape) # N = 3 here

沿第三個/最后一個軸堆疊

要沿第三個軸堆疊輸入arr ,創建3D視圖的解決方案是 -

np.broadcast_to(arr[...,None],arr.shape+(3,))

如果我們真的需要一個內存副本,我們總是可以在那里附加.copy() 因此,解決方案將是 -

np.broadcast_to(arr,(3,)+arr.shape).copy()
np.broadcast_to(arr[...,None],arr.shape+(3,)).copy()

以下是兩種情況下堆疊的工作方式,顯示了示例情況下的形狀信息 -

# Create a sample input array of shape (4,5)
In [55]: arr = np.random.rand(4,5)

# Stack along first axis
In [56]: np.broadcast_to(arr,(3,)+arr.shape).shape
Out[56]: (3, 4, 5)

# Stack along third axis
In [57]: np.broadcast_to(arr[...,None],arr.shape+(3,)).shape
Out[57]: (4, 5, 3)

相同的解決方案將沿着第一個和最后一個軸將n-dim輸入擴展到n+1-dim視圖輸出。 讓我們探索一些更暗的情況 -

3D輸入案例:

In [58]: arr = np.random.rand(4,5,6)

# Stack along first axis
In [59]: np.broadcast_to(arr,(3,)+arr.shape).shape
Out[59]: (3, 4, 5, 6)

# Stack along last axis
In [60]: np.broadcast_to(arr[...,None],arr.shape+(3,)).shape
Out[60]: (4, 5, 6, 3)

4D輸入案例:

In [61]: arr = np.random.rand(4,5,6,7)

# Stack along first axis
In [62]: np.broadcast_to(arr,(3,)+arr.shape).shape
Out[62]: (3, 4, 5, 6, 7)

# Stack along last axis
In [63]: np.broadcast_to(arr[...,None],arr.shape+(3,)).shape
Out[63]: (4, 5, 6, 7, 3)

等等。

時間安排

讓我們使用一個大樣本2D案例並獲取時間並驗證輸出是view

# Sample input array
In [19]: arr = np.random.rand(1000,1000)

讓我們證明所提出的解決方案確實是一個視圖。 我們將沿第一個軸使用堆疊(沿第三個軸堆疊的結果非常相似)-

In [22]: np.shares_memory(arr, np.broadcast_to(arr,(3,)+arr.shape))
Out[22]: True

讓我們看看時間來證明它實際上是免費的 -

In [20]: %timeit np.broadcast_to(arr,(3,)+arr.shape)
100000 loops, best of 3: 3.56 µs per loop

In [21]: %timeit np.broadcast_to(arr,(3000,)+arr.shape)
100000 loops, best of 3: 3.51 µs per loop

作為一種觀點,將N3增加到3000對計時沒有任何改變,並且兩者在計時單位上都可以忽略不計。 因此,在內存和性能上都很有效!

這現在也可以使用np.tile 實現,如下所示:

import numpy as np

a = np.array([[1,2],[1,2]])
b = np.tile(a,(3, 1,1))

b.shape
(3,2,2)

b
array([[[1, 2],
        [1, 2]],

       [[1, 2],
        [1, 2]],

       [[1, 2],
        [1, 2]]])
A=np.array([[1,2],[3,4]])
B=np.asarray([A]*N)

編輯@Mr.F,以保留維度順序:

B=B.T

這是一個廣播示例,它完全符合要求。

a = np.array([[1, 2], [1, 2]])
a=a[:,:,None]
b=np.array([1]*5)[None,None,:]

然后b*a是所需的結果, (b*a)[:,:,0]產生array([[1, 2],[1, 2]]) ,它是原始的a(b*a)[:,:,1]

6 年后: numpy仍然沒有“只添加一個軸”的操作。 Einops 會:

y = einops.repeat(x, 'i j -> 3 i j')

此代碼向二維 numpy 數組添加了一個長度為 3 的軸。

暫無
暫無

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

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