拆分一个二维数组,从行到行的值创建数组,并使用单位位移

时间:2017-12-04 00:13:16

标签: python arrays numpy math slice

我想以这种方式拆分2D数组:

示例:

从这个4x4 2D阵列:

np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])

创建这五个2x2 2D阵列,具有单位位移(移位):

np.array([[1,2],[3,4]])
np.array([[4,5],[6,7]])
np.array([[7,8],[9,10]])
np.array([[10,11],[12,13]])
np.array([[13,14],[15,16]])

在一般情况下,从NXN 2D阵列(方阵)开始,尽可能多地创建具有MXM形状的Y 2D阵列。

更确切地说:要创建输出数组,不一定要使用行中的所有值。

示例:

从2D 8x8数组中,值为1到64,如果我想在2D 2x2数组中拆分此数组,则8x8数组中的第一行是1到8的行,第一个输出2D 2x2数组将是是np.array([[1,2],[3,4]]),第二个输出2D 2x2数组将是np.array([[4,5],[6,7]])...它一直持续到最后一个输出2D数组,即np.array([[61,62],[63,64]])。看看每个2D 2x2阵列都没有填充行中的所有值(CORRECT)。并且存在从前一个数组到下一个数组的单一位移(移位)。

有一种Numpy方法可以做到这一点吗?

MSeifert在这里回答(How to split an 2D array, creating arrays from "row to row" values)一个问题解决了这个问题的近95%,除了单一位移(移位)部分。

所以,从4x4 2D数组示例:

np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])

而不是创建这四个2x2 2D阵列(没有单位移位/位移):

np.array([[1,2],[3,4]])
np.array([[5,6],[7,8]])
np.array([[9,10],[11,12]])
np.array([[13,14],[15,16]])

创建这些五个2x2 2D阵列(具有单位移位/位移):

np.array([[1,2],[3,4]])
np.array([[4,5],[6,7]])
np.array([[7,8],[9,10]])
np.array([[10,11],[12,13]])
np.array([[13,14],[15,16]])

当然,它应该适用于给定方形NXN 2D阵列的一般情况,以创建Y MXM方形2D阵列。

示例:从60x60平方的2d阵列中,创建Y MXM方形2D阵列(例如10x10)。

另外:我需要知道与原始方形2D阵列(在示例中为4x4 2D阵列)的点数相关的规则是什么,以及迷你方形2D阵列的点(示例中为2X2 2D阵列) )。在这种情况下,给定16个点(4x4 2D阵列),可以创建5个2x2 2D阵列(每个阵列有4个点)。

1 个答案:

答案 0 :(得分:3)

子阵列完全适合的条件是(M+1)*(M-1)(N+1)*(N-1),您可以放置​​的子阵列数是这些数字的商。请注意,这些数字等于M*M-1N*N-1。在这种形式下,该规则也适用于非方形矩阵。

实施例

M      N      M*M-1  N*N-1  Y
-----------------------------
3      5      8      24     3
3      7      8      48     6
5      7      24     48     2
4      11     15     120    8

实施:请注意,这会将重叠的视图返回到原始数组中。如果要修改它们,可能需要复制。另请注意,此实现适合尽可能多的子方形,较大矩阵中的任何剩余元素都会被删除。

更新:我添加了两个函数,用于计算给定N所有可能的M,反之亦然。

输出:

# Testing predictions ...
# ok

# M = 105
# solutions: [105, 1273, 1377, 4135, 4239, 5407, 5511, 5513]
# this pattern repeats at offsets 5512, 11024, 16536, ...

# N = 1000001
# solutions: [2, 3, 4, 5, 7, 9, 11, 31, 49, 1000001]

# example N, M = (5, 3)
# [[[ 0  1  2]
#   [ 3  4  5]
#   [ 6  7  8]]

#  [[ 8  9 10]
#   [11 12 13]
#   [14 15 16]]

#  [[16 17 18]
#   [19 20 21]
#   [22 23 24]]]

代码:

import numpy as np
import sympy
import itertools as it
import functools as ft
import operator as op

def get_subsquares(SqNN, M0, M1=None):
    M1 = M0 if M1 is None else M1
    N0, N1 = SqNN.shape
    K = (N0*N1-1) // (M0*M1-1)
    SqNN = SqNN.ravel()
    s, = SqNN.strides
    return np.lib.stride_tricks.as_strided(SqNN, (K, M0, M1),
                                           (s*(M0*M1-1), s*M1, s))


def get_M_for_N(N):
    """Given N return all possible M
    """
    assert N >= 2
    f = 1 + (N & 1)
    factors = sympy.factorint((N+1)//f)
    factors.update(sympy.factorint((N-1)//f))
    if f == 2:
        factors[2] += 2
    factors = [ft.reduce(op.mul, fs) for fs in it.product(
        *([a**k for k in range(n+1)] for a, n in factors.items()))]
    return [fs + 1 for fs in sorted(set(factors) & set(fs - 2 for fs in factors)) if (N*N-1) % (fs * (fs+2)) == 0]

def get_N_for_M(M):
    """Given M return all possible N in the form period, smallest

    smallest is a list of all solutions between M and M*M if M is even
    and between M and (M*M+1) / 2 if M is odd, all other solutions can be
    obtained by adding multiples of period
    """
    assert M >= 2
    f = 1 + (M & 1)
    factors = sympy.factorint((M+1)//f)
    factors.update(sympy.factorint((M-1)//f))
    factors = [k**v for k, v in factors.items()]
    rep = (M+1)*(M-1) // f
    f0 = [ft.reduce(op.mul, fs) for fs in it.product(*zip(it.repeat(1), factors))]
    f1 = [rep // (f*a) for a in f0]
    inv = [f if b==1 else f*b + 2 if a==1 else 2 * sympy.mod_inverse(a, b)
           for a, b in zip(f1, f0)]
    if f==1:
        inv[1:-1] = [a%b for a, b in zip(inv[1:-1], f0[1:-1])]
    return rep, sorted(a*b - 1 for a, b in zip(f1, inv))

def test_predict(N):
    def brute_force(a, b):
        return [i for i in range(a, b) if (i*i-1) % (a*a-1) == 0]
    for x in range(2, N+1):
        period, pred = get_N_for_M(x)
        assert brute_force(x, period*4+2) \
            == [a + b for b in range(0, 4*period, period) for a in pred]
    def brute_force(b):
        return [i for i in range(2, b+1) if (b*b-1) % (i*i-1) == 0]
    for x in range(2, N+1):
        assert brute_force(x) == get_M_for_N(x)
    print('ok')

# test
print("Testing predictions ...")
test_predict(200)
print()
# examples
M = 105
period, pred = get_N_for_M(M)
print(f"M = {M}")
print(f"solutions: {pred}")
print(f"this pattern repeats at offsets {period}, {2*period}, {3*period}, ...")
print()
N = 1000001
pred = get_M_for_N(N)
print(f"N = {N}")
print(f"solutions: {pred}")
print()
N, M = 5, 3
SqNN = np.arange(N*N).reshape(N, N)
print(f"example N, M = ({N}, {M})")
print(get_subsquares(SqNN, M))