Photo by SpaceX on Unsplash
每个数据分析师都应该熟悉的重要主题之一是分布式数据处理技术(例如Spark)。作为数据分析师,需要对数据集应用不同的查询,以从数据集中提取有用的信息。
但是,如果您的数据很庞大以至于无法在本地计算机上使用它,该怎么办?
使用分布式数据处理和Spark技术将很方便解决这个问题。Apache Spark是用于大数据处理的快速通用引擎,它具有用于流,SQL,机器学习和图处理的内置模块。它以其速度,易用性,通用性以及几乎可以在任何地方运行的能力而闻名。
数据科学家可通过Spark,对大批量数据进行探索性数据分析,特征提取,机器学习和模型评估。
在分布式数据处理中,将数据分为较小部分称为 RDD,并分别处理每个部分。这样,我们可以更快地运行查询。
下面将通过一个小项目,入门使用Spark进行数据探索分析,并实现以下目标:
- 学会使用pySpark模块;
- 了解如何在数据集中,通过不同的查询以提取有用的信息;
- 了解如何在pySpark下使用matplotlib可视化;
- 读取数据 -
本文将使用 lastfm 网站公开的数据集,使用 pySpark 进行一些查询和探索。该网站是一种在线音乐服务,用户可以在其中收听不同的音乐歌曲。该数据集包含两个csv文件listen.csv和genre.csv。
下面导入 pySpark 模块:
from pyspark.sql import SparkSessionfrom pyspark.sql.functions import count, desc , col, max, structimport matplotlib.pyplot as plts
读取 CSV 数据集:
listening_csv_path="/dataset/listenings.csv"listening_df = spark.read.format('csv').option('inferSchema', True).option('header', True).load(listening_csv_path)
因为此处加载的 CSV 文件的文件大小为 1 GB,可能需要一些时间。上面这行代码将返回一个 pySpark 的 DataFrame,并将其存储在 listening_df 中。
- 查看数据基本信息 -
首先可以查看下数据内容:
listening_df.show()
show() 默认最多显示20 条数据。listening_df 是属于 pySpark 的 DataFrame。pySpark 的 DataFrame 和pandas 的 DataFrame 是不同的,它们之间有很多差异。
下面可以删除数据集中无用的列,这里date列是没有用的:
listening_df = listening_df.drop('date')
接下来,我要删除内部具有空值的那些行。可以这样做:
br
我们可以检查下上面操作的结果:
listening_df.show()
可以看到空值和 date 列均被删除。
在操作 DataFrame 前,查看数据集中列的数据类型是很重要的。可以使用printSchema 查看:
listening_df.printSchema()
让我们再看看 DataFrame 中有多少行和列:
shape = (listening_df.count(), len(listening_df.columns))print(shape)
因为 pySpark 的 DataFrame 没有 shape 属性,想要查看它的行和列数,并不像 pandas 的 DataFrame 那样简单。
- 探索分析数据 -
现在将开始做些查询操作,以提取数据集中有用的信息。这里会从简单的开始直到更高级的查询。下面以几个查询任务来介绍这一块。
Q0 :查看关注的某些列数据,而不是全部列数据
可使用 select 。例如,只想查出 track 和 artist的数据:
q0 = listening_df.select('track', 'artist')q0.show()
Q1 :查找听过 Rihanna 的用户的所有记录
用filter可以进行条件过滤:
q1 = listening_df.select('*').filter(listening_df.album == 'Rihanna')q1.show()
其中,select中的 * 和 SQL 中的 * 作用相同,都是查询出所有的列。
Q2 :找到对 Rihanna 感兴趣的 TOP 10 用户
这个查询稍微复杂些。因此,为了查询出结果,必须首先过滤出听过 rihanna的所有用户。然后根据用户进行汇总以计算用户收听 Rihanna 的次数。最后进行降序排序,并取出 top 10 的用户。
q2 = listening_df.select('user_id').filter(listening_df.artist=='Rihanna').groupby("user_id").agg(count("user_id").alias("count")).orderBy(desc("count")).limit(10)q2.show()
Q3 :找出最受欢迎的 TOP 10 歌曲
这个跟 Q2 的类似,都是需要汇总统计和排序。需要注意的一点是,在分组时不能只是以歌曲进行分组汇总统计,而是需要将歌手和歌曲一起进行分组汇总。因为歌曲存在重名的情况。
所以这里主要关注多列分组的用法。
q3 = listening_df.select('artist', 'track').groupby('artist', 'track').agg(count("*").alias("count")).orderBy(desc("count")).limit(10)q3.show()
Q4 :找出 Rihanna 最受欢迎的 TOP 10 歌曲
此查询与上一个Q3查询基本一样,只不过要加一个过滤条件就可以了。
q4 =listening_df.select('artist', 'track').filter(listening_df.artist == 'Rihanna').groupby("artist", "track").agg(count("*").alias("count")).orderBy(desc("count")).limit(10)q4.show()
- 合并数据 -
本模块主要介绍pySpark中合并数据的方法,会涉及到不同来源的数据集。所以下面,会导入第二份数据集, 此数据集包含歌手的曲风类型(简称:流派)数据:
genre_csv_path = "/dataset/genre.csv"genre_df = spark.read.format('csv').option("inferSchema",True).option("header", True).load(genre_csv_path)genre_df.show()
可以看到此数据集只包含歌手名 artist 和对应的流派 genre。下面将两个数据集进行合并。
使用 join 方法, 参数 how 指定要合并的方式。on 指定合并时的主键,可以是一个列,也可以是多个列。
data = listening_df.join(genre_df, how='inner', on=['artist'])data.show()
可以看到流派genre被合并了进来。
- 复杂查询 -
合并数据后,做一个复杂查询吧.
Q5 .查找 top10 流派
q5 = data.select("genre").groupby("genre").agg(count("*").alias("count")).orderBy(desc("count")).limit(10)q5.show()
Q6. 找出每个用户最喜欢的流派
因此,我们要做的就是为每个用户找到他或她喜欢的类型。或者说她或他喜欢什么类型的音乐。
这个查询有点复杂,因此下面将分为两部分进行查询操作。
首先,计算每个用户收听特定流派的次数,然后我们会发现他或她听过很多次或最多次的流派。
q6_1 = data.select("user_id", 'genre').groupby("user_id","genre").agg(count("*").alias("count"))q6_1.show()
下面,我将找出每个用户听过最大次数的流派:
q8_2 = q8_1.groupby("user_id").agg(max(struct('count', 'genre')).alias("max")).select("user_id", "max.genre", "max.count")q8_2.show()
可以看到已经成功输出要查询的结果。
这里需要注意max的使用和select的使用:
# max(struct('count', 'genre'))# select("user_id", "max.genre", "max.count")
用到了struct函数,struct函数将允许您将两个或多个列组合在一起。所以这里使用了一个技巧,在使用max找到最大值时,也可以返回流派名称。
- 数据可视化 -
现在让我们继续进行最后的可视化操作。我们需要找出有多少摇滚(rock),流行(pop),金属乐(metal)和嘻哈(hip hop)的歌手,然后要使用条形图将其可视化。
所以先需要统计出这些流派的歌手数:
q7.show()
然后构造数据并进行可视化:
labels = [row["genre"] for row in q7_list]counts = [row["count"] for row in q7_list]plts.bar(labels, counts)
另一种可视化的方法是,可以把 pySpark 的DataFrame 转化成 pandas 的DataFrame,然后进行可视化。
pySpark 提供了 to_pandas() 方法,可以方便的进行转换。
- End -
参考及示例:
http://spark.apache.org/docs/latest/sql-programming-guide.html
https://www.coursera.org/projects/data-analysis-using-pyspark
to_pandas():
http://spark.apache.org/docs/latest/sql-pyspark-pandas-with-arrow.html
数据集:
http://millionsongdataset.com/