sklearn纵向(面板)研究按受试者划分训练/测试集相关问题
问题解答
1. test_size参数解读与受试者观测数不均的处理
test_size在按受试者维度划分时有两种可选择的解读逻辑,根据你的建模目标选择即可:
- 按受试者个数比例解读:test_size=0.1代表将10%的独立受试者整体划入测试集,不考虑单个受试者的观测记录数量,适用于模型目标是泛化到全新受试者的场景。
- 按总观测记录比例解读:test_size=0.1代表测试集的总记录数占全数据集总记录数的10%,需要动态调整抽取的受试者数量直到满足比例要求,适用于更看重整体预测精度指标的场景。
如果不同受试者的观测记录数量差异较大:
- 优先选择按受试者个数比例划分,避免测试集被少数高观测数的受试者占据,导致评估结果偏向这类群体。
- 若必须按记录数比例划分,可采用加权抽样逻辑,给观测数少的受试者更高的抽取权重,保证测试集的受试者覆盖度。
2. 基于其他属性的分层划分实现
分层划分的核心目标是保证训练集、测试集的目标属性分布与原数据集一致,实现逻辑如下:
- 先将所有独立受试者按照你要分层的属性做分组(如果是连续属性可以先做分箱处理转为离散类别)。
- 在每个属性分组内部,单独按照test_size比例抽取对应数量的受试者划入测试集。
这样就能保证分层属性在训练、测试集的分布和原集完全对齐,比如你要按示例数据中的group(患者/对照组)分层,就能保证训练测试集中的患者占比和原集一致。
3. 纵向数据最优划分方式与可用工具
最优划分原则
纵向数据划分最核心的要求是避免数据泄露,根据你的建模目标分为两种最优方案:
- 如果模型最终用于给全新受试者做预测:必须保证同一受试者的所有记录只会出现在训练集或测试集其中一个,不能拆分,否则同一个体的时序相关性会导致评估结果虚高。如果同时有时序预测需求,再在训练集/测试集内部对每个受试者的记录单独按时间截断,避免使用未来数据训练。
- 如果模型最终用于给已有历史数据的老受试者预测未来行为:优先按时间点全局截断,所有受试者该时间点之前的记录划入训练集,之后的划入测试集,不需要按受试者拆分。
可用工具
scikit-learn自带的拆分工具已经完全覆盖需求,不需要引入额外第三方库:
GroupShuffleSplit:专门用于按组(此处组就是受试者ID)拆分数据集,自动保证组不会同时出现在训练测试集,直接满足你的基础需求。StratifiedGroupShuffleSplit:在GroupShuffleSplit的基础上支持分层划分,可同时满足按受试者拆分+按指定属性分层的要求。
可直接使用的实现代码
from sklearn.model_selection import GroupShuffleSplit, StratifiedGroupShuffleSplit # 基础版:仅按受试者拆分 def train_test_split_by_subject(df, subject_col='subject', test_size=0.1, random_state=42): gss = GroupShuffleSplit(n_splits=1, test_size=test_size, random_state=random_state) train_idx, test_idx = next(gss.split(df, groups=df[subject_col])) return df.iloc[train_idx], df.iloc[test_idx] # 调用示例 train_df, test_df = train_test_split_by_subject(longitudinal_study, test_size=0.1) # 验证受试者无重叠 assert len(set(train_df.subject).intersection(set(test_df.subject))) == 0 # 进阶版:按受试者拆分+按指定属性分层(示例按group列分层) def stratified_train_test_split_by_subject(df, subject_col='subject', stratify_col='group', test_size=0.1, random_state=42): sgss = StratifiedGroupShuffleSplit(n_splits=1, test_size=test_size, random_state=random_state) train_idx, test_idx = next(sgss.split(df, y=df[stratify_col], groups=df[subject_col])) return df.iloc[train_idx], df.iloc[test_idx]
内容的提问来源于stack exchange,提问作者Ufos
相关产品推荐
相关产品推荐

