【问题标题】:numpy random shuffle by row independentlynumpy 随机随机随机播放
【发布时间】:2016-03-28 23:37:31
【问题描述】:

我有以下数组:

 a= array([[  1,  2, 3],
           [  1,  2, 3],
           [  1,  2, 3])

我知道np.random,shuffle(a.T) 将沿行对数组进行洗牌,但我需要的是它独立地洗牌每一行。如何在 numpy 中做到这一点?速度至关重要,因为会有几百万行。

对于这个特定的问题,每一行将包含相同的起始人口。

【问题讨论】:

  • @jpp 最初的问题并没有要求每列,到目前为止也没有答案试图解决它。所以,回滚到旧标题。这是公认解决方案的简单扩展,但可能会使寻找特定案例的人感到困惑。
  • @Divakar,关键是答案是相同的(只需将axis=-1 更改为axis=0)。并且标题更改是微不足道的为了让更多人找到unutbu的优秀解决方案。这里没有隐藏的动机,除了改进 SO。你不同意吗?
  • @jpp 和高级索引部分。我们已经通过 dup 链接对其进行了改进,但正如我对寻找每列基础案例的人所说的那样,他们可能不知道如何扩展。因此,他们可能会遵循链接的副本。

标签: numpy shuffle


【解决方案1】:
import numpy as np
np.random.seed(2018)

def scramble(a, axis=-1):
    """
    Return an array with the values of `a` independently shuffled along the
    given axis
    """ 
    b = a.swapaxes(axis, -1)
    n = a.shape[axis]
    idx = np.random.choice(n, n, replace=False)
    b = b[..., idx]
    return b.swapaxes(axis, -1)

a = a = np.arange(4*9).reshape(4, 9)
# array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8],
#        [ 9, 10, 11, 12, 13, 14, 15, 16, 17],
#        [18, 19, 20, 21, 22, 23, 24, 25, 26],
#        [27, 28, 29, 30, 31, 32, 33, 34, 35]])

print(scramble(a, axis=1))

产量

[[ 3  8  7  0  4  5  1  2  6]
 [12 17 16  9 13 14 10 11 15]
 [21 26 25 18 22 23 19 20 24]
 [30 35 34 27 31 32 28 29 33]]

沿 0 轴加扰时:

print(scramble(a, axis=0))

产量

[[18 19 20 21 22 23 24 25 26]
 [ 0  1  2  3  4  5  6  7  8]
 [27 28 29 30 31 32 33 34 35]
 [ 9 10 11 12 13 14 15 16 17]]

首先将目标轴与最后一个轴交换:

b = a.swapaxes(axis, -1)

这是用于标准化处理一个轴的代码的常用技巧。 它将一般情况简化为处理最后一个轴的特定情况。 由于在 NumPy 1.10 或更高版本中,swapaxes 返回视图,因此不涉及复制,因此调用swapaxes 非常快。

现在我们可以为最后一个轴生成一个新的索引顺序:

n = a.shape[axis]
idx = np.random.choice(n, n, replace=False)

现在我们可以洗牌b(沿最后一个轴独立):

b = b[..., idx]

然后反转swapaxes 以返回a 形的结果:

return b.swapaxes(axis, -1)

【讨论】:

  • @Gioelelm: a[np.arange(a.shape[0])[:, None], idx] 的形式为a[ind_1, ind_2],其中ind_1ind_2 是整数值数组。 a[ind_1, ind_2]integer array indexing 的一个示例。特别是,请查看链接页面上显示的第二个示例。一旦你了解了整数数组索引,上面的代码应该会更清晰。
  • @Gioelelm:(续)唯一增加的一点是np.arange(a.shape[0]) 是一个一维数组,而[:, None] is being used 使ind_1 是一个形状为(a.shape[0], 1) 的二维数组。当在 a[ind_1, ind_2], the indexing arrays ind_1` 和 ind_2 broadcast 中一起使用时,形状相同 -- (a.shape[0], 1) 广播到 a.shape
  • @UlfAslak:感谢您提醒我有关此错误的信息。我相信它现在已经修复了。
  • 请注意,每一行的随机播放都是相同的,索引为idx。您仍然没有为每一行获得不同的改组。
  • 这没有回答问题。每一行都以相同的方式洗牌。我可以通过简单的索引来实现。
【解决方案2】:

如果您不想要return 值并想直接对数组进行操作,您可以指定要随机播放的索引。

>>> import numpy as np
>>>
>>>
>>> a = np.array([[1,2,3], [1,2,3], [1,2,3]])
>>>
>>> # Shuffle row `2` independently
>>> np.random.shuffle(a[2])
>>> a
array([[1, 2, 3],
       [1, 2, 3],
       [3, 2, 1]])
>>>
>>> # Shuffle column `0` independently
>>> np.random.shuffle(a[:,0])
>>> a
array([[3, 2, 3],
       [1, 2, 3],
       [1, 2, 1]])

如果您还想要返回值,可以使用numpy.random.permutation,在这种情况下,将np.random.shuffle(a[n]) 替换为a[n] = np.random.permutation(a[n])

警告,不要这样做a[n] = np.random.shuffle(a[n])shuffle 没有 return 任何东西,所以你最终“洗牌”的行/列将被 nan 填充。

【讨论】:

    【解决方案3】:

    上面的答案很好。但我会以一种快速而肮脏的方式抛出:

    a = np.array([[1,2,3], [1,2,3], [1,2,3]])
    ignore_list_outpput = [np.random.shuffle(x) for x in a]
    Then, a can be something like this
    array([[2, 1, 3],
           [4, 6, 5],
           [9, 7, 8]])
    

    不是很优雅,但你可以用一条短线完成这项工作。

    【讨论】:

    • 我相信这将需要很长时间才能执行,因为它不是纯 numpy
    • 这个想法与 np.apply_along_axis 相同,它是纯 numpy 函数。但是这个函数也逐行处理每一行。您不会获得很多性能提升。
    • 基本上你是在内存中逐行重新排列numpy数组。
    • 你能计时,看看它在 2m 行时的表现如何?
    • 我认为这是你的工作 :-)
    【解决方案4】:

    根据我对@Hun 的回答的评论,这里是最快的方法

    def shuffle_along(X):
        """Minimal in place independent-row shuffler."""
        [np.random.shuffle(x) for x in X]
    

    这可以就地工作,并且只能随机排列行。如果您需要更多选择:

    def shuffle_along(X, axis=0, inline=False):
        """More elaborate version of the above."""
        if not inline:
            X = X.copy()
        if axis == 0:
            [np.random.shuffle(x) for x in X]
        if axis == 1:
            [np.random.shuffle(x) for x in X.T]
        if not inline:
            return X
    

    然而,这有只能在二维数组上工作的限制。对于更高维度的张量,我会使用:

    def shuffle_along(X, axis=0, inline=True):
        """Shuffle along any axis of a tensor."""
        if not inline:
            X = X.copy()
        np.apply_along_axis(np.random.shuffle, axis, X)  # <-- I just changed this
        if not inline:
            return X
    

    【讨论】:

    • 干得好!对于一个巨大的矩阵,这种就地洗牌应该是要走的路。如果不是就地,您可以使用 np.random.permutation 或 np.random.choice。这样,不需要显式复制数组,可能是更可读/更干净的代码,也是强调 np.random.shuffle() 的内联性质的好方法。
    【解决方案5】:

    您可以使用 numpy 来完成,无需任何循环或额外功能,而且速度更快。例如,我们有一个大小为 (2, 6) 的数组,我们想要一个子数组 (2,2),每列都有独立的随机索引。

    import numpy as np
    
    test = np.array([[1, 1],
                     [2, 2],
                     [0.5, 0.5],
                     [0.3, 0.3],
                     [4, 4],
                     [7, 7]])
    
    id_rnd = np.random.randint(6, size=(2, 2))  # select random numbers, use choice and range if don want replacement.
    new = np.take_along_axis(test, id_rnd, axis=0)
    
    Out: 
    array([[2. , 2. ],
           [0.5, 2. ]])
    

    它适用于任意数量的维度。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-01-23
      • 2016-12-29
      • 2015-11-16
      • 1970-01-01
      • 2012-03-11
      • 2014-04-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多