
本文介绍一种结合家族约束与神经质(neofac_n)分布均衡性的分层样本划分方法,利用 stratifiedkfold 实现“同家族不共存于同一子集 + 各子集神经质分布近似一致”的双重目标。
本文介绍一种结合家族约束与神经质(neofac_n)分布均衡性的分层样本划分方法,利用 stratifiedkfold 实现“同家族不共存于同一子集 + 各子集神经质分布近似一致”的双重目标。
在行为心理学或遗传学研究中,常需将被试划分为两个互斥子样本用于建模、验证或交叉分析,但必须同时满足两个关键约束:
- 家族独立性:来自同一家庭(Family_ID 相同)的被试不能出现在同一子样本中(避免血缘相关性引入偏差);
- 分布一致性:两组在关键协变量(如 Neuroticism 分数 NEOFAC_N)上的分布应尽可能相似,以保障组间可比性。
直接使用 train_test_split 或简单随机划分无法兼顾二者;而 StratifiedKFold 虽支持按标签分层,但其默认逻辑不感知“家族结构”。因此,我们需要对策略进行适配:将每个家族视为一个不可分割的“超样本单元”,并在该单元内分配成员至不同子集,同时依据其神经质分数进行分层决策。
核心思路如下:
- 首先按
Family_ID分组,确保每个家族最多含 2 名被试(如题设); - 对每个家族,将其成员视为一个候选对(单人家庭则仅一人),我们需为该家族指定“哪位成员进入子集 A,哪位进入子集 B”;
- 将所有家族的神经质均值(或代表性分数)作为分层依据,构造虚拟“家族级标签”,再用
StratifiedKFold(n_splits=2)划分家族 → 从而间接控制两组中高/中/低神经质家族的均衡分布; - 最后,根据家族分配结果,将对应成员分别填入
sample_1和sample_2。
以下是完整可运行的实现代码:
from sklearn.model_selection import StratifiedKFold
import numpy as np
import pandas as pd
# 假设 df_behavioral 已就绪(如题中生成逻辑)
# 步骤 1:按 Family_ID 分组,提取每家族的 Neuroticism 代表值(取均值;单人则直接取)
family_groups = df_behavioral.groupby('Family_ID')
family_neuroticism = family_groups['NEOFAC_N'].mean().round().astype(int) # 离散化便于分层
# 步骤 2:为每个 Family_ID 构造分层标签(此处用四分位分组增强稳定性)
neuro_q4 = pd.qcut(family_neuroticism, q=4, labels=False, duplicates='drop').fillna(0).astype(int)
family_labels = neuro_q4.reindex(family_neuroticism.index)
# 步骤 3:使用 StratifiedKFold 将 Family_ID 划分为两组(stratified by family-level neuroticism)
skf = StratifiedKFold(n_splits=2, shuffle=True, random_state=42)
family_ids = family_neuroticism.index.values
_, family_test_idx = next(skf.split(family_ids, family_labels))
family_set_a = set(family_ids[family_test_idx])
family_set_b = set(family_ids) - family_set_a
# 步骤 4:按家族分配被试 —— 每个家族中,成员交替分配至 sample_1 / sample_2
sample_1_idx, sample_2_idx = [], []
for family_id, group in family_groups:
indices = group.index.tolist()
if len(indices) == 1:
# 单人家庭:按家族分组归属决定去向
if family_id in family_set_a:
sample_1_idx.append(indices[0])
else:
sample_2_idx.append(indices[0])
else: # 双人家庭:强制分拆
if family_id in family_set_a:
sample_1_idx.append(indices[0])
sample_2_idx.append(indices[1])
else:
sample_1_idx.append(indices[1])
sample_2_idx.append(indices[0])
# 构建最终子样本
sample_1 = df_behavioral.loc[sample_1_idx].reset_index(drop=True)
sample_2 = df_behavioral.loc[sample_2_idx].reset_index(drop=True)
print(f"Sample 1 size: {len(sample_1)}, Sample 2 size: {len(sample_2)}")
print("Neuroticism distribution check:")
print(f"Sample 1 — Mean: {sample_1['NEOFAC_N'].mean():.2f}, Std: {sample_1['NEOFAC_N'].std():.2f}")
print(f"Sample 2 — Mean: {sample_2['NEOFAC_N'].mean():.2f}, Std: {sample_2['NEOFAC_N'].std():.2f}")✅ 关键优势说明:
- 家族完整性保障:
groupby('Family_ID')确保无跨家族混入; - 分层有效性:通过家族级神经质聚合 +
qcut分箱 +StratifiedKFold,显著提升两组在原始 NEOFAC_N 上的分布重叠度(可用 KS 检验或直方图验证); - 可扩展性:若某家族含 >2 成员,可扩展为“轮询分配”或“贪心匹配”,本例双人假设已覆盖多数家系设计。
⚠️ 注意事项:
- 若家族规模高度不均(如存在 5+ 人家庭),需改用更复杂的图着色或整数规划策略;
-
StratifiedKFold要求每类标签至少有 2 个样本,故分箱时使用duplicates='drop'并填充缺失; - 建议后续用
scipy.stats.ks_2samp对两组 NEOFAC_N 进行分布等价性检验,确保 stratification 成效。
该方案在保持统计严谨性的同时,完全兼容 pandas 生态,无需额外依赖图算法库,是处理小规模家系数据分层划分的高效实践路径。

















