如何根据列(id)洗牌但保持降序 True

How to shuffle according column (id) but keep descending True

我有一个数据框,其结构如下:

>>>df
   a  b  id  
0  1  4  3  
1  4  1  2  
2  7  5  1

3  2  9  3
4  4  11 2
5  2  7  1

6  3  4  2
7  9  2  1

为了便于阅读,我在代码中添加了段落。

现在我想根据 id 随机播放但保持列 id True 的初始降序。什么是最好的方法? 可能的输出如下所示:

>>>df
   a  b  id  
0  3  4  2
1  9  2  1

2  2  9  3
3  4  11 2
4  2  7  1

5  1  4  3  
6  4  1  2  
7  7  5  1

所以原则上我只是希望块混合或随机放置在另一个地方。

根据 id 中的差异创建组 - 如果差异不是 -1,则每个组都是 strat,然后获得唯一的组 ids,通过 DataFrame.loc 洗牌和更改顺序:

df['g'] = df['id'].diff().ne(-1).cumsum()
#if possible differency is not always -1
df['g'] = df['id'].ge(df['id'].shift()).cumsum()
print (df)
   a   b  id  g
0  1   4   3  1
1  4   1   2  1
2  7   5   1  1
3  2   9   3  2
4  4  11   2  2
5  2   7   1  2
6  3   4   2  3
7  9   2   1  3

ids = df['g'].unique()
np.random.shuffle(ids)
df = df.set_index('g').loc[ids].reset_index(drop=True)
print (df)
   a   b  id
0  1   4   3
1  4   1   2
2  7   5   1
3  3   4   2
4  9   2   1
5  2   9   3
6  4  11   2
7  2   7   1

如果需要按帮助列更改最后的测试组 reset_index(drop=True):

ids = df['g'].unique()
np.random.shuffle(ids)
df = df.set_index('g').loc[ids].reset_index()
print (df)
   g  a   b  id
0  2  3   4   2
1  2  9   2   1
2  1  2   9   3
3  1  4  11   2
4  1  2   7   1
5  0  1   4   3
6  0  4   1   2
7  0  7   5   1

性能:在示例数据中,我猜重复排序应该是另一个解决方案中性能较慢的原因。

#4k rows
df = pd.concat([df] * 500, ignore_index=True)
print (df)

In [70]: %%timeit
    ...: out = df.assign(order=df['id'].ge(df['id'].shift()).cumsum()).sample(frac=1)
    ...: cat = pd.CategoricalDtype(out['order'].unique(), ordered=True)
    ...: out = out = out.astype({'order': cat}).sort_values(['order', 'id'], ascending=False)
    ...: 
6.13 ms ± 845 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)


%%timeit
df['g'] = df['id'].diff().ne(-1).cumsum()
ids = df['g'].unique()
np.random.shuffle(ids)
df.set_index('g').loc[ids].reset_index(drop=True)


3.93 ms ± 161 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

使用分类索引按块对值进行排序:

out = df.assign(order=df['id'].ge(df['id'].shift()).cumsum()).sample(frac=1)
cat = pd.CategoricalDtype(out['order'].unique(), ordered=True)
out = out = out.astype({'order': cat}).sort_values(['order', 'id'], ascending=False)
print(out)

# Output:
   a   b  id order
0  1   4   3     0
1  4   1   2     0
2  7   5   1     0
6  3   4   2     2
7  9   2   1     2
3  2   9   3     1
4  4  11   2     1
5  2   7   1     1

显然,您可以通过在 sort_values 之后附加 .drop(columns='order') 来删除 order 列,但我将其保留在这里用于演示目的。

此处的关键是将 ordered=True 设置为新的分类数据类型。

>>> cat
CategoricalDtype(categories=[1, 2, 0], ordered=True)