Выбирать ячейки случайным образом из массива NumPy - без замены - PullRequest
13 голосов
/ 08 октября 2010

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

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

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

Ответы [ 6 ]

20 голосов
/ 08 октября 2010

Как насчет использования 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]]
3 голосов
/ 09 сентября 2013

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

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

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

#!/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 может стать довольно значительным.Поэтому было бы неплохо иметь генератор .К сожалению, я не могу придумать, как создать перемешанный итератор без посторонней помощи.

2 голосов
/ 08 октября 2010

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

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

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

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

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

OR

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 для перебора произвольно упорядоченных индексов двумерного массива по своему усмотрению и для получения значений по этому индексу из data, который остается неизменным.

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

1 голос
/ 12 августа 2013

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

1 голос
/ 15 января 2011

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

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

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

#generate a permutation of data's size, coerced to data's 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 массиву просто, чтобы перемешать их, в этом случае можно быть еще более компактным.просто сделайте 1-мерный просмотр массива и сэкономьте себе немного разбитости индексов.

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
1 голос
/ 08 октября 2010

Используйте random.sample для генерации целых чисел в 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]
...