Python 学习行为分析:学生作答数据的特征工程与模型训练
Python 学习行为分析学生作答数据的特征工程与模型训练一、平台存了 500 万条做题记录却不知道学生为什么会放弃一个在线教育平台每天产生几十万条学生作答记录——什么时候开始做题、答了哪道题、花了多少秒、是对是错、错了之后有没有看解析。这些数据孤零零地躺在 MySQL 里没人知道怎么用。产品经理问为什么这个学生连续 7 天登录第 8 天突然不来了数据分析师给不出答案——因为答案不在某一条记录里而在一系列行为模式中。学习行为分析的挑战是原始数据答题日志和业务洞察流失原因、薄弱点之间有巨大的语义鸿沟。这个鸿沟需要用特征工程来桥接——将原始的时间-事件序列转化为模型可理解的特征向量。二、学习行为特征工程的完整 Pipeline从原始日志到模型特征需要经历三个层次的抽象每提升一个抽象层特征的预测能力增强但可解释性下降。对于学习行为分析三层特征全保留让模型自己学权重——事实证明低层统计特征和高层时序特征对预测同等重要。三、Python 实现学习行为特征提取import pandas as pd import numpy as np from typing import Dict, List, Optional, Tuple from dataclasses import dataclass from sklearn.preprocessing import StandardScaler from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report import warnings warnings.filterwarnings(ignore) dataclass class AnswerLog: 单条作答记录 student_id: str question_id: str knowledge_id: str # 考察的知识点 is_correct: bool time_spent: int # 作答耗时秒 timestamp: pd.Timestamp viewed_solution: bool # 是否查看解析 skipped: bool # 是否跳过 class BehaviorFeatureExtractor: 学习行为特征提取器 def __init__(self, session_gap_minutes: int 30): self.session_gap pd.Timedelta(minutessession_gap_minutes) self.scaler StandardScaler() def _sessionize(self, logs: pd.DataFrame) - pd.DataFrame: 阶段1会话切分30分钟无操作视为新会话 logs logs.sort_values([student_id, timestamp]) logs[prev_timestamp] logs.groupby( student_id )[timestamp].shift(1) logs[gap] logs[timestamp] - logs[prev_timestamp] # 新会话标记 logs[is_new_session] ( logs[gap] self.session_gap ) | logs[prev_timestamp].isna() logs[session_id] logs.groupby( student_id )[is_new_session].cumsum() return logs def extract_basic_features( self, logs: pd.DataFrame ) - pd.DataFrame: 阶段2基础统计特征 features logs.groupby(student_id).agg( total_questions(question_id, count), correct_rate(is_correct, mean), avg_time_spent(time_spent, mean), std_time_spent(time_spent, std), max_time_spent(time_spent, max), view_solution_rate(viewed_solution, mean), skip_rate(skipped, mean), total_sessions(session_id, nunique), unique_knowledge(knowledge_id, nunique), ).reset_index() # 填空缺失值 features[std_time_spent] features[ std_time_spent ].fillna(0) return features def extract_temporal_features( self, logs: pd.DataFrame, window_days: List[int] [1, 3, 7, 14] ) - pd.DataFrame: 阶段3时序特征 —— 滚动窗口统计 logs logs.copy() logs[date] logs[timestamp].dt.date now logs[timestamp].max() all_features [] for student_id, group in logs.groupby(student_id): row {student_id: student_id} for window in window_days: cutoff now - pd.Timedelta(dayswindow) recent group[group[timestamp] cutoff] row[fquestions_{window}d] len(recent) row[fcorrect_rate_{window}d] ( recent[is_correct].mean() if len(recent) 0 else 0 ) row[favg_time_{window}d] ( recent[time_spent].mean() if len(recent) 0 else 0 ) row[fsessions_{window}d] ( recent[session_id].nunique() if len(recent) 0 else 0 ) # 趋势特征最近3天 vs 前3-7天 recent_3d group[ group[timestamp] now - pd.Timedelta(days3) ] older_3d group[ (group[timestamp] now - pd.Timedelta(days7)) (group[timestamp] now - pd.Timedelta(days3)) ] if len(recent_3d) 0 and len(older_3d) 0: row[correct_rate_trend] ( recent_3d[is_correct].mean() - older_3d[is_correct].mean() ) row[time_trend] ( recent_3d[time_spent].mean() - older_3d[time_spent].mean() ) else: row[correct_rate_trend] 0 row[time_trend] 0 # 连续错误/正确模式 last_10 group.tail(10)[is_correct].values row[consecutive_errors] self._count_consecutive( last_10, False ) row[consecutive_corrects] self._count_consecutive( last_10, True ) all_features.append(row) return pd.DataFrame(all_features) staticmethod def _count_consecutive(series: np.ndarray, target: bool) - int: 计算最近连续的 target 数量 count 0 for val in reversed(series): if val target: count 1 else: break return count def prepare_training_data( self, logs: pd.DataFrame, labels: pd.DataFrame ) - Tuple[np.ndarray, np.ndarray, List[str]]: 准备训练数据 logs self._sessionize(logs) basic_feats self.extract_basic_features(logs) temporal_feats self.extract_temporal_features(logs) # 特征融合 merged basic_feats.merge(temporal_feats, onstudent_id) merged merged.merge(labels, onstudent_id) feature_cols [c for c in merged.columns if c not in [student_id, label]] X merged[feature_cols].fillna(0).values X self.scaler.fit_transform(X) y merged[label].values return X, y, feature_cols def train_risk_model( self, X: np.ndarray, y: np.ndarray, feature_names: List[str] ) - Tuple[RandomForestClassifier, Dict]: 训练流失风险预测模型 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) model RandomForestClassifier( n_estimators100, max_depth8, min_samples_leaf10, random_state42, n_jobs-1, ) model.fit(X_train, y_train) y_pred model.predict(X_test) report classification_report( y_test, y_pred, output_dictTrue ) # 输出 Top-5 重要特征 importances sorted( zip(feature_names, model.feature_importances_), keylambda x: x[1], reverseTrue ) print(Top-5 重要特征:) for name, imp in importances[:5]: print(f {name}: {imp:.4f}) return model, report # 示例使用 def example_usage(): 演示完整的特征工程 模型训练流程 # 模拟数据 np.random.seed(42) n_students 1000 n_logs 50000 students [fS{i:04d} for i in range(n_students)] logs_data [] for _ in range(n_logs): sid np.random.choice(students) logs_data.append({ student_id: sid, question_id: fQ{np.random.randint(1, 5001)}, knowledge_id: fK{np.random.randint(1, 101)}, is_correct: np.random.random() 0.35, time_spent: int(np.random.exponential(60)), timestamp: pd.Timestamp(2026-06-01) pd.Timedelta( daysnp.random.randint(0, 30) ), viewed_solution: np.random.random() 0.6, skipped: np.random.random() 0.85, }) logs_df pd.DataFrame(logs_data) labels_df pd.DataFrame({ student_id: students, label: np.random.choice([0, 1], n_students, p[0.8, 0.2]), }) extractor BehaviorFeatureExtractor() X, y, feature_names extractor.prepare_training_data( logs_df, labels_df ) model, report extractor.train_risk_model(X, y, feature_names) print(f\n模型准确率: {report[accuracy]:.3f}) return model, extractor四、边界分析与 Trade-offs特征爆炸问题滑动窗口 × 统计指标 特征数爆炸7 个窗口 × 8 个指标 56 个特征。但实际上大部分特征之间存在高相关性如questions_1d和questions_3d需要通过特征选择卡方检验或基于树的特征重要性降维。经验是保留 Top-15 个特征模型效果与全特征相当。冷启动学生的处理新学生没有时序特征所有滚动窗口都是 0模型会把他们统一预测为低风险——因为缺少活动信号。应该在输出预测时标注数据不足置信度低同时基于静态人口学特征年级、注册渠道做辅助预测。样本不均衡的典型问题流失学生通常只占 5%-15%模型会倾向于预测不流失。用 SMOTE 过采样或调整类别权重class_weightbalanced可以有效缓解。但要注意SMOTE 生成的人工样本会降低模型在真实数据上的校准度。特征因果 vs 相关模型可能发现跳过题目多 流失风险高但这是相关性而不是因果——学生在决定放弃之前行为已经发生变化。真正的 actionable insight 是当检测到连续跳过 3 题信号时推送干预如降低难度或推荐视频而不是等到学生已经流失才触发挽留。五、总结学习行为分析的核心是从事件流到特征向量的转化过程。分三个阶段提取——基础统计快照、时序窗口趋势、序列模式状态变化——是实践证明有效的特征工程范式。代码上要注意三点会话切分的阈值调优30 分钟是通用经验但不同学段可能需要调整、特征空值处理新学生缺少的历史特征用 0 填充但要标记 low_confidence、以及模型输出的校准不能只给概率要附带置信度和关键特征贡献。

相关新闻

MSPM0微控制器SPI模块配置与DMA高效数据传输实战指南

MSPM0微控制器SPI模块配置与DMA高效数据传输实战指南

1. 项目概述与SPI核心价值 如果你在嵌入式开发中打过交道,尤其是用过像MSPM0这样的微控制器,那么对SPI(Serial Peripheral Interface)这个名字一定不会陌生。它就像电路板上的“高速公路”,负责在微控制器和各种传感器…

2026/10/1 18:57:58 阅读更多 →
找不到创新点怎么办?实用方法帮你快速梳理思路高效挖掘核心创新方向

找不到创新点怎么办?实用方法帮你快速梳理思路高效挖掘核心创新方向

对于科研人员来说,文献工作往往伴随着两个极端的痛苦:一是搜索时的大海捞针,为了几篇核心文献,不得不花费数小时翻阅成百上千条琐碎的摘要;二是阅读时的翻译折磨,在专业术语和复杂的 LaTeX 公式间反复推敲&…

2026/9/28 21:45:04 阅读更多 →
Clawdbot开源项目:自主智能体开发实战指南

Clawdbot开源项目:自主智能体开发实战指南

1. 项目概述:Clawdbot与自主智能体开发全景在2026年的技术圈,一个名为Clawdbot的开源项目突然引爆了开发者社区。这个项目最颠覆性的创新在于——它让AI从被动应答的"工具"变成了主动介入生活的"伙伴"。与传统的聊天机器人不同&…

2026/9/29 5:00:12 阅读更多 →

最新新闻

β-环糊精组合修饰全解析:PEG链连接FITC、Biotin、DBCO的设计与应用

β-环糊精组合修饰全解析:PEG链连接FITC、Biotin、DBCO的设计与应用

拿到“PEG-荧光素修饰β-环糊精,β-CD-PEG-FITC,β-CD-PEG-Biotin,DBCO-PEG修饰β-环糊精,β-CD-DBCO-PEG”这一串产品名时,多数人的第一反应是“这到底是个东西还是好几个东西”。其实这是一类典型的组合修饰型环糊精…

2026/10/1 18:57:55 阅读更多 →
C++工厂方法模式实战:从日志系统到调试排查全解

C++工厂方法模式实战:从日志系统到调试排查全解

聊到C里的创建型设计模式,工厂方法模式(Factory Method)是我在工程实践中用得最频繁,也最容易被低估的一个。早些年我在面试候选人的时候,几乎每次都会问到一个场景:一段代码里有七八个if分支去创建不同类型…

2026/10/1 18:57:55 阅读更多 →
Madeira 整合 Wine、FEX-Emu 与 DXMT:ARM 平台运行 x86-64 Windows 程序实战

Madeira 整合 Wine、FEX-Emu 与 DXMT:ARM 平台运行 x86-64 Windows 程序实战

1. 从“Madeira”这个名字说起:它到底想解决什么问题 第一次看到“Madeira”这个项目名,很多人会以为是某个葡萄酒产区的工具,或者干脆是个地名相关的应用。但结合热搜词里的 Wine、FEX-Emu、DXMT、x86-64 这几个关键词,方向就清晰…

2026/10/1 18:57:55 阅读更多 →
声音克隆 + 文本转语音:narrator-ai-cli-skill 独立任务实战,AI 解说大师配音两大技能

声音克隆 + 文本转语音:narrator-ai-cli-skill 独立任务实战,AI 解说大师配音两大技能

声音克隆 文本转语音:narrator-ai-cli-skill 独立任务实战,AI 解说大师配音两大技能 【免费下载链接】narrator-ai-cli-skill AI 解说大师 — Agent skill;封装 narrator-ai-cli 供 Claude/Codex 等工具调用 项目地址: https://gitcode.co…

2026/10/1 18:57:55 阅读更多 →
Windows英文系统中文显示异常的注册表级修复方案

Windows英文系统中文显示异常的注册表级修复方案

1. 问题本质与真实场景还原这个问题我从2015年就开始反复处理,不是什么新毛病,但每次Windows大版本更新(比如1809、20H2、22H2)它就准时回来“打卡”。核心现象非常典型:你把系统语言从中文改成英文(比如为…

2026/10/1 18:57:55 阅读更多 →
C++桥接模式四种实现:从指针到std::variant的工程实践

C++桥接模式四种实现:从指针到std::variant的工程实践

从"类和类纠缠"到"接口和实现各过各的"做C开发这么多年,我一直觉得设计模式这事儿最怕"背名字"。桥接模式(Bridge Pattern)就是个典型——很多人能把定义背出来:"将抽象部分与实现部分分离&am…

2026/10/1 18:56:55 阅读更多 →

日新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 1:01:17 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/30 13:14:22 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/30 18:13:06 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/9/30 13:14:49 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 0:00:30 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/1 1:01:17 阅读更多 →