You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为每个ID抽取1行样本且保证组间样本数量均等?

问题:按ID抽样并保证组间样本数量均等

现有一数据集,每个ID对应多行数据,共约5000个ID,每个ID的行数为1至22行,每行分属不同group。需求为:为每个ID抽取1行样本,且最终样本中各组的样本数量均等。

示例数据集

简化示例含8个ID,每个ID对应1至4行数据:

id group
1  a
1  b
1  c
1  d
2  a
2  b
3  a
3  b
3  c
3  d
4  a
4  b
4  d
5  a
5  b
5  c
5  d
6  a
6  d
7  a
7  b
7  d
8  a
8  b
8  c
8  d

期望结果

因共8个ID、4个group,最终样本需每个group对应2个ID(如20个ID、4个group则每个group对应5个ID),且所有ID仅出现一次。示例输出如下:

id  group
1   d
2   b
3   a
4   d
5   c
6   a
7   b
8   c

Python解决方案

直接用权重抽样无法保证严格的组间均衡,这里通过贪心分配+随机调整实现:

步骤1:构建示例数据

import pandas as pd
import numpy as np

d = {'id': [1,1,1,1,
            2,2,
            3,3,3,3,
            4,4,4,
            5,5,5,5,
            6,6,
            7,7,7,
            8,8,8,8], 
     'group': ['a','b','c','d',
            'a','b',
            'a','b','c','d',
            'a','b','d',
            'a','b','c','d',
            'a','d',
            'a','b','d',
            'a','b','c','d']}
df = pd.DataFrame(data=d)

步骤2:实现均衡抽样

# 计算每个group需要分配的ID数量
total_ids = df['id'].nunique()
groups = df['group'].unique()
target_per_group = total_ids // len(groups)

# 按ID分组,记录每个ID可选的group
id_groups = df.groupby('id')['group'].apply(list).reset_index(name='available_groups')

# 初始化结果字典和组计数
result = {}
group_count = {g:0 for g in groups}

# 先处理只有单个可选group的ID,避免无法分配
for idx, row in id_groups.iterrows():
    if len(row['available_groups']) == 1:
        g = row['available_groups'][0]
        result[row['id']] = g
        group_count[g] += 1

# 处理剩余ID,优先分配给未达目标的组
remaining_ids = id_groups[~id_groups['id'].isin(result.keys())]
for idx, row in remaining_ids.iterrows():
    # 筛选当前ID可选且未达目标的group
    eligible_groups = [g for g in row['available_groups'] if group_count[g] < target_per_group]
    # 随机选一个符合条件的group
    selected_g = np.random.choice(eligible_groups)
    result[row['id']] = selected_g
    group_count[selected_g] += 1

# 转换为DataFrame并排序
final_df = pd.DataFrame.from_dict(result, orient='index', columns=['group']).reset_index().rename(columns={'index':'id'})
final_df = final_df.sort_values('id').reset_index(drop=True)
print(final_df)

说明

  • 先锁定只能选单个group的ID,避免这类ID无法分配导致组计数失衡
  • 剩余ID优先分配给未达目标数量的组,保证最终各组样本数严格均等
  • 随机选择符合条件的group,保留抽样随机性

R语言解决方案

采用相同的贪心分配逻辑:

步骤1:构建示例数据

library(dplyr)
library(tibble)

d <- list(
  id = c(1,1,1,1,
         2,2,
         3,3,3,3,
         4,4,4,
         5,5,5,5,
         6,6,
         7,7,7,
         8,8,8,8),
  group = c('a','b','c','d',
            'a','b',
            'a','b','c','d',
            'a','b','d',
            'a','b','c','d',
            'a','d',
            'a','b','d',
            'a','b','c','d')
)
df <- as_tibble(d)

步骤2:实现均衡抽样

# 计算每个group的目标数量
total_ids <- length(unique(df$id))
groups <- unique(df$group)
target_per_group <- total_ids %/% length(groups)

# 按ID整理可选group
id_groups <- df %>%
  group_by(id) %>%
  summarise(available_groups = list(group), .groups = 'drop')

# 初始化结果和计数
result <- list()
group_count <- setNames(rep(0, length(groups)), groups)

# 处理只有单个可选group的ID
single_group_ids <- id_groups %>% filter(lengths(available_groups) == 1)
for (i in 1:nrow(single_group_ids)) {
  id_val <- single_group_ids$id[i]
  g_val <- single_group_ids$available_groups[[i]][1]
  result[[as.character(id_val)]] <- g_val
  group_count[g_val] <- group_count[g_val] + 1
}

# 处理剩余ID
remaining_ids <- id_groups %>% filter(!id %in% as.numeric(names(result)))
for (i in 1:nrow(remaining_ids)) {
  id_val <- remaining_ids$id[i]
  eligible_groups <- remaining_ids$available_groups[[i]][group_count[remaining_ids$available_groups[[i]]] < target_per_group]
  selected_g <- sample(eligible_groups, 1)
  result[[as.character(id_val)]] <- selected_g
  group_count[selected_g] <- group_count[selected_g] + 1
}

# 转换为数据框并排序
final_df <- enframe(result, name = 'id', value = 'group') %>%
  mutate(id = as.numeric(id)) %>%
  arrange(id)
print(final_df)

说明

  • 逻辑与Python版本一致,先处理无选择空间的ID,再对剩余ID做均衡分配
  • 使用sample()实现随机选择,保证抽样的随机性

内容的提问来源于stack exchange,提问作者natnicha teja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 00:15:49