Случайно перемешать элементы в каждой строке массива numpy

У меня есть массив numpy, например:

Xtrain = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [1, 7, 3]])

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

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

output = np.array([[3, 2, 1],
                   [4, 6, 5],
                   [7, 3, 1]])

Как я могу случайным образом перетасовать каждую из строк случайным образом эффективным образом? Мой фактический массив np содержит более 100000 строк и 1000 столбцов.

Ответ 1

Поскольку вы хотите только перетасовать столбцы, вы можете просто выполнить перетасовку на транспонированной матрице:

In [86]: np.random.shuffle(Xtrain.T)

In [87]: Xtrain
Out[87]: 
array([[2, 3, 1],
       [5, 6, 4],
       [7, 3, 1]])

Обратите внимание, что random.suffle() на двумерном массиве перемещает строки не в элементах в каждой строке. т.е. изменяет положение строк. Поэтому, если вы измените положение транспонированных строк матрицы, вы фактически перетасовываете столбцы исходного массива.

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

In [172]: def crazyshuffle(arr):
     ...:     x, y = arr.shape
     ...:     rows = np.indices((x,y))[0]
     ...:     cols = [np.random.permutation(y) for _ in range(x)]
     ...:     return arr[rows, cols]
     ...: 

Демо-версия:

In [173]: crazyshuffle(Xtrain)
Out[173]: 
array([[1, 3, 2],
       [6, 5, 4],
       [7, 3, 1]])

In [174]: crazyshuffle(Xtrain)
Out[174]: 
array([[2, 3, 1],
       [4, 6, 5],
       [1, 3, 7]])

Ответ 2

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

import numpy as np

r, c = 3, 4  # x.shape

x = np.arange(12) + 1  # Already raveled 
inds = np.arange(x.size)
rows = np.repeat(np.arange(r).reshape(-1, 1), c, axis=1).ravel()

np.random.shuffle(inds)
x = x[inds]
rows = rows[inds]

inds = np.argsort(rows, kind='mergesort')
x = x[inds].reshape(r, c)

Вот ссылка IDEOne

Ответ 3

От: https://github.com/numpy/numpy/issues/5173

def disarrange(a, axis=-1):
    """
    Shuffle 'a' in-place along the given axis.

    Apply numpy.random.shuffle to the given axis of 'a'.
    Each one-dimensional slice is shuffled independently.
    """
    b = a.swapaxes(axis, -1)
    # Shuffle 'b' in-place along the last axis.  'b' is a view of 'a',
    # so 'a' is shuffled in place, too.
    shp = b.shape[:-1]
    for ndx in np.ndindex(shp):
        np.random.shuffle(b[ndx])
    return

Ответ 4

Мы можем создать случайную двумерную матрицу, отсортировать ее по каждой строке, а затем использовать индексную матрицу, заданную argsort для изменения порядка целевой матрицы.

target = np.random.randint(10, size=(5, 5))
# [[7 4 0 2 5]
# [5 6 4 8 7]
# [6 4 7 9 5]
# [8 6 6 2 8]
# [8 1 6 7 3]]

shuffle_helper = np.argsort(np.random.rand(5,5), axis=1)
# [[0 4 3 2 1]
# [4 2 1 3 0]
# [1 2 3 4 0]
# [1 2 4 3 0]
# [1 2 3 0 4]]

target[np.arange(shuffle_helper.shape[0])[:, None], shuffle_helper]
# array([[7, 5, 2, 0, 4],
#       [7, 4, 6, 8, 5],
#       [4, 7, 9, 5, 6],
#       [6, 6, 8, 2, 8],
#       [1, 6, 7, 8, 3]])

объяснение

  • Мы используем np.random.rand и argsort чтобы имитировать эффект от перетасовки.
  • random.rand дает случайность.
  • Затем мы используем argsort с axis=1 чтобы ранжировать каждую строку. Это создает индекс, который можно использовать для переупорядочения.

Ответ 5

Допустим, у вас есть массив a с формой 100000 x 1000.

b = np.random.choice(100000 * 1000, (100000, 1000), replace=False)
ind = np.argsort(b, axis=1)
a_shuffled = a[np.arange(100000)[:,np.newaxis], ind]

Я не знаю, если это быстрее, чем цикл, потому что он нуждается в сортировке, но с этим решением, возможно, вы придумаете что-то лучше, например, с np.argpartition вместо np.argsort