Python数据分析实战:学员学习进度统计与可视化全流程

Python数据分析实战:学员学习进度统计与可视化全流程 在实际教学管理、在线学习平台或教育数据分析项目中我们经常需要处理学员的学习进度数据。一个典型的需求是统计特定学员例如一名大学生在连续三天内的学习情况并生成结构化的报告。这不仅仅是简单的数据求和而是涉及数据采集、清洗、聚合、分析以及可视化呈现的完整数据处理流程。对于开发者、数据分析师或教育科技从业者而言掌握这套流程能有效支撑学情预警、个性化推荐和教学效果评估等核心功能。本文将以“统计一名大学生学员三天的学习情况”为具体目标带你从零构建一个可运行的数据处理示例。我们将模拟一个常见的学习记录数据源使用 Python 进行数据处理利用 Pandas 进行聚合分析并通过 Matplotlib 生成直观的图表。整个过程将覆盖环境准备、数据模拟、核心逻辑实现、结果验证以及生产环境中的注意事项。无论你是需要快速实现类似功能的工程师还是希望理解数据处理全貌的初学者都能按照本文的步骤完成实践。1. 理解需求与设计数据模型在开始编码之前必须明确“学习情况”具体包含哪些维度。一个完整的学情分析通常不会只记录“是否学习”而是包含多个可量化的指标。1.1 定义核心数据指标对于一名学员三天的学习情况我们通常关注以下几类数据学习时长每日总学习时间分钟或小时是衡量投入度的基础指标。学习内容每日学习的课程或知识点列表反映学习广度。任务完成度每日完成的练习题、作业或测验的数量及正确率反映学习深度和质量。活跃时段每日开始学习和结束学习的时间可用于分析学习习惯。持续性指标如是否连续学习、每日学习时间波动等。在本次实践中我们将聚焦于学习时长、学习课程和完成任务数这三个最核心的指标。1.2 设计原始数据表结构原始学习记录数据通常来自数据库表或日志文件。我们设计一张模拟的learning_records表其结构如下字段名数据类型说明student_idVARCHAR学员唯一标识student_nameVARCHAR学员姓名dateDATE学习发生的日期start_timeDATETIME单次学习开始时间end_timeDATETIME单次学习结束时间course_nameVARCHAR学习的课程名称task_completedINT本次学习期间完成的任务数如练习题一条记录代表学员一次连续的学习会话。学员一天内可能有多次学习会话。1.3 设计目标汇总报表结构我们的目标是生成一个针对特定学员、连续三天的汇总报告。报告的数据结构可以设计如下{ student_id: S1001, student_name: 张三, report_date_range: [2023-10-01, 2023-10-03], daily_stats: [ { date: 2023-10-01, total_study_minutes: 125, courses_covered: [Python基础, 数据结构], total_tasks_completed: 15, study_sessions: 2 }, { date: 2023-10-02, total_study_minutes: 90, courses_covered: [数据结构, 算法入门], total_tasks_completed: 10, study_sessions: 1 }, { date: 2023-10-03, total_study_minutes: 180, courses_covered: [算法入门, 数据库概论], total_tasks_completed: 22, study_sessions: 3 } ], summary: { total_study_minutes: 395, total_tasks_completed: 47, unique_courses_count: 4, avg_study_minutes_per_day: 131.67 } }这个结构清晰地展示了每日明细和总体摘要非常适合前端展示或进一步分析。2. 环境准备与依赖配置我们将使用 Python 作为主要实现语言因为它拥有丰富的数据处理库和简洁的语法。项目结构将保持清晰便于扩展。2.1 创建项目目录与虚拟环境首先创建一个独立的项目目录并设置 Python 虚拟环境以隔离依赖。# 创建项目目录 mkdir student_study_analysis cd student_study_analysis # 创建虚拟环境 (Python 3.6) python -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # Linux/Mac: source venv/bin/activate激活后命令行提示符前会出现(venv)标识。2.2 安装必要的 Python 库我们将主要依赖pandas进行数据处理matplotlib进行绘图numpy辅助生成模拟数据。使用pip安装它们。(venv) pip install pandas matplotlib numpy安装完成后可以通过以下命令验证(venv) python -c import pandas as pd; print(fPandas version: {pd.__version__}) (venv) python -c import matplotlib; print(fMatplotlib version: {matplotlib.__version__})2.3 项目文件结构规划一个清晰的文件结构有助于代码管理。建议按如下方式组织student_study_analysis/ ├── venv/ # 虚拟环境目录由上一步创建 ├── data/ │ ├── raw/ # 存放原始或模拟的原始数据 │ └── processed/ # 存放处理后的结果 ├── src/ │ ├── __init__.py │ ├── data_generator.py # 模拟数据生成脚本 │ ├── data_processor.py # 核心数据处理逻辑 │ └── report_generator.py # 报告生成与可视化脚本 ├── config.py # 配置文件如数据库连接、日期范围 ├── main.py # 主程序入口 └── requirements.txt # 项目依赖列表你可以先创建src和data目录。(venv) mkdir -p src data/raw data/processed (venv) touch src/__init__.py src/data_generator.py src/data_processor.py src/report_generator.py (venv) touch config.py main.py3. 模拟学习记录数据在实际项目中数据可能来自 MySQL、PostgreSQL 或日志文件。为了演示我们编写一个脚本生成符合learning_records表结构的模拟数据。3.1 编写数据生成脚本编辑src/data_generator.py文件import pandas as pd import numpy as np from datetime import datetime, timedelta import os def generate_learning_records(student_idS1001, student_name张三, days3, base_dateNone): 生成指定学员多日的模拟学习记录。 参数: student_id: 学员ID student_name: 学员姓名 days: 生成数据的天数 base_date: 基准日期默认为昨天。生成的数据日期为 base_date 往前推 (days-1) 天到 base_date。 返回: pandas.DataFrame: 模拟的学习记录数据框 if base_date is None: base_date datetime.now().date() records [] course_pool [Python基础, 数据结构, 算法入门, 数据库概论, 计算机网络] # 生成指定天数内的数据 for day_offset in range(days-1, -1, -1): # 从较远日期生成到最近日期 current_date base_date - timedelta(daysday_offset) # 模拟一天内的学习会话次数1-3次 sessions_per_day np.random.randint(1, 4) for session in range(sessions_per_day): # 模拟单次学习开始时间在当天8点到22点之间 start_hour np.random.randint(8, 22) start_minute np.random.randint(0, 60) start_time datetime.combine(current_date, datetime.min.time()) timedelta(hoursstart_hour, minutesstart_minute) # 模拟学习时长30到120分钟 duration_minutes np.random.randint(30, 121) end_time start_time timedelta(minutesduration_minutes) # 模拟学习的课程每次会话可能学习1-2门课 courses_in_session np.random.choice(course_pool, sizenp.random.randint(1, 3), replaceFalse) course_name , .join(courses_in_session) # 模拟完成的任务数与时长正相关 tasks_completed np.random.randint(duration_minutes // 10, duration_minutes // 5 1) record { student_id: student_id, student_name: student_name, date: current_date, start_time: start_time, end_time: end_time, course_name: course_name, task_completed: tasks_completed } records.append(record) df pd.DataFrame(records) # 按开始时间排序更符合实际情况 df df.sort_values(start_time).reset_index(dropTrue) return df def save_records_to_csv(df, filepath../data/raw/learning_records.csv): 将数据框保存为CSV文件 os.makedirs(os.path.dirname(filepath), exist_okTrue) df.to_csv(filepath, indexFalse, encodingutf-8-sig) # utf-8-sig 支持Excel直接打开 print(f模拟数据已保存至: {filepath}) print(f共生成 {len(df)} 条记录。) if __name__ __main__: # 生成最近三天的数据基准日期设为今天 base_date datetime.now().date() df_records generate_learning_records(days3, base_datebase_date) print(生成的模拟数据前5行) print(df_records.head()) save_records_to_csv(df_records)3.2 运行数据生成并查看结果在项目根目录下运行(venv) python src/data_generator.py运行后你会在data/raw/目录下看到learning_records.csv文件。用文本编辑器或 Excel 打开可以看到类似以下的数据student_id,student_name,date,start_time,end_time,course_name,task_completed S1001,张三,2023-10-01,2023-10-01 09:15:00,2023-10-01 10:45:00,Python基础,12 S1001,张三,2023-10-01,2023-10-01 19:30:00,2023-10-01 20:35:00,数据结构,8 S1001,张三,2023-10-02,2023-10-02 14:00:00,2023-10-02 15:30:00,数据结构, 算法入门,15 ...注意模拟数据中的日期是动态生成的基于运行脚本的当天。这保证了每次练习都能获得“最近三天”的鲜活数据更贴近真实场景。4. 实现核心数据处理逻辑数据生成后我们需要编写核心的处理逻辑将原始的、按次记录的数据聚合成为按天统计的学情报告。4.1 编写数据处理脚本编辑src/data_processor.py文件import pandas as pd from datetime import timedelta def load_data_from_csv(filepath): 从CSV文件加载学习记录数据 df pd.read_csv(filepath, parse_dates[date, start_time, end_time]) return df def analyze_study_data(df, student_idNone): 分析学习数据生成按日统计的报告和总体摘要。 参数: df: 包含学习记录的DataFrame student_id: 要分析的学员ID为None则分析所有学员 返回: tuple: (daily_stats_df, summary_dict) daily_stats_df: 按日统计的DataFrame summary_dict: 总体摘要字典 # 1. 数据筛选与预处理 if student_id: df df[df[student_id] student_id].copy() if df.empty: raise ValueError(f未找到学员 {student_id} 的学习记录。) # 计算每次学习会话的时长分钟 df[study_minutes] (df[end_time] - df[start_time]).dt.total_seconds() / 60 df[study_minutes] df[study_minutes].round(2) # 2. 按日期聚合计算每日指标 daily_stats df.groupby(date).agg( total_study_minutes(study_minutes, sum), total_tasks_completed(task_completed, sum), study_sessions(start_time, count), # 学习次数 # 收集当日所有不重复的课程先拆分由逗号连接的课程字符串 courses_covered(course_name, lambda x: list(set(course for sublist in x.str.split(, ) for course in sublist))) ).reset_index() # 调整列顺序和数据类型 daily_stats[total_study_minutes] daily_stats[total_study_minutes].round(2) daily_stats daily_stats[[date, total_study_minutes, total_tasks_completed, study_sessions, courses_covered]] # 3. 计算总体摘要 summary { total_study_minutes: daily_stats[total_study_minutes].sum().round(2), total_tasks_completed: int(daily_stats[total_tasks_completed].sum()), unique_courses_count: len(set(course for sublist in daily_stats[courses_covered] for course in sublist)), avg_study_minutes_per_day: daily_stats[total_study_minutes].mean().round(2), total_study_days: len(daily_stats), date_range: [daily_stats[date].min().strftime(%Y-%m-%d), daily_stats[date].max().strftime(%Y-%m-%d)] } return daily_stats, summary def generate_report_dict(student_info, daily_stats_df, summary_dict): 将分析结果组装成结构化的报告字典JSON格式 report { student_id: student_info.get(student_id), student_name: student_info.get(student_name), report_date_range: summary_dict[date_range], daily_stats: daily_stats_df.to_dict(records), # 将DataFrame转为字典列表 summary: {k: v for k, v in summary_dict.items() if k ! date_range} } # 转换日期对象为字符串便于JSON序列化 for record in report[daily_stats]: record[date] record[date].strftime(%Y-%m-%d) return report if __name__ __main__: # 单元测试加载并处理数据 try: df load_data_from_csv(../data/raw/learning_records.csv) daily_stats, summary analyze_study_data(df, student_idS1001) print( 每日学习统计 ) print(daily_stats) print(\n 总体学习摘要 ) for key, value in summary.items(): print(f{key}: {value}) # 生成报告字典 student_info {student_id: S1001, student_name: 张三} report generate_report_dict(student_info, daily_stats, summary) print(\n 结构化报告示例 ) # 美化打印部分内容 import json print(json.dumps(report, ensure_asciiFalse, indent2)[:500] ...) except FileNotFoundError: print(错误未找到数据文件请先运行 data_generator.py 生成模拟数据。) except ValueError as e: print(f错误{e})4.2 关键逻辑解析数据加载与类型转换pd.read_csv使用parse_dates参数自动将字符串列转换为 Pandas 的datetime类型这是后续时间计算的基础。时长计算(df[‘end_time’] - df[‘start_time’]).dt.total_seconds() / 60是 Pandas 中计算时间差并转换为分钟的标准方法。.dt访问器用于处理 datetime 类型的 Series。分组聚合df.groupby(‘date’).agg(...)是核心操作。我们按日期分组并对不同列应用了不同的聚合函数sum: 对学习分钟数和任务数求和。count: 统计学习会话次数。自定义lambda函数用于处理课程名称将其拆分、展平、去重形成当日学习课程列表。报告组装daily_stats.to_dict(‘records’)可以方便地将 DataFrame 转换为前端或 API 常用的字典列表格式。运行这个脚本验证数据处理逻辑(venv) python src/data_processor.py你应该能看到控制台打印出格式清晰的每日统计、总体摘要以及部分报告 JSON。5. 生成可视化报告与输出数据分析的结果最好能以图表形式直观呈现。我们将使用 Matplotlib 生成学习时长与任务完成量的趋势图。5.1 编写报告生成与可视化脚本编辑src/report_generator.py文件import matplotlib.pyplot as plt import pandas as pd import os import json from src.data_processor import load_data_from_csv, analyze_study_data, generate_report_dict def plot_study_trend(daily_stats_df, student_name, output_dir../data/processed): 生成学习趋势图并保存。 参数: daily_stats_df: 按日统计的DataFrame student_name: 学员姓名用于图表标题 output_dir: 图片输出目录 os.makedirs(output_dir, exist_okTrue) # 确保日期是排序的 daily_stats_df daily_stats_df.sort_values(date) dates daily_stats_df[date].dt.strftime(%m-%d) # 格式化为 月-日 fig, ax1 plt.subplots(figsize(10, 6)) # 绘制学习时长柱状图主Y轴 color tab:blue ax1.set_xlabel(日期) ax1.set_ylabel(学习时长 (分钟), colorcolor) bars ax1.bar(dates, daily_stats_df[total_study_minutes], colorcolor, alpha0.6, label学习时长) ax1.tick_params(axisy, labelcolorcolor) ax1.set_ylim(bottom0) # 在柱子上方添加数值标签 for bar in bars: height bar.get_height() ax1.annotate(f{int(height)}, xy(bar.get_x() bar.get_width() / 2, height), xytext(0, 3), # 偏移量 textcoordsoffset points, hacenter, vabottom) # 创建第二个Y轴绘制任务完成量折线图 ax2 ax1.twinx() color tab:red ax2.set_ylabel(完成任务数, colorcolor) line ax2.plot(dates, daily_stats_df[total_tasks_completed], colorcolor, markero, linewidth2, label完成任务数) ax2.tick_params(axisy, labelcolorcolor) ax2.set_ylim(bottom0) # 添加标题和图例 plt.title(f学员 {student_name} 三日学习情况趋势图) # 合并图例 lines_labels_1 [bars, line[0]] labels_1 [bars.get_label(), line[0].get_label()] ax1.legend(lines_labels_1, labels_1, locupper left) fig.tight_layout() # 自动调整布局 output_path os.path.join(output_dir, fstudy_trend_{student_name}.png) plt.savefig(output_path, dpi300) print(f趋势图已保存至: {output_path}) # plt.show() # 如果在Jupyter或需要显示图形取消注释 def save_report_to_json(report_dict, output_dir../data/processed): 将报告字典保存为JSON文件 os.makedirs(output_dir, exist_okTrue) student_id report_dict[student_id] filepath os.path.join(output_dir, fstudy_report_{student_id}.json) with open(filepath, w, encodingutf-8) as f: json.dump(report_dict, f, ensure_asciiFalse, indent2) print(fJSON报告已保存至: {filepath}) def save_daily_stats_to_csv(daily_stats_df, output_dir../data/processed): 将每日统计保存为CSV文件 os.makedirs(output_dir, exist_okTrue) filepath os.path.join(output_dir, daily_study_stats.csv) daily_stats_df.to_csv(filepath, indexFalse, encodingutf-8-sig) print(f每日统计CSV已保存至: {filepath}) if __name__ __main__: # 主流程加载、处理、可视化、保存 data_path ../data/raw/learning_records.csv target_student_id S1001 try: df load_data_from_csv(data_path) # 获取学员姓名假设数据中该学员存在 student_name df[df[student_id]target_student_id][student_name].iloc[0] daily_stats, summary analyze_study_data(df, student_idtarget_student_id) # 生成报告字典 student_info {student_id: target_student_id, student_name: student_name} report generate_report_dict(student_info, daily_stats, summary) # 输出结果 print(数据处理完成。) print(f学员 {student_name}({target_student_id}) 在 {summary[date_range][0]} 至 {summary[date_range][1]} 期间的学习情况) print(f 总学习时长: {summary[total_study_minutes]} 分钟) print(f 总完成任务: {summary[total_tasks_completed]} 个) print(f 涉及课程数: {summary[unique_courses_count]} 门) print(f 日均学习: {summary[avg_study_minutes_per_day]} 分钟) # 保存和可视化 save_report_to_json(report) save_daily_stats_to_csv(daily_stats) plot_study_trend(daily_stats, student_name) except FileNotFoundError: print(f错误未找到数据文件 {data_path}请先运行 data_generator.py。) except IndexError: print(f错误未在数据中找到学员 {target_student_id} 的记录。) except Exception as e: print(f处理过程中发生未知错误: {e})5.2 运行完整流程并查看输出现在我们可以通过一个统一的主入口来执行整个流程。编辑main.py#!/usr/bin/env python3 大学生学员三日学习情况分析主程序 from src.data_generator import generate_learning_records, save_records_to_csv from src.report_generator import ( load_data_from_csv, analyze_study_data, generate_report_dict, save_report_to_json, save_daily_stats_to_csv, plot_study_trend ) def main(): target_student_id S1001 analysis_days 3 print( 开始生成模拟数据 ) # 生成数据基准日期设为今天 from datetime import datetime base_date datetime.now().date() df_raw generate_learning_records(student_idtarget_student_id, daysanalysis_days, base_datebase_date) save_records_to_csv(df_raw) print(\n 开始数据分析与报告生成 ) data_path ./data/raw/learning_records.csv df load_data_from_csv(data_path) student_name df[df[student_id]target_student_id][student_name].iloc[0] daily_stats, summary analyze_study_data(df, student_idtarget_student_id) student_info {student_id: target_student_id, student_name: student_name} report generate_report_dict(student_info, daily_stats, summary) # 控制台输出摘要 print(f\n学员 {student_name}({target_student_id}) 三日学习摘要:) print(f 日期范围: {summary[date_range][0]} 至 {summary[date_range][1]}) print(f 总学习时长: {summary[total_study_minutes]} 分钟) print(f 总完成任务数: {summary[total_tasks_completed]}) print(f 学习涉及课程: {summary[unique_courses_count]} 门) print(f 日均学习时长: {summary[avg_study_minutes_per_day]} 分钟) # 保存结果 save_report_to_json(report) save_daily_stats_to_csv(daily_stats) plot_study_trend(daily_stats, student_name) print(\n 流程执行完毕 ) print(请查看以下目录中的结果文件) print( - data/processed/study_report_S1001.json (结构化报告)) print( - data/processed/daily_study_stats.csv (每日统计表)) print( - data/processed/study_trend_张三.png (学习趋势图)) if __name__ __main__: main()在项目根目录运行主程序(venv) python main.py程序将依次执行数据生成、数据分析、报告生成和可视化保存。完成后检查data/processed/目录你会找到生成的 JSON 报告、CSV 统计表和 PNG 趋势图。6. 常见问题排查与优化建议将代码跑通只是第一步。在实际项目中你会遇到各种边界情况和性能问题。以下是三个最常见的坑及其解决方案。6.1 数据加载失败或格式错误现象运行脚本时出现FileNotFoundError、KeyError或ParserError。可能原因与排查文件路径错误脚本中的相对路径../data/raw/learning_records.csv是基于脚本所在目录的。如果从其他位置运行脚本路径会失效。检查使用os.path.exists(‘data/raw/learning_records.csv’)检查文件是否存在。解决使用os.path.join(os.path.dirname(__file__), ‘..’, ‘data’, ‘raw’, ‘learning_records.csv’)来构建绝对路径或通过配置文件统一管理路径。列名不匹配CSV 文件的列名与代码中引用的‘start_time’、‘date’等不一致。检查打印df.columns查看实际列名。解决修改代码中的列名或使用pd.read_csv的usecols、rename参数进行映射。日期解析失败原始数据中的日期时间字符串格式与 Pandas 默认解析格式不符。检查查看原始数据中start_time列的具体格式如2023/10/01 09:15与2023-10-01 09:15:00。解决在pd.read_csv中使用parse_dates并指定格式例如parse_dates[‘start_time’], date_parserlambda x: pd.to_datetime(x, format‘%Y/%m/%d %H:%M’)。6.2 聚合结果异常如时长计算为负数或极大值现象计算出的total_study_minutes为负数、0 或远超预期的数值。可能原因与排查时间数据错乱end_time早于start_time。检查运行df[‘duration_check’] df[‘end_time’] - df[‘start_time’]; print(df[df[‘duration_check’] pd.Timedelta(0)])找出异常记录。解决在数据清洗阶段加入校验和修正例如交换或丢弃异常记录。时区问题如果数据来自不同时区的服务器直接相减会导致错误。检查确认所有时间戳是否已统一转换为同一时区如 UTC。解决使用pd.to_datetime(…, utcTrue)或tz_localize、tz_convert方法处理时区。数据包含非学习记录原始数据中可能混入了其他类型记录如考试、休息其course_name或task_completed字段为NULL或特殊值。检查查看df.isnull().sum()统计空值。解决在聚合前进行数据清洗例如df df.dropna(subset[‘start_time’, ‘end_time’])或df df[df[‘course_name’].notnull()]。6.3 程序扩展性差处理大量数据时慢或内存溢出现象当学员记录达到数十万条时程序运行缓慢甚至崩溃。可能原因与优化Pandas 一次性加载全部数据pd.read_csv默认将整个文件读入内存。优化对于超大文件使用chunksize参数分块读取处理或考虑使用Dask、Modin等库。如果数据在数据库中直接使用 SQL 进行聚合GROUP BY后再由 Pandas 处理结果效率更高。分组聚合操作开销大groupby和自定义lambda函数在数据量大时较慢。优化对于课程去重可以尝试先拆分再聚合df_exploded df.assign(course_namedf[‘course_name’].str.split(‘, ‘)).explode(‘course_name’)然后对df_exploded进行groupby和去重操作有时比在lambda中处理更高效。内存中存储过多中间变量。优化使用del及时删除不再需要的大 DataFrame或使用函数封装利用局部作用域自动回收。7. 生产环境部署与最佳实践学习环境的脚本离生产可用的系统还有距离。以下是将此分析流程工程化时需要考虑的关键点。7.1 配置化管理将硬编码的参数如文件路径、学员ID、分析天数提取到配置文件如config.yaml或config.py或环境变量中。示例config.py# config.py ANALYSIS_CONFIG { data_source: { type: csv, # 或 database path: ./data/raw/learning_records.csv, # 如果是数据库 # db_url: postgresql://user:passlocalhost/dbname, # table_name: learning_logs }, target_student_id: S1001, analysis_days: 3, output: { json_report_path: ./data/processed/report.json, csv_stats_path: ./data/processed/stats.csv, chart_path: ./data/processed/trend.png } }在主程序中通过from config import ANALYSIS_CONFIG引入配置。7.2 数据源抽象当前代码紧耦合于 CSV 文件。生产环境中数据可能来自 MySQL、PostgreSQL、MongoDB 或 API。应抽象一个数据访问层。# src/data_source.py import pandas as pd from abc import ABC, abstractmethod class DataSource(ABC): abstractmethod def load_records(self, student_idNone, date_rangeNone): pass class CsvDataSource(DataSource): def __init__(self, filepath): self.filepath filepath def load_records(self, student_idNone, date_rangeNone): df pd.read_csv(self.filepath, parse_dates...) # ... 应用过滤逻辑 return df class DatabaseDataSource(DataSource): def __init__(self, connection_string): self.conn_string connection_string def load_records(self, student_idNone, date_rangeNone): import sqlalchemy engine sqlalchemy.create_engine(self.conn_string) query SELECT * FROM learning_records WHERE 11 if student_id: query f AND student_id {student_id} # ... 构建日期过滤 df pd.read_sql(query, engine) return df这样主程序只需调用data_source.load_records(...)无需关心底层实现。7.3 加入日志与异常处理生产代码必须有完善的日志记录和异常处理方便排查问题。import logging logging.basicConfig(levellogging.INFO, format%(asctime)s - %(name)s - %(levelname)s - %(message)s) logger logging.getLogger(__name__) def analyze_study_data(df, student_idNone): try: logger.info(f开始分析学员 {student_id} 的数据共 {len(df)} 条记录。) if student_id: df df[df[student_id] student_id].copy() if df.empty: logger.warning(f未找到学员 {student_id} 的数据。) return pd.DataFrame(), {} # ... 核心逻辑 logger.info(数据分析完成。) return daily_stats, summary except Exception as e: logger.error(f数据分析过程中发生错误: {e}, exc_infoTrue) raise7.4 制定数据校验清单在数据处理前执行一套数据质量校验能提前发现很多问题。校验项检查方法异常处理建议必要字段是否存在if not set(required_cols).issubset(df.columns):记录错误并终止或使用默认值关键字段无空值df[[start_time,end_time]].isnull().sum()丢弃记录或根据业务规则填充时间逻辑合理df[end_time] df[start_time]标记、丢弃或交换异常记录数值范围合理df[task_completed].between(0, 1000).all()将超出合理范围的值置为边界值或NULL日期范围符合预期df[date].min(), df[date].max()记录警告或按配置过滤7.5 性能与监控建议对于高频或大数据量的分析任务缓存结果如果原始数据变化不频繁可以将聚合结果缓存起来如存入 Redis下次请求直接读取缓存。异步处理对于耗时的报告生成请求可以将其放入任务队列如 Celery异步处理完成后通知用户或更新状态。监控关键指标监控每日处理的数据量、平均处理时间、失败率等便于发现性能瓶颈或系统异常。通过以上步骤我们不仅完成了一个从模拟数据到可视化报告的完整数据分析流程更构建了一个具备清晰结构、可维护、可扩展的代码框架。你可以在此基础上轻松地接入真实数据源、增加更多分析维度如学习效率、知识点掌握度、或将其封装为 RESTful API 供其他系统调用。