考虑用户行为的时间序列数据拆分:避泄漏且贴近70:30比例
问题分析与解决方案
你的核心问题在于当前代码对每个(user_id, time)组内部进行行级拆分,这不仅违反了“同一用户同一天记录不能拆分”的规则(直接导致数据泄漏),还因为不同组的大小差异,使得整体比例偏离70:30的目标。
正确的思路应该是:以**(user_id, time)为完整单位**,要么整个组进入训练集,要么整个组进入测试集;同时利用数据已按时间排序的特性,按时间顺序累加组的大小,找到最接近总数据量70%的分割点,确保训练集全为历史数据、测试集全为未来数据,彻底避免泄漏。
调整后的代码实现
1. 生成正确的分组掩码
import pandas as pd # 计算每个(user_id, time)组的行数 group_sizes = user_prod_data.groupby(['user_id', 'time']).size().reset_index(name='row_count') # 按时间顺序计算累计行数(数据已按时间排序,组顺序天然符合时间逻辑) group_sizes['cumulative_rows'] = group_sizes['row_count'].cumsum() total_rows = group_sizes['cumulative_rows'].iloc[-1] target_train_rows = total_rows * 0.7 # 找到最接近目标训练行数的分割点 split_index = group_sizes[group_sizes['cumulative_rows'] >= target_train_rows].index[0] # 比较前一个组和当前组的累计行数,选择更接近目标的分割位置 if split_index > 0: prev_cum = group_sizes['cumulative_rows'].iloc[split_index - 1] curr_cum = group_sizes['cumulative_rows'].iloc[split_index] if (target_train_rows - prev_cum) > (curr_cum - target_train_rows): split_index += 1 # 筛选出属于训练集的组 train_groups = group_sizes.iloc[:split_index][['user_id', 'time']] # 生成掩码:行所在组属于训练集则为True,否则为False mask = user_prod_data.merge(train_groups, on=['user_id', 'time'], how='left').notna().any(axis=1).tolist()
2. TensorFlow数据集拆分代码(无需修改)
你的TensorFlow拆分逻辑本身没问题,直接使用上面生成的mask即可:
import tensorflow as tf def splitting_data_tf(data_tf: tf.data.Dataset, use_time: bool, mask: list, random_seed: int = 18): """ Split TF data into train and tests :param data_tf: :param use_time: :param mask: :param random_seed: :return: """ tf.random.set_seed(random_seed) mask_tensor = tf.constant(mask, dtype=tf.bool) train = data_tf.enumerate().filter(lambda idx, _: mask_tensor[idx]) test = data_tf.enumerate().filter(lambda idx, _: ~mask_tensor[idx]) # Remove enumeration indices train = train.map(lambda idx, x: x) test = test.map(lambda idx, x: x) cached_train = train.apply(tf.data.experimental.dense_to_ragged_batch(batch_size=512)).cache() cached_test = test.apply(tf.data.experimental.dense_to_ragged_batch(batch_size=512)).cache() return cached_train, cached_test
关键说明
- 拆分逻辑完全基于时间顺序,保证训练集都是测试集的历史数据,符合“用历史行为预测未来”的需求;
- 每个(user_id, time)组作为整体拆分,彻底避免了同一用户同一天记录跨集的泄漏问题;
- 通过累计组大小找分割点,能让整体比例尽可能接近70:30,误差仅来自组的最小粒度(即无法拆分单个组)。
内容的提问来源于stack exchange,提问作者Yana
相关产品推荐
相关产品推荐

