《Spark機器學習》筆記——基於MovieLens資料集使用Spark進行電影資料分析
阿新 • • 發佈:2019-01-03
1、資料集下載
https://grouplens.org/datasets/movielens
2、資料集下檔案格式
u.user使用者屬性檔案
包含user.id使用者ID gender性別 occupation職業 ZIP code郵編等屬性,每個屬性之間用|分割
u.item電影元資料
包含movie.id電影ID title電影標題 release date電影上映日期 IMDB link 電影分類向量等屬性,每個屬性之間用|分割
u.data使用者對電影的評級
包含user.id使用者ID movie.id電影ID rating評分(從1-5) timestamp時間戳等屬性,
3、使用者資料分析
使用jupyter notebook進入編輯器
from pyspark import SparkContext #匯入Spark上下文 sc = SparkContext("local","movielens") #初始化Spark上下文,指定master為local,即本地執行,應用名稱為movielens user_data = sc.textFile("file:///home/chenjie/ml-100k/u.user") #載入本地movielens檔案中的使用者資訊檔案,file://開頭,後接本地檔案路徑;也可上傳至HDFS,hdfs://192.168.1.101:9000/ml-100k/u.user user_data.first() #輸出第一行 #u'1|24|M|technician|85711' #使用者資訊檔案包含 使用者ID|年齡|性別|職業|郵編 user_fields = user_data.map(lambda line: line.split("|")) #將使用者資訊檔案的每一行以|為分隔符【分開】 num_users = user_fields.map(lambda fields: fields[0]).count() #將使用者資訊檔案的使用者ID列取出,並且【計算總數】,得到使用者數目 num_genders = user_fields.map(lambda fields: fields[2]).distinct().count() #將使用者資訊檔案的性別列取出,並進行【去重】,並且計算總數,得到性別數目 num_occupations = user_fields.map(lambda fields: fields[3]).distinct().count() #將使用者資訊檔案的職業列取出,並進行去重,並且計算總數,得到職業數目 num_zipcodes = user_fields.map(lambda fields: fields[4]).distinct().count() print "Users: %d, genders: %d, occupations: %d, ZIP codes: %d" % (num_users, num_genders, num_occupations, num_zipcodes) #輸出上述資訊 #Users: 943, genders: 2, occupations: 21, ZIP codes: 795 ages = user_fields.map(lambda x : int(x[1])).collect() #將使用者資訊檔案的年齡列取出 import matplotlib.pyplot #匯入pyplot庫 matplotlib.pyplot.hist(ages,bins=20,color='lightblue',normed=True) #畫直方圖,引數列表如下 """ matplotlib.pyplot.hist(x, bins=None, range=None, normed=False, weights=None, cumulative=False, bottom=None, histtype=’bar’, align=’mid’, orientation=’vertical’, rwidth=None, log=False, color=None, label=None, stacked=False, hold=None, data=None, **kwargs) Parameters: x : (n,) array or sequence of (n,) arrays(可以是一個array也可以是多個array) integer or array_like or ‘auto’, optional(可以是整型來設定箱子的寬度,也可以是array,指定每個箱子的寬度) range : tuple or None, optional(設定顯示的範圍,範圍之外的將被捨棄) normed : boolean, optional(?) weights : (n, ) array_like or None, optional(?) cumulative : boolean, optional(?) bottom : array_like, scalar, or None(?) histtype : {‘bar’, ‘barstacked’, ‘step’, ‘stepfilled’}, optional(選擇展示的型別,預設為bar) align : {‘left’, ‘mid’, ‘right’}, optional(對齊方式) orientation : {‘horizontal’, ‘vertical’}, optional(箱子方向) log : boolean, optional(log刻度) color : color or array_like of colors or None, optional(顏色設定) label : string or None, optional(刻度標籤) stacked : boolean, optional(?) return n : array or list of arrays(箱子的值) bins : array(箱子的邊界) patches : list or list of lists """ fig = matplotlib.pyplot.gcf() #得到一個當前畫圖的引用 fig.set_size_inches(16,10) """ fig.set_size_inches(w,h,forward=False) atplotlib 包中提供的函式,用於設定圖形的尺寸,單位為英寸。1英寸等於 2.54 cm。 引數forward = True表示自動更新畫布大小。 """ matplotlib.pyplot.show() #顯示
使用者的年齡段分佈圖
下面進行使用者職業分佈圖,可以使用map+reduce,也可以使用countByValue函式
count_by_occupation = user_fields.map(lambda fields: (fields[3], 1)).reduceByKey(lambda x, y : x+y).collect() #統計每個職業的總數 import numpy as np x_axis1 = np.array([c[0] for c in count_by_occupation]) #將python陣列轉為numpy陣列 y_axis1 = np.array([c[1] for c in count_by_occupation]) x_axis1 = x_axis1[np.argsort(y_axis1)] y_axis1 = y_axis1[np.argsort(y_axis1)] #argsort以數量升序從各陣列中選取元素 pos = np.arange(len(x_axis1)) #np.arange(5)返回 array([0,1,2,3,4]) width = 1.0 ax = matplotlib.pyplot.axes() #向圖中新增一個軸 ax.set_xticks(pos + (width / 2)) ax.set_xticklabels(x_axis1) #要修改X軸的刻度,最簡單的辦法是使用set_xticks和set_xticklabels。前者告訴matplotlib要將刻度放在資料範圍中的哪些位置,預設情況下,這些位置也就是刻度標籤。但我們可以通過#set_xticklabels將任何其他的值用作標籤 matplotlib.pyplot.bar(pos,y_axis1,width,color='green' ) matplotlib.pyplot.xticks(rotation=30) fig = matplotlib.pyplot.gcf() fig.set_size_inches(16,10) matplotlib.pyplot.show() count_by_occupation2 = user_fields.map(lambda fields: (fields[3], 1)).countByValue() print "Map-reduce approach:" print dict(count_by_occupation2) print "" """ Map-reduce approach: {(u'homemaker', 1): 7, (u'marketing', 1): 26, (u'healthcare', 1): 16, (u'administrator', 1): 79, (u'doctor', 1): 7, (u'writer', 1): 45, (u'salesman', 1): 12, (u'librarian', 1): 51, (u'other', 1): 105, (u'lawyer', 1): 12, (u'engineer', 1): 67, (u'programmer', 1): 66, (u'entertainment', 1): 18, (u'artist', 1): 28, (u'none', 1): 9, (u'executive', 1): 32, (u'educator', 1): 95, (u'technician', 1): 27, (u'student', 1): 196, (u'scientist', 1): 31, (u'retired', 1): 14} """ print "countByValue approach:" print dict(count_by_occupation) print "" """ countByValue approach: {u'administrator': 79, u'executive': 32, u'retired': 14, u'doctor': 7, u'entertainment': 18, u'marketing': 26, u'writer': 45, u'none': 9, u'healthcare': 16, u'scientist': 31, u'homemaker': 7, u'student': 196, u'educator': 95, u'technician': 27, u'librarian': 51, u'programmer': 66, u'artist': 28, u'salesman': 12, u'other': 105, u'lawyer': 12, u'engineer': 67} """
使用者的職業分佈圖
下面進行電影年齡分析
注意到電影資料中有些資料不歸整,需要進行解析處理,如缺失年份的情況下將其設定為1900,然後後續處理中過濾掉這些資料
movie_data = sc.textFile("file:///home/chenjie/ml-100k/u.item")
print movie_data.first()
#1|Toy Story (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Toy%20Story%20(1995)|0|0|0|1|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0
num_movies = movie_data.count()
print "Movie: %d" % num_movies
#Movie: 1682
def conver_year(x):
try:
return int(x[-4:])
except :
return 1900
#若資料缺失年份則將其設為1900。在後續處理中會過濾掉這類資料
movie_fields = movie_data.map(lambda lines: lines.split("|"))
years = movie_fields.map(lambda fields: fields[2]).map(lambda x : conver_year(x))
years_filtered = years.filter(lambda x : x != 1900)
movie_ages = years_filtered.map(lambda yr: 2017-yr).countByValue()
values = movie_ages.values()
bins = movie_ages.keys()
matplotlib.pyplot.hist(values, bins=bins, color='green', normed=True)
fig = matplotlib.pyplot.gcf()
fig.set_size_inches(16,10)
matplotlib.pyplot.show()
電影的年齡分佈
5、評級資料分析
自己實現統計功能或者使用states函式
rating_data = sc.textFile("file:///home/chenjie/ml-100k/u.data")
print rating_data.first()
num_ratings = rating_data.count()
print "評分:%d條" % num_ratings
#196 242 3 881250949
#評分:100000條
rating_data_fields = rating_data.map(lambda line : line.split("\t"))
ratings = rating_data_fields.map(lambda fields : int(fields[2]))
max_rating = ratings.reduce(lambda x, y : max (x,y))
min_rating = ratings.reduce(lambda x,y : min(x,y))
mean_rating = ratings.reduce(lambda x,y : x+y) / num_ratings
ating_data = sc.textFile("file:///home/chenjie/ml-100k/u.data")
print rating_data.first()
num_ratings = rating_data.count()
print "評分:%d條" % num_ratings
#196 242 3 881250949
#評分:100000條
rating_data_fields = rating_data.map(lambda line : line.split("\t"))
ratings = rating_data_fields.map(lambda fields : int(fields[2]))
max_rating = ratings.reduce(lambda x, y : max (x,y))
min_rating = ratings.reduce(lambda x,y : min(x,y))
mean_rating = ratings.reduce(lambda x,y : x+y) / num_ratings
median_rating = np.median(ratings.collect())
user_data = sc.textFile("file:///home/chenjie/ml-100k/u.user")
user_fields = user_data.map(lambda line: line.split("|"))
num_users = user_fields.map(lambda fields: fields[0]).count()
ratings_per_user = num_ratings / num_users
movie_data = sc.textFile("file:///home/chenjie/ml-100k/u.item")
num_movies = movie_data.count()
ratings_per_movie = num_ratings / num_movies
print max_rating
print min_rating
print mean_rating
print median_rating
print ratings_per_user
print ratings_per_movie
ratings.stats()
#Spark自帶統計函式
count_by_rating = ratings.countByValue()
x_axis = np.array(count_by_rating.values())
y_axis = np.array([float(c) for c in count_by_rating.values()])
y_axis_normed = y_axis / y_axis.sum()
pos = np.arange(len(x_axis))
width = 1.0
import matplotlib.pyplot as plt
plt.bar(pos, y_axis_normed, width, color='green')
plt.xticks(rotation=30)
fig = plt.gcf()
fig.set_size_inches(16, 10)
plt.show()
電影評級分佈
rating_data = sc.textFile("file:///home/chenjie/ml-100k/u.data")
print rating_data.first()
rating_data_fields = rating_data.map(lambda line : line.split("\t"))
print rating_data_fields.first()
user_ratings_grouped = rating_data_fields.map(lambda fields : ( int (fields[0]), int(fields[2]) ) ).groupByKey()
user_ratings_buuser = user_ratings_grouped.map(lambda (k,v) : (k, len(v)))
user_ratings_buuser.take(5)
user_ratings_buuser_local = user_ratings_buuser.map(lambda (k,v) : v).collect()
import matplotlib.pyplot as plt
plt.hist(user_ratings_buuser_local, bins=200, color='green', normed=True)
fig = plt.gcf()
fig.set_size_inches(16,10)
plt.show()
各使用者的電影評級分佈圖