Как случайным образом перемешать «плитки» в массиве NumPy - PullRequest
3 голосов
/ 20 сентября 2019

У меня есть массив nxn numpy, и я хотел бы разделить его равномерно на плитки nxn и перемешать их случайным образом, сохраняя при этом шаблон внутри плиток.

Например, если у меня есть массив такого размера(200,200), я хочу иметь возможность разделить это на, скажем, 16 массивов размера (50,50) или даже 64 массива размера (25,25), и перемешать их случайным образом, сохраняя при этом ту же форму исходного массива(200,200) и сохраняя порядок чисел внутри меньших массивов.

Я просмотрел конкретные функции numpy и нашел функцию numpy.random.shuffle (x), но это случайным образом перетасует индивидуумаэлементы массива.Я хотел бы только перетасовать эти меньшие массивы в больший массив.

Есть ли какая-нибудь непонятная функция или быстрый способ сделать это?Я не уверен, с чего начать.

РЕДАКТИРОВАТЬ : Чтобы уточнить, что именно я хочу:

Допустим, у меня есть входной 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).

Ответы [ 5 ]

1 голос
/ 20 сентября 2019

вот мое решение с использованием цикла

import numpy as np

arr = np.arange(36).reshape(6,6)

def suffle_section(arr, n_sections):

    assert arr.shape[0]==arr.shape[1], "arr must be square"
    assert arr.shape[0]%n_sections == 0, "arr size must divideable into equal n_sections"

    size = arr.shape[0]//n_sections


    new_arr = np.empty_like(arr)
    ## randomize section's row index

    rand_indxes = np.random.permutation(n_sections*n_sections)

    for i in range(n_sections):
        ## randomize section's column index
        for j in  range(n_sections):

            rand_i = rand_indxes[i*n_sections + j]//n_sections
            rand_j = rand_indxes[i*n_sections + j]%n_sections

            new_arr[i*size:(i+1)*size, j*size:(j+1)*size] = \
                arr[rand_i*size:(rand_i+1)*size, rand_j*size:(rand_j+1)*size]

    return new_arr


result = suffle_section(arr, 3)


display(arr)
display(result)

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]])

array([[ 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]])
1 голос
/ 20 сентября 2019

Если у вас есть доступ к skimage (поставляется с Spyder), вы можете использовать view_as_blocks:

from skimage.util import view_as_blocks

def shuffle_tiles(arr, m, n):
    a_= view_as_blocks(arr,(m,n)).reshape(-1,m,n)
    # shuffle works along 1st dimension and in-place
    np.random.shuffle(a_)
    return a_
0 голосов
/ 21 сентября 2019

Вот подход, который старается избегать ненужных копий:

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

Тестовый скрипт (продолжение)

# 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))
0 голосов
/ 20 сентября 2019

Вот код, чтобы перетасовать порядок строк, но сохранить элементы строк точно такими, как есть:

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]])
0 голосов
/ 20 сентября 2019

Мы будем использовать 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)

Случайные блоки перемешивают друг с другом

Блоки рандомизированы друг с другом, сохраняя порядок в каждом блоке такой же, как в исходном массиве.

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]])
...