更好的方法是同时改变两个numpy阵列

Jos*_*der 214 python random numpy shuffle numpy-ndarray

我有两个不同形状的numpy数组,但长度相同(领先维度).我想改变它们中的每一个,使得相应的元素继续对应 - 即相对于它们的前导索引一致地混洗它们.

此代码有效,并说明了我的目标:

def shuffle_in_unison(a, b):
    assert len(a) == len(b)
    shuffled_a = numpy.empty(a.shape, dtype=a.dtype)
    shuffled_b = numpy.empty(b.shape, dtype=b.dtype)
    permutation = numpy.random.permutation(len(a))
    for old_index, new_index in enumerate(permutation):
        shuffled_a[new_index] = a[old_index]
        shuffled_b[new_index] = b[old_index]
    return shuffled_a, shuffled_b
Run Code Online (Sandbox Code Playgroud)

例如:

>>> a = numpy.asarray([[1, 1], [2, 2], [3, 3]])
>>> b = numpy.asarray([1, 2, 3])
>>> shuffle_in_unison(a, b)
(array([[2, 2],
       [1, 1],
       [3, 3]]), array([2, 1, 3]))
Run Code Online (Sandbox Code Playgroud)

然而,这种感觉笨重,效率低,而且速度慢,而且需要使阵列的复制 - 我宁愿他们洗牌在原地的,因为他们将是相当大的.

有没有更好的方法来解决这个问题?更快的执行速度和更低的内存使用率是我的主要目标,但优雅的代码也会很好.

我有另外一个想法是:

def shuffle_in_unison_scary(a, b):
    rng_state = numpy.random.get_state()
    numpy.random.shuffle(a)
    numpy.random.set_state(rng_state)
    numpy.random.shuffle(b)
Run Code Online (Sandbox Code Playgroud)

这工作...但它是一个有点吓人,因为我看不出有什么保证它会继续工作 - 它看起来并不像那种有保证跨numpy的版本中生存,例如事情.

mtr*_*trw 314

你可以使用NumPy的数组索引:

def unison_shuffled_copies(a, b):
    assert len(a) == len(b)
    p = numpy.random.permutation(len(a))
    return a[p], b[p]
Run Code Online (Sandbox Code Playgroud)

这将导致创建单独的unison-shuffled数组.

  • 这**会创建副本,因为它使用高级索引.但当然它比原来更快. (11认同)
  • 我非常喜欢这种简单性和可读性,高级索引继续令我惊讶和惊讶; 为此,这个答案很容易获得+1.但奇怪的是,在我的(大)数据集上,它比我原来的函数慢:我的原始函数需要大约1.8秒进行10次迭代,这需要大约2.7秒.两个数字都非常一致.我用来测试的数据集有`a.shape`是`(31925,405)`和`b.shape`是`(31925,)`. (2认同)

Jam*_*mes 154

X = np.array([[1., 0.], [2., 1.], [0., 0.]])
y = np.array([0, 1, 2])
from sklearn.utils import shuffle
X, y = shuffle(X, y, random_state=0)
Run Code Online (Sandbox Code Playgroud)

要了解更多信息,请访问http://scikit-learn.org/stable/modules/generated/sklearn.utils.shuffle.html

  • 该解决方案创建[副本](https://scikit-learn.org/stable/modules/ generated/sklearn.utils.shuffle.html)(_“原始数组不受影响”_),而作者的“可怕”解决方案没有。 (2认同)

Sve*_*ach 66

你的"可怕"解决方案对我来说并不可怕.调用shuffle()两个相同长度的序列导致对随机数生成器的相同调用次数,并且这些是随机数算法中唯一的"随机"元素.通过重置状态,您可以确保对随机数生成器的调用将在第二次调用时给出相同的结果shuffle(),因此整个算法将生成相同的排列.

如果您不喜欢这样,另一种解决方案是将数据存储在一个数组中,而不是从一开始就存储两个数组,并在此单个数组中创建两个视图,模拟您现在拥有的两个数组.您可以使用单个数组进行混洗,并将视图用于所有其他目的.

例如:假设数组ab看起来像这样:

a = numpy.array([[[  0.,   1.,   2.],
                  [  3.,   4.,   5.]],

                 [[  6.,   7.,   8.],
                  [  9.,  10.,  11.]],

                 [[ 12.,  13.,  14.],
                  [ 15.,  16.,  17.]]])

b = numpy.array([[ 0.,  1.],
                 [ 2.,  3.],
                 [ 4.,  5.]])
Run Code Online (Sandbox Code Playgroud)

我们现在可以构造一个包含所有数据的数组:

c = numpy.c_[a.reshape(len(a), -1), b.reshape(len(b), -1)]
# array([[  0.,   1.,   2.,   3.,   4.,   5.,   0.,   1.],
#        [  6.,   7.,   8.,   9.,  10.,  11.,   2.,   3.],
#        [ 12.,  13.,  14.,  15.,  16.,  17.,   4.,   5.]])
Run Code Online (Sandbox Code Playgroud)

现在我们创建模拟原始的视图,a并且b:

a2 = c[:, :a.size//len(a)].reshape(a.shape)
b2 = c[:, a.size//len(a):].reshape(b.shape)
Run Code Online (Sandbox Code Playgroud)

的数据a2b2与共享c.要同时对两个阵列进行洗牌,请使用numpy.random.shuffle(c).

在生产代码中,您当然会尽量避免创建原始文件a,b并且立即创建c,a2并且b2.

该解决方案能够适应的情况下a,并b有不同的dtypes.

  • @Josh:请注意,“numpy.random.shuffle()”对任意可变序列进行操作,例如 Python 列表或 NumPy 数组。数组的形状并不重要,重要的是序列的长度。在我看来,这“非常”不可能改变。 (2认同)

小智 30

很简单的解决方案

randomize = np.arange(len(x))
np.random.shuffle(randomize)
x = x[randomize]
y = y[randomize]
Run Code Online (Sandbox Code Playgroud)

现在,两个数组x,y都以相同的方式随机洗牌

  • 这相当于mtrw的解决方案.你的前两行只是产生一个排列,但这可以在一行中完成. (3认同)

小智 16

詹姆斯在2015年写了一个有用的sklearn 解决方案.但他添加了一个随机状态变量,这是不需要的.在下面的代码中,自动假设来自numpy的随机状态.

X = np.array([[1., 0.], [2., 1.], [0., 0.]])
y = np.array([0, 1, 2])
from sklearn.utils import shuffle
X, y = shuffle(X, y)
Run Code Online (Sandbox Code Playgroud)


ben*_*ers 11

from np.random import permutation
from sklearn.datasets import load_iris
iris = load_iris()
X = iris.data #numpy array
y = iris.target #numpy array

# Data is currently unshuffled; we should shuffle 
# each X[i] with its corresponding y[i]
perm = permutation(len(X))
X = X[perm]
y = y[perm]
Run Code Online (Sandbox Code Playgroud)


Isa*_*c B 9

仅使用NumPy就地将任意数量的阵列就地混合在一起.

import numpy as np


def shuffle_arrays(arrays, set_seed=-1):
    """Shuffles arrays in-place, in the same order, along axis=0

    Parameters:
    -----------
    arrays : List of NumPy arrays.
    set_seed : Seed value if int >= 0, else seed is random.
    """
    assert all(len(arr) == len(arrays[0]) for arr in arrays)
    seed = np.random.randint(0, 2**(32 - 1) - 1) if set_seed < 0 else set_seed

    for arr in arrays:
        rstate = np.random.RandomState(seed)
        rstate.shuffle(arr)
Run Code Online (Sandbox Code Playgroud)

并且可以像这样使用

a = np.array([1, 2, 3, 4, 5])
b = np.array([10,20,30,40,50])
c = np.array([[1,10,11], [2,20,22], [3,30,33], [4,40,44], [5,50,55]])

shuffle_arrays([a, b, c])
Run Code Online (Sandbox Code Playgroud)

有几点需要注意:

  • 断言确保所有输入数组沿其第一维具有相同的长度.
  • 阵列在他们的第一个维度上就地洗牌 - 没有任何回报.
  • 随机种子在int32范围内.
  • 如果需要可重复的shuffle,则可以设置种子值.

在shuffle之后,可以np.split使用切片分割数据或使用切片进行分割- 具体取决于应用程序.

  • 这是正确的答案。当您可以传递随机状态对象时,没有理由使用全局 np.random 。 (3认同)
  • 漂亮的解决方案,这对我来说很完美。即使有3+轴的阵列 (2认同)

moh*_*mlo 8

你可以做一个像这样的数组:

s = np.arange(0, len(a), 1)
Run Code Online (Sandbox Code Playgroud)

然后洗牌:

np.random.shuffle(s)
Run Code Online (Sandbox Code Playgroud)

现在使用此s作为数组的参数.相同的混洗参数返回相同的混洗向量.

x_data = x_data[s]
x_label = x_label[s]
Run Code Online (Sandbox Code Playgroud)


szi*_*qui 7

有一个众所周知的函数可以处理这个问题:

from sklearn.model_selection import train_test_split
X, _, Y, _ = train_test_split(X,Y, test_size=0.0)
Run Code Online (Sandbox Code Playgroud)

只需将 test_size 设置为 0 将避免拆分并为您提供混洗数据。虽然它通常用于拆分训练和测试数据,但它也会对它们进行混洗。
文档

将数组或矩阵拆分为随机训练和测试子集

包装输入验证和 next(ShuffleSplit().split(X, y)) 和应用程序的快速实用程序,用于将数据输入到单个调用中,以便在单行中拆分(和可选的子采样)数据。


Ada*_*der 6

可以对连接列表进行就地改组的一种方法是使用种子(它可以是随机的)并使用 numpy.random.shuffle 进行改组。

# Set seed to a random number if you want the shuffling to be non-deterministic.
def shuffle(a, b, seed):
   np.random.seed(seed)
   np.random.shuffle(a)
   np.random.seed(seed)
   np.random.shuffle(b)
Run Code Online (Sandbox Code Playgroud)

就是这样。这将以完全相同的方式对 a 和 b 进行混洗。这也是就地完成的,这总是一个优点。

编辑,不要使用 np.random.seed() 而是使用 np.random.RandomState

def shuffle(a, b, seed):
   rand_state = np.random.RandomState(seed)
   rand_state.shuffle(a)
   rand_state.seed(seed)
   rand_state.shuffle(b)
Run Code Online (Sandbox Code Playgroud)

调用它时,只需传入任何种子即可提供随机状态:

a = [1,2,3,4]
b = [11, 22, 33, 44]
shuffle(a, b, 12345)
Run Code Online (Sandbox Code Playgroud)

输出:

>>> a
[1, 4, 2, 3]
>>> b
[11, 44, 22, 33]
Run Code Online (Sandbox Code Playgroud)

编辑:修复了重新播种随机状态的代码


and*_*ndy 6

这似乎是一个非常简单的解决方案:

import numpy as np
def shuffle_in_unison(a,b):

    assert len(a)==len(b)
    c = np.arange(len(a))
    np.random.shuffle(c)

    return a[c],b[c]

a =  np.asarray([[1, 1], [2, 2], [3, 3]])
b =  np.asarray([11, 22, 33])

shuffle_in_unison(a,b)
Out[94]: 
(array([[3, 3],
        [2, 2],
        [1, 1]]),
 array([33, 22, 11]))
Run Code Online (Sandbox Code Playgroud)