news 2026/9/27 1:59:21

数据挖掘课设实战:Pandas处理成绩与决策树构建

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
数据挖掘课设实战:Pandas处理成绩与决策树构建

简介:一份数据挖掘课程设计完整报告类文档,面向数据挖掘、大数据分析方向的学生及互联网行业从业者,适合需要快速掌握数据挖掘全流程并完成课程设计的读者。文档以具体的学生成绩分析项目为载体,系统呈现了从项目背景介绍、工具选用(Word、Excel、Anaconda)、数据文件预处理(清理、集成、变换、归约)、异常值分析(统计量、箱型图),到探索性数据分析、挖掘建模、结果解释与应用等完整环节,覆盖数据挖掘项目生命周期中的关键步骤与代码实现过程。资源为单份docx文档,大小403KB,内容结构清晰、目录完整,可直接作为课程设计报告撰写模板或实际实验操作的参考蓝本。目前该资源已有1774人学习下载,在同类课程设计资料中具备较高参考价值,适合需要对照实操流程、撰写报告或梳理知识体系的学习者。

1. 数据挖掘课程设计:从大学物理与组成原理成绩里挖出一棵决策树

数据挖掘在互联网行业里最常见的落地方式,就是用一份看起来不相关的历史数据找出隐藏的关联关系。这份课程设计正好干了这么一件事:把大学物理、模拟电子技术和计算机组成原理三门课的学生成绩收进Excel,用Pandas做预处理和描述性统计,用Matplotlib画饼状图和柱状图,最后手工推算信息熵、信息增益,构建一棵能判断“组成原理学习情况”的决策树。整套流程没有额外硬件依赖,靠Anaconda自带的Pandas、Matplotlib、xlrd就能跑完。适合正在做数据挖掘课设、需要一条完整链路作参照的人,也适合想用Python快速做一次成绩分析实战的初学者。

2. 数据预处理:从Excel原始成绩表到Pandas描述性统计

2.1 预处理为什么被放在第一步

原始成绩Excel表里通常混着学号、班级、任课教师、平时分、实验分等一堆字段,和“物理成绩与组成原理成绩关系”这个分析目标并不直接相关。如果不先做裁剪,后边describe()输出的统计量会被无关列干扰,箱型图上的离群点也会失真。数据挖掘流程里这个阶段叫数据预处理,包含数据清理、数据集成、数据变换、数据归约四项工作,核心目标就两个:提升数据质量,让数据适应后续的挖掘算法。

这份课设里最现实的诉求是:把成绩表里跟项目无关的列删掉,把多次考试的数据求和汇总成单一成绩,再把百分制离散化成“不及格、及格、良好”档位,方便后边计算条件熵时分组统计。小数据量的课程设计直接用Excel手工清理是完全合理的做法,报告里还能顺手展示一张“处理前-处理后”的截图对比,这也是课设评分的常规加分点。

2.2 手工清理Excel:删列、求和、离散化的具体操作

拿到原始成绩表后,我一般按下面三步走,顺序和操作都比较好复制。

第一,删列。保留学生姓名、学号、以及三门课的成绩列,其余字段先隐藏,确认无关后再删除。这样做的好处是万一后续分析需要补字段,原始数据还在。第二,统一口径。如果有多次考试或分项成绩,按课程设计的需求用Excel的SUM或AVERAGE函数合成一列。原报告里明确写了“先对表格进行删减和求和”,说明数据源本身是做了多列合并处理的。第三,离散化。建立新表“离散化.xlsx”,把百分制成绩映射成档位标签,常见映射规则是:90分以上为“良好”,60到89分为“及格”,60分以下为“不及格”。离散化这一步很多人会忽略,但它直接决定后边条件熵能不能算出来,因为决策树建模用的是类别标签,不是连续数值。

2.3 Pandas读入Excel并输出describe()

清洗完成后的数据,用Pandas读入并做一次快速体检,代码如下:

import pandas as pd catering_sale = 'G:/scour.xlsx' data = pd.read_excel(catering_sale, index_col=u'学生') print(data.describe()) print(len(data))

这段代码做了三件事:read_excel把Excel读成DataFrame;index_col=u'学生'指定学生列作为行索引;describe()输出每列的Count、Mean、Std、Min、25%、50%、75%、Max。注意这里的Count不是有效记录数之外的东西,它代表该列非空样本量,如果各列Count不一致,说明存在缺失值,需要回到Excel里补齐或删行。50%就是中位数,和25%、75%一起构成四分位数。len(data)是总样本行数,用来核对读入是否完整,如果比Excel实际行数少,说明有整行空数据被Pandas默认跳过了。

提示:路径字符串里的反斜杠在Windows下没问题,但代码发给别人跑的时候要小心,G盘路径换机器基本必挂。更稳妥的做法是统一改成相对路径,比如data = pd.read_excel('data/scour.xlsx'),把Excel文件和代码放在同一个目录树下。

3. 异常值检测:统计量分析与箱型图的实操对比

3.1 简单统计量分析:最大最小值是最快的体检表

描述性统计能做的事情很多,但最直接的是看每个变量的最大值、最小值。学生成绩的正常区间就是0到100,任何超出这个范围的数字都值得怀疑。比如最小值是-12,说明数据录入时可能带了符号;最大值是152,大概是多位同学成绩被合并成了一行。这种错误靠肉眼扫Excel很难发现,describe()一行输出就能定位到列。

除了最大最小值,标准差(Std)也值得盯一下。课程成绩的标准差如果超过20,说明分数拉得很开,好学生和困难户之间差距巨大;如果标准差只有3左右,说明全班成绩高度集中,这时候箱型图上可能出现的“离群点”就格外值得注意——它可能是真正的异常值,也可能只是数据录入失误。统计量分析的意义就是先给数据做个体检,把明显不合理的情况圈出来,为后边箱型图精确揪出离群点做铺垫。

3.2 箱型图原理:四分位数与上下边缘

箱型图的原理不难,但很重要。它把一列数据从小到大排好,算出三个四分位数:Q1(下四分位数,25%位置)、Q2(中位数,50%位置)、Q3(上四分位数,75%位置)。然后把Q1到Q3的范围作为“箱子”,箱子的高度就是四分位距IQR(Interquartile Range)。在箱型图中,正常范围的上下边缘分别是Q1-1.5×IQR和Q3+1.5×IQR,超出这两个边缘的点会被单独画出来,这就是fliers,也就是我们常说的离群点。

有一点容易误解:超出边缘的点不一定就是错误数据。它可能是真实存在的极端值,比如一次特别难的考试里有个别人拿了满分。所以箱型图分析的目的不是自动删数据,而是把可疑的点暴露出来,让人去判断它是录入错误还是真实情况。课程设计报告里如果能写上“异常值经核实为真实成绩,予以保留”这类结论,比单纯贴一张箱型图要扎实得多。

3.3 箱型图绘制与离群点自动标注代码

Pandas的DataFrame自带boxplot方法,配合Matplotlib可以做离群点标注,代码核心部分如下:

import pandas as pd import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False catering_sale = 'G:/scour.xlsx' data = pd.read_excel(catering_sale, index_col=u'学生') plt.figure() p = data.boxplot(return_type='dict') fliers = p['fliers'][0] # 第一列的离群点对象 x = fliers.get_xdata() y = fliers.get_ydata() y.sort() for i in range(len(x)): if i > 0: plt.annotate(y[i], xy=(x[i], y[i]), xytext=(x[i] + 0.05 - 0.8 / (y[i] - y[i - 1]), y[i])) else: plt.annotate(y[i], xy=(x[i], y[i]), xytext=(x[i] + 0.08, y[i])) plt.show()

这段代码里最关键的是return_type='dict',它让boxplot返回一个字典结构,里面包含boxes、whiskers、medians、fliers等键。fliers是Line2D对象列表,取[0]表示第一列的离群点。get_xdata()拿离群点在X轴上的位置,get_ydata()拿具体的成绩数值。标注时用xytext控制文字偏移量,0.8 / (y[i] - y[i-1])这段是为了让相邻标注在纵向上错开,避免文字叠成一团。如果数据量很大、离群点很多,这个错位公式依然可能不够用,更省事的方法是只标注具体数值,不标坐标,或者改用plt.text(x[i], y[i], str(y[i]))直接贴在点旁边。

注意:这里用的是get_ydata(),不是get_xdata()。原报告里两行都写成get_xdata(),实际画出来会发现标注位置全偏了,这是个非常典型的笔误,后面避坑章节会再展开。

4. 数据可视化:饼状图与柱状图的绘制细节

4.1 饼状图:看成绩分布的整体占比

饼状图的适用场景只有一个数据系列,适合展示“部分占整体”的比例关系,这在课设报告里用来呈现某门课的成绩分布非常直观。原项目里对大学物理成绩做了分档统计,代码和逻辑如下:

import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False labels = ['不及格', '60-70', '70-80', '80-90', '90-100'] X = [9, 17, 7, 2, 0] plt.figure() plt.pie(X, labels=labels, autopct='%1.2f%%') plt.title('大学物理1成绩分布图') plt.show()

X是各档位人数,labels是档位名称,autopct='%1.2f%%'控制扇形上显示的百分比格式,保留两位小数。绘制之前先要确认两件事:X的元素和labels的元素一一对应,长度一致;X求和就是样本总数。这里有个实际存在的坑:90-100分这个档位人数是0,饼状图上不会画出对应扇形,标签也会跟着消失,看上去像图例少了一类。解决办法是把人数为0的档位从X和labels里一并移除,或者在报告里说明该档位无人。

4.2 柱状图:并排对比两组数据的关键参数

柱状图用在需要对比的场景。原项目里用两组数据做对比,绘制的是分组柱状图,代码如下:

import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False name_list = ['不及格', '及格', '良好'] num_list = [60, 49, 27] # 第一组:物理成绩人数 num_list1 = [5, 5, 9] # 第二组:组成原理成绩人数 x = list(range(len(num_list))) total_width, n = 0.8, 2 width = total_width / n plt.bar(x, num_list, width=width, label='物理', fc='y') for i in range(len(x)): x[i] = x[i] + width plt.bar(x, num_list1, width=width, label='组成原理', tick_label=name_list, fc='r') plt.legend() plt.show()

两个细节值得说清楚:total_width, n = 0.8, 2中,0.8是整个分组的宽度,2是组内柱子数量,每根柱子宽度是0.4,这样能保证两组柱子并排后恰好填满0.8的区间。第二个细节是tick_label=name_list只放在第二次bar调用里,因为只有第二次bar执行后,x数组整体右移了width,类目名称落在两组柱子的正中间。对照原代码,这段数据呈现的应该是“物理”与“组成原理”两个科目在各档位的人数对比,两个科目选同一套分档口径,柱子才具备可比性。

提示:图例label必须在两次bar里分别指定,否则legend()只能显示出一个图例项。

4.3 Matplotlib中文显示的两行关键配置

每次绘图前都写plt.rcParams['font.sans-serif'] = ['SimHei']和plt.rcParams['axes.unicode_minus'] = False,这两行不是玄学。第一行告诉Matplotlib在图中使用黑体显示中文,否则中文标签全部变成方框。第二行解决坐标轴上负号显示成方块的问题,False表示用正常的ASCII负号。这两行必须在pyplot第一次绘图前执行,放在文件头部是惯例。如果机器上没有SimHei字体,可以用以下命令查可用字体再替换成对应名称,这是一个在Anaconda环境里经常要用到的排查手段。

python -c "import matplotlib.font_manager as fm; [print(f.name) for f in fm.fontManager.ttflist if 'Hei' in f.name or 'Song' in f.name or 'YaHei' in f.name]"

5. 避坑与常见问题排查:这份课设最容易翻车的五个点

5.1 文件路径写死G盘,换电脑必报错

现象:代码在自己机器上跑得好好的,拷给别人后一运行就报FileNotFoundError,或者读出来是空DataFrame。原因:路径写的是G:/scour.xlsx,别人的机器上没有G盘,或者文件位置对不上。解决:把数据文件放到项目文件夹里,代码改成相对路径,比如data = pd.read_excel('data/scour.xlsx')。如果一定要保留绝对路径,至少用os.path.exists()先判断一下文件是否存在,不存在就抛个友好的提示,课设答辩时这能让演示顺畅很多。

5.2 中文标签全部变成方框

现象:饼状图、柱状图上的中文标签显示成一个个空心方框,标题也全是乱码。原因:Matplotlib默认字体不含中文字符,需要显式指定中文字体。解决:绘图之前执行plt.rcParams['font.sans-serif'] = ['SimHei']和plt.rcParams['axes.unicode_minus'] = False。如果黑体不可用,换成Microsoft YaHei或KaiTi也行,关键是字体名必须是系统真实安装的。这个坑在答辩演示时最容易社死,评委看到满屏方框,第一印象直接崩。

5.3 箱型图离群点标注全偏了

现象:箱型图画出来了,但离群点旁边的数字标注跑到了奇怪的位置,尤其是纵向坐标错得离谱。原因:绘制代码里x和y都取的是get_xdata(),而get_xdata()返回的是横坐标,导致标注的Y坐标用了X轴数据。这是原报告里一个隐蔽但实际存在的笔误,不仔细核对很难发现。解决:一个用get_xdata()取值,另一个必须改成get_ydata()。校验方法很简单:打印出来看一眼离群点数值是否和describe()里的Max/Min对得上,对不上就是取值取错了。

5.4 xlrd版本不兼容,读取Excel直接报错

现象:import xlrd后读取xlsx文件报Excel xlsx file; not supported,或者xlrd.biffh.XLRDError: Excel xlsx file; not supported。原因:xlrd新版本(2.0以上)移除了对xlsx格式的支持,只保留xls旧格式,而Anaconda默认安装的可能是新版本。解决:三种方案任选——方案一,降级安装pip install xlrd==1.2.0,最省事;方案二,把文件另存为xls旧格式;方案三,改用openpyxl或pandas.read_excel直接读,pd.read_excel底层会自动选择合适的引擎,避开xlrd依赖。后端在Anaconda里装了新版xlrd的同学,这段血泪经验基本都能用上。

5.5 决策树算条件熵时选错标签列

现象:手工算出来的条件熵和信息增益和Sklearn跑出来的完全对不上,根节点选择也和直觉不符。原因:离散化Excel表的列顺序一变,代码里v[-3]取的列就变了。v[-3]是“取每行倒数第三列”的意思,如果Excel里临时加了一列备注,倒数第三列就不再是目标标签了。解决:不要依赖负索引,改成按列名取索引,或者干脆在离散化表里保持列顺序固定。一个更稳妥的写法是读完Excel后用data.columns打印列名,肉眼确认目标特征在第几列,再回去改索引值。这个方法虽然土,但确实是最快的。

5.6 饼状图人数为0的档位标签消失

现象:饼状图上有时候少了一块,图例也对不上,比如有5个档位但图里只有4块扇形。原因:某个档位的人数为0,饼状图不会给0值的扇区画图,标签也随之丢失。解决:绘图前把人数为0的档位从数据和标签里过滤掉,或者将其人数改成很小的占位值并在图例中说明“该档位人数为0”。写报告时优先选择前一种,直接删掉空档位,让图例和扇形一一对应。

6. 信息熵与信息增益:从shannon_entropy到决策树根节点的完整验证

6.1 shannon_entropy代码逐行拆解

决策树建模的核心是算信息熵和信息增益,原项目给出了一个手工实现版本:

from math import log def shannon_entropy(data): enteries = len(data) label_count = {} for v in data: current_label = v[-3] if current_label not in label_count.keys(): label_count[current_label] = 0 label_count[current_label] += 1 entropy = 0.0 for key in label_count: prob = float(label_count[key]) / enteries entropy -= prob * log(prob, 2) return entropy

逻辑拆开看只有三步:统计每个类别出现的次数;把次数转成概率;按香农公式累加信息量。关键点在v[-3]这个索引,它默认目标标签存在离散化表的倒数第三列。数据通过creatData()函数从离散化.xlsx读入后,每行是一个列表结构,比如['物理良好', '组成原理不及格', '一类'],v[-3]就是末尾往前数第三项。这里能算对的前提是列顺序稳定,Excel里加列、删列后必须同步改索引。

对于纯Python实现的熵计算,数据量小的时候问题不大,但样本上千后建议用Pandas向量化写法替代,可读性和性能都好得多:

import pandas as pd from math import log def entropy_of_series(s): counts = s.value_counts(normalize=True) return -sum(p * log(p, 2) for p in counts)

value_counts(normalize=True)一步到位把频数转成概率,再按信息熵公式求和,逻辑和手工版完全一致,但不用自己维护字典。

6.2 手工验证信息增益的完整套路

信息增益的计算公式是:信息增益 = 总信息熵 - 条件熵。总信息熵用全部样本的标签列算,条件熵按特征的各个取值分组、加权求和。实操时用下面这段可以快速验证手工笔算的结果是否准确:

import pandas as pd from math import log def entropy_of_series(s): counts = s.value_counts(normalize=True) return -sum(p * log(p, 2) for p in counts) def condition_entropy(df, feature, label): total = len(df) result = 0.0 for val in df[feature].unique(): sub = df[df[feature] == val] result += len(sub) / total * entropy_of_series(sub[label]) return result # 总信息熵 total_entropy = entropy_of_series(df['组成原理档位']) # 条件熵:以物理档位为条件 cond_entropy = condition_entropy(df, '物理档位', '组成原理档位') gain = total_entropy - cond_entropy print('信息增益:', gain)

计算完成后,对每个候选特征都做一遍,信息增益最大的特征就是决策树的根节点。课程设计报告中手工画完那棵最浅决策树后,强烈建议用Sklearn跑一遍交叉验证,把根节点属性和自己手算的对照一下。一致性确认后,再用tree.export_graphviz导出图,放进报告里比手画图专业得多。从那以后我每次做这类数据挖掘课设,都强制走一遍“describe确认数据底数、箱型图找离群点、手工算信息增益、Sklearn对照验证”的完整流程,顺序调换容易漏坑,希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/27 1:58:21

CANoe LIN诊断配置避坑指南:CDD加载与调度表设置详解

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

作者头像 李华
网站建设 2026/9/27 1:57:05

维谛技术面试全攻略:25个高频问题与答题框架

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

作者头像 李华
网站建设 2026/9/27 1:57:04

全景图批量下载工具1.6.5.1实战:从解析到KRPano与Blender应用

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

作者头像 李华
网站建设 2026/9/27 1:55:44

Python+CNN驾驶员疲劳检测系统:数据集、模型训练与预警实现

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

作者头像 李华
网站建设 2026/9/27 1:55:40

用Uprecise轻松配置GPS/GNSS模块:从串口调试到RTK定位实战

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

作者头像 李华
网站建设 2026/9/27 1:55:36

Python数据挖掘实战:作者归属文本分类与字符N元语法特征工程

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

作者头像 李华