【pyspark学习从入门到精通4】弹性分布式数据集_2

目录

[Lambda 表达式](#Lambda 表达式)

全局作用域与局部作用域

Transformations

[.map(...) 转换](#.map(...) 转换)

[.filter(...) 转换](#.filter(...) 转换)

[.flatMap(...) 转换](#.flatMap(...) 转换)

[.distinct(...) 转换](#.distinct(...) 转换)

[.sample(...) 转换](#.sample(...) 转换)

[.leftOuterJoin(...) 转换](#.leftOuterJoin(...) 转换)

[.repartition(...) 转换](#.repartition(...) 转换)


Lambda 表达式

在这个例子中,我们将从 data_from_file 的加密外观记录中提取有用的信息。

首先,让我们使用以下代码定义一个方法,该方法将解析不可读的行,使其变得有用:

python 复制代码
def extractInformation(row):
 import re
 import numpy as np
 selected_indices = [
 2,4,5,6,7,9,10,11,12,13,14,15,16,17,18,
 ...
 77,78,79,81,82,83,84,85,87,89
 ]
 record_split = re\
 .compile(
 r'([\s]{19})([0-9]{1})([\s]{40})
 ...
 ([\s]{33})([0-9\s]{3})([0-9\s]{1})([0-9\s]{1})')
 try:
 rs = np.array(record_split.split(row))[selected_indices]
 except:
 rs = np.array(['-99'] * len(selected_indices))
 return rs

接下来,我们导入必要的模块:使用正则表达式解析记录的 re 模块,以及为了方便一次性选择多个元素而使用的 NumPy。

最后,我们创建一个 Regex 对象,根据指定提取信息,并通过它解析行。

一旦记录被解析,我们尝试将列表转换为 NumPy 数组并返回它;如果失败,我们返回一个默认值列表 -99,以便我们知道这个记录没有正确解析。

现在,我们将使用 extractInformation(...) 方法来拆分和转换我们的数据集。注意,我们只将方法签名传递给 .map(...):每次在每个分区中,方法将一次将 RDD 的一个元素交给 extractInformation(...) 方法:

运行 data_from_file_conv.take(1) 将产生以下结果(缩写):

全局作用域与局部作用域

作为潜在的 PySpark 用户,你需要习惯 Spark 的固有并行性。即使你精通 Python,执行 PySpark 脚本也需要稍微改变你的思维方式。

Spark 可以在两种模式下运行:本地和集群。当你在本地运行 Spark 时,你的代码可能与你目前习惯的 Python 运行没有太大不同:变化可能主要是语法上的,但增加了数据和代码可以在单独的工作进程之间复制的额外转折。

然而,如果你不小心,将相同的代码部署到集群可能会引起很多困惑。这需要理解 Spark 如何在集群上执行作业。

在集群模式下,当提交作业执行时,作业被发送到驱动程序(或主节点)。驱动程序节点为作业创建一个 DAG,并决定哪个执行器(或工作)节点将运行特定任务。

然后驱动程序指示工作执行它们的任务,并在完成后将结果返回给驱动程序。然而,在这之前,驱动程序准备每个任务的闭包:一组在驱动程序上存在,供工作在 RDD 上执行任务的变量和方法。

这组变量和方法在执行器的上下文中本质上是静态的,即每个执行器从驱动程序获得变量和方法的副本。如果执行器在运行任务时更改这些变量或覆盖方法,它这样做不会影响其他执行器的副本或驱动程序的变量和方法。这可能会导致一些意想不到的行为和运行时错误,有时可能很难追踪。

Transformations

转换可以塑造您的数据集。这些包括映射、过滤、连接和数据集中的值的转码。在本节中,我们将展示一些在 RDDs 上可用的转换。

由于 RDDs 是无模式的,因此在本节中,我们假设您知道生成的数据集的模式。如果您记不住解析后数据集中信息的位置,我们建议您参考 GitHub 上 extractInformation(...) 方法的定义,这是第 03 章的代码。

.map(...) 转换

可以说,您将最常使用 .map(...) 转换。该方法应用于 RDD 的每个元素:在 data_from_file_conv 数据集的情况下,您可以将其视为对每一行的转换。

在这个例子中,我们将创建一个新的数据集,将死亡年份转换为数值:

python 复制代码
data_2014 = data_from_file_conv.map(lambda row: int(row[16]))

运行 data_2014.take(10) 将产生以下结果:

您当然可以带来更多的列,但您必须将它们打包成元组、字典或列表。让我们还包括行的第 17 个元素,以便我们可以确认我们的 .map(...) 是否按预期工作:

python 复制代码
data_2014_2 = data_from_file_conv.map(
 lambda row: (row[16], int(row[16]):)
data_2014_2.take(5)

前面的代码将产生以下结果:

.filter(...) 转换

另一个经常使用的转换是 .filter(...) 方法,它允许您根据指定的条件从数据集中选择元素。例如,从 data_from_file_conv 数据集中,让我们计算一下有多少人在 2014 年因意外事故死亡:

python 复制代码
data_filtered = data_from_file_conv.filter(
 lambda row: row[16] == '2014' and row[21] == '0')
data_filtered.count()

.flatMap(...) 转换

.flatMap(...) 方法的工作原理类似于 .map(...),但它返回的是扁平化的结果而不是列表。如果我们执行以下代码:

python 复制代码
data_2014_flat = data_from_file_conv.flatMap(lambda row: (row[16], 
int(row[16]) + 1))
data_2014_flat.take(10)

它将产生以下输出:

您可以将这个结果与之前生成 data_2014_2 的命令的结果进行比较。注意,正如前面提到的,当您需要解析输入时,可以使用 .flatMap(...) 方法过滤掉一些格式错误的记录。在内部,.flatMap(...) 方法将每一行视为一个列表,然后将所有记录简单相加;通过传递一个空列表,格式错误的记录就会被丢弃。

.distinct(...) 转换

这个方法返回指定列中的唯一值列表。如果您想了解或验证您的数据集,这非常有用。让我们检查性别列是否只包含男性和女性;这将验证我们是否正确解析了数据集。让我们运行以下代码:

python 复制代码
distinct_gender = data_from_file_conv.map(
 lambda row: row[5]).distinct()
distinct_gender.collect()

这段代码将产生以下输出:

首先,我们只提取包含性别的列。接下来,我们使用 .distinct() 方法只选择列表中的唯一值。最后,我们使用 .collect() 方法返回屏幕上的值打印。

.sample(...) 转换

.sample(...) 方法从数据集中返回一个随机样本。第一个参数指定采样是否应该有放回,第二个参数定义要返回的数据比例,第三个参数是伪随机数生成器的种子:

python 复制代码
fraction = 0.1
data_sample = data_from_file_conv.sample(False, fraction, 666)

在这个例子中,我们从原始数据集中随机抽取了 10% 的样本。

为了确认这一点,让我们打印一下数据集的大小:

python 复制代码
print('Original dataset: {0}, sample: {1}'\
.format(data_from_file_conv.count(), data_sample.count()))

前面的命令产生了以下输出:

我们使用 .count() 动作来计算相应 RDDs 中的所有记录数。

.leftOuterJoin(...) 转换

.leftOuterJoin(...) 与 SQL 世界中的操作类似,基于两个数据集中找到的值连接两个 RDDs,并返回左 RDD 的记录以及在两个 RDD 匹配的地方附加的右 RDD 的记录:

python 复制代码
rdd1 = sc.parallelize([('a', 1), ('b', 4), ('c',10)])
rdd2 = sc.parallelize([('a', 4), ('a', 1), ('b', '6'), ('d', 15)])
rdd3 = rdd1.leftOuterJoin(rdd2)

在 rdd3 上运行 .collect(...) 将产生以下结果:

这里你可以看到来自 RDD rdd1 的所有元素以及来自 RDD rdd2 的相应值。如你所见,值 'a' 在 rdd3 中出现了两次,并且在 RDD rdd2 中也出现了两次。来自 rdd1 的值 'b' 只出现了一次,并与来自 rdd2 的值 '6' 连接。少了两件事:rdd1 中的值 'c' 在 rdd2 中没有对应的键,所以在返回的元组中显示为 None;而且,由于我们执行的是左外连接,所以 rdd2 中的值 'd' 按预期消失了。

如果我们使用 .join(...) 方法,我们只会得到 'a' 和 'b' 的值,因为这两个值在这两个 RDD 中相交。运行以下代码:

python 复制代码
rdd4 = rdd1.join(rdd2)
rdd4.collect()

它将产生以下输出:

另一个有用的方法是 .intersection(...),它返回两个 RDDs 中相等的记录。执行以下代码:

python 复制代码
rdd5 = rdd1.intersection(rdd2)
rdd5.collect()

输出如下:

.repartition(...) 转换

重新分区数据集会改变数据集被分成的分区数量。这个功能应该谨慎使用,只在真正必要时使用,因为它会重新整理数据,这实际上会导致性能上的显著下降:

python 复制代码
rdd1 = rdd1.repartition(4)
len(rdd1.glom().collect())

前面的代码打印出 4 作为新的分区数量。

与 .collect() 相反,.glom() 方法产生的是一个列表,其中每个元素是另一个列表,包含在指定分区中存在的数据集中的所有元素;返回的主列表的元素数量与分区数量相同。

相关推荐
致奋斗的我们9 分钟前
RHCE的学习(7)
linux·服务器·网络·学习·redhat·rhce·rhcsa
秦朝胖子得加钱20 分钟前
Flask
后端·python·flask
幽兰的天空24 分钟前
Python实现的简单时钟
开发语言·python
NCU_AI1 小时前
Python 网络爬虫快速入门
python·网络爬虫
幽兰的天空1 小时前
简单的Python爬虫实例
开发语言·爬虫·python
XinZong1 小时前
【AI开源项目】OneAPI -核心概念、特性、优缺点以及如何在本地和服务器上进行部署!
人工智能·开源
迷路爸爸1801 小时前
深入理解Allan方差:用体重数据分析误差的时间尺度与稳定性
机器学习·数据分析·概率论
机器之心1 小时前
Runway CEO:AI公司的时代已经结束了
人工智能·后端
孤客网络科技工作室2 小时前
深入学习 Scrapy 框架:从入门到精通的全面指南
学习·scrapy