梳理核心思路:
将event和step的预测问题,转化为新建标签asleep(表示是否在睡觉)的分类问题。
转化方式:
event列中:1-onset, 2-wakeup 表示入睡和醒来的时间点。 这里为了更好地展示event标签,选择了train_event的图片。
首先,以每个人的id_series为单位(每人只有一个id,每个id有很多睡眠数据,参考id_map),将入睡和醒来的时间点进行匹配组合后,就得到一组组睡觉的事件。将睡觉事件规定为:入睡在前,醒来在后。
对匹配组合的解释: 训练数据中event列可能存在以1-1-2这个顺序出现的情况,其代表的意思是 ,入睡-入睡-醒来。但根据常识,睡觉只有一次入睡,说明这个数据是有问题的。1-1-2这种情况导致我们的转化失败。所以在转化之前,先进行一次匹配,让1与2一一对应。
ps:睡觉事件与night基本对齐。在一个id的series中,step和timestamp基本对齐。
做法:
在每组睡觉事件中,onset和wakeup之间的时间 认为处于在睡眠状态。反之则处于清醒状态。
新建一个标签asleep,睡眠状态(onset-1 和wakeup-2之间)的标签为1,非睡眠状态为0。
到这里,就把问题转化为了一个二分类问题(是否处于睡眠状态)。// event本身任务的预测没有精准定位,也就是不确定为回归还是分类问题,现在确定为二分类。
具体代码实现:
y = y.vstack
(sample.with_columns
(sum([(onset <= pl.col('step')) & (pl.col('step') <= wakeup)
for onset, wakeup in zip(onsets, wakeups)])
.cast(pl.Boolean).alias('asleep')).select('asleep')
)
模型训练流程:
特征工程之后,做一个采样,原来是每5秒一个采样点。现在每隔5分钟采一次样。
根据转化方式,将采样后的数据加一个新label----asleep。将训练数据和新label一起放到随机森林算法中拟合。
rf_classifier.fit(X_train[feature_cols], y_train)
放入算法的训练数据,包括hour, 以及enmo,anglez的各种统计量。
测试:
将test_series对应数据列放入拟合的随机森林中,得到分类结果和对应的porb。
preds, probs = classifier.predict(X[feature_cols]), classifier.predict_proba(X[feature_cols])[:, 1]
将预测结果作为新的列/标签/特征 直接加入到测试数据集中后面,因为测试数据集的顺序没有改变,所以能实现:预测结果与原测试数据集的每条采样一一对应起来。
“解转化”:
预测结果为是否处于睡眠状态(asleep=1还是0)。
睡眠都是一段一段的,每段的开始标记为onset,结束标记为wakeup。
利用差分来寻找开始与结束。
pred_onsets = X.filter(X['prediction'].diff() > 0)['step'].to_list()
pred_wakeups = X.filter(X['prediction'].diff() < 0)['step'].to_list()
这里完成从asleep标签到event标签的映射。而在test_series数据中event标签又与step特征对应。
这样就可以得到入睡醒来的时间,也就是对应的submission需要的event,step。另外,submission需要的特征——series_id与step对应。需要的score = 分类时计算的prob取平均。
从而实现:
将是否睡觉的分类结果(asleep),映射到何时(step)入睡(onset),何时(step)醒来(wakeup)的预测问题的解上。