[英]numpy.tile a non-integer number of times
是否有更好的方法在numpy
中將數組平鋪非整數次? 這可以完成工作,但很笨重,並且不容易推廣到n維:
import numpy as np
arr = np.arange(6).reshape((2, 3))
desired_shape = (5, 8)
reps = tuple([x // y for x, y in zip(desired_shape, arr.shape)])
left = tuple([x % y for x, y in zip(desired_shape, arr.shape)])
tmp = np.tile(arr, reps)
tmp = np.r_[tmp, tmp[slice(left[0]), :]]
tmp = np.c_[tmp, tmp[:, slice(left[1])]]
這會產生:
array([[0, 1, 2, 0, 1, 2, 0, 1],
[3, 4, 5, 3, 4, 5, 3, 4],
[0, 1, 2, 0, 1, 2, 0, 1],
[3, 4, 5, 3, 4, 5, 3, 4],
[0, 1, 2, 0, 1, 2, 0, 1]])
編輯:績效結果
對三維答案的一些測試推廣到n維。 這些定義放在一個文件newtile.py
:
import numpy as np
def tile_pad(a, dims):
return np.pad(a, tuple((0, i) for i in (np.array(dims) - a.shape)),
mode='wrap')
def tile_meshgrid(a, dims):
return a[np.meshgrid(*[np.arange(j) % k for j, k in zip(dims, a.shape)],
sparse=True, indexing='ij')]
def tile_rav_mult_idx(a, dims):
return a.flat[np.ravel_multi_index(np.indices(dims), a.shape, mode='wrap')]
這是bash行:
python -m timeit -s 'import numpy as np' 'import newtile' 'newtile.tile_pad(np.arange(30).reshape(2, 3, 5), (3, 5, 7))'
python -m timeit -s 'import numpy as np' 'import newtile' 'newtile.tile_meshgrid(np.arange(30).reshape(2, 3, 5), (3, 5, 7))'
python -m timeit -s 'import numpy as np' 'import newtile' 'newtile.tile_rav_mult_idx(np.arange(30).reshape(2, 3, 5), (3, 5, 7))'
python -m timeit -s 'import numpy as np' 'import newtile' 'newtile.tile_pad(np.arange(2310).reshape(2, 3, 5, 7, 11), (13, 17, 19, 23, 29))'
python -m timeit -s 'import numpy as np' 'import newtile' 'newtile.tile_meshgrid(np.arange(2310).reshape(2, 3, 5, 7, 11), (13, 17, 19, 23, 29))'
python -m timeit -s 'import numpy as np' 'import newtile' 'newtile.tile_rav_mult_idx(np.arange(2310).reshape(2, 3, 5, 7, 11), (13, 17, 19, 23, 29))'
以下是小型陣列(2 x 3 x 5)的結果:
pad: 10000 loops, best of 3: 106 usec per loop
meshgrid: 10000 loops, best of 3: 56.4 usec per loop
ravel_multi_index: 10000 loops, best of 3: 50.2 usec per loop
以下是較大陣列(2 x 3 x 5 x 7 x 11)的結果:
pad: 10 loops, best of 3: 25.2 msec per loop
meshgrid: 10 loops, best of 3: 300 msec per loop
ravel_multi_index: 10 loops, best of 3: 218 msec per loop
所以使用np.pad
的方法可能是最高性能的選擇。
這是一個非常簡潔的方法:
In [57]: a
Out[57]:
array([[0, 1, 2],
[3, 4, 5]])
In [58]: old = a.shape
In [59]: new = (5, 8)
In [60]: a[(np.arange(new[0]) % old[0])[:,None], np.arange(new[1]) % old[1]]
Out[60]:
array([[0, 1, 2, 0, 1, 2, 0, 1],
[3, 4, 5, 3, 4, 5, 3, 4],
[0, 1, 2, 0, 1, 2, 0, 1],
[3, 4, 5, 3, 4, 5, 3, 4],
[0, 1, 2, 0, 1, 2, 0, 1]])
這是一個n維泛化:
def rep_shape(a, shape):
indices = np.meshgrid(*[np.arange(k) % j for j, k in zip(a.shape, shape)],
sparse=True, indexing='ij')
return a[indices]
例如:
In [89]: a
Out[89]:
array([[0, 1, 2],
[3, 4, 5]])
In [90]: rep_shape(a, (5, 8))
Out[90]:
array([[0, 1, 2, 0, 1, 2, 0, 1],
[3, 4, 5, 3, 4, 5, 3, 4],
[0, 1, 2, 0, 1, 2, 0, 1],
[3, 4, 5, 3, 4, 5, 3, 4],
[0, 1, 2, 0, 1, 2, 0, 1]])
In [91]: rep_shape(a, (4, 2))
Out[91]:
array([[0, 1],
[3, 4],
[0, 1],
[3, 4]])
In [92]: b = np.arange(24).reshape(2,3,4)
In [93]: b
Out[93]:
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])
In [94]: rep_shape(b, (3,4,5))
Out[94]:
array([[[ 0, 1, 2, 3, 0],
[ 4, 5, 6, 7, 4],
[ 8, 9, 10, 11, 8],
[ 0, 1, 2, 3, 0]],
[[12, 13, 14, 15, 12],
[16, 17, 18, 19, 16],
[20, 21, 22, 23, 20],
[12, 13, 14, 15, 12]],
[[ 0, 1, 2, 3, 0],
[ 4, 5, 6, 7, 4],
[ 8, 9, 10, 11, 8],
[ 0, 1, 2, 3, 0]]])
以下是第一個示例的工作原理......
我們的想法是使用數組來索引a
。 看看np.arange(new[0] % old[0])
:
In [61]: np.arange(new[0]) % old[0]
Out[61]: array([0, 1, 0, 1, 0])
該數組中的每個值都給出a
要在結果中使用的a
行。 與之相似,
In [62]: np.arange(new[1]) % old[1]
Out[62]: array([0, 1, 2, 0, 1, 2, 0, 1])
給出要在結果中使用的a
列。 要使這些索引數組創建二維結果,我們必須將第一個重新整形為一列:
In [63]: (np.arange(new[0]) % old[0])[:,None]
Out[63]:
array([[0],
[1],
[0],
[1],
[0]])
當數組用作索引時,它們會廣播 。 這是廣播索引的樣子:
n [65]: i, j = np.broadcast_arrays((np.arange(new[0]) % old[0])[:,None], np.arange(new[1]) % old[1])
In [66]: i
Out[66]:
array([[0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0]])
In [67]: j
Out[67]:
array([[0, 1, 2, 0, 1, 2, 0, 1],
[0, 1, 2, 0, 1, 2, 0, 1],
[0, 1, 2, 0, 1, 2, 0, 1],
[0, 1, 2, 0, 1, 2, 0, 1],
[0, 1, 2, 0, 1, 2, 0, 1]])
這些是我們生成具有形狀(5,8)的數組所需的索引數組:
In [68]: a[i,j]
Out[68]:
array([[0, 1, 2, 0, 1, 2, 0, 1],
[3, 4, 5, 3, 4, 5, 3, 4],
[0, 1, 2, 0, 1, 2, 0, 1],
[3, 4, 5, 3, 4, 5, 3, 4],
[0, 1, 2, 0, 1, 2, 0, 1]])
當索引數組在開頭的例子中給出時(即在第一個索引槽中使用(np.arange(new[0]) % old[0])[:,None]
,numpy實際上並不生成這些內存中的索引數組就像我用i
和j
。 i
和j
表示廣播發生時的有效內容。
函數rep_shape
執行相同的操作,使用np.meshgrid
為每個“slot”生成具有正確廣播形狀的索引數組。
也許效率不高但非常簡潔:
arr = np.arange(6).reshape((2, 3))
desired_shape = (5, 8)
arr.flat[np.ravel_multi_index(np.indices(desired_shape), arr.shape, mode='wrap')]
另一個更簡潔的解決方案:
arr = np.arange(6).reshape((2, 3))
desired_shape = np.array((5, 8))
pads = tuple((0, i) for i in (desired_shape-arr.shape))
# pads = ((0, add_rows), (0, add_columns), ...)
np.pad(arr, pads, mode="wrap")
但對於小型陣列來說速度較慢(盡管大型陣列要快得多)。 奇怪的是,np.pad不會接受np.array作為打擊墊。
不確定n維,但你可以考慮使用hstack
和vstack
。
arr = np.arange(6).reshape((2, 3))
nx, ny = shape(arr)
Nx, Ny = 5, 8 # These are the new shapes
iX, iY = Nx//nx+1, Ny//ny+1
result = vstack(tuple([ hstack(tuple([arr]*iX))[:, :Nx] ]*iY))[:Ny, : ]
有一個dstack
,但我懷疑這是否會有所幫助。 不完全確定3和更高的尺寸。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.