Выбирайте ячейки случайным образом из массива NumPy - без замены

Я пишу некоторые процедуры моделирования в NumPy, которые должны выбирать ячейки случайным образом из массива NumPy и выполнять некоторую обработку на них. Все ячейки должны быть выбраны без замены (как в случае, когда ячейка выбрана, ее нельзя выбрать снова, но все ячейки должны быть выбраны до конца).

Я перехожу из IDL, где я могу найти хороший способ сделать это, но я полагаю, что NumPy имеет хороший способ сделать это тоже. Что бы вы предложили?

Обновление: Я должен был сказать, что я пытаюсь сделать это на 2D-массивах и, следовательно, вернуть набор 2D-индексов.

Ответы

Ответ 1

Как насчет использования numpy.random.shuffle или numpy.random.permutation, если вам все еще нужен исходный массив?

Если вам нужно изменить массив на месте, вы можете создать такой массив индексов:

your_array = <some numpy array>
index_array = numpy.arange(your_array.size)
numpy.random.shuffle(index_array)

print your_array[index_array[:10]]

Ответ 2

Все эти ответы казались мне немного запутанными.

Я предполагаю, что у вас есть многомерный массив, из которого вы хотите создать исчерпывающий список индексов. Вы хотите, чтобы эти индексы перетасовывались, чтобы затем вы могли получить доступ к каждому из элементов массива в произвольном порядке.

Следующий код сделает это простым и прямым способом:

#!/usr/bin/python
import numpy as np

#Define a two-dimensional array
#Use any number of dimensions, and dimensions of any size
d=numpy.zeros(30).reshape((5,6))

#Get a list of indices for an array of this shape
indices=list(np.ndindex(d.shape))

#Shuffle the indices in-place
np.random.shuffle(indices)

#Access array elements using the indices to do cool stuff
for i in indices:
  d[i]=5

print d

Печать d подтвердила, что все элементы были доступны.

Обратите внимание, что массив может иметь любое количество измерений и размеры могут иметь любой размер.

Единственным недостатком этого подхода является то, что если d велико, то indices может стать довольно значительным. Поэтому было бы неплохо иметь генератор . К сожалению, я не могу придумать, как создать перетасованный итератор в автономном режиме.

Ответ 3

Расширение приятного ответа от @WoLpH

Для 2D-массива я думаю, что это будет зависеть от того, что вам нужно или нужно знать об индексах.

Вы можете сделать что-то вроде этого:

data = np.arange(25).reshape((5,5))

x, y  = np.where( a = a)
idx = zip(x,y)
np.random.shuffle(idx)

ИЛИ

data = np.arange(25).reshape((5,5))

grid = np.indices(data.shape)
idx = zip( grid[0].ravel(), grid[1].ravel() )
np.random.shuffle(idx)

Затем вы можете использовать список idx для итерации по произвольно упорядоченным индексам 2D-массивов, как вы пожелаете, и получить значения из этого индекса из data, который остается неизменным.

Примечание. Вы также можете генерировать случайно упорядоченные индексы с помощью itertools.product, если вам удобнее использовать этот набор инструментов.

Ответ 4

Используйте random.sample для генерации int в 0.. A.size без дубликатов, затем разделите их на пары индексов:

import random
import numpy as np

def randint2_nodup( nsample, A ):
    """ uniform int pairs, no dups:
        r = randint2_nodup( nsample, A )
        A[r]
        for jk in zip(*r):
            ... A[jk]
    """
    assert A.ndim == 2
    sample = np.array( random.sample( xrange( A.size ), nsample ))  # nodup ints
    return sample // A.shape[1], sample % A.shape[1]  # pairs


if __name__ == "__main__":
    import sys

    nsample = 8
    ncol = 5
    exec "\n".join( sys.argv[1:] )  # run this.py N= ...
    A = np.arange( 0, 2*ncol ).reshape((2,ncol))

    r = randint2_nodup( nsample, A )
    print "r:", r
    print "A[r]:", A[r]
    for jk in zip(*r):
        print jk, A[jk]

Ответ 5

Скажем, у вас есть массив точек данных размером 8x3

data = np.arange(50,74).reshape(8,-1)

Если вы действительно хотите пробовать, как вы говорите, все индексы как пары 2d, самый компактный способ сделать это, о котором я могу думать, это:

#generate a permutation of data size, coerced to data shape
idxs = divmod(np.random.permutation(data.size),data.shape[1])

#iterate over it
for x,y in zip(*idxs): 
    #do something to data[x,y] here
    pass

Мое, однако, часто не нужно обращаться к массивам 2d как к массиву 2d, просто чтобы перетасовать их, и в этом случае можно быть еще более компактным. просто сделайте 1d-представление на массив и сохраните себе некоторые отклонения от индекса.

flat_data = data.ravel()
flat_idxs = np.random.permutation(flat_data.size)
for i in flat_idxs:
    #do something to flat_data[i] here
    pass

Это по-прежнему будет переназначать массив 2d "оригинал", как вам хотелось бы. Чтобы увидеть это, попробуйте:

 flat_data[12] = 1000000
 print data[4,0]
 #returns 1000000

Ответ 6

люди, использующие numpy версии 1.7 или более поздней версии, могут также использовать встроенную функцию numpy.random.choice