python中带权值的随机洗牌



我正在尝试洗牌数组,并遇到一些问题。

What I have:

my_array=array([nan, 1, 1, nan, nan, 2, nan, ..., nan, nan, nan])

我想做的:
我想洗牌数据集,同时保持数字(例如数组中的1,1)在一起。我所做的是首先将每个nan转换成一个唯一的负数。

my_array=array([-1, 1, 1, -2, -3, 2, -4, ..., -2158, -2159, -2160])

之后我把所有的东西都和熊猫分开了:

df = pd.DataFrame(my_array)
df.rename(columns={0: 'sampleID'}, inplace=True)
groups = [df.iloc[:, 0] for _, df in df.groupby('sampleID')]

如果我知道洗牌我的数据集,我将有一个相等的概率,每个组出现在一个给定的位置,但这将忽略每组元素的数量。如果我有一组几个元素,如[9,9,9,9,9,9],它应该有更高的机会出现早于一些随机nan。如果我说错了,请纠正我。
解决这个问题的一个方法是numpys的选择方法。为此,我需要创建一个概率数组

probability_array = np.zeros(len(groups))
for index, item in enumerate(groups):
probability_array[index] = len(item) / len(groups)

所有这些最后调用:

groups=np.array(groups,dtype=object)
rng = np.random.default_rng()
shuffled_indices = rng.choice(len(groups), len(groups), replace=False, p=probability_array)
shuffled_array = np.concatenate(groups[shuffled_indices]).ravel()
shuffled_array[shuffled_array < 1] = np.NaN

所有这些都非常麻烦,而且速度不快。除了可以更好地编写代码之外,我觉得我错过了一些非常简单的解决方案。有人能给我指个正确的方向吗?

一种方法:

import numpy as np
from itertools import groupby
# toy data
my_array = np.array([np.nan, 1, 1, np.nan, np.nan, 2, 2, 2, np.nan, 3, 3, 3, np.nan, 4, 4, np.nan, np.nan])
# find groups
groups = np.array([[key, sum(1 for _ in group)] for key, group in groupby(my_array)])
# permute
keys, repetitions = zip(*np.random.permutation(groups))
# recreate new array
res = np.repeat(keys, repetitions)
print(res)

(单)

[ 3.  3.  3. nan nan nan nan  2.  2.  2.  1.  1. nan nan nan  4.  4.]

我已经在一些限制条件下解决了你的问题

  1. 我使用零作为分隔符,而不是NaN
  2. 我假设你的数组总是以一个非零整数序列开始,以另一个非零整数序列结束。

有了这些规定,我基本上已经打乱了整数序列的表示,然后我又把所有的东西缝合在一起。

In [102]: import numpy as np
...: from itertools import groupby
...: a = np.array([int(_) for _ in '1110022220003044440005500000600777'])
...: print(a)
...: n, z = [], []
...: for i,g in groupby(a):
...:     if i:
...:         n.append((i, sum(1 for _ in g)))
...:     else:
...:         z.append(sum(1 for _ in g))
...: np.random.shuffle(n)
...: nn = n[0]
...: b = [*[nn[0]]*nn[1]]
...: for zz, nn in zip(z, n[1:]):
...:     b += [*[0]*zz, *[nn[0]]*nn[1]]
...: print(np.array(b))
[1 1 1 0 0 2 2 2 2 0 0 0 3 0 4 4 4 4 0 0 0 5 5 0 0 0 0 0 6 0 0 7 7 7]
[7 7 7 0 0 1 1 1 0 0 0 4 4 4 4 0 6 0 0 0 5 5 0 0 0 0 0 2 2 2 2 0 0 3]

注意

洗牌数组中分隔符的运行长度与原始数组中完全相同,但洗牌也很容易。更困难的问题是在保持数组长度不变的情况下任意改变长度。

最新更新