为什么要讲这个知识点
孩子们学完列表、循环、条件判断之后,容易停在"语法会了,但不知道能干什么"。成绩分布图是一个很好的落点:数据是孩子自己身边的(一次测验的分数),处理过程用到列表统计和循环,输出结果又是一张看得见的图。更重要的是,画完之后要回答几个问题——哪一段分数的人最多?及格率是多少?最高分和最低分差多少?把"写代码"和"读数据"接在一起,孩子才知道程序是用来回答问题的。
这篇文章只用 Python 标准库和 matplotlib,代码在 Python 3.8 以上版本可直接运行。
完整代码
先准备一份成绩数据(可以让孩子换成自己班的真实分数):
importmatplotlib.pyplotasplt plt.rcParams["font.sans-serif"]=["SimHei"]# 正常显示中文plt.rcParams["axes.unicode_minus"]=False# 正常显示负号scores=[88,92,75,60,45,98,81,73,66,55,90,84,79,62,100,71,68,58,95,87]# 1. 按 10 分一段统计人数groups=["0-9","10-19","20-29","30-39","40-49","50-59","60-69","70-79","80-89","90-100"]counts=[0]*10forsinscores:ifs==100:counts[9]+=1else:counts[s//10]+=1# 2. 统计基本指标total=len(scores)average=sum(scores)/total pass_count=0forsinscores:ifs>=60:pass_count+=1pass_rate=pass_count/total*100print("总人数:",total)print("平均分:%.1f"%average)print("最高分:",max(scores)," 最低分:",min(scores))print("及格率:%.1f%%"%pass_rate)foriinrange(10):print(groups[i],"分:",counts[i],"人")# 3. 画分布图plt.figure(figsize=(9,5))bars=plt.bar(groups,counts,color="#4C9AFF")forb,cinzip(bars,counts):ifc>0:plt.text(b.get_x()+b.get_width()/2,c+0.1,str(c),ha="center",fontsize=10)plt.title("一次测验的成绩分布")plt.xlabel("分数段")plt.ylabel("人数")plt.xticks(rotation=45)plt.tight_layout()plt.show()代码要点拆解
第一处:counts = [0] * 10
这是"先造桶,再往里放东西"的思路。10 个分数段就是 10 个桶,初始都是 0。孩子需要理解列表乘法不是复制引用那么简单——这里元素是整数,所以没问题;如果元素是列表,就要用别的方式。这点可以留到以后再讲,现在先记住"整数列表可以这样初始化"。
第二处:counts[s // 10] += 1
整除是这段代码的核心。88 // 10 得到 8,正好对应第 8 个桶(下标从 0 开始,也就是 “80-89” 这一组)。这条规则对 0 到 99 分都成立,唯独 100 分算出来是 10,会越界。所以前面单独处理了 100 分的情况。让孩子自己想想:如果不写这个 if,程序会报什么错?
第三处:柱子上标数字
plt.text三个关键参数:横坐标取柱子中心,纵坐标取c + 0.1让数字浮在柱子上方一点,ha="center"保证文字水平居中对齐。不加ha的话数字会偏到柱子右边。
学生常见错误
1. 下标和分数段对不上。有孩子写成counts[s // 10 + 1],结果 88 分被算进了 “90-100”。要让孩子拿几个具体数字手动推一遍,比如 60、69、70 分别应该落在哪一段。
2. 100 分漏掉。上面说的越界问题,很多孩子第一次写必踩。这正好是一个"边界值要单独想"的训练机会。
3. 及格率算成整数。如果写成pass_count // total * 100,结果永远是 0(因为整除先算出 0 再乘 100)。要提醒用/而不是//,或者先乘后除。
4. 中文显示成方框。不设font.sans-serif就会看到一堆小方块,这不是程序错了,是字体没配。让孩子知道"报错"和"显示不对"是两类不同的问题。
5. 数据里出现超过 100 或负数。真实数据往往不干净。可以问孩子:如果有个同学考了 105 分(加分题),程序会怎样?
可以留的练习
- 把分数段改成 5 分一段,需要改哪几行?
- 增加统计:不及格(低于 60)、良好(70-89)、优秀(90 以上)各多少人,并在图上用不同颜色区分。
- 把
scores改成input()逐个输入,输入 -1 结束。想一想为什么要用一个特殊值当结束标志。
- 把
- 计算中位数:把分数排序后取中间那个数。如果人数是偶数怎么办?
小结
这张图的重点不在画得多好看,而在"统计口径"这四个字。同样的 20 个分数,按 10 分一段和按 5 分一段,看到的信息不一样;算不算 100 分那一档,结果也不一样。让孩子养成一个习惯:图表画出来之后,先问自己一句"这张图想说明什么,它有没有骗我"。这个问题比会写plt.bar重要得多。