鄧擎瓊,彭煒明,尹 乾,趙世鳳,王學(xué)松
(北京師范大學(xué)信息科學(xué)與技術(shù)學(xué)院,北京100875)
Python語(yǔ)言已經(jīng)成為國(guó)際最受歡迎的程序設(shè)計(jì)語(yǔ)言,國(guó)外眾多大學(xué)從2009年開始針對(duì)該語(yǔ)言廣泛開設(shè)相關(guān)課程。然而國(guó)內(nèi)起步較晚,直到2016年,教育部高等學(xué)校大學(xué)計(jì)算機(jī)課程教學(xué)指導(dǎo)委員會(huì)才首次建議將Python語(yǔ)言作為程序設(shè)計(jì)入門課程的教學(xué)語(yǔ)言[1],近2年來,國(guó)內(nèi)一批高校逐步開設(shè)了Python語(yǔ)言教學(xué)。由于教學(xué)實(shí)踐時(shí)間尚短,國(guó)內(nèi)Python教學(xué)中多沿用國(guó)外的教學(xué)案例或其他程序設(shè)計(jì)語(yǔ)言的教學(xué)案例,其中前者導(dǎo)致教學(xué)案例缺乏中國(guó)特色,后者導(dǎo)致教學(xué)案例缺乏Python特色。例如針對(duì)文本詞頻統(tǒng)計(jì)這一問題,國(guó)內(nèi)高校往往使用國(guó)外教材或教學(xué)中的案例,對(duì)莎士比亞的經(jīng)典作品《哈姆雷特》[2]、古騰堡文學(xué)作品[3]、美國(guó)總統(tǒng)就職演說等進(jìn)行詞頻統(tǒng)計(jì)和分析,顯然這樣的教學(xué)案例遠(yuǎn)離中國(guó)大學(xué)生的學(xué)習(xí)和生活,因此缺乏吸引力。再如在Python循環(huán)結(jié)構(gòu)的教學(xué)中,國(guó)內(nèi)高校往往還以閏年判斷、打印水仙花數(shù)等問題作為案例[4],這些問題既不是學(xué)生關(guān)心的問題,也不是熱點(diǎn)問題,因此也很難給學(xué)生留下深刻印象。
而另一方面,Python有強(qiáng)大的計(jì)算生態(tài)環(huán)境,全球有超過十萬個(gè)第三方庫(kù),幾乎覆蓋所有技術(shù)領(lǐng)域[5]。依托這樣的生態(tài)圈,國(guó)內(nèi)高校中從事Python教學(xué)的老師可以便捷高效地創(chuàng)建出新的教學(xué)案例。什么樣的教學(xué)案例能引起學(xué)生的共鳴,這是一個(gè)值得思考的問題。針對(duì)上述國(guó)內(nèi)Python教學(xué)案例中的問題,依托Python強(qiáng)大的計(jì)算生態(tài)環(huán)境,我們提出了Python實(shí)用型案例教學(xué)方法。一是通過分析大學(xué)生在學(xué)習(xí)和生活中遇到的實(shí)際問題,構(gòu)建相應(yīng)的教學(xué)案例;二是針對(duì)熱點(diǎn)問題、熱點(diǎn)資訊構(gòu)造教學(xué)案例。這些案例不僅能讓學(xué)生感知Python計(jì)算生態(tài)圈的功能之強(qiáng)大和多樣,更能增強(qiáng)學(xué)生程序設(shè)計(jì)學(xué)習(xí)興趣,激發(fā)學(xué)生自主學(xué)習(xí)和運(yùn)用Python計(jì)算生態(tài),最終提高實(shí)際解決問題的能力。
本文具體針對(duì)詞頻統(tǒng)計(jì)問題,給出了兩個(gè)教學(xué)案例。一是針對(duì)大學(xué)英語(yǔ)四、六級(jí)考試,通過對(duì)歷年試題分析獲得其中的高頻詞,輔助學(xué)生進(jìn)行英語(yǔ)復(fù)習(xí);二是在中央電視臺(tái)科教頻道大型文化益智節(jié)目《中國(guó)詩(shī)詞大會(huì)》的引領(lǐng)下,帶動(dòng)學(xué)生對(duì)不同詩(shī)人、不同朝代以及不同風(fēng)格的詩(shī)詞進(jìn)行比較,從中分享詩(shī)詞之美,感受詩(shī)詞之趣。
我們對(duì)大學(xué)英語(yǔ)四、六級(jí)歷年考試真題進(jìn)行分析,從中挖掘出??荚~。這一案例對(duì)學(xué)生吸引力非常大,且案例運(yùn)行結(jié)果實(shí)用性強(qiáng),能輔助學(xué)生進(jìn)行復(fù)習(xí),提高考試成績(jī)。
該案例是一項(xiàng)綜合性案例,展示了Python多個(gè)知識(shí)點(diǎn)的運(yùn)用,包括:首先利用Reqests和Beatifulsoup兩個(gè)Python第三方庫(kù)爬取相關(guān)網(wǎng)頁(yè)獲得英語(yǔ)考試歷年考題以及四、六級(jí)詞匯表;然后對(duì)試題文本進(jìn)行規(guī)范化處理,得到其中的四級(jí)或六級(jí)單詞,并進(jìn)行詞頻統(tǒng)計(jì);最后把詞頻統(tǒng)計(jì)結(jié)果保存為CSV文件,并利用Matplotlib庫(kù)對(duì)高頻詞及每年出現(xiàn)次數(shù)進(jìn)行直觀顯示。下面以英語(yǔ)六級(jí)考試為例,詳細(xì)介紹該案例的具體實(shí)現(xiàn)過程及結(jié)果。
很多英語(yǔ)學(xué)習(xí)網(wǎng)站,如新東方在線、滬江英語(yǔ)等都提供歷年考試真題和單詞詞匯表。通過比較這些網(wǎng)站,最終我們選擇在http://www.233.com/cet4/zhuanti/linianzhenti/上爬取2014年6月至2016年12月的考試真題,以及在http://cet6.koolearn.com/20160210/796642.html上爬取詞匯表。原因是上述兩個(gè)網(wǎng)站資料相對(duì)齊全且分類整齊,利于進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)獲取。在獲取過程中主要用到requests.get()和BeautifulSoup中的find()方法。其中前者用于得到網(wǎng)頁(yè)內(nèi)容,后者用于從網(wǎng)頁(yè)內(nèi)容中解析和獲取需要的數(shù)據(jù)。為便于后續(xù)分析,在爬取每一年的試題數(shù)據(jù)后,把該數(shù)據(jù)保存為txt文件,存在本地磁盤。單詞詞匯表數(shù)據(jù)做同樣處理。
通過上述步驟獲得的試題書寫很不規(guī)范,里面含有很多中英文標(biāo)點(diǎn)符號(hào)、中文字符等,且不同句子間經(jīng)常無空格分開,因此需要對(duì)這些文本進(jìn)行規(guī)范化處理。此外,英語(yǔ)單詞存在各種形態(tài)變化:動(dòng)詞的多種時(shí)態(tài)(過去時(shí)、過去完成時(shí)、一般現(xiàn)在時(shí)和現(xiàn)在進(jìn)行時(shí))、名詞復(fù)數(shù)形式、形容詞比較級(jí)等。而六級(jí)詞匯表中保存的都是單詞原型,如動(dòng)詞為一般現(xiàn)在時(shí)態(tài)、名詞為單數(shù)形式等。因此,單詞提取還包括一個(gè)“詞形還原”的過程,即對(duì)不同時(shí)態(tài)變化的動(dòng)詞、復(fù)數(shù)名詞、比較級(jí)形容詞等進(jìn)行還原處理,得到原型單詞(即詞元)。之后再判斷該單詞是否為六級(jí)單詞,如果是則保存至一個(gè)列表;不是則摒棄。詞形還原在自然語(yǔ)言處理中非常常見。常用第三方模塊NLTK來處理。處理過程一般為:首先用字符串的split()方法或NLTK提供的分句工具(例如punktsentencesegmenter)把整套試題分解成一個(gè)一個(gè)的句子,然后對(duì)每一個(gè)句子用NLTK提供的工具(例如word_tokenize和pos_tag)對(duì)其進(jìn)行分詞和詞性標(biāo)注,得到該句子中的每一個(gè)單詞和它的詞性,之后對(duì)每個(gè)單詞根據(jù)其詞性采用NLTK提供的詞性還原工具(例如WordNetLemmatizer)得到對(duì)應(yīng)的詞元。這樣得到的詞元很準(zhǔn)確,但國(guó)內(nèi)大多數(shù)高校將Python是作為入門語(yǔ)言進(jìn)行教學(xué),上述NLTK模塊的實(shí)現(xiàn)有些難度,因此可根據(jù)教學(xué)情況采用簡(jiǎn)化方法。例如,可采用字符串的方法對(duì)一般化變形的動(dòng)詞和名詞實(shí)現(xiàn)詞形還原,具體代碼如下:
importstring
defloadExamp(fname):#fname為一套txt試題文件
withopen(fname,'rt',encoding='UTF-8')asf:
text=f.read().lower()
lst=[]#lst保存試題中包含的六級(jí)單詞
text=text.replace('--','')
text=text.replace('(','')
text=text.replace(')','')
text=text.replace('?','')#去除無空格分割的標(biāo)點(diǎn)符號(hào)
wordlist=text.split()
forwordinwordlist:
word=word.strip(',。:、!“”‘’{}【】()')#去除中文標(biāo)點(diǎn)符號(hào)
word=word.strip(string.punctuation)#去除英文標(biāo)點(diǎn)符號(hào)
ifwordincet6:
#cet6為六級(jí)單詞列表,下面為詞形還原過程
lst.append(word)
elifword.endswith(('s','ed'))andword[0:-1]incet6:#例works→work、liked→like
lst.append(word[0:-1])
elifword.endswith(('es','ed'))andword[0:-2]in cet6:#例boxes→box、worked→work
lst.append(word[0:-2])
elifword.endswith('ing'):
ifword[0:-3]incet6:lst.append(word[0:-3])#例:working→work
elifword[0:-3]+'e'incet6:lst.append(word[0:-3]+'e')#例:liking→like
elifword.endswith(('ies','ied'))andword[0:-3]+'y'incet6:
lst.append(word[0:-3]+'y')#例:studies、studied→study
returnlst
該過程也可采用Python中的標(biāo)準(zhǔn)模塊——re模塊來實(shí)現(xiàn),這樣會(huì)更簡(jiǎn)潔,具體代碼如下:
importre
defloadExamp(fname):#fname為一套txt試題文件
withopen(fname,'rt',encoding='UTF-8')asf:
text=f.read()
lst=[]#lst保存試題中包含的六級(jí)單詞
forwordinre.findall('[A-Z]?[a-z]+',text):
#提取英語(yǔ)單詞
word=word.lower()
ifwordincet6:#cet6為六級(jí)單詞列表
lst.append(word)
else:#詞形還原后再判斷是否為六級(jí)單詞
forrulein[('(s|e[ds]|ing)$',''),('(e[ds]|ing)$','e'),('ie[sd]$','y')]:
stem=re.sub(rule[0],rule[1],word)
ifstemincet6:
lst.append(stem)
break
returnlst
在得到每一年試題中所出現(xiàn)的六級(jí)單詞列表之后,統(tǒng)計(jì)單詞頻率就變成很簡(jiǎn)單。同樣,有多種實(shí)現(xiàn)方式,具體代碼如下所示:
#wordDec保存詞頻統(tǒng)計(jì)結(jié)果,examList為每份試題中出現(xiàn)的六級(jí)單詞列表,cet6為六級(jí)單詞列表
#方法1
wordDec={}
forwincet6:
wordDec[w]=examList.count(w)
#方法2
wordDct=dict.fromkeys(cet6,0)
forwinexamList:
wordDct[w]+=1
#方法3
fromcollectionsimportCounter
wordDct=Counter(examList)
其中方法1和方法2采用字典和列表實(shí)現(xiàn),方法3則采用標(biāo)準(zhǔn)庫(kù)collections中的Counter類實(shí)現(xiàn)。相比之下,方法1效率低,不推薦使用。在每一年試題的六級(jí)單詞詞頻的基礎(chǔ)上,進(jìn)一步計(jì)算平均詞頻,然后采用Python內(nèi)置函數(shù)sorted()根據(jù)平均詞頻從高到低進(jìn)行排序,排在前面的單詞即為近幾年考試中的高頻詞。當(dāng)然,實(shí)現(xiàn)方法不限于上述3種,例如還可采用Python第三方模塊Pandas,把每一年試題所用的六級(jí)單詞列表作為一個(gè)Series對(duì)象,然后采用value_counts()方法可得到詞頻,并把所有年份的詞頻結(jié)果制成一張表格,即一個(gè)DataFrame對(duì)象,之后采用其apply()方法作用到表格的每一行得到平均詞頻,最后采用sort_values()方法根據(jù)平均詞頻對(duì)所有行進(jìn)行逆序排序,具體代碼如下:
importpandasaspd
examPd=pd.DataFrame(meanList,columns=['詞義',],index=cet6)#meanList為詞義列表
examPd.index.name='單詞'
fori,examListinenumerate(examLists):
#對(duì)歷年試題做詞頻統(tǒng)計(jì),統(tǒng)計(jì)結(jié)果保存為表格中的一列
tempPd=pd.Series(examList)statistic=tempPd.value_counts()
examPd[examFileName[i]]=statistic
#examFileName為歷年試題對(duì)應(yīng)的文件名列表
filledExamPd=examPd.fillna(value=0)
iflledExamPd['平均次數(shù)']=filledExamPd.iloc[:,1:].apply(lambdax:x.sum()/fileNum,axis=1)
sortedExamPd=filledExamPd.sort_values(by='平均次數(shù)',ascending=False)
把根據(jù)平均詞頻逆序排序后的歷年詞頻統(tǒng)計(jì)結(jié)果和平均詞頻以csv文件格式保存在本地磁盤中。csv是逗號(hào)分隔符文本格式,常用于Excel和數(shù)據(jù)庫(kù)的數(shù)據(jù)導(dǎo)入和導(dǎo)出。Python標(biāo)準(zhǔn)庫(kù)中的csv模塊提供了讀取和寫入csv格式文件的兩個(gè)對(duì)象:csv.reader和csv.writer。在本案例中,可利用csv.writer對(duì)象中的writerow()和writerows()方法分別寫入一行或多行數(shù)據(jù)。也可直接采用第三方模塊Pandas的DataFrame對(duì)象的to_csv()方法,更便捷地保存表格數(shù)據(jù)。文件保存結(jié)果如圖1所示,文件中的第一列是單詞,第二列是詞義,中間列是歷年試題的詞頻統(tǒng)計(jì)結(jié)果,最后一列是平均詞頻。
為更直觀地呈現(xiàn)結(jié)果,本案例通過第三方模塊Matplotlib采用柱狀圖的方式對(duì)高頻詞及每年出現(xiàn)次數(shù)進(jìn)行展示。如果統(tǒng)計(jì)結(jié)果表示成一個(gè)Pandas的DataFrame對(duì)象,則直接用該對(duì)象的plot(kind='bar')方法進(jìn)行柱狀圖繪制,因?yàn)镻andas整合了matplotlib的pyplot模塊中的函數(shù),提供了專門用于Series和DataFrame對(duì)象的繪圖功能。圖2展示了平均詞頻最大的三個(gè)單詞近年來的各自使用次數(shù)。進(jìn)一步,經(jīng)過統(tǒng)計(jì)得到:在所有試題中都出現(xiàn)了的單詞只有兩個(gè):correspond和given,而近年來在試題中出現(xiàn)過的六級(jí)單詞,即平均詞頻不為0的單詞有855個(gè)。
圖1 六級(jí)單詞詞頻統(tǒng)計(jì)保存結(jié)果
隨著央視首檔全民參與的詩(shī)詞節(jié)目《中國(guó)詩(shī)詞大會(huì)》的熱播,高校重新刮起了一股文化清風(fēng),學(xué)生對(duì)各詩(shī)人們的詩(shī)詞和風(fēng)格、古人行酒令時(shí)的文字游戲“飛花令”等產(chǎn)生了濃厚的興趣。為此,我們?cè)O(shè)計(jì)了古詩(shī)字頻統(tǒng)計(jì)及可視化展示案例。中國(guó)古詩(shī),無論篇幅短長(zhǎng),用字的凝練都是非常講究的。詩(shī)人在擇取意象、推敲用字時(shí),由于其時(shí)代背景、生活經(jīng)歷的不同,通常會(huì)表現(xiàn)出特定的偏好,形成不同的風(fēng)格。因此,可以通過對(duì)不同詩(shī)人用字情況的對(duì)比分析來窺探詩(shī)人的詩(shī)風(fēng)。對(duì)于計(jì)算機(jī)專業(yè)背景的大學(xué)生來說,這是以理科方法來研究文科內(nèi)容的嘗試,文理交叉,學(xué)以致用。下面以李白、杜甫為例,詳細(xì)介紹古詩(shī)字頻統(tǒng)計(jì)及其可視化展示的案例實(shí)施過程。
中國(guó)古詩(shī)浩如煙海,能夠?yàn)g覽和下載古詩(shī)文本的網(wǎng)站也很多。采用類似2.1節(jié)的方法,我們可以得到大量分類整理的古詩(shī)文本,將其按作者姓名分別存放在不同的文件目錄中。爬取后網(wǎng)頁(yè)文件后除了需要進(jìn)行一般的文本化處理(去除HTML標(biāo)簽)外,還應(yīng)注意文本中可能存在的一些注釋內(nèi)容。例如,在李白《將進(jìn)酒》的文本中混有“與君歌一曲,請(qǐng)君為我傾耳聽。(傾耳聽一作:側(cè)耳聽)”,括號(hào)中內(nèi)容即為字頻統(tǒng)計(jì)的噪聲,可利用re模塊對(duì)此噪聲進(jìn)行刪除。處理后的各詩(shī)人的作品分別保存為CSV文件。其中文件內(nèi)容包括詩(shī)詞名、朝代、作者、詩(shī)詞內(nèi)容以及詩(shī)詞風(fēng)格標(biāo)簽,如圖3所示。
圖3 李白詩(shī)集保存結(jié)果
相對(duì)于四、六級(jí)單詞詞頻統(tǒng)計(jì),古詩(shī)字頻統(tǒng)計(jì)更為簡(jiǎn)單。對(duì)每一位詩(shī)人,首先把他(她)的每一首詩(shī)詞內(nèi)容中的每一個(gè)漢字添加至一個(gè)漢字列表,然后把該漢字列表通過轉(zhuǎn)化成字典等方法得到不重復(fù)漢字集合,最后再通過2.3節(jié)中介紹的詞頻統(tǒng)計(jì)方法得到該詩(shī)人的字頻統(tǒng)計(jì)結(jié)果。值得注意的是,不同詩(shī)人的作品數(shù)量差別較大,因此我們對(duì)詞頻統(tǒng)計(jì)結(jié)果進(jìn)行了歸一化處理,即除以詩(shī)人的作品數(shù)量。圖4分別顯示了李白和杜甫的前十個(gè)使用最頻繁的字及歸一化后的字頻。
圖4 李白(左)和杜甫(右)的前十個(gè)高頻字和對(duì)應(yīng)的字頻
圖5 李白(左)和杜甫(右)的詞云繪制結(jié)果
為更進(jìn)一步對(duì)高頻詞予以視覺上的突出,我們利用Python第三方庫(kù)wordcloud構(gòu)建了詞云。wordcloud庫(kù)功能強(qiáng)大,且簡(jiǎn)單易用。首先構(gòu)建一個(gè)WordCloud對(duì)象,注意由于顯示的是中文,因此需要在構(gòu)建該對(duì)象時(shí),設(shè)置font_path為支持中文的字體的路徑,例如在Windows系統(tǒng)下,font_path可以為'C:WindowsFontssimsun.ttc';然后把上節(jié)中得到的詞頻統(tǒng)計(jì)結(jié)果作為參數(shù)傳遞給WordCloud對(duì)象的fit_words()方法;最后采用matplotlib.pyplot.imshow()函數(shù)進(jìn)行顯示即可。李白和杜甫的詞云繪制結(jié)果如圖5所示。
可把李白和杜甫兩位詩(shī)人的詩(shī)詞中的高頻字分別用一個(gè)Python集合類型表示,然后采用集合操作對(duì)兩位詩(shī)人的用字習(xí)慣進(jìn)行比較:兩個(gè)集合的交即為兩位詩(shī)人都愛用的字;兩個(gè)集合的差即為其中一位詩(shī)人愛用而另一位詩(shī)人不愛用的字。
為進(jìn)一步比較兩位詩(shī)人的詩(shī)風(fēng),可求解兩人字頻差異最大的一些字。首先通過兩個(gè)集合的交集得到兩位詩(shī)人總共喜歡的字,然后對(duì)其中的每一個(gè)字,求兩位詩(shī)人的字頻差異的絕對(duì)值,最后根據(jù)該值進(jìn)行逆序排序,排在前面的即為兩位詩(shī)人用字習(xí)慣差異最大的字。為直觀地顯示這些字以及對(duì)應(yīng)的字頻,可采用Matplotlib柱狀圖的方式進(jìn)行繪制,其中差異最大的前10個(gè)字的結(jié)果如圖6所示。
為了更加有效地統(tǒng)計(jì)、比較并直觀顯示不同詩(shī)人的用字習(xí)慣,同時(shí)如“飛花令”游戲中那樣對(duì)特定漢字實(shí)現(xiàn)詩(shī)句鏈接,我們對(duì)上述教學(xué)案例做了進(jìn)一步的擴(kuò)展。首先,詞云的可視化展示改用基于Web的網(wǎng)頁(yè)形式,這樣可以對(duì)其中的漢字添加HTML鏈接。此外,為了方便程序模塊的升級(jí),把圖3所示的表格數(shù)據(jù)都導(dǎo)入sqlite3數(shù)據(jù)庫(kù)(數(shù)據(jù)庫(kù)名:peoms.db,表名:gushi),通過數(shù)據(jù)庫(kù)查詢來實(shí)現(xiàn)可以提高程序的可擴(kuò)展性,具體實(shí)施步驟如下。
可視化頁(yè)面采用標(biāo)簽云插件jQCloud(https://github.com/lucaong/jQCloud)作為呈現(xiàn)工具,首先設(shè)置一個(gè)top-left-right的三分屏框架主頁(yè)面index.htm,分別鏈接top.htm(作為標(biāo)題顯示對(duì)比詩(shī)人的姓名)、left.htm(用于顯示左側(cè)詩(shī)人的用字情況)和right.htm(用于顯示右側(cè)詩(shī)人的用字情況),如圖7所示。為了使Python程序邏輯與頁(yè)面顯示邏輯分離,將詩(shī)詞用字統(tǒng)計(jì)的結(jié)果數(shù)據(jù)寫入單獨(dú)的兩個(gè)js文件(data-left.js和dataright.js),在left.htm和right.htm兩個(gè)頁(yè)面中分別導(dǎo)入。
圖6 李白和杜甫字頻差異最大的10個(gè)字
圖7 李白/杜甫詩(shī)詞的用字對(duì)比
以left.htm為例,其HTML代碼如下:
lt;htmlgt;
lt;headgt;lt;metahttpequiv="Content-Type"content="text/html;charset=utf8"/gt;
lt;linkrel="stylesheet"type="text/css"href="css/jqcloud.css"/gt;
lt;scripttype="text/javascript"src="js/jquery-1.4.4.js"gt;lt;/scriptgt;
lt;scripttype="text/javascript"src="js/jqcloud-1.0.4.js"gt;lt;/scriptgt;
lt;scripttype="text/javascript"src="data-left.js"gt;lt;/scriptgt;
lt;scripttype="text/javascript"gt;
$(function(){$("#chars").jQCloud(char_list);});
lt;/scriptgt;
lt;/headgt;
lt;bodygt;
lt;centergt;lt;divid="chars"style="width:480px;height:
lt;/bodygt;
lt;/htmlgt;
數(shù)據(jù)文件格式需求如下:以上節(jié)HTML中的data-left.js為例,即按JSON格式列出李白詩(shī)字頻率相比杜甫詩(shī)字頻率偏高的部分。其中每行屬性說明如下:text為詩(shī)中用字,weight為字頻差(Freq(李白)-Freq(杜甫)),link為該字具體詩(shī)句的鏈接頁(yè)面。
varchar_list=[
{text:"月",weight:0.00353834280190,link:{href:"poem/李白-月.htm",target:"_blank"}},
{text:"天",weight:0.00344150700915,link:{href:"poem/李白-天.htm",target:"_blank"}},
{text:"海",weight:0.003176168221019,link:{href:"poem/李白-海.htm",target:"_blank"}},
{text:"金",weight:0.00287300721580,link:{href:"poem/李白-金.htm",target:"_blank"}},
{text:"山",weight:0.002688791184576,link:{href:"poem/李白-山.htm",target:"_blank"}},
……(此處省略更多)
];
生成該數(shù)據(jù)文件的Python代碼及相關(guān)注釋如下:
importre
importsqlite3
fromcollectionsimportCounter
cxn=sqlite3.connect("./peoms.db")
defcharfreq(poet):#統(tǒng)計(jì)某位詩(shī)人的所有詩(shī)中漢字分布的頻率,poet為詩(shī)人名字
counter=Counter()
regex_han=re.compile(r'[u3400-u9fff]')#匹配漢字的正則表達(dá)式
cur=cxn.cursor()
cur.execute("selectbodyfromgushiwhere authorname=?",(poet,))forrowincur:
counter.update(regex_han.findall(row[0]))#Counter.update()實(shí)現(xiàn)動(dòng)態(tài)更新頻次計(jì)數(shù)
freqsum=sum(counter.values())#總頻次
forcharincounter.keys():counter[char]/=freqsum#計(jì)算字的分布頻率
returncounter defcharcloud(poet_left,poet_right):#求詞頻差,并生成詞云
cnter_left=charfreq(poet_left)
cnter_right=charfreq(poet_right)
withopen('data-left.js','w',encoding='utf8')asf_left:
f_left.write('varword_list=[ ')
forchar,freqin(cnter_left-cnter_right).most_
common(200):#Counter對(duì)象支持求差運(yùn)算,只取前200個(gè)相對(duì)頻率差高的,為詩(shī)人偏好用字。f_left.write('{{text:"{}",weight:{}}}, '.format(char,freq))
f_left.write(']; ')
withopen('data-right.js','w',encoding='utf8')asf_left:
f_right.write('varword_list=[ ')
forchar,freqin(cnter_right-cnter_left).most_common(200):f_right.write('{{text:"{}",weight:{}}}, '.format(char,freq))
f_right.write(']; ')
charcloud('李白','杜甫')
程序?qū)崿F(xiàn)模塊化后,可以很方便地比較任意兩個(gè)詩(shī)人的用字,李白、杜甫的比較頁(yè)面如圖7所示。
最后一步,為標(biāo)簽云中的每個(gè)用字添加鏈接,點(diǎn)擊后可以查看詩(shī)人使用該字的具體詩(shī)句及出處。因此,修訂函數(shù)charcloud()中的部分代碼,此處僅展示data-left.js文件的生成部分,如下:
forchar,freqin(cnter_left-cnter_right).most_common(200):
f_left.write('{{text:"{0}",weight:{1},link:{{href:"poem/left-{0}.htm",target:"_blank"}}, '.format(char,freq))#輸出數(shù)據(jù)中增加鏈接
gen_verse(poet_left,char)
#調(diào)用函數(shù),生成詩(shī)人特定用字的詩(shī)句列表,詳下#函數(shù)gen_verse()定義如下:defgen_verse(poet,char):
withopen('poem/{}-{}'.format(poet,char),'w',encoding='utf8')asfout:
cur=cxn.cursor()
forrowincur.execute("selectbodyfromgushi
whereauthorname=?andbodylike?",(poet,'%{}%'.format(char))):
forlinein[xforxinre.split(r'[ 。?!]',row[0])ifcharinx]:
fout.write('lt;pgt;{}lt;/pgt; '.format(line).replace(char,'lt;spancolor="blue"gt;{}lt;/spangt;'.format(char))+'lt;palign="right"gt;——{}《{}》lt;/pgt;lt;/divgt; '.format(poet,fname[0:-4]))
#詩(shī)句(高亮顯示漢字char)——詩(shī)人《出處》
以李白詩(shī)中的“?!弊譃槔唧w顯示效果如圖8所示。
由于我們的詩(shī)詞數(shù)據(jù)采用sqlite3數(shù)據(jù)庫(kù)進(jìn)行保存,同時(shí)每一條數(shù)據(jù)還包含朝代和詩(shī)詞風(fēng)格標(biāo)簽信息,因此,采用類似2.4.2的方法,我們很容易提取出不同朝代或不同風(fēng)格的詩(shī)詞數(shù)據(jù),然后生成對(duì)應(yīng)的標(biāo)簽云,如圖9和圖10所示,從而對(duì)不同朝代或風(fēng)格的詩(shī)詞進(jìn)行比較。
本文給出的兩個(gè)案例均為綜合性案例,涉及Python教學(xué)中的多個(gè)知識(shí)點(diǎn)以及多個(gè)標(biāo)準(zhǔn)模塊和第三方模塊,且有些功能有多種實(shí)現(xiàn)方式。因此,可以作為學(xué)期末大作業(yè)示范案例,也可在學(xué)期中根據(jù)教學(xué)進(jìn)度進(jìn)行靈活拆分和運(yùn)用。例如,在學(xué)習(xí)字符串、字典等Python內(nèi)置數(shù)據(jù)結(jié)構(gòu)時(shí),可把某一年的試題或李白、杜甫若干首詩(shī)作為一個(gè)長(zhǎng)字符串進(jìn)行詞頻統(tǒng)計(jì)練習(xí);在學(xué)習(xí)文件讀寫時(shí),可把每一年的試題或李白、杜甫詩(shī)集作為一個(gè)文本文件進(jìn)行讀操作練習(xí),并通過保存詞頻統(tǒng)計(jì)結(jié)果進(jìn)行寫操作練習(xí);在學(xué)習(xí)網(wǎng)絡(luò)爬蟲時(shí),可練習(xí)從相關(guān)網(wǎng)站上爬取這些內(nèi)容等。
圖8 “?!弊值脑?shī)句鏈接頁(yè)面
圖9 不同朝代詩(shī)詞的用字對(duì)比
圖10 不同風(fēng)格詩(shī)詞的用字對(duì)比
基于Python強(qiáng)大的計(jì)算生態(tài)環(huán)境,本文提出“Python實(shí)用型案例教學(xué)”的觀點(diǎn),并具體針對(duì)詞頻統(tǒng)計(jì)問題,給出了大學(xué)英語(yǔ)四、六級(jí)考試高頻詞求解和可視化展示案例以及古詩(shī)詞字頻統(tǒng)計(jì)及可視化展示案例。這兩個(gè)案例都貼近大學(xué)生的學(xué)習(xí)和生活,不僅能讓學(xué)生感知Python計(jì)算生態(tài)圈的功能之強(qiáng)大和多樣,更能增強(qiáng)學(xué)生程序設(shè)計(jì)學(xué)習(xí)興趣,引導(dǎo)學(xué)生自主學(xué)習(xí)和運(yùn)用Python計(jì)算生態(tài),把解決實(shí)際生活和學(xué)習(xí)中的具體任務(wù)作為學(xué)習(xí)的最終目標(biāo),最終提高解決實(shí)際問題的能力。上述教學(xué)理念和案例在北京師范大學(xué)計(jì)算機(jī)專業(yè)2個(gè)學(xué)期課堂教學(xué)實(shí)踐中運(yùn)用,取得了良好的教學(xué)效果。后續(xù)工作中,如何針對(duì)其他不同專業(yè)的應(yīng)用需求,設(shè)計(jì)實(shí)用性和個(gè)性化并存的教學(xué)案例將是下一步Python教改重點(diǎn)。
[1]教育部高等學(xué)校大學(xué)計(jì)算機(jī)課程教學(xué)指導(dǎo)委員會(huì).大學(xué)計(jì)算機(jī)基礎(chǔ)課程教學(xué)基本要求[M].北京:高等教育出版社,2017.
[2]嵩天,禮欣,黃天羽.Python語(yǔ)言程序設(shè)計(jì)基礎(chǔ)[M].2版.北京:高等教育出版社,2017:171-174.
[3]中國(guó)大學(xué)MOOC.用Python玩轉(zhuǎn)數(shù)據(jù)[EB/OL].[2017-10-05].http://www.icourse163.org/course/NJU-1001571005.
[4]江紅,余青松.Python程序設(shè)計(jì)教程[M].北京:清華大學(xué)出版社、北京交通大學(xué)出版社,2014:44,61.
[5]嵩天,黃天羽,禮欣.面向計(jì)算生態(tài)的Python語(yǔ)言入門課程教學(xué)方案[J].計(jì)算機(jī)教育,2017(8):7-12.