一、實驗內容
題目1
輸入文件為學生成績信息,包含了必修課與選修課成績,格式如下:
班級1, 姓名1, 科目1, 必修, 成績1 <br>
(注:<br>
為換行符)
班級2, 姓名2, 科目1, 必修, 成績2 <br>
班級1, 姓名1, 科目2, 選修, 成績3 <br>
………., ………, ………, ………, ……… <br>
編寫兩個Hadoop平臺上的MapReduce程序,分別實現如下功能:
-
計算每個學生必修課的平均成績。
-
按科目統(tǒng)計每個班的平均成績。
題目2
輸入文件的每一行為具有父子/父女/母子/母女/關系的一對人名,例如:
Tim, Andy <br>
Harry, Alice <br>
Mark, Louis <br>
Andy, Joseph <br>
…………, ………… <br>
假定不會出現重名現象。
- 編寫Hadoop平臺上的MapReduce程序,找出所有具有grandchild-grandparent關系的人名組。
題目3
輸入文件為學生成績信息,包含了必修課與選修課成績,格式如下:
班級1, 姓名1, 科目1, 必修, 成績1 <br>
(注:<br>
為換行符)
班級2, 姓名2, 科目1, 必修, 成績2 <br>
班級1, 姓名1, 科目2, 選修, 成績3 <br>
………., ………, ………, ………, … <br>
編寫一個Spark程序,同時實現如下功能:
- 計算每個學生必修課的平均成績。
- 統(tǒng)計學生必修課平均成績在:90100,8089,7079,6069和60分以下這5個分數段的人數。
二、設計思想
題目1
1. 計算每個學生必修課的平均成績
??Map階段:
??(1) 預處理。對txt文檔的每一行,先用split函數將其用","分隔成若干字符串,并存于數組splited中。
??(2) 過濾。由于成績文檔的每一行形如“班級,姓名,課程名,性質,分數”,而我們需要統(tǒng)計學生的必修課平均分,因此可以用splited[3].equals("必修")
的條件過濾掉選修課所在的行。
??(3) 設置Map的輸出格式。由于我們是對每個學生求一個平均分,故思路很直接:讓Map階段的輸出是形如<“姓名”: 成績>的kv對。其中學生姓名字符串和成績字符串可分別從splited[1]、splited[4]直接得到。
??Reduce階段:
??(1) 觀察Shuffling階段的輸出。根據我們上面設置的Map階段輸出,可以得知:Shuffling后單個學生的所有必修課成績已被歸并至一個列表中,作為以該生的姓名為key的鍵值對的value。
??(2) 求平均成績。對于每個學生,我們可以遍歷他的value列表并求出他的所有必修課總分。該過程涉及到數據類型的轉換,如sum += Integer.valueOf(grade.toString());
。在得到總分之后,直接除以該生的成績條目個數(用i自增1得到)即可得到他的平均成績。
??(3) 設置Reduce的輸出格式。這里我簡單地用String.format("%.2f", avg);
設置了輸出成績小數點后保留兩位小數。我將Reduce的輸出設置為形如<“姓名”: 必修課平均成績>的格式。
2. 按科目統(tǒng)計每個班的平均成績
??Map階段:
??(1) 預處理。對txt文檔的每一行,先用split函數將其用","分隔成若干字符串,并存于數組splited中。
??(2) 設置Map的輸出格式。題目要求按科目統(tǒng)計每個班的平均成績,只要我們將“科目”和“班級”這兩個字段看成是一個字段,就可以直接套用計算學生平均成績的方法來求解本題。因此,這里我將Map階段的輸出設置為形如<“科目 班級”: 成績>,其中科目、班級、成績的字符串均和上面的實驗類似,可直接從splited[0]、splited[2]、splited[4]得到。
??Reduce階段:
??(1) 觀察Shuffling階段的輸出。根據我們上面設置的Map階段輸出,可以得知:Shuffling后各班各科目的成績均已被歸并至一個列表中,形如<“科目 班級”: [成績1, 成績2, …, 成績m]>,其中m是該班級考這門課的人數。
??(2) 求平均成績。對于每個"科目 班級"鍵,我們可以遍歷其value列表來求出該班級所有學生在這門課的總成績。該過程涉及到數據類型的轉換。在得到總分之后,直接除以該班考這門課的學生個數(用i自增1得到)即可得到該班在該科目的平均成績。
??(3) 設置Reduce的輸出格式。這里我簡單地用String.format("%.1f", avg);
設置了輸出成績小數點后保留一位小數。我將Reduce的輸出設置為形如<“科目 班級”: 平均成績>的格式。
題目2
??Map階段:
??(1) 預處理。對txt文檔的每一行,先用split函數將其用","分隔成若干字符串,并存于數組splited中。splited[0]為父母姓名,splited[1]為子女姓名。
??(2) 設置Map的輸出格式。由于我的目的是讓任務經過Shuffling階段后生成類似<“某人姓名”: [“父親姓名”, “母親姓名”,“兒子姓名”]>這樣的輸出,因此我將Map階段的輸出設置為對于原始數據的每一行,輸出兩個kv對:一個是以該行的splited[0]為鍵,splited[1]為值,這么做可以保證Shuffling之后得到以某人姓名為鍵,其所有子女姓名為值的kv對;另一個是以該行的splited[1]為鍵,splited[0]為值(因為一個人他可能既有父母的身份,也有子女的身份),這么做可以使得Shuffling之后得到以某人姓名為鍵,其父母姓名為值的kv對。因為Shuffling會把所有同鍵的value聚集到一個列表中,因此不難發(fā)現Shuffling后我們可以得到以某人姓名為鍵,其子女、父母姓名為值的kv對。此外,為了便于Reduce階段將父母姓名和孩子姓名分別存入列表中,我在Map階段設置context.write時將孩子姓名開頭加上“0”,將父母姓名開頭加上“1”。
??Reduce階段:
??(1) 觀察Shuffling階段的輸出。根據上述分析可知:Shuffling后我們可以得到以某人姓名為鍵,其子女、父母姓名為值的kv對,即形如<“某人姓名”: [“父親姓名”, “母親姓名”,“兒子姓名”]>的kv對。
??(2) 構造grandparents和grandchildren列表。遍歷當前key的value列表,若姓名以0開頭,則將該姓名加入到grandchildren中,否則加入到grandparents中。
??(3) 設置Reduce的輸出格式。題目要求我們輸出所有具有grandchild-grandparent關系的人名組。因此,只需要寫一個二重循環(huán),遍歷grandchildren和grandparents列表,輸出所有可能的以grandchild姓名為鍵,grandparent姓名為值的kv對即可,因為可以肯定grandparents列表中的所有人必定是grandchild列表中所有人的祖輩。
題目3
??(1) 利用filter方法過濾掉不是必修課的行。
??(2) 利用map方法將一行數據映射為一個<姓名: (成績, 1)>對,其中用到了split方法分割出若干字段。
??(3) 利用reduceByKey方法將所有同key項聚合起來,聚合方式為所有同key的value的第一分量相加,得到一個學生的總必修成績,同時將同key的value的第二分量相加,得到該學生修的必修課總數。然后,利用mapValues方法求出每個學生的平均成績,即:將(總必修成績, 必修課程數)這一value映射為該學生的平均成績,方式為用總成績除以必修課程數。
??(4) 通過上述步驟我們已經求得了每個學生的必修課均分。先將各學生的<“姓名”: 必修課均分>kv對用map方法映射為<“分段”: 1>,然后再用reduceByKey方法將所有同分段的學生聚集起來,求出各分段的人數。
三、實驗結果
(1) 輸入命令hadoop fs -cat /output1/part-r-00000
查看實驗1功能1的程序執(zhí)行結果:
(2) 輸入命令hadoop fs -cat /output2/part-r-00000
查看實驗1功能2的程序執(zhí)行結果:
輸入命令hadoop fs -cat /output3/part-r-00000
查看實驗2的程序執(zhí)行結果,結果的第一列是孫輩姓名,第二列是與之對應的祖輩姓名:
輸入命令hadoop fs -cat /user/root/avg_grades/part-00000
查看實驗3的程序執(zhí)行結果,這里我將所有學生的必修課平均成績保留到了整數位,且進行了升序排列。
輸入命令hadoop fs -cat /user/root/interval_stu_nums/part-00000
可查看各分段的學生人數統(tǒng)計結果,這里我輸出的結果是按key升序排列的結果。
四、遇到的問題及解決方法
-
一開始我嘗試使用第一種方法搭建實驗環(huán)境,前面的步驟都很順利,但是ssh連接遠程主機sandbox一直提示connection refused或者是Connection closed by remote host。由于一開始不知道是client-node頻繁重啟造成的原因,所以去檢查本機的ssh服務等是否有問題。我嘗試ssh連接自己的主機,但是發(fā)現連接不上,搜了解決方案后安裝了Windows的OpenSSH服務器和客戶端,并且在系統(tǒng)的“服務”中設置了OpenSSH SSH Server的啟動類型為自動,這才解決了SSH連接方面的問題。后來導入了老師發(fā)的clientnode鏡像文件,解決了client-node頻繁重啟的問題,但由于這時候方法二已經跑通了,故放棄了繼續(xù)進行方法一的環(huán)境搭建。
-
在hadoopspark目錄下執(zhí)行docker-compose up -d失敗,原因是本地未開啟docker服務,開啟后成功解決。
-
在分布式文件系統(tǒng) HDFS 中創(chuàng)建子目錄時,使用老師文檔中給的
hadoop fs –mkdir test
提示hdfs://localhost:9000/user/root': No such file or directory
。后來多次嘗試后發(fā)現在要創(chuàng)建的子目錄名前加"/"即可成功創(chuàng)建:[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-MQ5583kt-1659514943855)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220611151838639.png)]
-
在寫MapReduce程序時,我先是對著老師寫的例程觀摩了一番,然后準備在此基礎上進行修改,以使之符合實驗要求。一開始我想將context.write的value的類型改成Text(字符串),但是發(fā)現有多處報錯,后來查閱資料得知需要相應地修改Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>類的out的數據類型和job.setMapOutputValueClass();的參數,使其均與Text類型一致才行。
-
在編寫尋找祖孫關系的程序時,我查看了child-parent.txt文檔,發(fā)現假設第一列是父母的話,會出現一個人有五六個父母的情況,這是不現實的,因此我將第二列當成父母,第一列當成子女進行了程序編寫。
-
在編寫pyspark程序時,由于一開始還不太能掌握編寫技巧,因此我先對著網上的例程學習了基本的filter、map、mapValues、reduceByKey等方法的使用要點,并且逐步將這些方法運用在測試數據上,然后觀察hadoop平臺的運行結果,再根據結果不斷調整代碼,最終完成了程序編寫。
-
在提交程序至hadoop平臺運行之前,若輸入的命令中把要處理的文檔寫錯了(比如child-parent.txt寫成grades.txt)則會導致輸出的文檔大小為0B,因此在輸入命令時一定要小心而細致。文章來源:http://www.zghlxwxcb.cn/news/detail-476727.html
源碼:分布式計算MapReduce實驗 + 分布式計算Spark實驗文章來源地址http://www.zghlxwxcb.cn/news/detail-476727.html
到了這里,關于分布式計算MapReduce | Spark實驗的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!