
刷题数据可视化面板用图表追踪自己的薄弱环节与进步曲线一、刷题不是刷完就完了刷完 100 道 LeetCode 题后如果有人问你你的薄弱环节是什么 你能给出有数据支撑的回答吗大多数人只能说DP 好像不太行或数学题比较难。这不叫分析这叫感觉。一个真正有用的刷题系统应该能够用图表告诉你过去 30 天你在哪个标签下错误率最高、你的解题速度是在提升还是下降、你在工作日和周末的刷题效率差多少。这篇文章讨论刷题数据可视化面板的设计——不是追求花哨的图表而是追求能反映真实学习状态的数据呈现。二、四个核心维度的数据模型flowchart TD A[刷题数据中心] -- B[正确率维度] A -- C[速度维度] A -- D[覆盖度维度] A -- E[趋势维度] B -- B1[按标签的正确率热力图] B -- B2[按难度的通过率对比] C -- C1[每日平均解题时间趋势] C -- C2[首次通过 vs 复习通过的耗时对比] D -- D1[标签覆盖雷达图] D -- D2[Easy/Medium/Hard 分布] E -- E1[连续打卡记录] E -- E2[周均题量变化] E -- E3[正确率 30 天移动均线]三、实现数据聚合与 ECharts 可视化from dataclasses import dataclass from collections import Counter, defaultdict from datetime import datetime, timedelta from typing import Optional dataclass class SubmissionRecord: 单条提交记录 user_id: str problem_id: int problem_title: str tags: list[str] difficulty: str # Easy | Medium | Hard passed: bool elapsed_minutes: int submitted_at: datetime is_review: bool False # 是复习还是首次尝试 class DashboardDataAggregator: 仪表盘数据聚合器从原始提交记录生成可视化数据 def __init__(self, submissions: list[SubmissionRecord]): self.submissions submissions def tag_accuracy_matrix(self) - dict: 按标签统计正确率 返回示例 { 数组: {total: 50, passed: 42, rate: 0.84}, DP: {total: 30, passed: 18, rate: 0.60}, } tag_stats defaultdict(lambda: {total: 0, passed: 0}) for sub in self.submissions: for tag in sub.tags: tag_stats[tag][total] 1 if sub.passed: tag_stats[tag][passed] 1 # 计算比率 for tag, stats in tag_stats.items(): stats[rate] ( round(stats[passed] / stats[total], 2) if stats[total] 0 else 0 ) return dict(tag_stats) def daily_speed_trend(self, days: int 30) - list[dict]: 计算每日平均解题时间趋势 返回适合 ECharts 折线图的数据格式。 now datetime.now() start now - timedelta(daysdays) daily_data defaultdict(list) for sub in self.submissions: if sub.submitted_at start: date_key sub.submitted_at.strftime(%Y-%m-%d) daily_data[date_key].append(sub.elapsed_minutes) trend [] for i in range(days): date (now - timedelta(daysdays - 1 - i)).strftime(%Y-%m-%d) times daily_data.get(date, []) trend.append({ date: date, avg_time: round(sum(times) / len(times), 1) if times else 0, count: len(times), }) return trend def difficulty_distribution(self) - dict: 按难度统计题目分布 dist Counter() for sub in self.submissions: dist[sub.difficulty] 1 return dict(dist) def weekly_progress(self, weeks: int 4) - dict: 按周统计进度 返回每周的刷题数、通过率、总耗时。 now datetime.now() weekly defaultdict(lambda: {count: 0, passed: 0, total_time: 0}) for sub in self.submissions: # 计算所属周次 week_start sub.submitted_at - timedelta( dayssub.submitted_at.weekday() ) week_key week_start.strftime(%Y-%m-%d) weekly[week_key][count] 1 weekly[week_key][total_time] sub.elapsed_minutes if sub.passed: weekly[week_key][passed] 1 # 只保留最近 N 周 recent_weeks {} for i in range(weeks - 1, -1, -1): week_key (now - timedelta(weeksi)).strftime(%Y-%m-%d) # 取该周周一 monday datetime.strptime(week_key, %Y-%m-%d) monday - timedelta(daysmonday.weekday()) wk monday.strftime(%Y-%m-%d) data weekly.get(wk, {count: 0, passed: 0, total_time: 0}) data[rate] ( round(data[passed] / data[count], 2) if data[count] 0 else 0 ) recent_weeks[wk] data return recent_weeks def generate_echarts_option(self) - dict: 生成 ECharts 图表配置 每个图表对应一个核心维度的可视化。 tag_data self.tag_accuracy_matrix() speed_data self.daily_speed_trend(30) weekly_data self.weekly_progress(4) options { # 图 1标签正确率柱状图 tag_accuracy_bar: { title: 各标签正确率, xAxis: list(tag_data.keys()), series: [{ name: 正确率, data: [d[rate] * 100 for d in tag_data.values()], type: bar, itemStyle: { color: { # 低于 60% 红色警告高于 80% 绿色 function: if value 60 return red; return green } } }], }, # 图 2每日速度趋势折线图 daily_speed_line: { title: 每日平均解题时间分钟, xAxis: [d[date] for d in speed_data], series: [{ name: 平均耗时, data: [d[avg_time] for d in speed_data], type: line, smooth: True, }], }, # 图 3周进度仪表盘 weekly_progress: { title: 每周刷题统计, weeks: list(weekly_data.keys()), counts: [d[count] for d in weekly_data.values()], rates: [d[rate] * 100 for d in weekly_data.values()], }, } return optionsECharts 集成示例HTML 片段!-- 标签正确率热力图 -- div idtag-heatmap stylewidth:600px;height:400px;/div script var chart echarts.init(document.getElementById(tag-heatmap)); var option { title: { text: 各算法标签掌握情况 }, radar: { indicator: [ { name: 数组, max: 100 }, { name: 链表, max: 100 }, { name: 树, max: 100 }, { name: DP, max: 100 }, { name: 图论, max: 100 }, { name: 回溯, max: 100 }, ] }, series: [{ type: radar, data: [{ value: [84, 72, 65, 55, 40, 68], name: 掌握度(%) }] }] }; chart.setOption(option); /script四、边界与设计考量4.1 冷启动时的数据不足新用户前几周的数据量太少图表波动大。应该在图表上标注数据量少统计意义有限避免用户误读。同时用灰色区域标记置信区间。4.2 数据解读的引导光有图表不够需要配合文字解读。例如当正确率 30 天移动均线连续下行超过 7 天时系统应该推送提示你的正确率连续下降建议降低新题量增加复习比例。4.3 性能聚合策略对于大量提交记录10^5 级别实时聚合可能较慢。使用预聚合每小时/每天定时计算汇总数据写入缓存。用户在仪表盘看到的是预计算的结果延迟在可接受范围内。五、总结刷题数据可视化的价值不在于图表好看而在于把我感觉 DP 不太行变成近 30 天 DP 标签正确率 55%比平均值低 20 个百分点。量化是优化的前提数据是决策的基础。一个好的可视化面板能让你看到自己的进步轨迹和薄弱环节的具体位置而不只是今天刷了几道题。