Python “如何随机洗牌”;瓷砖“;在numpy数组中

Python “如何随机洗牌”;瓷砖“;在numpy数组中,python,python-3.x,numpy,shuffle,Python,Python 3.x,Numpy,Shuffle,我有一个nxn numpy数组,我想将它均匀地分成nxn块,并随机地洗牌,同时保留块内的图案 例如,如果我有一个大小为(200200)的数组,我希望能够将其划分为16个大小为(50,50)的数组,或者甚至64个大小为(25,25)的数组,并随机洗牌这些数组,同时保留原始数组(200200)的相同形状,并保留较小数组中的数字顺序 我查找了特定的numpy函数,找到了numpy.random.shuffle(x)函数,但这将随机地洗牌数组中的各个元素。我只想在较大的数组中洗牌这些较小的数组 是否有任

我有一个nxn numpy数组,我想将它均匀地分成nxn块,并随机地洗牌,同时保留块内的图案

例如,如果我有一个大小为(200200)的数组,我希望能够将其划分为16个大小为(50,50)的数组,或者甚至64个大小为(25,25)的数组,并随机洗牌这些数组,同时保留原始数组(200200)的相同形状,并保留较小数组中的数字顺序

我查找了特定的numpy函数,找到了numpy.random.shuffle(x)函数,但这将随机地洗牌数组中的各个元素。我只想在较大的数组中洗牌这些较小的数组

是否有任何numpy功能或快速方法可以做到这一点?我不知道从哪里开始

编辑:为了进一步明确我想要什么:

假设我有一个输入2D数组,其中包含值的形状(10,10):

0   1   2   3   4   5   6   7   8   9
10  11  12  13  14  15  16  17  18  19
20  21  22  23  24  25  26  27  28  29
30  31  32  33  34  35  36  37  38  39
40  41  42  43  44  45  46  47  48  49
50  51  52  53  54  55  56  57  58  59
60  61  62  63  64  65  66  67  68  69
70  71  72  73  74  75  76  77  78  79
80  81  82  83  84  85  86  87  88  89
90  91  92  93  94  95  96  97  98  99
我选择一个瓷砖大小,使其均匀地适合此阵列,因此,由于此阵列具有形状(10,10),我可以选择将其拆分为4(5,5)个瓷砖或25(2,2)个瓷砖。因此,如果我选择4(5,5)个分片,我想随机洗牌这些分片,结果是一个输出数组,看起来像这样:

50  51  52  53  54  0   1   2   3   4
60  61  62  63  64  10  11  12  13  14
70  71  72  73  74  20  21  22  23  24
80  81  82  83  84  30  31  32  33  34
90  91  92  93  94  40  41  42  43  44
55  56  57  58  59  5   6   7   8   9
65  66  67  68  69  15  16  17  18  19
75  76  77  78  79  25  26  27  28  29
85  86  87  88  89  35  36  37  38  39
95  96  97  98  99  45  46  47  48  49

每个数组(输入数组、输出数组和单独的分片)都是正方形的,因此当随机洗牌时,主数组的大小和维度保持不变(10,10)

我们将使用
np.random.shuffle
以及轴排列来实现所需的结果。对此有两种解释。因此,有两种解决方案

在每个块内随机洗牌

每个区块中的元素都是随机的,所有区块中都保持相同的随机顺序

def randomize_tiles_shuffle_within(a, M, N):
    # M,N are the height and width of the blocks
    m,n = a.shape
    b = a.reshape(m//M,M,n//N,N).swapaxes(1,2).reshape(-1,M*N)
    np.random.shuffle(b.T)
    return b.reshape(m//M,n//N,M,N).swapaxes(1,2).reshape(a.shape)
随机地相互移动块

块彼此随机w.r.t,同时保持每个块内的顺序与原始数组中的顺序相同

def randomize_tiles_shuffle_blocks(a, M, N):    
    m,n = a.shape
    b = a.reshape(m//M,M,n//N,N).swapaxes(1,2).reshape(-1,M*N)
    np.random.shuffle(b)
    return b.reshape(m//M,n//N,M,N).swapaxes(1,2).reshape(a.shape)
样本运行-

In [47]: a
Out[47]: 
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],
       [24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35]])

In [48]: randomize_tiles_shuffle_within(a, 3, 3)
Out[48]: 
array([[ 1,  7, 13,  4, 10, 16],
       [14,  8, 12, 17, 11, 15],
       [ 0,  6,  2,  3,  9,  5],
       [19, 25, 31, 22, 28, 34],
       [32, 26, 30, 35, 29, 33],
       [18, 24, 20, 21, 27, 23]])

In [49]: randomize_tiles_shuffle_blocks(a, 3, 3)
Out[49]: 
array([[ 3,  4,  5, 18, 19, 20],
       [ 9, 10, 11, 24, 25, 26],
       [15, 16, 17, 30, 31, 32],
       [ 0,  1,  2, 21, 22, 23],
       [ 6,  7,  8, 27, 28, 29],
       [12, 13, 14, 33, 34, 35]])

如果您有权访问
skimage
(它随Spyder提供),您可以使用:


下面的代码可以洗牌行顺序,但保持行项目的原样:

import numpy as np 
np.random.seed(0)

#creates a 6x6 array
a = np.random.randint(0,100,(6,6))
a
array([[44, 47, 64, 67, 67,  9],
       [83, 21, 36, 87, 70, 88],
       [88, 12, 58, 65, 39, 87],
       [46, 88, 81, 37, 25, 77],
       [72,  9, 20, 80, 69, 79],
       [47, 64, 82, 99, 88, 49]])

#creates a number for each row index, 0,1,2,3,4,5
order = np.arange(6)

#shuffle index array
np.random.shuffle(order)

#make new array in shuffled order
shuffled = np.array([a[y] for y in order])
shuffled
array([[46, 88, 81, 37, 25, 77],
       [88, 12, 58, 65, 39, 87],
       [83, 21, 36, 87, 70, 88],
       [47, 64, 82, 99, 88, 49],
       [44, 47, 64, 67, 67,  9],
       [72,  9, 20, 80, 69, 79]])

下面是我使用循环的解决方案

将numpy导入为np
arr=np.arange(36)。重塑(6,6)
def suffle_段(arr、n_段):
断言arr.shape[0]==arr.shape[1],“arr必须是正方形”
断言arr.shape[0]%n\u节==0,“arr大小必须可划分为相等的n\u节”
size=arr.shape[0]//n\u节
新arr=np.空类(arr)
##随机化节的行索引
rand_indxes=np.随机排列(n_段*n_段)
对于范围内的i(n_部分):
##随机化节的列索引
对于范围内的j(n_段):
rand_i=rand_indxes[i*n_sections+j]//n_sections
rand_j=rand_索引[i*n_段+j]%n_段
新阵列[i*尺寸:(i+1)*尺寸,j*尺寸:(j+1)*尺寸]=\
arr[rand_i*大小:(rand_i+1)*大小,rand_j*大小:(rand_j+1)*大小]
返回新地址
结果=覆盖部分(arr,3)
显示(arr)
显示(结果)
数组([[0,1,2,3,4,5],
[ 6,  7,  8,  9, 10, 11],
[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23],
[24, 25, 26, 27, 28, 29],
[30, 31, 32, 33, 34, 35]])
数组([[4,5,16,17,24,25],
[10, 11, 22, 23, 30, 31],
[14, 15,  2,  3,  0,  1],
[20, 21,  8,  9,  6,  7],
[26, 27, 12, 13, 28, 29],
[32, 33, 18, 19, 34, 35]])

以下是一种尽量避免不必要拷贝的方法:

import numpy as np

def f_pp(a,bs):
    i,j = a.shape
    k,l = bs
    esh = i//k,k,j//l,l
    bc = esh[::2]
    sh1,sh2 = np.unravel_index(np.random.permutation(bc[0]*bc[1]),bc)
    ns1,ns2 = np.unravel_index(np.arange(bc[0]*bc[1]),bc)
    out = np.empty_like(a)
    out.reshape(esh)[ns1,:,ns2] = a.reshape(esh)[sh1,:,sh2]
    return out
时间:

pp 0.41529153706505895
dv 1.3133141631260514
br 1.6034217830747366
测试脚本(续)


你的意思是说每一行保持完全相同,并且行的顺序不同吗?或者你的意思是一个数字可以跳行,但它应该保持在同一列中?或者,您的意思是,只要生成的行符合某种模式(例如行中的升序),数字就可以跳转列和行?这是三个非常不同的输出,您的描述似乎可以是其中的任何一个。是的,澄清一下,我的意思是数字本身可以跳转行和列,但我希望实际较小的4x4矩阵在这种随机洗牌过程中保持圆滑。我将尝试编辑我的帖子,以使我想要的内容成为一个好主意的示例输出。谢谢,这肯定很接近,但当我将其用于形状(10,10)的数组和大小(5,5)的四个瓷砖时,此函数返回一个形状(4,5,5)的数组,刚刚返回到这个-感谢解决方案。这非常接近,但似乎前三个块(左上角为0、2和4)总是混洗在一起,因此即使该行是随机的,它们也最终位于同一行。但我可以简单地取一个矩阵,用你的函数将它洗牌,转置它,然后再次洗牌,我认为应该可以这样做:result=suffle_部分(suffle_部分(arr,5).T,5)。T@gram_schmidt你抓得很好,我现在已经改正了。在此之前,它是分别洗牌行和列,而不是采取所有可能的排列洗牌。现在,我一次洗牌了所有节的总计数,并在此基础上计算了行和列
pp 0.41529153706505895
dv 1.3133141631260514
br 1.6034217830747366
# Divakar
def f_dv(a,bs):
    M,N = bs
    m,n = a.shape
    b = a.reshape(m//M,M,n//N,N).swapaxes(1,2).reshape(-1,M*N)
    np.random.shuffle(b)
    return b.reshape(m//M,n//N,M,N).swapaxes(1,2).reshape(a.shape)

from skimage.util import view_as_blocks

# Brenlla shape fixed by pp
def f_br(arr,bs):
    m,n = bs
    a_= view_as_blocks(arr,(m,n))
    sh = a_.shape
    a_ = a_.reshape(-1,m,n)
    # shuffle works along 1st dimension and in-place
    np.random.shuffle(a_)
    return a_.reshape(sh).swapaxes(1,2).reshape(arr.shape)

ex = np.arange(100000).reshape(1000,100)
bs = 10,10
tst = np.tile(np.arange(np.prod(bs)).reshape(bs),np.floor_divide(ex.shape,bs))

from timeit import timeit
for n,f in list(globals().items()):
    if n.startswith('f_'):
        assert (tst==f(tst,bs)).all()
        print(n[2:],timeit(lambda:f(ex,bs),number=1000))