在NumPy数组的每一行中随机洗牌项目。

13

我有一个类似下面的 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
根据np文档,多维数组只能沿着第一个轴进行洗牌:>>> >>> arr = np.arange(9).reshape((3, 3)) >>> np.random.shuffle(arr) >>> arr array([[3, 4, 5], [6, 7, 8], [0, 1, 2]]) - Jack Arnestad
1
是的,suffle()函数不接受轴参数。这里有一个类似的问题:https://dev59.com/wKvka4cB1Zd3GeqPt36z#50416056 - Mazdak
如果我理解正确的话,这个问题想要洗牌行的顺序,而不是行中实际的值。 - Jack Arnestad
6个回答

7

如果你只想对列进行洗牌,你可以在矩阵的转置上执行 洗牌 操作:

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]])

3

From: 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

3

这个解决方案并不高效,但我觉得思考它很有趣,所以写下了它。基本上,您需要把数组打乱,并创建一个行标签和一个索引的数组。您随机打乱索引数组,并使用该数组对原始数组和行标签数组进行索引。然后,您需要将行标签应用到 稳定的 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链接


2
我们可以创建一个随机的二维矩阵,按行排序,然后使用由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.randargsort来模拟洗牌的效果。
  • random.rand提供随机性。
  • 接着,我们使用axis=1argsort来帮助排列每一行。这样就创建了可以用来重新排序的索引。

那样做怎么比直接对原始行进行排序更好呢? - Mad Physicist
@MadPhysicist 直接排序会导致相同的结果,没有任何随机性。 - Tai
我明白了。你利用argsort有一个轴参数来弥补shuffle的不足。聪明。 - Mad Physicist
@MadPhysicist,没错!感谢你花时间理解这个想法。 - Tai

1
假设您有一个形状为100000 x 1000的数组a
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


0

您可以使用Pandas

df = pd.DataFrame(X_train)
_ = df.apply(lambda x: np.random.permutation(x), axis=1, raw=True)
df.values

如果您想要洗牌列,请将关键字更改为axis=0


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接