В Python, как я могу случайным образом распределить набор чисел по сетке/матрице?

#python #numpy #random

Вопрос:

У меня следующая проблема: я хочу сгенерировать сетку 100х100 ( numpy.ndarray ), заполнив ее числами из заданного списка ( [-1,0,1,2] ). Я хочу распределить их случайным образом по этой сетке. Кроме того, числа должны поддерживать следующие соотношения: число 0 должно занимать 10% сетки, в то время как остальные числа имеют соотношение 30% каждое, поэтому их сумма равна 100%. С помощью np.random.choice() этого я смог генерировать случайные числа, каждое из которых распределялось с соответствующими вероятностями. Однако у меня возникают проблемы, потому что я должен убедиться, что число 0 составляет ровно 10% от всей сетки, а ненулевые числаровно по 30% каждый. Используя np.random.choice() функцию, это не всегда так (особенно если размер выборки невелик), потому что я назначил только вероятности, а не соотношения:

 import numpy as np

numbers = np.random.choice([-1,0,1,2],(100,100),p=[0.3,0.1,0.3,0.3])
print(np.count_nonzero(numbers)) #must be = 0.1 always!
 

Другая идея, которая у меня была, состояла в том, чтобы сначала установить всю матрицу как np.zeros((100,100)) , а затем заполнить только 90% ее ненулевыми элементами, однако я не знаю, как подойти к этой проблеме таким образом, чтобы числа распределялись случайным образом по сетке, т. Е. Случайным местоположением/индексом.

Изменить: Соотношение каждого отдельного ненулевого числа в сетке будет зависеть только от того, сколько ячеек я хочу, чтобы они были пустыми, или 0 в этом случае. Все остальные ненулевые элементы должны иметь одинаковое соотношение. Например, я хочу, чтобы 20% сетки были нулями, остальные числа будут иметь соотношение (1 — ratio_of_zero)/amount_of_non-нулевые элементы.

Комментарии:

1. Ключ состоит в том, чтобы создать массив длиной 1D 10000, который имеет правильное число для каждого выбора, а затем np.random.shuffle (). Затем преобразуйте его в массив 100×100.

2. @RemcoGerlich Спасибо, это делает именно то, что я хочу для этого конкретного примера. Однако как я могу сделать это более гибким, т. Е., если количество чисел, которые я хочу распределить, меняется, скажем, в следующий раз, когда мне понадобится 12 чисел в сетке 1000×1000? В этом случае мне нужно было бы применить цикл for, верно? (обратите внимание, что фактическое значение чисел не имеет значения, мне нужно только, чтобы они были различимы)

Ответ №1:

Это должно делать то, что вы хотите (предложено Ремкогерлихом), хотя я не знаю, насколько эффективен этот метод:

 import numpy as np

# Constants
SHAPE = (100, 100)
LENGTH = SHAPE[0] * SHAPE[1]

REST = [-1, 1, 2]
ZERO_PROB = 10
BASE_PROB = (100 - ZERO_PROB) // len(REST)

NUM_ZERO = round(LENGTH * (ZERO_PROB / 100))
NUM_REST = round(LENGTH * (BASE_PROB / 100))

# Make base 1D array
base_arr = [0 for _ in range(NUM_ZERO)]
for i in REST:
    base_arr  = [i for _ in range(NUM_REST)]
base_arr = np.array(base_arr)

# Give it a random order
np.random.shuffle(base_arr)

# Finally, reshape the array
result_arr = base_arr.reshape(SHAPE)
 

Глядя на ваш комментарий, для гибкости, которая зависит от того, сколько чисел должно иметь разные вероятности, я полагаю. Вы могли бы просто иметь цикл for, который проходит и создает массив нужной длины для каждого, чтобы добавить в base_arr него . Кроме того, это, конечно, может быть функция, в которую вы передаете переменные, а не просто скрипт с жестко закодированными константами, как это.

Отредактировано на основе комментария.

Комментарии:

1. Соотношение на самом деле будет зависеть только от того, сколько ячеек я хочу, чтобы они были пустыми, или от 0 в случае шляпы. Все остальные ненулевые элементы должны иметь одинаковое соотношение. Скажем, я хочу, чтобы 20% сетки были нулями, остальные числа будут иметь соотношение (1 — ratio_of_zero)/amount_of_non-zero_elements.

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

3. Спасибо вам за такой подход, для меня это прекрасно работает. Это также отражает идею, предложенную @RemcoGerlich, так что спасибо вам обоим. Я также подумал о создании массивов одинаковой длины, но разных значений с помощью цикла for, но затем np.concatenate вместо этого использовал их для склеивания. Затем, применив np.random.schuffle(base_arr) , я получаю вектор с точными соотношениями, но в случайном порядке. Если кто-то хочет преобразовать в матрицу, его можно просто использовать np.reshape() . При этом эта матрица/сетка также может быть построена, например, с plt.pcolormesh помощью .