В дальнейшем я не использую numba, но все операции используют векторизованные numpy функции.
Каждая строка сгенерированного вами результата может интерпретироваться как целое число, выраженное в базе N, где N - количество категорий. С этой интерпретацией вы хотите произвести выборку без замены целых чисел [0, 1, ... N ** R-1], где R - количество «записей». Для этого вы можете использовать функцию choice
с аргументом replace=False
. Получив это, вам нужно преобразовать выбранные целые числа в основание N. Для этого я использую функцию int2base
, которая является урезанной версией функции, которую я написал в другом ответе .
Вот код:
import numpy as np
def int2base(x, base, ndigits):
# x = np.asarray(x) # Uncomment this line for general purpose use.
powers = base ** np.arange(ndigits)
digits = (x.reshape(x.shape + (1,)) // powers) % base
return digits
def makesample(ncategories, nrecords, nsamples, rng=None):
if rng is None:
rng = np.random.default_rng()
n = ncategories ** nrecords
choices = rng.choice(n, replace=False, size=nsamples)
return int2base(choices, ncategories, nrecords)
В makesample
я включил необязательный аргумент rng
. Позволяет указать объект, который содержит функцию choice
. Если не предоставлено, используется np.random.default_rng()
.
Пример:
In [118]: makesample(2, 3, 6)
Out[118]:
array([[0, 1, 1],
[0, 0, 1],
[1, 0, 1],
[0, 0, 0],
[1, 1, 0],
[1, 1, 1]])
In [119]: makesample(5, 4, 12)
Out[119]:
array([[3, 4, 0, 1],
[2, 0, 2, 0],
[4, 2, 4, 3],
[0, 1, 0, 4],
[0, 2, 0, 1],
[1, 2, 0, 1],
[0, 3, 0, 4],
[3, 3, 0, 3],
[3, 4, 1, 4],
[2, 4, 1, 1],
[3, 4, 1, 0],
[1, 1, 4, 4]])
makesample
вызовет исключение, если вы запросите слишком много образцов:
In [120]: makesample(2, 3, 10)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-120-80044e78a60a> in <module>
----> 1 makesample(2, 3, 10)
~/code_snippets/python/numpy/random_samples_for_so_question.py in makesample(ncategories, nrecords, nsamples, rng)
17 rng = np.random.default_rng()
18 n = ncategories ** nrecords
---> 19 choices = rng.choice(n, replace=False, size=nsamples)
20 return int2base(choices, ncategories, nrecords)
_generator.pyx in numpy.random._generator.Generator.choice()
ValueError: Cannot take a larger sample than population when 'replace=False'