用python自動生成全校學(xué)生成績報(bào)告

在高中階段源祈,學(xué)生考試成績的分析是一個(gè)重要的而頻繁的應(yīng)用場景,快速墓塌、有效瘟忱、精準(zhǔn)的生成學(xué)生成績的分析報(bào)告,是學(xué)情監(jiān)控和開展個(gè)性化教學(xué)的前提苫幢。這個(gè)問題是基礎(chǔ)性問題访诱,實(shí)現(xiàn)的方法非常多,主要是基于Excel韩肝。在這里用python的pandas做一遍触菜,體會一下辦公自動化的樂趣。這篇文章以高中學(xué)生的成績分析為背景哀峻,使用pandas(是一個(gè)基于numpy的python的數(shù)據(jù)分析包)對學(xué)生成績進(jìn)行分析涡相。本文分為如下部分:

  1. 全校成績表的生成(虛構(gòu))哲泊;
  2. 年級成績分析;
  3. 班級成績分析催蝗;
  4. 學(xué)科成績分析切威;
  5. 總結(jié)與思考。

學(xué)生成績分析是本文的場景丙号,寫這篇文章的目的是總結(jié)我參加華為云大數(shù)據(jù)挑戰(zhàn)賽時(shí)對于pandas的學(xué)習(xí)體會先朦,供大家參考,本文的源碼地址:https://github.com/Fire2341/Learning_Summary槽袄。

在開始之前烙无,導(dǎo)入numpy和pandas,按照習(xí)慣寫成如下形式遍尺。如果沒有這個(gè)模塊截酷,還是老規(guī)矩,使用pip install numpy和pip install pandas安裝一下乾戏。

import numpy as np
import pandas as pd

一迂苛、全校成績表的生成(虛構(gòu))

在開始之前,先生成我們的分析對象鼓择,學(xué)生成績表三幻,假設(shè)本次考試為理科班的摸底考試。學(xué)生成績表包括:

  1. 基本信息呐能,包括:學(xué)生姓名念搬、學(xué)生年級、學(xué)生班級摆出;
  2. 學(xué)生成績朗徊,假設(shè)學(xué)生的成績服從正態(tài)分布,生成的成績包括如下科目:語文(150分)偎漫、數(shù)學(xué)(150分)爷恳、英語(150)、物理(100分)象踊、化學(xué)(100分)温亲、生物(100分),并計(jì)算總分杯矩。

生成的表格流程如下:先確定每個(gè)年級的班級數(shù)目栈虚,并隨機(jī)生成各班人數(shù)(55-68人之間),由此計(jì)算得到全校人數(shù)菊碟。根據(jù)全校人數(shù)隨機(jī)生成學(xué)生姓名节芥,并在確定各科平均值和標(biāo)準(zhǔn)差后,根據(jù)正態(tài)分布規(guī)律隨機(jī)生成各個(gè)學(xué)生的各科成績逆害,并計(jì)算每位學(xué)生的總分头镊,以此獲得一份總的成績匯總表,主要代碼如下魄幕。由于這部分代碼較為冗長且不是主要部分相艇,感興趣的朋友可以點(diǎn)擊源碼查看。

class_name, student_num = generate_class() # 生成班級信息
all_num = students_sum(student_num) # 生成全校學(xué)生總數(shù)
student_name_group = generate_student_name(all_num) # 生成全校學(xué)生名字
student_info = init_table(class_name, student_num, student_name_group) # 將年級纯陨、班級坛芽、學(xué)生信息初始化到表格中
student_list = get_list(all_num, student_info) #生成成績匯總表
student_list.to_excel('學(xué)生成績表2.xlsx') # 保存成績表

取數(shù)據(jù)表的前5個(gè)來看,還真像那么回事(所有名字和成績數(shù)據(jù)均為python隨機(jī)生成翼抠,如有雷同咙轩,純屬巧合)。在生成的成績表中阴颖,一共有3個(gè)年級活喊,其中高一26個(gè)班,高二27個(gè)班量愧,高三個(gè)23班钾菊,各班學(xué)生人數(shù)介于55-68人之間,全校一共4619名學(xué)生偎肃。

為了展現(xiàn)pandas的相較于excel的優(yōu)越性煞烫,在下面的分析中,各部分使用的代碼盡量不超過5行累颂。

二滞详、年級分析

2.1 各年級的最低分、最高分紊馏、平均分和中位數(shù)

為了直觀的反映各年級的整體教學(xué)情況料饥,在這里計(jì)算各年級的各科最高分、最低分瘦棋、平均分和中位數(shù)稀火。在這一部分用到的函數(shù)主要是.groupby和.agg。groupby可以按年級分組赌朋,.agg能夠?qū)Ω髂昙壐鞣纸M應(yīng)用各個(gè)函數(shù)(求最大值凰狞、最小值、平均值沛慢、中位數(shù))進(jìn)行計(jì)算赡若。

subject_name = ['語文','數(shù)學(xué)','英語','物理','化學(xué)','生物','總分']
grade_analysis = student_list.groupby('年級')[subject_name].agg(['max','min','mean','median']).reset_index()
grade_analysis.head()

從結(jié)果來看,這份隨機(jī)生成的成績表团甲,各個(gè)科目的都有人考滿分逾冬,不符合實(shí)際情況,但是符合我的正態(tài)分布規(guī)律了……

2.2 獲取各年級的成績前5的學(xué)生

不管哪個(gè)層級的學(xué)校,拔尖學(xué)生都在學(xué)校人才培養(yǎng)工作占有重要地位身腻,而學(xué)習(xí)成績可以在一個(gè)側(cè)面反映拔尖學(xué)生的范圍产还。在這里篩選各年級成績排名前5的學(xué)生。這一部分用到的函數(shù)主要是.groupby和.sort_values嘀趟。使用.groupby的.rank()獲取年級排名脐区,使用.sort_values按照年級和總分進(jìn)行分組,使用.groupby的.get_group('高三')獲得高三學(xué)生的年級排名她按,在這里展示高三前五名的情況牛隅。

student_list['年級排名'] = student_list['總分'].groupby(student_list['年級']).rank(ascending=False).astype(int)
student_list.sort_values(['年級','總分'], ascending=False, inplace=True)
student_list.groupby('年級').get_group('高三').head()

第1名真是天選之子,隨機(jī)生成的成績都能有3科拿滿分酌泰。

獲取年級倒數(shù)前5的代碼一樣媒佣,把.sort_values()的ascending參數(shù)(是否升序)改為True就可以。

student_list.sort_values(['年級','總分'], ascending=True, inplace=True)
student_list.groupby('年級').get_group('高三').head()
2.3 數(shù)據(jù)保存

數(shù)據(jù)保存是一個(gè)重要的環(huán)節(jié)陵刹,畢竟學(xué)校的成績分析報(bào)告是要打印出來發(fā)給各個(gè)年級主任默伍、班主任的。使用.to_excel()導(dǎo)出到excel中授霸,在這里導(dǎo)出整體情況巡验,以及各個(gè)年級的前五和倒數(shù)前五的情況。

grade_excel = pd.ExcelWriter(r'年級分析.xlsx')
grade_analysis.to_excel(grade_excel, sheet_name='整體情況')

grade_name = ['高一','高二','高三']
student_list.sort_values(['年級','總分'], ascending=False, inplace=True)
for name in grade_name:
    student_list.groupby('年級').get_group(name).head().to_excel(grade_excel, sheet_name=name+'-前五')

student_list.sort_values(['年級','總分'], ascending=True, inplace=True)
for name in grade_name:
    student_list.sort_values(['年級','總分'], ascending=False, inplace=True)
    student_list.groupby('年級').get_group(name).head().to_excel(grade_excel, sheet_name=name+'-倒數(shù)前五')
grade_excel.save()

導(dǎo)出的效果還是不錯(cuò)的碘耳。

3 班級分析

3.1 班級整體情況分析

分析各班第1在年級的位置显设,能夠幫助學(xué)校在整體層面把握各班的教學(xué)質(zhì)量。在班級排名的獲取方法與獲取年級排名的方法一致辛辨,對數(shù)據(jù)表“總分”這一列用“班級”這一列取groupby捕捂,然后對每個(gè)groupby進(jìn)行rank()計(jì)算。

student_list['班級排名'] = student_list['總分'].groupby(student_list['班級']).rank(ascending=False).astype(int)
student_list[student_list['班級排名'] == 1].groupby('年級').get_group('高三')

從下表可以看出斗搞,有個(gè)班級有兩位同學(xué)拿了年級前10指攒,真是天選之班。

使用如下代碼僻焚,進(jìn)一步分析是哪個(gè)班允悦。

student_list.sort_values(['年級排名'],ascending=True,inplace=True)
student_list.groupby('年級').get_group('高三').head(10).groupby(['班級'])['年級排名'].count()

原來天選之班不僅僅只有一個(gè)……

3.2 各班一本率及學(xué)科成績分析

一本率可以說是高中班級中的重要數(shù)據(jù)了,以廣西2019年高考理科一本線(509分)為標(biāo)準(zhǔn)虑啤,計(jì)算各班一本率隙弛。在對各班進(jìn)行g(shù)roupby的基礎(chǔ)上,使用一個(gè)apply函數(shù)計(jì)算各班過一本線的人數(shù)狞山,進(jìn)而計(jì)算一本率全闷。與年級整體數(shù)據(jù)的分析方法類似,按班級分析各科成績的最高分萍启、最低分总珠、平均分屏鳍、中位數(shù),使用.merge()函數(shù)(相當(dāng)于Excel中的vlookup函數(shù))將各班各科的數(shù)據(jù)與前面的一本率數(shù)據(jù)匹配局服,最后按照一本線降序呈現(xiàn)數(shù)據(jù)钓瞭。

class_list = pd.DataFrame()
class_list['一本人數(shù)'] = student_list.groupby('班級')['總分'].apply(lambda x: np.sum((x >=510).astype(int)))
class_list['班級人數(shù)'] = student_num
class_list['一本率'] = class_list['一本人數(shù)']/class_list['班級人數(shù)']
subject_name = ['語文','數(shù)學(xué)','英語','物理','化學(xué)','生物','總分','年級排名']
class_list = class_list.merge(student_list.groupby('班級')[subject_name].agg(['max','min','mean','median']).reset_index(), on='班級', how='left')
class_list.head()

從圖中可以看出,全校一本率最高的班級是1704班腌逢,但是一本率僅有36.2%降淮,看來這個(gè)虛構(gòu)的學(xué)校成績并不好……

3.3 數(shù)據(jù)保存

按照類似與年級數(shù)據(jù)的保存方法超埋,保存班級分析的數(shù)據(jù)搏讶。

class_excel = pd.ExcelWriter(r'班級分析.xlsx')
class_list.to_excel(class_excel, sheet_name='整體情況')
for name in grade_name:
    student_list[student_list['班級排名'] == 1].groupby('年級').get_group(name).to_excel(class_excel, sheet_name=name)
class_excel.save()

四、學(xué)科分析

4.1 分?jǐn)?shù)段分析

對于各個(gè)學(xué)科霍殴,比較重要的數(shù)據(jù)是各個(gè)年級媒惕、各個(gè)分?jǐn)?shù)段的人數(shù)情況。使用的方法pd.cut()函數(shù)来庭,對各個(gè)學(xué)科的各個(gè)分?jǐn)?shù)段進(jìn)行切割妒蔚,并且保存為DataFrame(本文所有數(shù)據(jù)均為此格式)。由于在這個(gè)分析中有按照年級的循環(huán)求解月弛,所以直接在開頭就定義寫入excel的subject_excel對象肴盏,在循環(huán)結(jié)尾直接.to_excel()保存。

subject_excel = pd.ExcelWriter(r'學(xué)科分析.xlsx')

bins = [0,40,60,80,100,120,140,150]

group_name = ['高一','高二','高三']
subject_name = ['語文','數(shù)學(xué)','英語','物理','化學(xué)','生物']
for name in group_name:
    grade = student_list.groupby('年級').get_group(name)
    df = pd.DataFrame()
    for s_name in subject_name:
        cuts = pd.cut(grade[s_name],bins=bins) #可選label添加自定義標(biāo)簽
        subject_cut = grade.groupby(cuts)[s_name].count()
        df[s_name] = subject_cut
    df.index.name = name
    df.to_excel(subject_excel, sheet_name=name+'成績分布')
df

從成績區(qū)間可以看出帽衙,成績主要分布在平均數(shù)附近菜皂,是很符合正態(tài)分布了。想起在前面分析年級倒數(shù)的時(shí)候有個(gè)學(xué)生生物81分厉萝,但是年級排名倒數(shù)第3恍飘,是很偏科了。

4.2 偏科學(xué)生分析

偏科學(xué)生在某些學(xué)科是潛力股谴垫,發(fā)現(xiàn)偏科學(xué)生在某種意義上是發(fā)現(xiàn)“好苗子”的過程章母。在本次成績表中,假定數(shù)學(xué)成績大于130且語文和英語成績小于90分的學(xué)生為偏科學(xué)生翩剪。先構(gòu)建一個(gè)'數(shù)學(xué)偏科'的flag乳怎,然后進(jìn)行篩選。在這一部分的最后前弯,把學(xué)科分析部分的數(shù)據(jù)保存到excel中蚪缀。

student_list['數(shù)學(xué)偏科'] = ((student_list['數(shù)學(xué)']>=130) & (student_list['語文']<90) & (student_list['英語']<90)).astype(int)
partial = student_list[student_list['數(shù)學(xué)偏科'] == 1]
partial.drop(['數(shù)學(xué)偏科'],axis=1,inplace=True)
partial.to_excel(subject_excel, sheet_name='偏科學(xué)生')
subject_excel.save()
partial.head()

圖中的晉啟同學(xué)數(shù)學(xué)考了149,其他學(xué)科都不超過80博杖,是很偏科了椿胯。

使用如下代碼,看看各個(gè)年級有多少人是數(shù)學(xué)偏科的剃根×ぃ可以看到,高二年級的偏科人數(shù)最多。

partial.groupby('年級')['姓名'].count()

五廉油、思考

代碼和文章寫到這里已經(jīng)寫了 6個(gè)多小時(shí)了惠险,需要寫一個(gè)結(jié)尾總結(jié)一下收獲,減輕一下不務(wù)正業(yè)抒线,沒有讀文獻(xiàn)的負(fù)罪感班巩。有人說這些事情用Excel完全可以辦到,為什么要在這里寫代碼呢嘶炭?全文中使用到的方法抱慌,函數(shù)均為我在做一個(gè)大數(shù)據(jù)比賽時(shí)學(xué)到的函數(shù),換一個(gè)場景使用證明我學(xué)會了眨猎;其次抑进,那個(gè)比賽需要處理的數(shù)據(jù)量是1.47億條,用Excel打不開(T_T)睡陪,所以寫代碼也是適用于大規(guī)模的數(shù)據(jù)處理寺渗;另外,代碼具有復(fù)用性兰迫,只要使用場景不變信殊,數(shù)據(jù)格式不變,可以說是一勞永逸的汁果,適合于重復(fù)機(jī)械的工作場景涡拘。比如高中學(xué)生考試,有周測须鼎、月考鲸伴、段考、期末考晋控、摸底考……汞窗,而跑一次代碼就可以生成成績報(bào)告,為啥還要每次重復(fù)操作Excel呢赡译?

附錄

這篇文章主要用到如下方法:

  1. .groupby()系列方法仲吏,包括.groupby().agg(), .groupby().apply(), .groupby().get_group()等,用以實(shí)現(xiàn)分年級蝌焚、分班級計(jì)算裹唆;
  2. pd.read_excel(), pd.to_excel(),Excel的讀取與存儲只洒;
  3. pd.merge()许帐,數(shù)據(jù)篩選,相當(dāng)于Excel中的vlookup毕谴;
  4. df.sort_values()成畦,對數(shù)據(jù)進(jìn)行排序距芬;
  5. df.drop(),按條件去除某行或某列循帐;
  6. df.cut()框仔,給定區(qū)間,分析數(shù)據(jù)所屬區(qū)間拄养;
  7. df.count()离斩,分析某列數(shù)據(jù)各個(gè)元素的值,結(jié)合.groupby可以實(shí)現(xiàn)Excel的數(shù)據(jù)透視表效果瘪匿。

其他方法(未在此場景中應(yīng)用):

  1. np.unique(), 去除numpy的重復(fù)值跛梗,df.drop_duplicates(),按條件去除DataFrame的重復(fù)值柿顶;
  2. pd.to_datetime()茄袖,將其他類型的時(shí)間數(shù)據(jù)轉(zhuǎn)換為時(shí)間戳,df.dt.total_seconds()嘁锯,把時(shí)間數(shù)據(jù)轉(zhuǎn)化為秒;
  3. df.diff(1)聂薪,計(jì)算某兩列的差值(一般是計(jì)算dt時(shí)間內(nèi)變量的變化量)
  4. df.shift()家乘,向上或向下移動某列數(shù)據(jù);
  5. df.fillna()藏澳,缺失值填充仁锯;
  6. .loc[],按當(dāng)前索引提取某行翔悠;.iloc[]业崖,按數(shù)字提取某行;
  7. .isin()蓄愁,分析某列元素是否在另一數(shù)組中双炕。

源碼鏈接:https://github.com/Fire2341/Learning_Summary

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市撮抓,隨后出現(xiàn)的幾起案子妇斤,更是在濱河造成了極大的恐慌,老刑警劉巖丹拯,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件站超,死亡現(xiàn)場離奇詭異,居然都是意外死亡乖酬,警方通過查閱死者的電腦和手機(jī)死相,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咬像,“玉大人算撮,你說我怎么就攤上這事双肤。” “怎么了钮惠?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵茅糜,是天一觀的道長。 經(jīng)常有香客問我素挽,道長蔑赘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任预明,我火速辦了婚禮缩赛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘撰糠。我一直安慰自己酥馍,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布阅酪。 她就那樣靜靜地躺著旨袒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪术辐。 梳的紋絲不亂的頭發(fā)上砚尽,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音辉词,去河邊找鬼必孤。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瑞躺,可吹牛的內(nèi)容都是我干的敷搪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼幢哨,長吁一口氣:“原來是場噩夢啊……” “哼赡勘!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嘱么,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤狮含,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后曼振,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體几迄,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年冰评,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了映胁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡甲雅,死狀恐怖解孙,靈堂內(nèi)的尸體忽然破棺而出坑填,到底是詐尸還是另有隱情,我是刑警寧澤弛姜,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布脐瑰,位于F島的核電站,受9級特大地震影響廷臼,放射性物質(zhì)發(fā)生泄漏苍在。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一荠商、第九天 我趴在偏房一處隱蔽的房頂上張望寂恬。 院中可真熱鬧,春花似錦莱没、人聲如沸初肉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽牙咏。三九已至,卻和暖如春属铁,著一層夾襖步出監(jiān)牢的瞬間眠寿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工焦蘑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人盒发。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓例嘱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親宁舰。 傳聞我的和親對象是個(gè)殘疾皇子拼卵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355