按“标签”列对数据框进行排序,按每个“标签”随机播放,按“组”保留顺序

Sort a dataframe by a `label` column, shuffle per each `label`, preserve order per `group`

给定以下数据框:

df = pd.DataFrame(data={'value': ['all', 'moon', 'less', 'cat', 'pen' , 'dark', 'pile'],
                        'label': [0, 1, 1, 0, 1, 0, 0],
                        'group': ['A', 'B', 'B', 'B', 'A', 'B', 'A']})

输出:

    value      label  group
0   'all'      0      'A'
1   'moon'     1      'B'
2   'less'     1      'B'
3   'cat'      0      'B'
4   'pen'      1      'A'
5   'dark'     0      'B'
6   'pile'     0      'A'

我想生成一个具有以下条件的新 dataframe

  1. Rows are sorted by label
  2. Per each label, rows are shuffled
  3. But maintaining order based on the value

例如,这是一个可能的结果:

    value      label  group
0   'all'      0      'A'
3   'cat'      0      'B'
5   'dark'     0      'B'
6   'pile'     0      'A'
2   'less'     1      'B'
4   'pen'      1      'A'
1   'moon'     1      'B'

所以关于条件 3,'pile' 出现在 'all' 之后,两者具有相同的标签并且来自同一组。任何其他洗牌和排序,都不应该让 'pile' 出现在 'all'.

之前

或另一个具有不同洗牌的:

    value      label  group
3   'cat'      0      'B'
0   'all'      0      'A'
6   'pile'     0      'A'
5   'dark'     0      'B'
4   'pen'      1      'A'
2   'less'     1      'B'
1   'moon'     1      'B'

对实现此目标的干净方法有任何想法吗?

这实际上实现起来相当复杂。

首先使用 sample(frac=1):

完全洗牌数据帧
# np.random.seed(0) # for reproducibility
df2 = df.sample(frac=1).sort_values(by='label', ignore_index=True)

输出:

    value  label group
0  'pile'      0   'A'
1   'cat'      0   'B'
2   'all'      0   'A'
3  'dark'      0   'B'
4  'less'      1   'B'
5  'moon'      1   'B'
6   'pen'      1   'A'

然后按标签对值进行排序并确定每组的排序顺序:

idx = (df2.reset_index()                  # save index as column
       .sort_values(by='value')           # sort values
       .groupby(['label', 'group'])['index']  # reorder the index per value
       .transform(sorted).sort_values()       # using sorted
       .index
      )
# Int64Index([2, 1, 0, 3, 4, 5, 6], dtype='int64')

最后用这个重新索引你的 df2:

df2.loc[idx]

输出:

    value  label group
2   'all'      0   'A'
1   'cat'      0   'B'
0  'pile'      0   'A'
3  'dark'      0   'B'
4  'less'      1   'B'
5  'moon'      1   'B'
6   'pen'      1   'A'