只按行移动多维数组,保持列顺序不变

如何在 Python 中只对多维数组进行逐行洗牌(因此不要对列进行洗牌)。

我正在寻找最有效的解决方案,因为我的矩阵是非常巨大的。是否也可以在原始数组上高效地执行此操作(以节省内存) ?

例如:

import numpy as np
X = np.random.random((6, 2))
print(X)
Y = ???shuffle by row only not colls???
print(Y)

我现在期待的是原始矩阵:

[[ 0.48252164  0.12013048]
[ 0.77254355  0.74382174]
[ 0.45174186  0.8782033 ]
[ 0.75623083  0.71763107]
[ 0.26809253  0.75144034]
[ 0.23442518  0.39031414]]

输出对行进行洗牌,而不是对协议进行洗牌,例如:

[[ 0.45174186  0.8782033 ]
[ 0.48252164  0.12013048]
[ 0.77254355  0.74382174]
[ 0.75623083  0.71763107]
[ 0.23442518  0.39031414]
[ 0.26809253  0.75144034]]
136521 次浏览

你可以使用 numpy.random.shuffle()

此函数仅将数组沿 多维数组。子数组的顺序改变,但它们的 内容保持不变。

In [2]: import numpy as np


In [3]:


In [3]: X = np.random.random((6, 2))


In [4]: X
Out[4]:
array([[0.71935047, 0.25796155],
[0.4621708 , 0.55140423],
[0.22605866, 0.61581771],
[0.47264172, 0.79307633],
[0.22701656, 0.11927993],
[0.20117207, 0.2754544 ]])


In [5]: np.random.shuffle(X)


In [6]: X
Out[6]:
array([[0.71935047, 0.25796155],
[0.47264172, 0.79307633],
[0.4621708 , 0.55140423],
[0.22701656, 0.11927993],
[0.20117207, 0.2754544 ],
[0.22605866, 0.61581771]])

至于其他功能,你也可以查看以下功能:

该功能是在 Numpy 的1.20.0版本中引入的。

新函数与 shufflepermutation的不同之处在于 由一个轴索引的子阵列是置换的,而不是轴被 作为一个独立的1-D 数组对其他的每个组合 例如,现在可以对行或 二维数组的列。

您还可以使用 np.random.permutation生成行索引的随机排列,然后使用 np.takeaxis=0索引到 X的行中。此外,np.take使用 out=选项便于覆盖输入数组 X本身,这将节省我们的内存。因此,实现应该是这样的-

np.take(X,np.random.permutation(X.shape[0]),axis=0,out=X)

样本运行-

In [23]: X
Out[23]:
array([[ 0.60511059,  0.75001599],
[ 0.30968339,  0.09162172],
[ 0.14673218,  0.09089028],
[ 0.31663128,  0.10000309],
[ 0.0957233 ,  0.96210485],
[ 0.56843186,  0.36654023]])


In [24]: np.take(X,np.random.permutation(X.shape[0]),axis=0,out=X);


In [25]: X
Out[25]:
array([[ 0.14673218,  0.09089028],
[ 0.31663128,  0.10000309],
[ 0.30968339,  0.09162172],
[ 0.56843186,  0.36654023],
[ 0.0957233 ,  0.96210485],
[ 0.60511059,  0.75001599]])

额外的性能提升

这里有一个诀窍,以加速 np.random.permutation(X.shape[0])np.argsort()-

np.random.rand(X.shape[0]).argsort()

加快结果-

In [32]: X = np.random.random((6000, 2000))


In [33]: %timeit np.random.permutation(X.shape[0])
1000 loops, best of 3: 510 µs per loop


In [34]: %timeit np.random.rand(X.shape[0]).argsort()
1000 loops, best of 3: 297 µs per loop

因此,洗牌溶液可以修改为-

np.take(X,np.random.rand(X.shape[0]).argsort(),axis=0,out=X)

运行时测试-

这些测试包括本文中列出的两种方法和 @Kasramvd's solution中基于 np.shuffle的方法。

In [40]: X = np.random.random((6000, 2000))


In [41]: %timeit np.random.shuffle(X)
10 loops, best of 3: 25.2 ms per loop


In [42]: %timeit np.take(X,np.random.permutation(X.shape[0]),axis=0,out=X)
10 loops, best of 3: 53.3 ms per loop


In [43]: %timeit np.take(X,np.random.rand(X.shape[0]).argsort(),axis=0,out=X)
10 loops, best of 3: 53.2 ms per loop

因此,似乎使用这些基于 np.take只能用于内存是一个问题,否则基于 np.random.shuffle的解决方案看起来像走的路。

经过一些实验(i)发现了在 nD 数组中对数据进行行洗牌的最有效的内存和时间方法。首先,对数组的索引进行洗牌,然后使用洗牌后的索引获取数据。例如:。

rand_num2 = np.random.randint(5, size=(6000, 2000))
perm = np.arange(rand_num2.shape[0])
np.random.shuffle(perm)
rand_num2 = rand_num2[perm]

在更多细节 < br > 这里,我使用 Memory _ profiler查找内存使用情况,使用 python 内置的“ time”模块记录时间并比较以前的所有答案

def main():
# shuffle data itself
rand_num = np.random.randint(5, size=(6000, 2000))
start = time.time()
np.random.shuffle(rand_num)
print('Time for direct shuffle: {0}'.format((time.time() - start)))
    

# Shuffle index and get data from shuffled index
rand_num2 = np.random.randint(5, size=(6000, 2000))
start = time.time()
perm = np.arange(rand_num2.shape[0])
np.random.shuffle(perm)
rand_num2 = rand_num2[perm]
print('Time for shuffling index: {0}'.format((time.time() - start)))
    

# using np.take()
rand_num3 = np.random.randint(5, size=(6000, 2000))
start = time.time()
np.take(rand_num3, np.random.rand(rand_num3.shape[0]).argsort(), axis=0, out=rand_num3)
print("Time taken by np.take, {0}".format((time.time() - start)))

时间的结果

Time for direct shuffle: 0.03345608711242676   # 33.4msec
Time for shuffling index: 0.019818782806396484 # 19.8msec
Time taken by np.take, 0.06726956367492676     # 67.2msec

内存分析器结果

Line #    Mem usage    Increment   Line Contents
================================================
39  117.422 MiB    0.000 MiB   @profile
40                             def main():
41                                 # shuffle data itself
42  208.977 MiB   91.555 MiB       rand_num = np.random.randint(5, size=(6000, 2000))
43  208.977 MiB    0.000 MiB       start = time.time()
44  208.977 MiB    0.000 MiB       np.random.shuffle(rand_num)
45  208.977 MiB    0.000 MiB       print('Time for direct shuffle: {0}'.format((time.time() - start)))
46
47                                 # Shuffle index and get data from shuffled index
48  300.531 MiB   91.555 MiB       rand_num2 = np.random.randint(5, size=(6000, 2000))
49  300.531 MiB    0.000 MiB       start = time.time()
50  300.535 MiB    0.004 MiB       perm = np.arange(rand_num2.shape[0])
51  300.539 MiB    0.004 MiB       np.random.shuffle(perm)
52  300.539 MiB    0.000 MiB       rand_num2 = rand_num2[perm]
53  300.539 MiB    0.000 MiB       print('Time for shuffling index: {0}'.format((time.time() - start)))
54
55                                 # using np.take()
56  392.094 MiB   91.555 MiB       rand_num3 = np.random.randint(5, size=(6000, 2000))
57  392.094 MiB    0.000 MiB       start = time.time()
58  392.242 MiB    0.148 MiB       np.take(rand_num3, np.random.rand(rand_num3.shape[0]).argsort(), axis=0, out=rand_num3)
59  392.242 MiB    0.000 MiB       print("Time taken by np.take, {0}".format((time.time() - start)))

您可以使用 np.vectorize()函数对二维数组 A 划船进行洗牌:

shuffle = np.vectorize(np.random.permutation, signature='(n)->(n)')


A_shuffled = shuffle(A)

我尝试了很多方法,最后我用了这个简单的方法:

from sklearn.utils import shuffle
x = np.array([[1, 2],
[3, 4],
[5, 6]])
print(shuffle(x, random_state=0))

产出:

[
[5 6]
[3 4]
[1 2]
]

如果你有三维数组,循环第一个轴(轴 = 0)并应用这个函数,如:

np.array([shuffle(item) for item in 3D_numpy_array])