#!/usr/bin/env python # coding: utf-8 # # 14.2 MovieLens 1M Dataset(MovieLens 1M数据集) # # 这个数据集是电影评分数据:包括电影评分,电影元数据(风格类型,年代)以及关于用户的人口统计学数据(年龄,邮编,性别,职业等)。 # # MovieLens 1M数据集含有来自6000名用户对4000部电影的100万条评分数据。分为三个表:评分,用户信息,电影信息。这些数据都是dat文件格式,可以通过pandas.read_table将各个表分别读到一个pandas DataFrame对象中: # In[2]: import pandas as pd # In[3]: # Make display smaller pd.options.display.max_rows = 10 # In[4]: unames = ['user_id', 'gender', 'age', 'occupation', 'zip'] users = pd.read_table('../datasets/movielens/users.dat', sep='::', header=None, names=unames) # 因为sep='::'有点像是正则表达式,于是有了上面的错误。在这个[帖子](https://stackoverflow.com/questions/27301477/python-file-path-failing-in-pycharm-regex-confusion)找到了解决方法,设置engine为python即可。 # # Looks like on Python 2.7 Pandas just doesn't handle separators that look regexish. The initial "error" can be worked around by adding engine='python' as a named parameter in the call, as suggested in the warning. # # # In[5]: users = pd.read_table('../datasets/movielens/users.dat', sep='::', header=None, names=unames, engine='python') # In[6]: rnames = ['user_id', 'movie_id', 'rating', 'timestamp'] ratings = pd.read_table('../datasets/movielens/ratings.dat', sep='::', header=None, names=rnames, engine='python') # In[7]: mnames = ['movie_id', 'title', 'genres'] movies = pd.read_table('../datasets/movielens/movies.dat', sep='::', header=None, names=mnames, engine='python') # 加载前几行验证一下数据加载工作是否顺利 # In[8]: users[:5] # In[9]: ratings[:5] # In[10]: movies[:5] # 注意,年龄和职业是以编码形式给出的,它们的具体含义请参考改数据集的REAMDE文件。分析散布在三个表中的数据不是一件轻松的事情。假设我们想要根据性别和年龄来计算某部电影的平均得分,如果将所有的数据都合并到一个表中的话,问题就简单多了。我们先用pandas的merge函数将ratings和users合并到一起,然后再将movies也合并进去。pandas会根据列名的重叠情况推断出哪些列是合并(或连接)键: # In[11]: data = pd.merge(pd.merge(ratings, users), movies) # In[12]: data.head() # In[13]: data.iloc[0] # 现在,只要稍微熟悉一下pandas,就能轻松地根据任意个用户或电影属性对评分数据进行聚合操作了。为了按性别计算每部电影的平均得分,我们可以使用pivot_table方法: # In[14]: mean_ratings = data.pivot_table('rating', index='title', columns='gender', aggfunc='mean') # In[15]: mean_ratings[:5] # 该操作产生了另一个DataFrame,其内容为电影平均得分,行标为电影名称,列表为性别。现在,我们打算过滤掉评分数据不够250条的电影(这个数字可以自己设定)。为了达到这个目的,我们先对title进行分组,然后利用size()得到一个含有各电影分组大小的Series对象: # In[16]: ratings_by_title = data.groupby('title').size() # In[17]: ratings_by_title[:10] # In[18]: active_titles = ratings_by_title.index[ratings_by_title >= 250] # In[21]: print(active_titles) # 上面的active_titles中的电影,都是评论是大于250条以上的。我们可以用这些标题作为索引,从mean_ratings中选出这些评论大于250条的电影: # In[24]: mean_ratings = mean_ratings.loc[active_titles] mean_ratings # 想要查看女性观众喜欢的电影,可以按F列进行降序操作: # In[25]: top_female_ratings = mean_ratings.sort_values(by='F', ascending=False) top_female_ratings[:10] # # 1 Measuring Rating Disagreement(计算评分分歧) # # 假设我们想要找出男性和女性观众分歧最大的电影。一个办法是给mean_ratings加上一个用于存放平均得分之差的列,并对其进行排序: # In[26]: mean_ratings['diff'] = mean_ratings['M'] - mean_ratings['F'] # 按‘diff’排序即可得到分歧最大且女性观众更喜欢的电影: # In[27]: sorted_by_diff = mean_ratings.sort_values(by='diff') sorted_by_diff[:15] # 对行进行反序操作,并取出前15行,得到的则是男性更喜欢,而女性评价较低的电影: # In[30]: # Reverse order of rows, take first 10 rows sorted_by_diff[::-1][:10] # 如果只是想要找出分歧最大的电影(不考虑性别因素),则可以计算得分数据的方差或标准差: # In[31]: # 根据电影名称分组的得分数据的标准差 rating_std_by_title = data.groupby('title')['rating'].std() # In[32]: # 根据active_titles进行过滤 rating_std_by_title = rating_std_by_title.loc[active_titles] # In[33]: # Order Series by value in descending order rating_std_by_title.sort_values(ascending=False)[:10] # 这里我们注意到,电影分类是以竖线`|`分割的字符串形式给出的。如果想对不同的电影分类进行分析的话,就需要先将其转换成更有用的形式才行。