Генерировать случайную матрицу в numpy без строк из всех единиц

#python #arrays #numpy

#python #массивы #numpy

Вопрос:

Я генерирую случайную матрицу с

 np.random.randint(2, size=(5, 3))
  

это выводит что-то вроде

 [0,1,0],
[1,0,0],
[1,1,1],
[1,0,1],
[0,0,0]
  

Как мне создать случайную матрицу с условием, что каждая строка не может содержать все 1 ‘s? То есть каждая строка может быть [1,0,0] или [0,0,0] или [1,1,0] [1,0,1 ] или [0,0,1 [0,1,0] ] или [0,1,1] или , но не может [1,1,1] быть не может.

Спасибо за ваши ответы

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

1. Я предполагаю, что вы хотите, чтобы это работало не только при создании массива определенного размера 5 x 3 . В общем, вам нужны случайные массивы, где в каждой строке не может быть всех единиц или где общее количество единиц в каждой строке не может превышать некоторого постоянного значения? Это должно работать только для 2D-массивов?

2. Да, это работает только для 2D-массивов, поскольку размер может быть 5×3, 6×3 и т.д., Но у массива нет значения типа [1,1,1]

3. Всегда ли количество столбцов (размер второго измерения) равно 3?

4. Я не понимаю, почему вопрос неясен. Проверенный ответ не так уж хорош, но, похоже, это не гарантирует закрытия.

5. @DanielF на 100% согласен с вами в этом вопросе. Тем не менее, название можно было бы улучшить.

Ответ №1:

Вот интересный подход:

 rows = np.random.randint(7, size=(6, 1), dtype=np.uint8)
np.unpackbits(rows, axis=1)[:, -3:]
  

По сути, вы выбираете целые числа 0-6 для каждой строки, то есть 000-110 в двоичном формате. 7 будет равно 111 (все единицы). Вам просто нужно извлечь двоичные цифры в виде столбцов и взять последние 3 цифры (ваши 3 столбца), поскольку вывод unpackbits равен 8 цифрам.

Вывод:

 array([[1, 0, 1],
       [1, 0, 0],
       [1, 0, 0],
       [1, 0, 0],
       [0, 1, 1],
       [0, 0, 0]], dtype=uint8)
  

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

1. Умно, я об этом не подумал! И вы могли бы обобщить это на любое количество столбцов, также используя, high = 2 ** n_cols - 1 где high — верхняя граница для randint , да? Хотя я полагаю, что это работает только до тех пор, пока у вас не будет слишком много столбцов для размещения, например, int64.

2. Вау. Спасибо, что еще раз напоминаете, что всегда есть радикально новый способ подойти к любой проблеме!

3. @Nathan unpackbits похоже, что она ограничена 8-разрядными целыми числами, но вы могли бы заменить эту функцию своей, чтобы расширить ее за пределы 8 цифр / столбцов. Не уверен, что в numpy уже есть альтернатива для этого.

4. @fountainhead определенно делает кодирование более интересным!

5. Я ожидаю, что по мере того, как количество битов превышает 8, мы сталкиваемся с неопределенностями в представлении, зависящими от конкретной платформы, такими как big-endian против little endian

Ответ №2:

Если у вас всегда есть 3 столбца, один из подходов заключается в явном перечислении возможных строк, а затем в случайном выборе среди них, пока у вас не наберется достаточное количество строк:

 import numpy as np

# every acceptable row
choices = np.array([
    [1,0,0],
    [0,0,0],
    [1,1,0],
    [1,0,1],
    [0,0,1],
    [0,1,0],
    [0,1,1]
])

n_rows = 5
# randomly pick which type of row to use for each row needed
idx = np.random.choice(range(len(choices)), size=n_rows)

# make an array by using the chosen rows
array = choices[idx]
  

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

 n_rows = 5
n_cols = 4

array = np.random.randint(2, size=(n_rows, n_cols))
all_1s_idx = array.sum(axis=-1) == n_cols
while all_1s_idx.any():
    array[all_1s_idx] = np.random.randint(2, size=(all_1s_idx.sum(), n_cols))
    all_1s_idx = array.sum(axis=-1) == n_cols
  

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

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

1. Чрезмерно сложное решение, использование sum() намного элегантнее.

2. Спасибо Натану за лучший ответ!

Ответ №3:

@busybear опередил меня в этом, но я все равно опубликую это, поскольку это немного более общее:

 def not_all(m, k):
    if k>64 or sys.byteorder != 'little':
        raise NotImplementedError
    sample = np.random.randint(0, 2**k-1, (m,), dtype='u8').view('u1').reshape(m, -1)
    sample[:, k//8] <<= -k%8                                                        
    return np.unpackbits(sample).reshape(m, -1)[:, :k]                         
  

Например:

 >>> sample = not_all(1000000, 11)
# sanity checks
>>> unq, cnt = np.unique(sample, axis=0, return_counts=True)
>>> len(unq) == 2**11-1
True
>>> unq.sum(1).max()
10
>>> cnt.min(), cnt.max()
(403, 568)
  

И пока я пытаюсь перехватить ответы других людей, вот упрощенная версия метода принятия- отклонения @Nathan .

 def accrej(m, k):
    sample = np.random.randint(0, 2, (m, k), bool)
    all_ones, = np.where(sample.all(1))
    while all_ones.size:
        resample = np.random.randint(0, 2, (all_ones.size, k), bool)
        sample[all_ones] = resample
        all_ones = all_ones[resample.all(1)]
    return sample.view('u1')
  

Ответ №4:

Попробуйте это решение с помощью sum() :

 import numpy as np

array = np.random.randint(2, size=(5, 3))
for i, entry in enumerate(array):
    if entry.sum() == 3:
        while True:
            new = np.random.randint(2, size=(1, 3))
            if new.sum() == 3:
                continue
            break
        array[i] = new

print(array)
  

Удачи, мой друг!

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

1. Это не обязательно сработает. При вызове np.random.randint во второй раз вы все равно можете сгенерировать строку из всех единиц. Вам нужно будет использовать while цикл — смотрите Мой ответ для примера, который также выполняет повторную выборку всех необходимых строк сразу.

2. Очень просто проверить, приемлема ли новая запись. Это исправлено.

3. Это устранило первую проблему, но вы ввели вторую — вы фактически не перезаписываете исходную строку с помощью new .

4. На самом деле я делаю. Мое решение очень элегантное.

5. вау, это больше ответов решило мои проблемы, заранее спасибо, даршвадер