在 Spark 中使用 IPython Notebook
2016-05-07 23:07
681 查看
本文是从 IPython Notebook 转化而来,效果没有本来那么好。
主要为体验 IPython Notebook。至于题目,改成《在 IPython Notebook 中使用 Spark》也可以,没什么差别。为什么是 Spark?因为这两天在看《Spark 机器学习》这本书第 3 章,所以就顺便做个笔记。
简单介绍下,IPython notebook 对数据科学家来说是个交互地呈现科学和理论工作的必备工具,它集成了文本和 Python 代码。Spark 是个通用的集群计算框架,通过将大量数据集计算任务分配到多台计算机上,提供高效内存计算。
CPU:1核
内存:2048 MB
操作系统:Ubuntu 14.04 64位
固定带宽:1Mbps
IPython Notebook 的安装很简单,强烈推荐一个预编译的科学 Python 套件 Anacaonda,按照官方网站安装,然后在 Terminal 里执行
在我的 Ubuntu 服务器上打开
这个问题耗了我一天时间。关于这个问题,众 说 纷 纭,但只有下面两种方法管用。
一种是,在命令后加参数 --ip:
另一种是,先生成 notebook 配置文件:命令行执行
如果想外网也可以访问,ip 就设为外网 IP 地址。我选择的是第二种,设的外网 IP 地址,这样就可以在 Windows 上编辑 ipython notebook 文件了,非常方便。
Spark 的安装也很简单,具体安装和使用可参考我之前的笔记和官方网站。
如何在 Spark 中使用 IPython Notebook,或者如何在 IPython Notebook 中使用 spark,也耗费了我一天时间。
网上很多文章都是建议:1、执行
但这种方法在我这行不通,百般折腾,就是各种不行。
后来终于发现一种简单可行的方法,那就是修改 ~/.bashrc 文件,添加以下内容:
export PYSPARK_DRIVER_PYTHON=ipython2 # As pyspark only works with python2 and not python3
export PYSPARK_DRIVER_PYTHON_OPTS="notebook"
然后
下面我们通过实例演示 Spark 在 IPython Notebook 中的使用。
可到官方网站下载,解压后会创建一个名为 ml-100k 的文件夹,该目录中重要的文件有 u.user(用户属性文件)、u.item(电影元数据) 和 u.data(用户对电影的评分)。
数据集的更多信息可以从 README 获得,包括每个数据文件里的变量定义。我们可以使用 head 命令来查看各个文件的内容。
先来看 u.user:
1|24|M|technician|85711
2|53|F|other|94043
3|23|M|writer|32067
4|24|M|technician|43537
5|33|F|other|15213
可以看到 u.user 文件包含 user id、age、gender、occupation 和 ZIP code 这些属性,各属性之间用管道符(|)隔开。
再来看 u.item:
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
2|GoldenEye (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?GoldenEye%20(1995)|0|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0
3|Four Rooms (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Four%20Rooms%20(1995)|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0
4|Get Shorty (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Get%20Shorty%20(1995)|0|1|0|0|0|1|0|0|1|0|0|0|0|0|0|0|0|0|0
5|Copycat (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Copycat%20(1995)|0|0|0|0|0|0|1|0|1|0|0|0|0|0|0|0|1|0|0
u.item 文件包含 movie id、title、release date 以及若干与 IMDB Link 和电影分类相关的属性,各属性之间也用 | 符号分隔。电影分类的属性有 unknown | Action | Adventure | Animation | Children's | Comedy | Crime | Documentary | Drama | Fantasy | Film-Noir | Horror | Musical | Mystery | Romance | Sci-Fi | Thriller | War | Western。
最后 u.data:
196 242 3 881250949
186 302 3 891717742
22 377 1 878887116
244 51 2 880606923
166 346 1 886397596
u.data 文件包含 user id、movie id、rating(从 1 到 5)和 timestamp 属性。各属性间用指标符分隔。timestamp 是从 1/1/1970 UTC 开始的秒数。
sc 是 Spark shell 启动时自动创建的一个 SparkContext 对象,shell 通过该对象来访问 Spark。可以通过下列方法输出 sc 来查看它的类型。
一旦有了 SparkContext,你就可以用它来创建 RDD。RDD 是弹性分布式数据集(Resilient Distributed Dataset),在 Spark 中,我们通过对 RDD 的操作来表达我们的计算意图,这些计算会自动地在集群上并行进行。
如上面代码创建了一个名为 user_data 的 RDD,然后使用 user_data.first() 输出了 RDD 中的第一个元素。
下面用“|”字符来分隔各行数据。这将生成一个 RDD,其中每一个记录对应一个 Python 列表,各列表由用户 ID、年龄、性别、职业和邮编五个属性构成。之后再统计用户、性别、职业和邮编的数目。可通过下列代码实现。
接着用 Matplotlib 的 hist 函数来创建一个直方图,以分析用户年龄的分布情况。
这里 hist 函数的输入参数有 ages 数组、直方图的 bins 数目(即区间数,这里为 20),同时,还使用了 normed=True 参数来正则化直方图,即让每个方条表示年龄在该区间内的数量占总数量的比。
从中可以看出 MovieLens 的大量用户处于 15 到 55 之间。
若想了解用户的职业分布情况,可以用如下代码来实现。首先利用 MapReduce 方法来计算数据集中各种职业的出现次数,然后用 matplotlib 的 bar 函数来会绘制一个不同职业的数量的条形图。
从中可以看出,数量最多的职业是 student、other、educator、administrator、engineer 和 programmer。
Spark 对 RDD 提供了一个名为 countByValue 的便捷函数,它会计算 RDD 里各不同值所分别出现的次数,并将其以 Python dict 函数的形式返回给驱动程序。
可以看到,上述两种方式的结果相同。
接下来探索电影数据,跟之前一样,先简单看下第一行记录,然后再统计电影总数。
在此要绘制电影年龄分布图,电影年龄即其发行年份相对于现在过了多少年(在本数据中现在是 1998 年)。电影数据有些不完整,需要一个函数来处理解析 release date 时可能出现的解析错误。这里命名该函数为 convert_year。
有了以上函数来解析发行年份后,便可在调用电影数据进行 map 转换时应用该函数,并取回其结果。
从图中可以看到,大部分电影发行于 1998 年的前几年。
现实的数据经常会有不规整的情况,对其解析时就需要进一步处理。上面即是一个很好的例子。事实上,这也表明了数据探索的重要性所在,即它有助于发现数据在完整性和质量上的问题。
现在来探索评级数据:
可以看到评级次数共有 10 万条。另外和用户与电影数据不同,评分记录用“\t”分隔。
从中可以看到,最低的评级为 1,而最大的评级为 5.这并不意外,因为评级的范围便是从 1 到 5。
Spark 对 RDD 也提供一个名为 states 的函数。该函数包含一个数值变量用于做类似的统计:
可以看出,用户对电影的平均评级是 3.5 左右,而评分中位数为 4,。说明评级的分布稍倾向高分。要验证这点,可创建一个评级值分布的条形图。
其特征和之前的期待相同,评分分布确实偏向中等以上。
同样,也可以求各个用户评级次数的分布情况。计算各用户评级次数的分布时,先从 rating_data RDD 里提取出以用户 ID 为主键、评级为值的键值对。之后调用 Spark 的 groupByKey 函数,来对评级以用户 ID 为主键进行分组。
接着求出每一个主键(用户 ID)对应的评级集合的大小,这会给出各用户评级的次数:
最后,用 hist 来绘制用户评级分布的直方图。
可以看出,大部分用户的评级次数少于 100,但也表明仍然有较多用户做出过上百次的评级。
Ipython-Spark setup for pyspark application
《Spark 机器学习》 第 3 章
主要为体验 IPython Notebook。至于题目,改成《在 IPython Notebook 中使用 Spark》也可以,没什么差别。为什么是 Spark?因为这两天在看《Spark 机器学习》这本书第 3 章,所以就顺便做个笔记。
简单介绍下,IPython notebook 对数据科学家来说是个交互地呈现科学和理论工作的必备工具,它集成了文本和 Python 代码。Spark 是个通用的集群计算框架,通过将大量数据集计算任务分配到多台计算机上,提供高效内存计算。
搭建环境
一台阿里云服务器,配置如下,108 元/月,然后在 Windows 7 上使用 Putty 和远程桌面操作服务器。CPU:1核
内存:2048 MB
操作系统:Ubuntu 14.04 64位
固定带宽:1Mbps
IPython Notebook 的安装很简单,强烈推荐一个预编译的科学 Python 套件 Anacaonda,按照官方网站安装,然后在 Terminal 里执行
ipython notebook即可。
在我的 Ubuntu 服务器上打开
ipython notebook时报错了:socket.error Errno 99 Cannot assign requested address。
这个问题耗了我一天时间。关于这个问题,众 说 纷 纭,但只有下面两种方法管用。
一种是,在命令后加参数 --ip:
ipython notebook --ip=127.0.0.1。
另一种是,先生成 notebook 配置文件:命令行执行
jupyter notebook --generate-config,然后打开生成的文件:
vi ~/.jupyter/jupyter_notebook_config.py,修改 c.NotebookApp.ip = '127.0.0.1'。
如果想外网也可以访问,ip 就设为外网 IP 地址。我选择的是第二种,设的外网 IP 地址,这样就可以在 Windows 上编辑 ipython notebook 文件了,非常方便。
Spark 的安装也很简单,具体安装和使用可参考我之前的笔记和官方网站。
如何在 Spark 中使用 IPython Notebook,或者如何在 IPython Notebook 中使用 spark,也耗费了我一天时间。
网上很多文章都是建议:1、执行
ipython profile create spark;2、创建 ~/.ipython/profile_spark/startup/00-pyspark-setup.py 文件并修改;3、启动 IPython notebook:ipython notebook --profile spark。
但这种方法在我这行不通,百般折腾,就是各种不行。
后来终于发现一种简单可行的方法,那就是修改 ~/.bashrc 文件,添加以下内容:
export PYSPARK_DRIVER_PYTHON=ipython2 # As pyspark only works with python2 and not python3
export PYSPARK_DRIVER_PYTHON_OPTS="notebook"
然后
source .bashrc,就可以通过启动 pyspark 来启动 IPython Notebook 了。也就可以在 IPython Notebook 中使用 pyspark 了。
下面我们通过实例演示 Spark 在 IPython Notebook 中的使用。
Spark 上数据的探索和处理
MovieLens 100k 数据集
这里要使用的是著名的 MovieLens 100k 数据集,该数据集包含用户对电影的 10 万次评分数据,也包含电影元数据和用户属性数据。数据集不大,压缩文件不到 5M,常用于推荐系统研究。可到官方网站下载,解压后会创建一个名为 ml-100k 的文件夹,该目录中重要的文件有 u.user(用户属性文件)、u.item(电影元数据) 和 u.data(用户对电影的评分)。
数据集的更多信息可以从 README 获得,包括每个数据文件里的变量定义。我们可以使用 head 命令来查看各个文件的内容。
先来看 u.user:
#head -5 u.user
1|24|M|technician|85711
2|53|F|other|94043
3|23|M|writer|32067
4|24|M|technician|43537
5|33|F|other|15213
可以看到 u.user 文件包含 user id、age、gender、occupation 和 ZIP code 这些属性,各属性之间用管道符(|)隔开。
再来看 u.item:
# head -5 u.item
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
2|GoldenEye (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?GoldenEye%20(1995)|0|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0
3|Four Rooms (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Four%20Rooms%20(1995)|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0
4|Get Shorty (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Get%20Shorty%20(1995)|0|1|0|0|0|1|0|0|1|0|0|0|0|0|0|0|0|0|0
5|Copycat (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Copycat%20(1995)|0|0|0|0|0|0|1|0|1|0|0|0|0|0|0|0|1|0|0
u.item 文件包含 movie id、title、release date 以及若干与 IMDB Link 和电影分类相关的属性,各属性之间也用 | 符号分隔。电影分类的属性有 unknown | Action | Adventure | Animation | Children's | Comedy | Crime | Documentary | Drama | Fantasy | Film-Noir | Horror | Musical | Mystery | Romance | Sci-Fi | Thriller | War | Western。
最后 u.data:
# head -5 u.data
196 242 3 881250949
186 302 3 891717742
22 377 1 878887116
244 51 2 880606923
166 346 1 886397596
u.data 文件包含 user id、movie id、rating(从 1 到 5)和 timestamp 属性。各属性间用指标符分隔。timestamp 是从 1/1/1970 UTC 开始的秒数。
探索与可视化数据集
先来探索用户数据。user_data = sc.textFile("ml-100k/u.user") user_data.first() #此处如能输出数据文件首行,则说明环境搭建没问题
u'1|24|M|technician|85711'
sc 是 Spark shell 启动时自动创建的一个 SparkContext 对象,shell 通过该对象来访问 Spark。可以通过下列方法输出 sc 来查看它的类型。
sc
<pyspark.context.SparkContext at 0x7fb65c173450>
一旦有了 SparkContext,你就可以用它来创建 RDD。RDD 是弹性分布式数据集(Resilient Distributed Dataset),在 Spark 中,我们通过对 RDD 的操作来表达我们的计算意图,这些计算会自动地在集群上并行进行。
如上面代码创建了一个名为 user_data 的 RDD,然后使用 user_data.first() 输出了 RDD 中的第一个元素。
下面用“|”字符来分隔各行数据。这将生成一个 RDD,其中每一个记录对应一个 Python 列表,各列表由用户 ID、年龄、性别、职业和邮编五个属性构成。之后再统计用户、性别、职业和邮编的数目。可通过下列代码实现。
user_fields = user_data.map(lambda line: line.split("|")) num_users = user_fields.map(lambda fields: fields[0]).count() 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
接着用 Matplotlib 的 hist 函数来创建一个直方图,以分析用户年龄的分布情况。
%pylab inline
Populating the interactive namespace from numpy and matplotlib
ages = user_fields.map(lambda x: int(x[1])).collect() hist(ages, bins=20, color='lightblue', normed=True) fig = matplotlib.pyplot.gcf() fig.set_size_inches(16, 10)
这里 hist 函数的输入参数有 ages 数组、直方图的 bins 数目(即区间数,这里为 20),同时,还使用了 normed=True 参数来正则化直方图,即让每个方条表示年龄在该区间内的数量占总数量的比。
从中可以看出 MovieLens 的大量用户处于 15 到 55 之间。
若想了解用户的职业分布情况,可以用如下代码来实现。首先利用 MapReduce 方法来计算数据集中各种职业的出现次数,然后用 matplotlib 的 bar 函数来会绘制一个不同职业的数量的条形图。
count_by_occupation = user_fields.map(lambda fields: (fields[3], 1)).reduceByKey(lambda x, y: x + y).collect() x_axis1 = np.array([c[0] for c in count_by_occupation]) y_axis1 = np.array([c[1] for c in count_by_occupation]) x_axis = x_axis1[np.argsort(y_axis1)] y_axis = y_axis1[np.argsort(y_axis1)] pos = np.arange(len(x_axis)) width = 1.0 ax = plt.axes() ax.set_xticks(pos + (width / 2)) ax.set_xticklabels(x_axis) plt.bar(pos, y_axis, width, color='lightblue') plt.xticks(rotation=30) fig = matplotlib.pyplot.gcf() fig.set_size_inches(16, 10)
count_by_occupation
[(u'administrator', 79), (u'writer', 45), (u'retired', 14), (u'student', 196), (u'doctor', 7), (u'entertainment', 18), (u'marketing', 26), (u'executive', 32), (u'none', 9), (u'scientist', 31), (u'educator', 95), (u'lawyer', 12), (u'healthcare', 16), (u'technician', 27), (u'librarian', 51), (u'programmer', 66), (u'artist', 28), (u'salesman', 12), (u'other', 105), (u'homemaker', 7), (u'engineer', 67)]
x_axis1
array([u'administrator', u'writer', u'retired', u'student', u'doctor', u'entertainment', u'marketing', u'executive', u'none', u'scientist', u'educator', u'lawyer', u'healthcare', u'technician', u'librarian', u'programmer', u'artist', u'salesman', u'other', u'homemaker', u'engineer'], dtype='<U13')
y_axis1
array([ 79, 45, 14, 196, 7, 18, 26, 32, 9, 31, 95, 12, 16, 27, 51, 66, 28, 12, 105, 7, 67])
从中可以看出,数量最多的职业是 student、other、educator、administrator、engineer 和 programmer。
Spark 对 RDD 提供了一个名为 countByValue 的便捷函数,它会计算 RDD 里各不同值所分别出现的次数,并将其以 Python dict 函数的形式返回给驱动程序。
count_by_occupation2 = user_fields.map(lambda fields: fields[3]).countByValue() print "Map-reduce approach:" print dict(count_by_occupation2) print "" print "countByValue approach:" print dict(count_by_occupation)
Map-reduce approach: {u'administrator': 79, u'retired': 14, u'lawyer': 12, u'healthcare': 16, u'marketing': 26, u'executive': 32, u'scientist': 31, u'student': 196, u'technician': 27, u'librarian': 51, u'programmer': 66, u'salesman': 12, u'homemaker': 7, u'engineer': 67, u'none': 9, u'doctor': 7, u'writer': 45, u'entertainment': 18, u'other': 105, u'educator': 95, u'artist': 28} 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}
可以看到,上述两种方式的结果相同。
接下来探索电影数据,跟之前一样,先简单看下第一行记录,然后再统计电影总数。
movie_data = sc.textFile("ml-100k/u.item") print movie_data.first() num_movies = movie_data.count() print "Movies: %d" % num_movies
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 Movies: 1682
在此要绘制电影年龄分布图,电影年龄即其发行年份相对于现在过了多少年(在本数据中现在是 1998 年)。电影数据有些不完整,需要一个函数来处理解析 release date 时可能出现的解析错误。这里命名该函数为 convert_year。
def convert_year(x): try: return int(x[-4:]) except: return 1900 # there is a 'bad' data point with a blank year, which we set to 1900 and will filter out later
有了以上函数来解析发行年份后,便可在调用电影数据进行 map 转换时应用该函数,并取回其结果。
movie_fields = movie_data.map(lambda lines: lines.split("|")) years = movie_fields.map(lambda fields: fields[2]).map(lambda x: convert_year(x)) # we filter out any 'bad' data points here years_filtered = years.filter(lambda x: x != 1900) # plot the movie ages histogram movie_ages = years_filtered.map(lambda yr: 1998-yr).countByValue() values = movie_ages.values() bins = movie_ages.keys() hist(values, bins=bins, color='lightblue', normed=True) fig = matplotlib.pyplot.gcf() fig.set_size_inches(16,10)
从图中可以看到,大部分电影发行于 1998 年的前几年。
现实的数据经常会有不规整的情况,对其解析时就需要进一步处理。上面即是一个很好的例子。事实上,这也表明了数据探索的重要性所在,即它有助于发现数据在完整性和质量上的问题。
现在来探索评级数据:
rating_data_raw = sc.textFile("ml-100k/u.data") print rating_data_raw.first() num_ratings = rating_data_raw.count() print "Ratings: %d" % num_ratings
196 242 3 881250949 Ratings: 100000
可以看到评级次数共有 10 万条。另外和用户与电影数据不同,评分记录用“\t”分隔。
rating_data = rating_data_raw.map(lambda line: line.split("\t")) ratings = rating_data.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) / float(num_ratings) median_rating = np.median(ratings.collect()) ratings_per_user = num_ratings / num_users ratings_per_movie = num_ratings / num_movies print "Min rating: %d" % min_rating print "Max rating: %d" % max_rating print "Average rating: %2.2f" % mean_rating print "Median rating: %d" % median_rating print "Average # of ratings per user: %2.2f" % ratings_per_user print "Average # of ratings per movie: %2.2f" % ratings_per_movie
Min rating: 1 Max rating: 5 Average rating: 3.53 Median rating: 4 Average # of ratings per user: 106.00 Average # of ratings per movie: 59.00
从中可以看到,最低的评级为 1,而最大的评级为 5.这并不意外,因为评级的范围便是从 1 到 5。
Spark 对 RDD 也提供一个名为 states 的函数。该函数包含一个数值变量用于做类似的统计:
ratings.stats()
(count: 100000, mean: 3.52986, stdev: 1.12566797076, max: 5.0, min: 1.0)
可以看出,用户对电影的平均评级是 3.5 左右,而评分中位数为 4,。说明评级的分布稍倾向高分。要验证这点,可创建一个评级值分布的条形图。
# create plot of counts by rating value count_by_rating = ratings.countByValue() x_axis = np.array(count_by_rating.keys()) y_axis = np.array([float(c) for c in count_by_rating.values()]) # we normalize the y-axis here to percentages y_axis_normed = y_axis / y_axis.sum() pos = np.arange(len(x_axis)) width = 1.0 ax = plt.axes() ax.set_xticks(pos + (width / 2)) ax.set_xticklabels(x_axis) plt.bar(pos, y_axis_normed, width, color='lightblue') plt.xticks(rotation=30) fig = matplotlib.pyplot.gcf() fig.set_size_inches(16, 10)
其特征和之前的期待相同,评分分布确实偏向中等以上。
同样,也可以求各个用户评级次数的分布情况。计算各用户评级次数的分布时,先从 rating_data RDD 里提取出以用户 ID 为主键、评级为值的键值对。之后调用 Spark 的 groupByKey 函数,来对评级以用户 ID 为主键进行分组。
# to compute the distribution of ratings per user, we first group the ratings by user id user_ratings_grouped = rating_data.map(lambda fields: (int(fields[0]), int(fields[2]))).groupByKey()
接着求出每一个主键(用户 ID)对应的评级集合的大小,这会给出各用户评级的次数:
# then, for each key (user id), we find the size of the set of ratings, which gives us the # ratings for that user user_ratings_byuser = user_ratings_grouped.map(lambda (k, v): (k, len(v))) user_ratings_byuser.take(5)
[(1, 272), (2, 62), (3, 54), (4, 24), (5, 175)]
最后,用 hist 来绘制用户评级分布的直方图。
# and finally plot the histogram user_ratings_byuser_local = user_ratings_byuser.map(lambda (k, v): v).collect() hist(user_ratings_byuser_local, bins=200, color='lightblue', normed=True) fig = matplotlib.pyplot.gcf() fig.set_size_inches(16,10)
可以看出,大部分用户的评级次数少于 100,但也表明仍然有较多用户做出过上百次的评级。
参考资料
Spark入门(Python版)Ipython-Spark setup for pyspark application
《Spark 机器学习》 第 3 章
相关文章推荐
- Python 时间处理函数
- leetcode(2), Nin Game详解(python)
- while死循环
- python基本数据类型之集合set
- Python3安装xlutils
- python入门学习课程推荐
- 关于在Ubuntu下安装配置numpy,scipy,matplotlibm,pandas 以及sklearn
- 最新python中一升级所有已安装的包方法
- python进阶学习笔记(三)数据库支持 (实际工作用到了)
- Python运行机制
- python实现简单爬虫功能
- python小程序---20以内的加减法进位运算
- python基础之函数进阶之函数作为返回值/装饰器
- 用python爬虫爬HRBUSTOJ代码
- python中的赋值
- python 文件读写项目实践
- python 参数传递[打包与解包问题]
- DayDayUP_Python自学教程[12]_Python正则表达式
- python numpy
- python的一些函数