MovieLens數據集包含多個用戶對多部電影的評級數據,也包括電影元數據信息和用戶屬性信息。python
這個數據集常常用來作推薦系統,機器學習算法的測試數據集。尤爲在推薦系統領域,不少著名論文都是基於這個數據集的。(PS: 它是某次具備歷史意義的推薦系統競賽所用的數據集)。算法
下載地址爲:http://files.grouplens.org/datasets/movielens/,有好幾種版本,對應不一樣數據量,可任君選用。shell
本文下載數據量最小的100k版本,對該數據集進行探索:機器學習
本人機器所用的操做系統爲號稱國產操做系統的Ubuntu Kylin 14.04,美化後的界面仍是蠻酷炫的:函數
spark版本爲:v1.5.2,下面是集成了Ipython,pylab的python-shell:學習
1. 首先是用戶信息:測試
其中各列數據分別爲:編碼
用戶id | 用戶年齡 | 用戶性別 | 用戶職業 | 用戶郵政編碼spa
2. 而後是影片信息:操作系統
其中前幾列數據分別爲:
影片id | 影片名 | 影片發行日期 | 影片連接 | (後面幾列先不去管)
3. 最後是評分數據:
用戶id | 影片id | 評分值 | 時間戳(timestamp格式)
1. 打開Spark的python-shell,執行如下代碼載入數據集並打印首行記錄:
1 # 載入數據集 2 user_data = sc.textFile("/home/kylin/ml-100k/u.user") 3 # 展現首行記錄 4 user_data.first()
結果以下:
2. 分別統計用戶,性別,職業的個數:
1 # 以' | '切分每列,返回新的用戶RDD 2 user_fields = user_data.map(lambda line: line.split("|")) 3 # 統計用戶數 4 num_users = user_fields.map(lambda fields: fields[0]).count() 5 # 統計性別數 6 num_genders = user_fields.map(lambda fields: fields[2]).distinct().count() 7 # 統計職業數 8 num_occupations = user_fields.map(lambda fields: fields[3]).distinct().count() 9 # 統計郵編數 10 num_zipcodes = user_fields.map(lambda fields: fields[4]).distinct().count() 11 # 返回結果 12 print "用戶數: %d, 性別數: %d, 職業數: %d, 郵編數: %d" % (num_users, num_genders, num_occupations, num_zipcodes)
結果以下:
3. 查看年齡分佈狀況:
1 # 獲取用戶年齡RDD,並將其落地到驅動程序 2 ages = user_fields.map(lambda x: int(x[1])).collect() 3 # 繪製用戶年齡直方圖 4 hist(ages, bins=20, color='lightblue', normed=True)
結果以下:
18歲以上觀看者人數激增,估計是「高考」完了時間多了?
20多歲的年輕人,我猜主要是大學生和剛工做不久的年輕人?觀看者最多。
而後50歲的觀看者也蠻多的,估計是快退休了,開始享受生活了。
4. 查看職業分佈狀況:
1 # 並行統計各職業人數的個數,返回職業統計RDD後落地 2 count_by_occupation = user_fields.map(lambda fields: (fields[3], 1)).reduceByKey(lambda x, y: x + y).collect() 3 4 # 生成x/y座標軸 5 x_axis1 = np.array([c[0] for c in count_by_occupation]) 6 y_axis1 = np.array([c[1] for c in count_by_occupation]) 7 x_axis = x_axis1[np.argsort(y_axis1)] 8 y_axis = y_axis1[np.argsort(y_axis1)] 9 10 # 生成x軸標籤 11 pos = np.arange(len(x_axis)) 12 width = 1.0 13 ax = plt.axes() 14 ax.set_xticks(pos + (width / 2)) 15 ax.set_xticklabels(x_axis) 16 17 # 繪製職業人數條狀圖 18 plt.xticks(rotation=30) 19 plt.bar(pos, y_axis, width, color='lightblue')
值得注意的是,統計各職業人數的時候,是將不一樣職業名記錄蒐集到不一樣節點,而後開始並行統計。
結果以下:
果真,是學生,教育工做者觀看影片的多。
不過程序猿觀衆也很多...... 醫生是最少看電影的。
這裏再給出一種統計各職業人數的解決方案:
1 count_by_occupation2 = user_fields.map(lambda fields: fields[3]).countByValue()
countByValue是Spark提供的便捷函數,它會自動統計每一個Key下面的Value個數,並以字典的格式返回。
1. 打開Spark的python-shell,執行如下代碼載入數據集並打印首行記錄:
1 # 載入數據集 2 movie_data = sc.textFile("/home/kylin/ml-100k/u.item") 3 # 展現首行記錄 4 print movie_data.first()
結果以下:
2. 查看下有多少部電影吧:
1 num_movies = movie_data.count() 2 print num_movies
結果爲:
3. 過濾掉沒有發行時間信息的記錄:
1 # 輸入影片的發行時間字段,若非法則返回1900 2 def convert_year(x): 3 try: 4 return int(x[-4:]) 5 except: 6 return 1900 7 8 # 以' | '切分每列,返回影片RDD 9 movie_fields = movie_data.map(lambda lines: lines.split("|")) 10 # 生成新的影片發行年份RDD,並將空/異常的年份置爲1900, 11 years = movie_fields.map(lambda fields: fields[2]).map(lambda x: convert_year(x)) 12 # 過濾掉影片發行年份RDD中空/異常的記錄 13 years_filtered = years.filter(lambda x: x != 1900)
4. 統計影片的年齡分佈:
1 # 生成影片年齡RDD,而後統計不一樣年齡的影片數並落地 2 movie_ages = years_filtered.map(lambda yr: 1998-yr).countByValue() 3 # 得到影片數 4 values = movie_ages.values() 5 # 得到年齡 6 bins = movie_ages.keys() 7 # 繪製電影年齡分佈圖 8 hist(values, bins=bins, color='lightblue', normed=True)
由於這份數據集比較老,1998年提供的,因此就按當時的年齡來統計吧。另外此次使用了countByValue來統計個數,而它是執行函數,不須要再collect了。
結果爲:
看得出電影庫中的電影大都仍是比較新的。
1. 打開Spark的python-shell,執行如下代碼載入數據集並打印首行記錄:
1 # 載入數據集 2 rating_data_raw = sc.textFile("/home/kylin/ml-100k/u.data") 3 # 展現首行記錄 4 print rating_data_raw.first()
結果爲:
2. 先看看有多少評分記錄吧:
1 num_ratings = rating_data .count() 2 print num_ratings
結果爲:。果真共有10萬條記錄,沒下載錯版本。
3. 統計最高評分,最低評分,平均評分,中位評分,平均每一個用戶的評分次數,平均每部影片被評分次數:
1 # 獲取評分RDD 2 rating_data = rating_data_raw.map(lambda line: line.split("\t")) 3 ratings = rating_data.map(lambda fields: int(fields[2])) 4 # 計算最大/最小評分 5 max_rating = ratings.reduce(lambda x, y: max(x, y)) 6 min_rating = ratings.reduce(lambda x, y: min(x, y)) 7 # 計算平均/中位評分 8 mean_rating = ratings.reduce(lambda x, y: x + y) / float(num_ratings) 9 median_rating = np.median(ratings.collect()) 10 # 計算每一個觀衆/每部電影平均打分/被打分次數 11 ratings_per_user = num_ratings / num_users 12 ratings_per_movie = num_ratings / num_movies 13 # 輸出結果 14 print "最低評分: %d" % min_rating 15 print "最高評分: %d" % max_rating 16 print "平均評分: %2.2f" % mean_rating 17 print "中位評分: %d" % median_rating 18 print "平均每一個用戶打分(次數): %2.2f" % ratings_per_user 19 print "平均每部電影評分(次數): %2.2f" % ratings_per_movie
結果爲:
另外Spark有個挺實用的統計函數stats可直接獲取經常使用的統計信息,相似R語言的summary函數:
ratings.stats()
結果爲:
4. 統計評分分佈:
1 # 生成評分統計RDD,並落地 2 count_by_rating = ratings.countByValue() 3 # 生成x/y座標軸 4 x_axis = np.array(count_by_rating.keys()) 5 y_axis = np.array([float(c) for c in count_by_rating.values()]) 6 # 對人數作標準化 7 y_axis_normed = y_axis / y_axis.sum() 8 9 # 生成x軸標籤 10 pos = np.arange(len(x_axis)) 11 width = 1.0 12 ax = plt.axes() 13 ax.set_xticks(pos + (width / 2)) 14 ax.set_xticklabels(x_axis) 15 16 # 繪製評分分佈柱狀圖 17 plt.bar(pos, y_axis_normed, width, color='lightblue') 18 plt.xticks(rotation=30)
結果爲:
評分分佈看來也應該挺知足正態分佈的。
5. 統計不一樣用戶的評分次數:
1 # 首先將數據以用戶id爲Key分發到各個節點 2 user_ratings_grouped = rating_data.map(lambda fields: (int(fields[0]), int(fields[2]))).groupByKey() 3 # 而後統計每一個節點元素的個數,也即每一個用戶的評論次數 4 user_ratings_byuser = user_ratings_grouped.map(lambda (k, v): (k, len(v))) 5 # 輸出前5條記錄 6 user_ratings_byuser.take(5)
注意到此次使用了groupyByKey方法,這個方法和reduceByKey功能有點類似,可是有區別。請讀者自行百度。
結果爲: