利用 Python 分析 MovieLens 1M 数据集

1 数据集简介

MovieLens数据集是一个关于电影评分的数据集,里面包含了从IMDB, The Movie DataBase上面得到的用户对电影的评分信息,详细请看下面的介绍。

1 links.csv

文件里面的内容是帮助你如何通过网站id在对应网站上找到对应的电影链接的。

1.1 数据格式

movieId, imdbId, tmdbId

1.1.1 movieId

表示这部电影在movielens上的id,可以通过链接https://movielens.org/movies/(movieId)来得到。

1.1.2 imdbId

表示这部电影在imdb上的id,可以通过链接http://www.imdb.com/title/(imdbId)/
来得到。

2 movies.csv

movieId, title, genres

文件里包含了一部电影的id和标题,以及该电影的类别

2.1 数据格式

movieId, title, genres

2.1.1 movieId

每部电影的id

2.1.2 title

电影的标题

2.1.3 genres

电影的类别(详细分类见readme.txt)

3 ratings.csv

文件里面的内容包含了每一个用户对于每一部电影的评分。

3.1 数据格式

  • userId: 每个用户的id
  • movieId: 每部电影的id
  • rating: 用户评分,是5星制,按半颗星的规模递增(0.5 stars - 5 stars)
  • timestamp: 自1970年1月1日零点后到用户提交评价的时间的秒数

数据排序的顺序按照userId,movieId排列的。

4 tags.csv

文件里面的内容包含了每一个用户对于每一个电影的分类

4.1 数据格式

  • userId: 每个用户的id
  • movieId: 每部电影的id
  • tag: 用户对电影的标签化评价
  • timestamp: 自1970年1月1日零点后到用户提交评价的时间的秒数

数据排序的顺序按照userId,movieId排列的。

摘要

=======

该数据集(ml-latest-small)描述了电影推荐服务[MovieLens](http://movielens.org)的5星评级和自由文本标记活动。它包含9742部电影的100836个评级和3683个标签应用程序。这些数据由610位用户在1996年3月29日到2018年9月24日之间创建。该数据集于2018年9月26日生成。

随机选择用户以包含在内。所有选定的用户评分至少20部电影。不包括人口统计信息。每个用户都由一个id表示,并且不提供其他信息。

数据包含在links.csvmovies.csvratings.csvtags.csv文件中。有关所有这些文件的内容和用法的更多详细信息如下。

这是一个发展的数据集。因此,它可能会随着时间的推移而发生变化,并不是共享研究结果的适当数据集。

引文

========

要确认在出版物中使用数据集,请引用以下文件:

F. Maxwell Harper和Joseph A. Konstan。 2015.MovieLens数据集:历史和背景。 ACM交互式智能系统交易(TiiS)5,4:19:1-19:19。 https://doi.org/10.1145/2827872

文件的内容和使用

========================

格式化和编码

数据集文件以[逗号分隔值]文件写入,并带有单个标题行。包含逗号()的列使用双引号(```)进行转义。这些文件编码为UTF-8。如果电影标题或标签值中的重音字符(例如Misérables,Les(1995))显示不正确,确保读取数据的任何程序(如文本编辑器,终端或脚本)都配置为UTF-8。

用户ID

MovieLens用户随机选择包含。他们的ID已经匿名化了。用户ID在ratings.csvtags.csv之间是一致的(即,相同的id指的是两个文件中的同一用户)。

电影Ids

数据集中仅包含至少具有一个评级或标记的电影。这些电影ID与MovieLens网站上使用的电影ID一致(例如,id1对应于URL https://movielens.org/movies/1)。电影ID在ratings.csvtags.csvmovies.csvlinks.csv之间是一致的.

2 Python 数据处理

2.1 转化DataFrame对象

通过[pandas.read_csv]将各表转化为pandas 的DataFrame对象

# 用户信息
unames = ['user_id', 'gender', 'age', 'occupation', 'zip']
users = pd.read_csv('/Volumes/doc/PyCharmProjects/MovieLenData/users.dat',
                    sep=',', header=None, names=unames, engine='python')
# 评分
rnames = ['user_id', 'movieId', 'rating', 'timestamp']
ratings = pd.read_csv('/Volumes/doc/PyCharmProjects/MovieLenData/ratings.csv',
                      sep=',', header=None, names=rnames, engine='python')
# 电影信息
mnames = ['movie_id', 'title', 'genres']
movies = pd.read_csv('/Volumes/doc/PyCharmProjects/MovieLenData/movies.csv',
                     sep=',', header=None, names=mnames, engine='python')

# 链接信息
lnames = ['movieId', 'imdbId', 'tmdbId']
links = pd.read_csv('/Volumes/doc/PyCharmProjects/MovieLenData/links.csv',
                    sep=',', header=None, names=mnames, engine='python')

# 标签信息
tnames = ['userId', 'movieId', 'tag', 'timestamp']
tags = pd.read_csv('/Volumes/doc/PyCharmProjects/MovieLenData/tags.csv',
                   sep=',', header=None, names=mnames, engine='python')

其中用到的参数为分隔符sep、头文件header、列名定义names、解析器引擎engine
这里和书上相比多用了engine参数,engine参数有C和Python,C引擎速度更快,而Python引擎目前功能更完整。

  • 利用python的切片查看每个DataFrame
## 2.2  检查数据的输出
print(users[:5])
print("===================================================================")
print(ratings[:5])
print("===================================================================")
print(movies[:5])
print("===================================================================")
print(links[:5])
print("===================================================================")
print(tags[:5])
print("===================================================================")
  • 查看dataframe的summary
users.info()
print("-----------------------------------")
ratings.info()
print("-----------------------------------")
movies.info()
print("-----------------------------------")
links.info()
print("-----------------------------------")
tags.info()

2.3 根据性别和年龄计算某部电影的平均得分

可用pandas.merge 将所有数据都合并到一个表中。merge有四种连接方式(默认为inner),分别为

  • 内连接(inner),取交集;
  • 外连接(outer),取并集,并用NaN填充;
  • 左连接(left),左侧DataFrame取全部,右侧DataFrame取部分;
  • 右连接(right),右侧DataFrame取全部,左侧DataFrame取部分;
data = pd.merge(pd.merge(ratings, users), movies)
data.info()

通过索引器查看第一行数据,使用基于标签的索引.loc或基于位置的索引.iloc


2.4 按性别计算每部电影的平均得分

可通过数据透视表(pivot_table)实现

该操作产生了另一个DataFrame,输出内容为rating列的数据,行标index为电影名称,列标为性别,aggfunc参数为函数或函数列表(默认为numpy.mean),其中“columns”提供了一种额外的方法来分割数据。


2.5 过滤评分数据不够250条的电影

  • 通过groupby()对title进行分组
  • 利用size()得到一个含有各电影分组大小的Series对象
print("过滤评分数据不够250条的电影")
ratings_by_title = data.groupby('title').size()
print(ratings_by_title[:10])
  • 最后通过index索引筛选出评分数据大于250条的电影名称
print("通过index索引筛选出评分数据大于250条的电影名称")
active_titles = ratings_by_title.index[ratings_by_title >= 250]
print(active_titles)
  • 使用mean_ratings选取所需的行
mean_ratings = mean_ratings.loc[active_titles]
mean_ratings.info()
print(mean_ratings[:5])

2.6 了解女性观众最喜欢的电影

top_female_ratings = mean_ratings.sort_index(by='F', ascending=False)
print(top_female_ratings[:10])

by参数的作用是针对特定的列进行排序(不能对行使用),ascending的作用是确定排序方式,默认为升序


2.7 计算评分分歧

增加一列存放平均得分之差,并对其排序,得到分歧最大且女性观众更喜欢的电影

mean_ratings['diff'] = mean_ratings['M'] - mean_ratings['F']
sorted_by_diff = mean_ratings.sort_index(by='diff')
print(sorted_by_diff[:10])
  • 对排序结果反序可得男性观众更喜欢的电影


3 再处理

3.1 数据集整合

movie_ratings = pd.merge(movies, ratings)
lens = pd.merge(movie_ratings, users)

3.2 列出被评价过次数最多的20部电影

按照电影标题将数据集分为不同的groups,并且用size( )函数得到每部电影的个数(即每部电影被评论的次数),按照从大到小排序,取最大的前20部电影列出如下

most_rated = lens.groupby('title').size().sort_values(ascending=False)[:20]
print(most_rated)

3.3 评分最高的十部电影

按照电影名称分组,用agg函数通过一个字典{‘rating’: [np.size, np.mean]}来按照key即rating这一列聚合,查看每一部电影被评论过的次数和被打的平均分。取出至少被评论过100次的电影按照平均评分从大到小排序,取最大的10部电影。

movie_stats = lens.groupby('title').agg({'rating': [np.size, np.mean]})
atleast_100 = movie_stats['rating']['size'] >= 100
print(movie_stats[atleast_100].sort_values([('rating', 'mean')], ascending=False)[:10])

3.4 查看不同年龄见争议最大的电影

  • 查看用户的年龄分布:
users.age.plot.hist(bins=30)
plt.title("Distribution of users' ages")
plt.ylabel('count of users')
plt.xlabel('age');
  • 用pandas.cut函数将用户年龄分组
labels = ['0-9', '10-19', '20-29', '30-39', '40-49', '50-59', '60-69', '70-79']
lens['age_group'] = pd.cut(lens.age, range(0, 81, 10), right=False, labels=labels)
lens[['age', 'age_group']].drop_duplicates()[:10]
  • 每个年龄段用户评分人数和打分偏好,看起来年轻人更挑剔一点点
lens.groupby('age_group').agg({'rating': [np.size, np.mean]})
  • 查看被评价过最多次的50部电影在不同年龄段之间的打分差异。并且用unstack函数将数据转换为一个表格,每一行为电影名称,每一列为年龄组,值为该年龄组的用户对该电影的平均评分。

3.5 不同性别间争议最大的电影

lens.reset_index(inplace=True)
pivoted = lens.pivot_table(index=['movieId', 'title'],
                           columns=['gender'],
                           values='rating',
                           fill_value=0)
pivoted['diff'] = pivoted.M - pivoted.F
print(pivoted.head())
pivoted.reset_index('movieId', inplace=True)
disagreements = pivoted[pivoted.movieId.isin(most_50.index)]['diff']
disagreements.sort_values().plot(kind='barh', figsize=[9, 15])
plt.title('Male vs. Female Avg. Ratings\n(Difference > 0 = Favored by Men)')
plt.ylabel('Title')
plt.xlabel('Average Rating Difference')
plt.show()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容