用python預測高考成績
導讀: >1、資料集選用“某年上海某區高考二模成績”
2、上海過去幾年的高考制度採用“3+1+1”的模式,語數英 + 加一學科 + 綜合,加一學科指在物理、化學、生物、地理、政治、歷史中選其一。(這條資訊很重要,有助於幫我們理解資料)
使用工具 >pandas、numpy
>matplotlib 資料視覺化 >Jupyter Notebook 資料分析利器 # 一、匯入資料
# 導包
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei'] # 用來正常顯示中文標籤
# plt.rcParams['axes.unicode_minus']=False # 用來正常顯示負號
import pandas as pd
import numpy as np
# 匯入資料
filename = './**.xls'
# df_w 文科生原始資料
# df_l 理科生原始資料
df_w = pd.read_excel(filename, sheet_name=0)
df_l = pd.read_excel(filename, sheet_name=1 )
# 檢視文科生資料
print(df_w.columns)
df_w.head(3)
Index([‘學科’, ‘學校’, ‘准考證號’, ‘姓名’, ‘語文’, ‘數學’, ‘英語’, ‘五門調整總分’, ‘總分排名’], dtype=’object’)
學科 | 學校 | 准考證號 | 姓名 | 語文 | 數學 | 英語 | 五門調整總分 | 總分排名 | |
---|---|---|---|---|---|---|---|---|---|
0 | 政治 | 吳淞中學 | 110203110** | 周** | 102.0 | 110.0 | 127.5 | 459.1 | 8 |
1 | 政治 | 吳淞中學 | 110203110** | 楊** | 102.0 | 101.0 | 114.5 | 452.7 | 15 |
2 | 政治 | 吳淞中學 | 110203110** | 趙** | 95.0 | 108.0 | 128.5 | 448.3 | 19 |
# 檢視理科生資料
print(df_l.columns)
df_l.head(3)
Index([‘學科’, ‘學校’, ‘准考證號’, ‘姓名’, ‘語文’, ‘數學’, ‘英語’, ‘加一調整分’, ‘五門總分’, ‘總分排名’], dtype=’object’)
學科 | 學校 | 准考證號 | 姓名 | 語文 | 數學 | 英語 | 加一調整分 | 五門總分 | 總分排名 | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 物理 | 行知中學 | 110101410** | 秦** | 109.0 | 127.0 | 123.5 | 117 | 494.9 | 38 |
1 | 物理 | 行知中學 | 110101410** | 王** | 98.5 | 103.5 | 128.5 | 111 | 461.3 | 189 |
2 | 物理 | 行知中學 | 110101410** | 金** | 102.0 | 135.0 | 123.0 | 127 | 507.0 | 21 |
# 檢視資料格式
# 是否需要轉換
df_l.dtypes
學科 object
學校 object
准考證號 int64
姓名 object
語文 float64
數學 float64
英語 float64
加一調整分 int64
五門總分 float64
總分排名 int64
dtype: object
df_w.dtypes
學科 object
學校 object
准考證號 int64
姓名 object
語文 float64
數學 float64
英語 float64
五門調整總分 float64
總分排名 int64
dtype: object
從上面發現數據質量很高,非常工整。但也存在幾個小問題。
>1、文科生沒有“加一調整分”列2、文科生“五門調整總分”同 文科生“五門總分”列名不相同。 # 二、資料處理
# 修改文科生列名
df_w = df_w.rename(columns = {'五門調整總分':'五門總分'})
df_w.head()
學科 | 學校 | 准考證號 | 姓名 | 語文 | 數學 | 英語 | 五門總分 | 總分排名 | |
---|---|---|---|---|---|---|---|---|---|
0 | 政治 | 吳淞中學 | 110203110** | 周** | 102.0 | 110.0 | 127.5 | 459.1 | 8 |
1 | 政治 | 吳淞中學 | 110203110** | 楊** | 102.0 | 101.0 | 114.5 | 452.7 | 15 |
2 | 政治 | 吳淞中學 | 110203110** | 趙** | 95.0 | 108.0 | 128.5 | 448.3 | 19 |
3 | 政治 | 吳淞中學 | 110203111** | 李** | 107.0 | 96.0 | 116.0 | 446.6 | 23 |
4 | 歷史 | 吳淞中學 | 110203211** | 陳** | 103.0 | 97.0 | 121.5 | 445.9 | 26 |
# 合併文科和理科學生資料
df_new = pd.concat([df_w, df_l], ignore_index=True)
# 調整列的順序,使看起來更順眼
columns = ['學科','學校','准考證號','姓名','語文','數學','英語','加一調整分','五門總分','總分排名']
df_new = pd.DataFrame(df_new, columns=columns)
# 在“五門總分”前插入新的一列“三門總分”
sanmen = df_new['語文'] + df_new['數學'] + df_new['英語']
df_new.insert(8,'三門總分',sanmen)
df_new.head()
.
學科 | 學校 | 准考證號 | 姓名 | 語文 | 數學 | 英語 | 加一調整分 | 三門總分 | 五門總分 | 總分排名 | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 政治 | 吳淞中學 | 110203110** | 周** | 102.0 | 110.0 | 127.5 | NaN | 339.5 | 459.1 | 8 |
1 | 政治 | 吳淞中學 | 110203110** | 楊** | 102.0 | 101.0 | 114.5 | NaN | 317.5 | 452.7 | 15 |
2 | 政治 | 吳淞中學 | 110203110** | 趙** | 95.0 | 108.0 | 128.5 | NaN | 331.5 | 448.3 | 19 |
3 | 政治 | 吳淞中學 | 110203111** | 李** | 107.0 | 96.0 | 116.0 | NaN | 319.0 | 446.6 | 23 |
4 | 歷史 | 吳淞中學 | 110203211** | 陳** | 103.0 | 97.0 | 121.5 | NaN | 321.5 | 445.9 | 26 |
簡單資料處理後,我們把文科生成績和理科生成績的兩張資料表合併了,由於文科生資料表中沒有“加一調整分”一列,因此所有文科生的該列為空(NaN),然後我們可以進入進一步挖掘了。
# 三、資料分析 經過之前的資料處理,現在我們可以對資料集進行基礎的描述性統計分析。# 分析資料 這裡只對文科學生分析
#獲取描述統計資訊
df_w.describe()
准考證號 | 語文 | 數學 | 英語 | 五門總分 | 總分排名 | |
---|---|---|---|---|---|---|
count | 7.310000e+02 | 731.000000 | 731.000000 | 731.000000 | 731.000000 | 731.000000 |
mean | 1.105844e+10 | 90.929549 | 74.932969 | 94.608071 | 363.442134 | 366.000000 |
std | 3.591213e+07 | 9.131769 | 19.268461 | 19.775992 | 52.073473 | 211.165812 |
min | 1.101091e+10 | 60.000000 | 10.000000 | 24.500000 | 191.500000 | 1.000000 |
25% | 1.103102e+10 | 85.000000 | 64.000000 | 83.750000 | 333.950000 | 183.500000 |
50% | 1.105071e+10 | 92.000000 | 77.000000 | 98.000000 | 371.700000 | 366.000000 |
75% | 1.108063e+10 | 98.000000 | 88.000000 | 108.875000 | 399.400000 | 548.500000 |
max | 1.114013e+10 | 119.000000 | 128.000000 | 132.000000 | 483.500000 | 731.000000 |
#先按五門總分排序
df_w2 = df_w.sort_values(by='五門總分',ascending=False)
#重新命名行名(index):排序後的列索引值是之前的行號,需要修改成從0到N按順序的索引值
df_w=df_w2.reset_index(drop=True)
df_w.head()
學科 | 學校 | 准考證號 | 姓名 | 語文 | 數學 | 英語 | 五門總分 | 總分排名 | |
---|---|---|---|---|---|---|---|---|---|
0 | 歷史 | 行知中學 | 11011021346 | 張則見 | 114.0 | 105.0 | 131.5 | 483.5 | 1 |
1 | 歷史 | 行知中學 | 110110213** | 葉** | 105.0 | 112.0 | 130.5 | 479.5 | 2 |
2 | 歷史 | 行知中學 | 110110213** | 李** | 107.0 | 115.0 | 119.0 | 476.2 | 3 |
3 | 歷史 | 行知中學 | 110110213** | 陳** | 103.0 | 128.0 | 121.0 | 475.2 | 4 |
4 | 政治 | 羅店中學 | 110401123** | 周** | 105.0 | 112.0 | 121.0 | 461.8 | 5 |
從上表中我們可以看到
1、最高分(max)和最低分(min)相差非常大,說明學生成績個體差異很大。
2、平均值(mean)低於中們數(50%),說明成績差的學生拖後腿嚴重。
下面我們用更直觀的圖表來分析
# 再進行資料視覺化
fig = plt.figure(figsize=(18,10))
ax1 = plt.subplot2grid((1,1),(0,0))
zf_mean = df_w['五門總分'].mean() # 五門總分平均數
pm_mean = df_w['總分排名'].mean() # 總分排名平均數
w_num = len(df_w) # 文科生學生數量
sanmen = df_w['語文']+df_w['數學']+df_w['英語']
x = range(w_num)
ax1.scatter(x, df_w['語文'], color='r', edgecolor='none', s=10)
ax1.scatter(x, df_w['數學'], color='g', edgecolor='none', s=10)
ax1.scatter(x, df_w['英語'], color='b', edgecolor='none', s=10)
ax1.plot(df_w['五門總分'],label='五門總分',color='b',lw=0.8)
ax1.plot(sanmen,label='三門總分', color='r',lw=0.8)
# 填充顏色
ax1.fill_between(x,df_w['五門總分'],sanmen, facecolor='g', alpha=0.4)
# 五門總分平均分點
ax1.scatter(pm_mean,zf_mean, color='r', s=100)
# 畫水平線
ax1.axhline(100, color='k', lw=0.8)
# 畫垂直線
ax1.axvline(pm_mean, color='k', lw=0.6, linestyle = "dashed")
#調整座標軸, w_num為學生人數,500為五門總分最高分
plt.axis([0,w_num, 0, 500])
plt.xlabel('學生排名',fontsize=18)
plt.ylabel('分數',fontsize=18)
plt.title('上海某區高考二模成績排名表\n(文科生)',fontsize=25)
plt.legend()
plt.show()
上圖說明:
1、縱座標表示成績,橫座標表示排名
2、紅點表示語文,結點表示數學,藍點表示英語,他們的高低分佈表示分數的高低。
3、綠色填充部分表示“ 加一學科 + 綜合”和的成績。(五門總成績-三門語數英成績和)
4、垂直虛線表示排名平均數位置,大紅點表示總分平均分。
5、沿著橫座標往右,每一個垂直線表示一個學生的成績。(為使影象簡潔,網路線沒畫)
如果只分析語文、數學、英語對總成績的影響,我們可以得出以下結論:
1、成績好的學生這三門成績相對都很好,成績好的學生英語成績普遍較好
2、極個別同學偏科非常嚴重,英語最容易得高分,數學失分最嚴重。
3、三科中語文成績差值最小,也就是上面分析到描述統計資訊中的標準差(std)值最小。所以對總分影響最小
理科生分析略。
四、分析全部學生
下面開始對全部學生的分析。
首先,我們對“學科”列進行分組(groupby),來看一個各科目的學生情況:
# 按學科分組,再對統計准考證數檢視各科目的考和人數
g_xueke = df_new.groupby('學科')
g_xueke['准考證號'].count().sort_values(ascending=False)
學科
化學 1072
物理 758
歷史 383
生物 261
政治 212
地理 136
Name: 准考證號, dtype: int64
緊拉著,我們對各學科考生的平均成績狀況進行一個大致的判斷:
# 各學科考生的平均分
g_xueke[['語文']].mean().sort_values(by='語文', ascending=True)
g_xueke[['數學']].mean().sort_values(by='數學', ascending=True)
g_xueke[['英語']].mean().sort_values(by='英語', ascending=True)
g_xueke[['三門總分']].mean().sort_values(by='三門總分', ascending=True)
g_xueke[['五門總分']].mean().sort_values(by='五門總分', ascending=True)
得到表格如下
我們可以得出以下結論:
1、選化學的學生最多,其次是物理,最少的是生物。
2、政治班語文平均分最高,物理班其次。
3、物理班英語平均分最高,化學班其次。
4、物理班三門總分平均分和五門總分平均分最高,其次是化學。
總體來說物理班的總體實力最強,五門總分平均分高的學生,語文、數學、英語成績也普遍較好,和之前分析文科班的情況相似。
再業,我們對“學校”特徵進行分組(groupby):
# 按照學校分組,並檢視各學校學生數量
g_xuexiao = df_new.groupby('學校')
g_xuexiao['准考證號'].count().sort_values(ascending=False)
學校
行知中學 520
上大附中 447
吳淞中學 352
羅店中學 310
寶山中學 287
顧村中學 168
通河中學 167
行知實驗 136
高境一中 104
和衷高中 100
淞浦中學 99
上大附中新疆部 66
同洲模範 26
行中中學 22
建峰高中 18
Name: 准考證號, dtype: int64
這邊我們發現了3組異常值:
同洲模範、行中中學、建峰高中三所學校的人數比較奇怪,只有20人左右
查閱網上資料後瞭解到:
1、行中中學是行知中學放置借讀生名額的學校(為了不影響升學率的手段)
2、建峰高中是一所高職,其中會參加高考的學生並不多,因此參加高考模擬考的學生也不多
3、許多人指責同洲模範高中部很差(似乎初中部還不錯?),可能校方有意收縮高中部招生
同樣地,我們對各學校考生的平均成績狀況進行一個大致的判斷:
# 各學校考生的平均分
g_xuexiao[['語文']].mean().sort_values(by='語文', ascending=True)
g_xuexiao[['數學']].mean().sort_values(by='數學', ascending=True)
g_xuexiao[['英語']].mean().sort_values(by='英語', ascending=True)
g_xuexiao[['三門總分']].mean().sort_values(by='三門總分', ascending=True)
g_xuexiao[['五門總分']].mean().sort_values(by='五門總分', ascending=True)
得到表格如下
得到的結果是:
語文平均分第一:羅店中學
數學平均分第一:行知中學
英語平均分第一:吳淞中學
三門平均分第一:行知中學
五門平均分第一:行知中學
有趣的是,行知中學資料平均分第一,並且也獲得了三門平均分第一和五門平均分第一
難道數學考得好,更容易拿更高的名次?
從上面的文科生分析我們可以得出,數學的離散性非常大,標準差(std)也最大。
我們再嘗試通過皮爾森相關係數驗證一下(皮爾森相關係數能夠體現兩組資料之間的線性關係):
# 分析各科成績與總分之間的相關係數
df_new[['數學','語文','英語','三門總分','五門總分']].corr()
數學 | 語文 | 英語 | 三門總分 | 五門總分 | |
---|---|---|---|---|---|
數學 | 1.000000 | 0.370773 | 0.544709 | 0.867267 | 0.858378 |
語文 | 0.370773 | 1.000000 | 0.503907 | 0.653351 | 0.617937 |
英語 | 0.544709 | 0.503907 | 1.000000 | 0.856589 | 0.824188 |
三門總分 | 0.867267 | 0.653351 | 0.856589 | 1.000000 | 0.972618 |
五門總分 | 0.858378 | 0.617937 | 0.824188 | 0.972618 | 1.000000 |
果然數學與總分的相關係數最高,英語略低於數學,語文最低,而且低很多
我們進一步觀測語數英成績這三組的資料:
# 獲取描述統計資訊
df_new[['數學','語文','英語']].describe()
數學 | 語文 | 英語 | |
---|---|---|---|
count | 2822.000000 | 2822.000000 | 2822.000000 |
mean | 87.368179 | 91.223600 | 99.694011 |
std | 21.731483 | 9.186677 | 18.098783 |
min | 0.000000 | 0.000000 | 18.000000 |
25% | 73.000000 | 86.000000 | 90.000000 |
50% | 88.000000 | 92.000000 | 102.000000 |
75% | 103.000000 | 97.000000 | 113.000000 |
max | 147.000000 | 120.000000 | 141.000000 |
我們發現數學的標準差最大,這說明了數學成績離散程度比較高,每一分的考生人數比較少,因此:數學每提高1分能幹掉的其他考生比其他科目少,所以似乎看來提高數學成績是收益最低的?
的確,數學沒提高1分,對於考生在數學考試上的排名提升效果較小,但是在實踐考試中我們是按照語數英三門科目加總後進行排名的(而且在加總的時候不分配權重),這就說明數學的1分,英語的1分,語文的1分,在最終三門總排名上的影響是等價的。
我們再回到數學標準差較大的討論上,我認為數學成績方差高是一種現象,導致這種現象的是因為數學的評分機制的關係:數學題不會就是不會,經常會出現一道大題20份只拿1分的情況(解: +1分),而語文不會做也能瞎謅謅,英語則題量比較大,得分點分配的很散。再進一步說,就是方差高說明考生在該項考試中成績差距顯著,考生們更容易在該項考試中被拉出等第,也更容易方便學校來選拔考生(之前復旦大學自主招生千分考專案,應該也是同樣的思路,高方差為了更便於篩選考生)。
舉個例子就是說:假設某個考生A,數學略高於平均水平,英語語文略低於平均水平,在數學考試高方差的放大下,最後考生A的三門總分排名可能是高於平均水平的。而考生B,語文略高於平均水平,數學英語略低於平局水平,在語文考試低方差的影響下,最後考生B的三門總分排名往往是低於平均水平的。
五、結論
最終我得出的結論是,如果將考試分為n門,理論上來說這n門的最終成績的方差應該是近似的,如果其中有一門的方差偏高,說明這一門更能凸顯考生之間的差距(也就是可以拉開排名上的差距),如果作為考生,應該在方差大的科目上多努力,這樣有助於最後提高排名(當然是在最終成績不加權的情況下)。
以上分析結論複製自https://www.zhihu.com/people/paladinxiao-key/activities’ ButtersPC)向你學習!