繁体   English   中英

将 1D 数组重复到具有移位行的 2D 数组

[英]Repeat 1D array to 2D array with shifted rows

我想使用一个数组a = [1, 2, 3, 4, 5]并将它变成一个 5x5 数组,如下所示:

[[1, 2, 3, 4, 5],
 [2, 3, 4, 5, 0],
 [3, 4, 5, 0, 0],
 [4, 5, 0, 0 ,0],
 [5, 0, 0, 0, 0]]

我怎样才能用numpy做到这一点?

我还希望能够使用像a = [1, 6, 2, 3, 8]这样的任意数组并得到:

[[1, 6, 2, 3, 8],
 [6, 2, 3, 8, 0],
 [2, 3, 8, 0, 0],
 [3, 8, 0, 0, 0],
 [8, 0, 0, 0, 0]]

免责声明:

以下解决方案仅在您只需要一个视图时才有效。 此解决方案不返回可写数组。 如果你需要一个可写数组,你可以简单地将视图传递给数组构造函数: np.array(array_view) ,但在这一点上,@ linalg.hankel解决方案中的 linalg.hankel 是一个更好的方法,除非你想使用任何我添加的有趣功能。

如果你只需要一个视图

这是一个适用于任意一维 arrays 并且速度极快的解决方案:

def fast_hankel_view(a: np.ndarray) -> np.ndarray:
    arr = np.append(a, np.zeros(a.size - 1, a.dtype))
    return np.lib.stride_tricks.sliding_window_view(arr, a.size)

Output:

>>> a = np.array([1, 2, 3, 4, 5])
>>> fast_hankel_view(a)
array([[1, 2, 3, 4, 5],
       [2, 3, 4, 5, 0],
       [3, 4, 5, 0, 0],
       [4, 5, 0, 0, 0],
       [5, 0, 0, 0, 0]])

>>> a = np.array([1, 4, 3, 5, 8, 3, 8, 3, 2, 1, 8])
>>> fast_hankel_view(a)
array([[1, 4, 3, 5, 8, 3, 8, 3, 2, 1, 8],
       [4, 3, 5, 8, 3, 8, 3, 2, 1, 8, 0],
       [3, 5, 8, 3, 8, 3, 2, 1, 8, 0, 0],
       [5, 8, 3, 8, 3, 2, 1, 8, 0, 0, 0],
       [8, 3, 8, 3, 2, 1, 8, 0, 0, 0, 0],
       [3, 8, 3, 2, 1, 8, 0, 0, 0, 0, 0],
       [8, 3, 2, 1, 8, 0, 0, 0, 0, 0, 0],
       [3, 2, 1, 8, 0, 0, 0, 0, 0, 0, 0],
       [2, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0],
       [1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])

100,000 个随机整数数组的小型基准测试:

In [3]: x = np.random.randint(0, 255, (1, 100000), dtype="uint8")
In [4]: %timeit fast_hankel_view(x)
25.8 µs ± 165 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

同样,这非常快,因为它只是构建输入数组x视图

In [5]: h = fast_hankel_view(x)

In [6]: h += 1
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-14-ce0b251d63e1> in <module>
----> 1 h += 1

ValueError: output array is read-only

然而,重申一下,您可以通过将视图传递给np.array()构造函数来从 output 获取可写数组:

In [7]: x = np.array([1, 3, 5, 7])

In [8]: h = np.array(fast_hankel_view(x))

In [9]: h
Out[9]:
array([[1, 3, 5, 7],
       [3, 5, 7, 0],
       [5, 7, 0, 0],
       [7, 0, 0, 0]])

In [10]: h += 1

In [11]: h
Out[11]:
array([[2, 4, 6, 8],
       [4, 6, 8, 1],
       [6, 8, 1, 1],
       [8, 1, 1, 1]])

一些有趣的修改

通过一个小的修改,你也可以在对角线上反射,如果你愿意,或者用另一个数组填充(注意,两个 arrays 的元素总数必须是奇数才能使结果为正方形):

import numpy as np


def fast_hankel_view(x: np.ndarray) -> np.ndarray:
    return np.lib.stride_tricks.sliding_window_view(x, (x.size + 1) // 2)


def hankel_view_builder(a: np.ndarray, pad="zeros") -> np.ndarray:
    if type(pad) == np.ndarray:
        arr = np.append(a, pad)
    elif pad == "zeros":
        arr = np.append(a, np.zeros(a.size - 1, a.dtype))
    elif pad == "reflect":
        arr = np.append(a, np.flip(a)[1:])
    else:
        raise ValueError("invalid pad type!")
    return fast_hankel_view(arr)

附加到两个 arrays 并使用sliding_window_view的一个很好的好处是您可以提供不同长度的 arrays(在我看来,使用 scipy 的linalg.hankel返回不直观的结果):

In [11]: x
Out[11]: array([1, 2, 3])

In [12]: y
Out[12]: array([4, 5, 6, 7, 8, 9])

In [13]: hankel_view_builder(x, y)  # note the parity of |x| + |y|
Out[13]:
array([[1, 2, 3, 4, 5],
       [2, 3, 4, 5, 6],
       [3, 4, 5, 6, 7],
       [4, 5, 6, 7, 8],
       [5, 6, 7, 8, 9]])

In [14]: hankel_view_builder(y, "reflect")
Out[14]:
array([[4, 5, 6, 7, 8, 9],
       [5, 6, 7, 8, 9, 8],
       [6, 7, 8, 9, 8, 7],
       [7, 8, 9, 8, 7, 6],
       [8, 9, 8, 7, 6, 5],
       [9, 8, 7, 6, 5, 4]])

In [15]: hankel_view_builder(y)
Out[15]:
array([[4, 5, 6, 7, 8, 9],
       [5, 6, 7, 8, 9, 0],
       [6, 7, 8, 9, 0, 0],
       [7, 8, 9, 0, 0, 0],
       [8, 9, 0, 0, 0, 0],
       [9, 0, 0, 0, 0, 0]])

如果可以使用 scipy:

from scipy import linalg

linalg.hankel([1,2,3,4,5])
# array([[1, 2, 3, 4, 5],
#        [2, 3, 4, 5, 0],
#        [3, 4, 5, 0, 0],
#        [4, 5, 0, 0, 0],
#        [5, 0, 0, 0, 0]])

如果不:

def hankel(a):
    n = len(a)
    out = np.zeros((n,n),int)
    out.ravel()[:-1].reshape(n+1,n-1)[1:,0] = a[-1]
    np.copyto(out.ravel()[:1-2*n].reshape(n-1,n-1),a[:-1],where=np.tri(n-1,dtype=bool).T)
    return out                                

hankel([1,2,3,4,5])
# array([[1, 2, 3, 4, 5],
#        [2, 3, 4, 5, 0],
#        [3, 4, 5, 0, 0],
#        [4, 5, 0, 0, 0],
#        [5, 0, 0, 0, 0]])

使用np.add.outernp.where

import numpy as np

a = [1, 2, 3, 4, 5]
r = np.add.outer(a,a) - 1
np.where(r <= len(a), r, 0)

Output

array([[1, 2, 3, 4, 5],
       [2, 3, 4, 5, 0],
       [3, 4, 5, 0, 0],
       [4, 5, 0, 0, 0],
       [5, 0, 0, 0, 0]])

更一般的不断增加 integer 范围

a = [-2, -1, 0 , 1, 2, 3]
r = np.add.outer(a,a) - np.min(a)
np.where(r <= np.max(a), r, 0)

Output

array([[-2, -1,  0,  1,  2,  3],
       [-1,  0,  1,  2,  3,  0],
       [ 0,  1,  2,  3,  0,  0],
       [ 1,  2,  3,  0,  0,  0],
       [ 2,  3,  0,  0,  0,  0],
       [ 3,  0,  0,  0,  0,  0]])

对于任意 arrays

import numpy as np

a = np.array([1, 4, 3, 5, 8, 3, 8, 3, 2, 1, 8]) 
x = np.arange(len(a))
r = np.add.outer(x,x)
idx = np.where(r <= len(x)-1, r, 0)
np.where(r <= len(x)-1, np.full(r.shape, a)[np.arange(len(a)),idx], 0)

Output

array([[1, 4, 3, 5, 8, 3, 8, 3, 2, 1, 8],
       [4, 3, 5, 8, 3, 8, 3, 2, 1, 8, 0],
       [3, 5, 8, 3, 8, 3, 2, 1, 8, 0, 0],
       [5, 8, 3, 8, 3, 2, 1, 8, 0, 0, 0],
       [8, 3, 8, 3, 2, 1, 8, 0, 0, 0, 0],
       [3, 8, 3, 2, 1, 8, 0, 0, 0, 0, 0],
       [8, 3, 2, 1, 8, 0, 0, 0, 0, 0, 0],
       [3, 2, 1, 8, 0, 0, 0, 0, 0, 0, 0],
       [2, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0],
       [1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])

对于想要沿轴 0 移动的任意 arrays:

import numpy as np
a = np.array([1,2,3,4,5])
padded = np.append(np.zeros((1, a.size)), a)
print("padded:")
print(padded)
duplicated = np.tile(padded, a.size)
print("duplicated:")
print(duplicated)
resized = np.resize(duplicated, (a.size+1, padded.size - 1))
print("resized:")
print(resized)
flipped = resized[::-1]
print("flipped:")
print(flipped)
result = flipped[:a.size,:a.size]
print("result:")
print(result)

Output

padded:
[0. 0. 0. 0. 0. 1. 2. 3. 4. 5.]
duplicated:
[0. 0. 0. 0. 0. 1. 2. 3. 4. 5. 0. 0. 0. 0. 0. 1. 2. 3. 4. 5. 0. 0. 0. 0.
 0. 1. 2. 3. 4. 5. 0. 0. 0. 0. 0. 1. 2. 3. 4. 5. 0. 0. 0. 0. 0. 1. 2. 3.
 4. 5.]
resized:
[[0. 0. 0. 0. 0. 1. 2. 3. 4.]
 [5. 0. 0. 0. 0. 0. 1. 2. 3.]
 [4. 5. 0. 0. 0. 0. 0. 1. 2.]
 [3. 4. 5. 0. 0. 0. 0. 0. 1.]
 [2. 3. 4. 5. 0. 0. 0. 0. 0.]
 [1. 2. 3. 4. 5. 0. 0. 0. 0.]]
flipped:
[[1. 2. 3. 4. 5. 0. 0. 0. 0.]
 [2. 3. 4. 5. 0. 0. 0. 0. 0.]
 [3. 4. 5. 0. 0. 0. 0. 0. 1.]
 [4. 5. 0. 0. 0. 0. 0. 1. 2.]
 [5. 0. 0. 0. 0. 0. 1. 2. 3.]
 [0. 0. 0. 0. 0. 1. 2. 3. 4.]]
result:
[[1. 2. 3. 4. 5.]
 [2. 3. 4. 5. 0.]
 [3. 4. 5. 0. 0.]
 [4. 5. 0. 0. 0.]
 [5. 0. 0. 0. 0.]]

arr = np. array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
# Convert 1D array to a 2D numpy array of 2 rows and 3 columns.
arr_2d = np. reshape(arr, (2, 5))
print(arr_2d)

您可以使用 reshape function。根据您的要求填充数组,然后使用 reshape。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM