广东省和其它八个省份一起开始了他们的第一届新高考,这个改变其实是换汤不换药。总分还是750分,考试科目选择是“3+1+2”,是语文、数学、外语(注意不是英语,英语是外语之一)的3门必考科目、物理或历史的1门首选选择科目,政治、地理、生物、化学4门中选2门的再选选择科目组成的考试科目元组。本次的可视化以“1”对应的物理的选科区(即“物理类考生”)作为成绩分布样本。首先在互联网上找到广东省2021年普通高等学校招生全国统一考试物理类考生的“一分一段表”并将数据抽象成关系模式以便讲述。
上述关系模式中,G是成绩,Gr是真实成绩值,Q是人数,这个关系模式至少是第三范式,数据分析时出错的概率低。因为出错概率较低,所以不必要用C#.NET中的System.Linq进行二次筛选,直接上Python上进行数据分析。
import numpy import matplotlib.pyplot # make axises allow Chinese character to present matplotlib.pyplot.rcParams['font.family'] = 'sans-serif' matplotlib.pyplot.rcParams['font.sans-serif'] = 'SimHei'
上面是用到的库,numpy不用说了,可视化我利用的是pyplot模块。
表格文件有了,但是在怎么确定成绩和人数是服从什么分布的?在表格文件先画出散点图:
将这张图横坐标坐标500以后的值去掉,把y轴(人数轴)的值除以337988后会得很像正态分布的图。这些依据不能确定成绩服从正态分布,所以有必要从卡方分布和F分布入手去推测是否服从。考虑到卡方分布和F分布的最高点小的都有百分位,而现行的分数表格中最大的也只有千分位。所以正态分布较为合适。于是,对样本求平均数和方差的函数诞生了。
file = open(grade_file)
x_list = []
y_list = []
grade_list = []
for line in file:
s = line.split(",")
x_list.append(float(s[0]))
y_list.append(float(s[1]))
grade_list.append(float(s[2]))
average = 0
length = len(x_list)
y_sum = sum(y_list)
for i in range(0, length):
average += x_list[i] * y_list[i] / y_sum
real_average = grade_list[0] - average
difference = 0
for i in x_list:
difference += (i - average) ** 2 / length
error = 0
for i in range(0, length):
error += (y_list[i] / y_sum - numpy.exp(-(x_list[i] - average) ** 2 / (2 * difference)) / (numpy.sqrt(2 * numpy.pi * difference))) ** 2
file.close()
这里有一个real_average和average的变量,前者指的是分数的平均值,后者指的是对文件进行读取时取得的平均值。grade_list[0]指最高分。封装之。
def analysing_grade(grade_file):
file = open(grade_file, encoding="utf-8")
x_list = []
y_list = []
grade_list = []
for line in file:
s = line.split(",")
x_list.append(float(s[0]))
y_list.append(float(s[1]))
grade_list.append(float(s[2]))
average = 0
length = len(x_list)
y_sum = sum(y_list)
for i in range(0, length):
average += x_list[i] * y_list[i] / y_sum
real_average = grade_list[0] - average
difference = 0
for i in x_list:
difference += (i - average) ** 2 / length
error = 0
for i in range(0, length):
error += (y_list[i] / y_sum - numpy.exp(-(x_list[i] - average) ** 2 / (2 * difference)) / (numpy.sqrt(2 * numpy.pi * difference))) ** 2
file.close()
return real_average, difference, error, y_sum, x_list, y_list, grade_list
最后得到的结果是:
我们可以看到广东一点都不卷,说实话,本人在珠三角高考的一点都有点不相信这个数据,但是用了正态分布去套还是得接受。言归正传,得到了数据后我们就要着手写第二个函数了,数据需求那就是2021年在广东省招生的高校的投档分(最低分)对应物理类的表格即可。本人在互联网上找到并转成表格,因为2021年广东省进行投档招生的专业都叫“专业组”,所以一个学校有几个甚至十几个专业组的比比皆是。如:吉林大学今年在广东招生的时设置了12个专业组,苏州大学设置了9个专业组。对这些专业组、招生人数、对应最低分,本人第二次利用加权平均值算吉林大学对应的分数是612分,苏州大学是607分。对这些平均值本人重新做了一个表格,并且根据关系实例抽象出关系模式。
在这个关系模式中,前者是高校,后者是对各专业组的分数按照人数加权平均得到的量。原表的关系范式是第二范式,对于它的处理需要栈和查询——难点在这里,所以本人先将专业组的分数进行加权,然后重新制作了投档情况表格。并且对表格的组织结构设置函数,上代码:
def plot_normal_distribution_with_school(average, difference, school_file, initial_person_list, initial_grade_list):
matplotlib.pyplot.figure(figsize=(32, 24), )
x = numpy.arange(550, 700, 10 ** (-2))
# set border
border = matplotlib.pyplot.gca()
border.spines['right'].set_color('none')
border.spines['top'].set_color('none')
# plot
matplotlib.pyplot.xlim(550, 700)
matplotlib.pyplot.ylim(0.001, 0.0019)
matplotlib.pyplot.plot(x,numpy.exp(-(x - average) ** 2 / (2 * difference)) / (numpy.sqrt(2 * numpy.pi * difference)),
linewidth=1.5, label='$f(x)=N({},{})$'.format(average, difference))
matplotlib.pyplot.xlabel('x', fontsize=24)
matplotlib.pyplot.ylabel('y', fontsize=24)
matplotlib.pyplot.legend(prop={'size': 24})
# school and grade file dealing
school = open(school_file)
school_list = []
grade_list = []
for line in school:
ls = line.split(",")
school_list.append(ls[0])
grade_list.append(int(ls[1]))
# mark up every school and ranks
matplotlib.pyplot.xticks(grade_list, school_list, rotation=270)
grade_probability_list = []
person_probability_list = []
for item in grade_list:
grade_probability_list.append(numpy.exp(-(item - average) ** 2 / (2 * difference)) / (numpy.sqrt(2 * numpy.pi * difference)))
for i in range(0, len(initial_grade_list)):
if initial_grade_list[i] == item:
person_probability_list.append(sum(initial_person_list[:i]))
break
matplotlib.pyplot.yticks(grade_probability_list, person_probability_list)
# plot lines and show
for i in range(0, len(grade_probability_list)):
matplotlib.pyplot.plot([grade_list[i], grade_list[i]], [0, grade_probability_list[i]], '*-')
matplotlib.pyplot.plot([0, grade_list[i]], [grade_probability_list[i], grade_probability_list[i]], '*-')
matplotlib.pyplot.show()
诸位也从函数中看出来了本人是在掐尖(在第三行),对这两个函数加以运行的时候是这样的:
tuple_list = []
tuple1 = analysing_grade("test_guangdong2021.csv")
plot_normal_distribution_with_school(tuple1[0], tuple1[1], "accept_guangdong_2021.csv", tuple1[3],tuple1[4], tuple1[5], tuple1[6])
最后运行的图是这样的(图片的长和宽是3200和2400):
图上显示的x轴,颜色越深说明越多的高校(四五所也是有可能的)大致在这个分数招生。还有一张图:
这张图的目的就是说明广东还是卷的。
如果有时间,本人可以再搜集一下资料做多几个省份的图,也算看看怎么投成这种情况的。



