Spark RDD (二)

在Spark RDD(一)https://blog.csdn.net/katherine_hsr/article/details/80743626中,介绍了RDD的原理,创建RDD和全局作用域和局部作用域,这里我们介绍一下RDD的操作(Operations)。
我们可以通过转换来调整数据集,包括映射、筛选、连接、转换数据集中的值。
RDD支持两种类型的操作:转换:是从一个已有的数据集中建立一个新的数据集,并在这个数据集运算结束后返回值到驱动程序中。例如,map将数据集中的每个元素都执行一个函数,并将结果返回给一个新的RDD。Reduce通过一些方法将RDD中所有的元素聚集起来然后返回一个最终结果到驱动程序上。
Spark中所有的转换都是惰性的,他们不立刻计算结果。它们仅仅记录应用在一下基础数据集中的转换操作。只有当一个转换需要结果返回到驱动程序上时转换才被计算,这个设计是的Spark更高效的运行。例如,我们可以实现通过map创建的数据集,然后在reduce中使用然后仅仅返回reduce后的结果到驱动程序上,而不是更大的map操作中产生的数据集。
每一次运行一个action的时候每一个转换的RDD都被计算一次。但是,我们可以使用persist方法保存在RDD的内存中,在这里Spark能够把数据保存在集群中,使得下次查询时能够更快的访问。也可以保存RDD到磁盘上,或者复制多个节点。

lines = sc.textFile("data.txt")
lineLengths = lines.map(lambda s: len(s))
totalLength = lineLengths.reduce(lambda a, b: a + b)

在以上操作中,第一行通过读取一个文件创建了一个基本的RDD。这个数据集没有加载到内存,也没有进行其他的操作,变量lines仅仅是一个指向文件的指针。第二行为transformation操作map的结果。此时lineLengths也没有进行运算,因为map操作为懒执行。最后,执行action操作reduce。此时Spark将运算分隔成多个任务分发给多个机器,每个机器执行各自部分的map并进行本地reduce,最后返回运行结果给驱动程序
如果我们之后还想使用lineLengths,可以执行以下操作使lineLengths在第一次计算后就保存在内存中:

lineLengths.persist()

1. 转换

map()转换

Map: 将原数据的每个元素传给函数func进行格式化,返回一个新的分布式数据集。(原文:Return a new distributed dataset formed by passing each element of the source through a function func.)。

flatMap()转换

Flatmap: 跟map(func)类似,但是每个输入项和成为0个或多个输出项(所以func函数应该返回的是一个序列化的数据而不是单个数据项)。(原文:Similar to map, but each input item can be mapped to 0 or more output items (so func should return a Seq rather than a single item).)

filter()转换

.filter()方法可以让我们从数据集中选择该元素

data_filter = data_from_file(
    lambda row: row[16] == '2014' and row[21] == '0'
)
data_filter.count()

distinct()转换

该方法返回指定列中不同值的列表

distinct_gender = data_from_file.map(
    lambda row: row[5]).distinct()
distinct_gender.collect()

distinct()转换效率很低,通常只有在必要的时候才使用。

sample()转换

.sample()转换返回数据集的随机样本。

fraction = 0.1
data_sample = data_from_file.sample(
    False, fraction, 123
)

.sample()方法的第一个参数指定采样是否应该被替换,第二个参数定义返回数据的分数,第三个参数是定义随机种子。
在上面的例子中,我们从原始数据集中随机抽样了10%。

leftOuterJoin()转换

.leftOuterJoin()方法与SQL中的方法是一样的,根据两个数据集中都有的值来连接两个RDD并返回左连接后的RDD记录

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

同样的,这也是一个效率很低的方法。

repartition()转换

.repartition()重新定义对数据集的分区,改变了数据集分区的数量。它会导致数据重组,导致对性能方面产生的巨大影响

rdd1 = rdd1.repartition(4)
len(rdd1.glom().collect())

这段代码的打印结果是4,作为新的分区数目
.glom()方法会产生一个列表,其中每个元素是指定分区中数据集的所有元素的另一个列表,返回的主要列表的元素和分区的数量一样多。

2. 操作

操作执行数据集上的计划任务,可以操作转换后的数据集也可以操作没有进行任何转换的数据集

take()方法

.take()方法优于.collect()方法,它只返回单个数据分区的前n行,而.collect()返回的是整个RDD。

data_first = data_from_file.take(1)

如果想返回随机数据,则可以使用.takeSample(), 这个方法有三个参数,第一个参数代表采样是否应该被替换,第二个参数代表要返回的数据记录,第三个参数是随机种子

data_take_sample = data_from_file.takeSample(False, 1, 123)

collect()方法

该方法返回RDD中的所有元素给驱动程序

reduce()方法

lines = sc.textFile("data.txt")
lineLengths = lines.map(lambda s: len(s))
totalLength = lineLengths.reduce(lambda a, b: a + b)

首先通过map转换生成lineLengths,然后使用.reduce()方法对结果进行处理。在每一个分区里,reduce()方法运行求和方法将总和返回给最终聚合在所在的驱动程序节点。

.count()方法

.count()方法统计RDD里的元素的数量

totalLength.count()

这里将会返回totalLength中所有元素的数量
如果数据集是key-value形式的,可以使用.countByKey()方法获取不同键的计数

totalLength.countByKey().items()

saveAsTextFile()方法

对RDD执行saveAsTextFile()方法会让RDD保存为文本文件:每个文件一个分区:

data_key.saveAsTextFile(
    '/Users/huoshirui/Desktop/data_key.txt'
)

读取的时候需要进行解析,因为所有的行都被视为字符串:

def parseInput(row):
    import re
    pattern = re.compile(r'\(\'[a-z])\', ([0-9])\)')
    row_split = pattern.split(row)
    return (row_split[1], int(row_split[2]))

data_key_reread = sc.textFile('/Users/huoshirui/Desktop/data_key.txt').map(parseInput)
data_key_reread.collect()

foreach()方法

.foreach()方法对RDD里面的每个元素用迭代的方法应用相同的函数

def f(x):
    print(x)
data_key.foreach(f)

猜你喜欢

转载自blog.csdn.net/katherine_hsr/article/details/80755005