将数据框拆分为唯一值块

Split dataframe into chunks of unique values

我有两个组(AB)的数据框,在这些组中,有 6 个子组(abcdef)。以下示例数据:

index   group    subgroup    value
0       A        a           1
1       A        b           1
2       A        c           1
3       A        d           1
4       A        e           1
5       A        f           1
6       B        a           1
7       B        b           1
8       B        c           1
9       B        d           1
10      B        e           1
11      B        f           1
...     ...      ...         ...

虽然我在这里只列出了 12 行,但实际数据集中有 300 行。我正在尝试将数据框随机拆分为 50 批 6 个值。重要的是,每个批次应该有每个子组 1 个,并且组的分布大致相等。

期望的输出:

index   group    subgroup    batch
0       A        a           1
1       A        b           1
2       A        c           1
3       B        d           1
4       B        e           1
5       B        f           1
6       A        d           2
7       A        e           2
8       A        f           2
9       B        a           2
10      B        b           2
11      B        c           2
...     ...      ...         ...

在我的数据集中,有 150 个 A 和 150 个 B,但不幸的是,没有相同数量的子组(例如,我有 25 as, 27 bs, 23 cs, etc. contained with A) 因此,如果批次 1-48 包含唯一的子组(即每个 a-f 中的 1 个),那将是首选),但是第 49 批和第 50 批的剩菜没有平均分配——事后我需要以某种方式手动随机化这些!最重要的是一个batch中每个子组都有一个,但不太重要的是一个batch中恰好有3个As和3个Bs。谢谢!

此解决方案从批次 1-48 的每个 subgroup 中准确挑选出一个元素。第 49-50 批是随机挑选的。来自 As 和 Bs 的数字不被考虑。

逻辑

  1. 通过对每个子组的索引执行随机排列来打乱每个子组
  2. 每个子组的第一个元素构成第一批,第二个元素构成第二批,依此类推

代码

# data
df = pd.DataFrame(
    {"group": ["A"]*150 + ["B"]*150,
     "subgroup": ["a", "b", "c", "d", "e", "f"] * 48 + ["a", "b"]*3 + ["c", "d"]*2 + ["e", "f"],
     "value": range(0, 3000, 10)}
)
df.index.name = "index"

# length parameters
subgroups = df["subgroup"].unique()  # np.array(["a", "b", "c", "d", "e", "f"])
l = len(subgroups)  # 6
m = 48  # number of regular batches
n = int(len(df) / l)  # 300/6=50

# storage
arr = np.zeros((m, l))  # for batch 1-48
ls_rest = []  # for batch 49-50

# shuffle
for i, subgroup in enumerate(subgroups):
    perm = np.random.permutation(df.index[df["subgroup"] == subgroup])
    arr[:, i] = perm[:m]  # for regular batches
    ls_rest += list(perm[m:])  # for the rest

# assign batch 1-48
df["batch"] = 0
for i in range(m):
    df.loc[arr[i,:], "batch"] = i+1

# assign batch 49-50
for i in range(n-m):
    df.loc[ls_rest[i*l:(i+1)*l], "batch"] = m+1+i

# sorting is omitted

输出

常规批次

可以看到每个批次中每个子组确实有一个元素。

print(df.sort_values(["batch", "subgroup"]).head(13))

      group subgroup  value  batch
index                             
48        A        a    480      1
13        A        b    130      1
134       A        c   1340      1
171       B        d   1710      1
262       B        e   2620      1
5         A        f     50      1
240       B        a   2400      2
291       B        b   2910      2
152       B        c   1520      2
93        A        d    930      2
136       A        e   1360      2
59        A        f    590      2
24        A        a    240      3

其余

print(df.sort_values(["batch", "subgroup"]).tail(13))

      group subgroup  value  batch
index                             
29        A        f    290     48
120       A        a   1200     49
222       B        a   2220     49
276       B        a   2760     49
61        A        b    610     49
133       A        b   1330     49
289       B        b   2890     49
98        A        c    980     50
206       B        c   2060     50
45        A        d    450     50
295       B        d   2950     50
166       B        e   1660     50
233       B        f   2330     50