Перетасовать часть массива в numpy

#python #arrays #numpy #shuffle

#python #массивы #numpy #перетасовать

Вопрос:

У меня есть массив numpy, и я хотел бы перетасовать его части. Например, со следующим массивом:

 import numpy as np
import random

a = np.arange(15)
# => array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14])
  

Я хочу сделать:

 shuffle_parts(a, [(0, 3), (10, 13)])
# => array([ 2,  0,  1,  3,  4,  5,  6,  7,  8,  9, 12, 11, 10, 13, 14])
#            ^^^^^^^^^                              ^^^^^^^^^^
#            Shuffle those 3 values                 and those 3 values
  

Следующее перетасовало бы весь массив: (Не то, что я хочу)

 random.shuffle(a) 
# => array([10, 11,  8,  1, 13,  5,  9, 14,  4,  7,  2, 12,  3,  0,  6])
  

Одним из способов было бы использовать разделение / объединение следующим образом:

 splits = np.split(a, 5)
random.shuffle(splits[0])
random.shuffle(splits[3])
np.concatenate(splits)
# => array([ 2,  0,  1,  3,  4,  5,  6,  7,  8, 11, 10, 9, 12, 13, 14])
#            ^^^^^^^^^                          ^^^^^^^^^^
#            Correctly shuffled                 Shuffled but off by 1 index
  

Это почти то, чего я хочу. Мои вопросы:

  • Могу ли я написать, shuffle_parts где индексы являются пользовательскими (части с произвольными индексами, не ограниченные модулями, и части с различной длиной)
  • Есть ли в numpy метод, который я пропустил, и который помог бы мне это сделать?

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

1. Просто перетасуйте нарезанные представления массива, например np.random.shuffle(a[0:3])

Ответ №1:

Это можно сделать напрямую:

 >>> import numpy as np
>>> import random
>>> a = np.arange(15)
>>> s=3
>>> f=7
>>> random.shuffle(a[s:f])
>>> a
array([ 0,  1,  2,  5,  4,  3,  6,  7,  8,  9, 10, 11, 12, 13, 14])
  

Индексирование напрямую ссылается на данные, что делает это возможным.

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

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

2. @BenjaminCrouzier Нет, случайный алгоритм сам по себе «небезопасен». Я не думаю, что это должно иметь значение, хотя для программ, которым просто нужна некоторая случайность в них. Я отметил это на случай, если вы захотите прочитать больше.

3. Вы путаете случайность и безопасность. Python (и в расширении numpy) использует генератор псевдослучайных чисел, поэтому он небезопасен для криптографических целей. Для всего остального это просто отлично. Это не слабость или недостаток библиотеки или языка — это то же самое на любом другом языке. Если вам нужны действительно случайные числа, они должны поступать из действительно случайного источника, а не из алгоритма (и если вы не знаете, есть ли у вас, вы этого не делаете).

4. @user2699 Очень хорошо, я не хотел вдаваться в такие глубины, но конец вашего комментария, я думаю, подтверждает вашу точку зрения — я отредактирую примечание.

Ответ №2:

фрагменты numpy — это представления приведенных ниже данных; поэтому вы можете напрямую перетасовывать фрагменты:

 import numpy as np
import random

a = np.arange(15)

random.shuffle(a[0:3])
random.shuffle(a[10:13])
print(a)
# [ 2  0  1  3  4  5  6  7  8  9 12 10 11 13 14]
  

тогда вы могли бы реализовать свою shuffle_parts функцию, используя slice этот способ:

 def shuffle_parts(array, slices):
    for s in slices:
        random.shuffle(a[slice(*s)])

shuffle_parts(array=a, slices=((0, 3), (10, 13)))
  

или (в зависимости от того, как вы хотите передать фрагменты в вашу функцию):

 def shuffle_parts(array, slices):
    for s in slices:
        random.shuffle(a[s])

shuffle_parts(array=a, slices=(slice(0, 3), slice(10, 13)))
  

лично я бы предпочел вторую версию (таким образом, вы также могли бы, например, перетасовать четные индексы: shuffle_parts(array=a, slices=(slice(None, None, 2), )) )…