求从DataFrame筛选各Bin列值唯一子集的高效算法
问题描述
现有一个约1万行的DataFrame,结构示例如下:
x y Bin1 Bin2 Bin3 153.0303 -27.17894 10 6 5 153.0303 -27.17916 8 7 8 153.0303 -27.17938 1 6 3 153.0300 -27.17960 10 1 8
其中Bin1、Bin2、Bin3列的取值均为1-10的整数。需求是从中选取一个随机子集,要求每个Bin列内的取值均唯一(即每个Bin列中1-10各出现一次)。当前采用重复随机选行直至符合条件的暴力方法,效率极低,需更高效的解决方案。
高效解决方案
方法1:逐列锚定+定向替换
核心思路是先锚定一个Bin列的唯一性,再通过定向替换逐步修正其他Bin列的重复值,避免暴力随机的无效尝试。
步骤说明
- 锚定首个Bin列:对
Bin1列的每个取值(1-10),从原DataFrame中随机抽取一行,组成初始的10行子集,此时Bin1列已完全唯一。 - 修正Bin2列重复:检查子集的
Bin2列,若存在重复值,针对每个重复值,从原DataFrame中选取满足「Bin2等于该重复值、且Bin1未在当前子集的Bin1中出现」的行,随机替换子集里的重复行,直到Bin2列无重复。 - 修正Bin3列重复:同理,针对
Bin3列的重复值,从原DataFrame中选取满足「Bin3等于该重复值、且Bin1和Bin2均未在当前子集对应列中出现」的行,随机替换重复行,直到Bin3列无重复。
代码实现
import pandas as pd import numpy as np # 假设原数据存储在df中 bin_columns = ['Bin1', 'Bin2', 'Bin3'] target_values = list(range(1, 11)) # 覆盖1-10所有值 # 1. 构建Bin1唯一的初始子集 subset = pd.DataFrame() for val in target_values: # 筛选Bin1等于当前值的所有行 candidate_rows = df[df['Bin1'] == val] # 随机选一行加入子集 subset = pd.concat([subset, candidate_rows.sample(n=1)], ignore_index=True) # 2. 修正Bin2列的重复值 while len(subset['Bin2'].unique()) != 10: # 找出所有重复的Bin2值 duplicate_bin2 = subset['Bin2'][subset['Bin2'].duplicated(keep=False)].unique() for dup_val in duplicate_bin2: # 随机选一个要替换的重复行 row_to_replace = subset[subset['Bin2'] == dup_val].sample(n=1).iloc[0] # 筛选符合条件的候选行:Bin2=dup_val且Bin1不在当前子集的Bin1中 valid_candidates = df[(df['Bin2'] == dup_val) & (~df['Bin1'].isin(subset['Bin1']))] if not valid_candidates.empty: new_row = valid_candidates.sample(n=1) # 替换行 subset = subset.drop(row_to_replace.name) subset = pd.concat([subset, new_row], ignore_index=True) # 3. 修正Bin3列的重复值 while len(subset['Bin3'].unique()) != 10: duplicate_bin3 = subset['Bin3'][subset['Bin3'].duplicated(keep=False)].unique() for dup_val in duplicate_bin3: row_to_replace = subset[subset['Bin3'] == dup_val].sample(n=1).iloc[0] # 筛选候选行:Bin3=dup_val且Bin1、Bin2均不在当前子集对应列中 valid_candidates = df[(df['Bin3'] == dup_val) & (~df['Bin1'].isin(subset['Bin1'])) & (~df['Bin2'].isin(subset['Bin2']))] if not valid_candidates.empty: new_row = valid_candidates.sample(n=1) subset = subset.drop(row_to_replace.name) subset = pd.concat([subset, new_row], ignore_index=True) # 输出最终符合要求的子集 print(subset)
方法2:排列组合匹配(适用于数据多样性高的场景)
如果原DataFrame中不同Bin值的组合足够丰富,可以将问题转化为寻找一组10行,使得三个Bin列分别是1-10的排列:
- 将原数据按
(Bin1, Bin2, Bin3)三元组分组,记录每个三元组对应的行(若有重复三元组则保留多行)。 - 生成Bin1、Bin2、Bin3的排列组合,筛选出存在于分组中的三元组集合,从中随机选取一组对应的行。
该方法的前提是原数据有足够多的三元组组合,否则可能找不到符合要求的子集,因此方法1的通用性更强。
内容的提问来源于stack exchange,提问作者Loleman
相关产品推荐
相关产品推荐

