spark算子实战

本文介绍了Spark算子的概念,将其分为Transformation和Action两大类,并详细讲解了Value数据类型的Transformation算子,包括map、flatMap、mapPartitions、glom、union、cartesian、groupBy、filter、distinct、subtract、sample和takeSample等算子的使用和实战示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

spark算子

什么是算子

百度百科上对算子的解释是这样的,算子是一个函数空间到函数空间上的映射O:X→X。广义的讲,对任何函数进行某一项操作都可以认为是一个算子,甚至包括求幂次,开方都可以认为是一个算子,只是有的算子我们用了一个符号来代替他所要进行的运算罢了,所以大家看到算子就不要纠结,他和f(x)的f没区别,它甚至和加减乘除的基本运算符号都没有区别,只是他可以对单对象操作罢了(有的符号比如大于、小于号要对多对象操作)。又比如取概率P{X<x},概率是集合{X<x}(他是属于实数集的子集)对[0,1]区间的一个映射,我们知道实数域和[0,1]区间是可以一一映射的(这个后面再说),所以取概率符号P,我们认为也是一个算子,和微分,积分算子算子没区别。总而言之,算子就是映射,就是关系,就是变换

spark算子分类

从大方向来说,Spark 算子大致可以分为以下两类

  1. Transformation算子:操作是延迟计算的,也就是说从一个RDD 转换生成另一个 RDD 的转换操作不是马上执行,需要等到有Action 操作的时候才会真正触发运算。
  2. Action算子:这类算子会触发 SparkContext 提交作业(Job),并将数据输出 Spark系统。

根据Transaction处理数据类型的不同,也可以分为三类:

  1. Value数据类型的Transformation算子,这种变换不触发提交作业,针对处理的数据项是Value型的数据。
  2. Key-Value数据类型的Transformation算子,这种变换不触发提交作业,针对处理的数据项是Key-Value型的数据。
  3. Action算子,这类算子会触发SparkContext提交作业。

spark算子实战

Value数据类型的Transformation算子

算子说明
map将原来数据集中的每个元素经过用户自定义的函数转换为一个新的RDD(MappedRDD)
flatMap与map类似,但每个元素输入项可以被映射到0个或多个输出项,最终将结果“扁平化”后输出
mapPartitionsmap的输入函数是作用于RDD中每个元素,而mapPartitions的输入函数是作用于每个分区,也就是把每个分区中的内容作为整体来处理的。
glom将RDD的每个分区中的类型为T的元素转换为数组Array[T]。
union将两个RDD中的数据集进行合并,最终返回两个RDD的并集,若RDD中存在相同的元素也不会去重。
cartesian对两个RDD中的所有元素进行笛卡尔积操作。
groupBy生成相应的key,相同的放在一起。
filter对元素进行过滤,对每个元素应用f函数,返回值为true的元素在RDD中保留,返回为false将过滤掉。
distinct对RDD去重。
subtract去掉含有重复的项。
sample对RDD内的元素进行采样,并生成一个新的RDD,这个新的RDD只有原来RDD的部分数据。用户可以设定是否有放回的抽样、百分比、随机种子,进而决定采样方式。
takeSample上面的sample函数是一个原理,但是不使用相对比例采样,而是按设定的采样个数进行采样,同时返回结果不再是RDD,而是相当于对采样后的数据进行collect(),返回结果的集合为单机的数组。
cache、persist都用于将一个RDD进行缓存的,这样在之后使用的过程中就不需要重复计算了,可大大节省程序运行的时间。

为了让大家更好的了解算子,我对有些算子写了两个实战例子

1. map

实例1:
scala代码

val a = sc.parallelize(List(1,2,3,4,5),3)
val b = a.map(a => a*a)
b.collect()

结果:
这里写图片描述

python代码

a = sc.parallelize([1,2,3,4,5],3)
b = a.map(lambda a:a*a)
b.collect()

结果:
这里写图片描述

实例2
scala代码

val a = sc.parallelize(List("beijing", "shanghai", "guangzhou", "shenzhen", "hangzhou"), 3)
val b = a.map(_.length)
val c = a.zip(b)
c.collect

结果:
这里写图片描述

python代码

a = sc.parallelize(["beijing", "shanghai", "guangzhou", "shenzhen", "hangzhou"], 3)
b = a.map(lambda a:len(a))
c =a.zip(b)
c.collect()

结果:
这里写图片描述

2.flatMap

实例1:
scala代码

val a = sc.parallelize(List("Hello world", "My name is Patrick"))
val b = a.flatMap(a => a.split(" "))
b.collect()

结果:
这里写图片描述

python代码

a = sc.parallelize(['Hello world', 'My name is Patrick'])
b = a.flatMap(lambda a: a.split(" "))
b.collect()

结果:
这里写图片描述

为了让大家更好的理解map和flatMap的区别,这里给出了将此题改为map的输出,以更好的对比两者的区别
这里写图片描述
实例2:
scala代码

val a = sc.parallelize(1 to 10, 5)
a.flatMap(1 to _).collect

结果:
这里写图片描述

sc.parallelize(List(1, 2, 3), 2).flatMap(x => List(x, x, x)).collect

结果:
这里写图片描述

python代码

a = sc.parallelize(range(1,10+1), 5)
a.flatMap(lambda a:range(1,a+1)).collect()

结果:
这里写图片描述

3.mapPartitions

这里再简单说一下map和mapPartitions 的区别,map作用于RDD中每个元素,而mapPartitions作用于每个分区。假设有N个元素,存在于M个分区,执行map函数将被调用N次,而mapPartitions 将被调用M次,当在映射的过程中不断的创建对象时就可以使用mapPartitions ,比map的效率高很多。
比如当想数据库写入数据时,如果使用map就需要为每个元素创建connection对象,而mapPartitions 是需为每个分区创建connection对象。

实例1:
scala代码

val a = sc.parallelize(1 to 10,3)
a.mapPartitions(x=>x.filter(_%2 == 0)).foreachPartition(p=>{println(p.toList)
                                                            println("============")})

结果:
这里写图片描述
python代码

def func(x):
    for i in x:
        if i %2 ==0:
            yield i
def eachPartition(partition):
    print(list(partition))
    print("======")

a = sc.parallelize(range(1,10),3)
a.mapPartitions(func ).foreachPartition(eachPartition)

结果:
这里写图片描述

4.glom

实例1:
scala代码

a = sc.parallelize(1 to 10 ,3)
a.glom().collect()

结果:
这里写图片描述

python代码

a = sc.parallelize(1 to 10 ,3)
a.glom().collect()

结果:
这里写图片描述

5.union

实例1:
scala代码

val a = sc.parallelize(1 to 3 ,2)
val b = sc.parallelize(4 to 8 ,2)
(a ++ b).collect

结果:
这里写图片描述

python代码

a = sc.parallelize(range(1,3+1) ,2)
b = sc.parallelize(range(4,8+1) ,2)
a.unionononon(b).collect()

结果:
这里写图片描述

实例2:
scala代码

val a = sc.parallelize(1 to 5 ,2)
val b = sc.parallelize(4 to 8 ,2)
(a ++ b).collect

结果:
这里写图片描述
python代码

a = sc.parallelize(range(1,5+1) ,2)
b = sc.parallelize(range(4,8+1) ,2)
a.union(b).collect()

结果:
这里写图片描述

6.cartesian

实例1:
scala代码

val a = sc.parallelize(List(1,2,3))
val b = sc.parallelize(List(4,5,6))
a.cartesian(b).collect

结果:
这里写图片描述

python代码

a = sc.parallelize([1,2,3])
b = sc.parallelize([4,5,6])
a.cartesian(b).collect()

结果:
这里写图片描述

7.groupBy

实例1:
scala代码

val list = List(("tom","F","20"),("lucy","F","19"),("rose","F","21"),("alex","M","22"),("lily","F","18"))
val a = sc.parallelize(list,3)
a.groupBy(x=> {if (x._2=="F" ) "Female" else "Male"}).collect

结果:
这里写图片描述

python代码

list = [("tom","F","20"),("lucy","F","19"),("rose","F","21"),("alex","M","22"),("lily","F","18")]
a = sc.parallelize(list,3)
a.groupBy(lambda x:"Female" if x[1]=="F" else "Male").map(lambda x: (x[0],x[1])).collect()[0][1].data

结果:
这里写图片描述

a.groupBy(lambda x:"Female" if x[1]=="F" else "Male").map(lambda x: (x[0],x[1])).collect()[1][1].data

结果:
这里写图片描述

8.filter

实例1:
scala代码

val a = sc.parallelize(1 to 10, 3)
val b = a.filter(_ % 2 == 0)
b.collect

结果:
这里写图片描述

python代码

a = sc.parallelize(range(1,10+1), 3)
b = a.filter(lambda x:x%2==0)
b.collect()

结果:
这里写图片描述

9.distinct

实例1:
scala代码

val c = sc.parallelize(List("hello","world","hello","spark","scala"), 2)
c.distinct.collect

结果:
这里写图片描述

python代码

c = sc.parallelize(["hello","world","hello","spark","scala"], 2)
c.distinct().collect()

结果:
这里写图片描述

10.subtract

实例1:
scala代码

val a = sc.parallelize(List("tom","lucy","lili","jack","rose"), 3)
val b = sc.parallelize(List("natasha","andy","jack","alice","lucy"), 3)
val c = a.subtract(b)
c.collect

结果:
这里写图片描述
python代码

a = sc.parallelize(["tom","lucy","lili","jack","rose"], 3)
b = sc.parallelize(["natasha","andy","jack","alice","lucy"],3)
c = a.subtract(b)
c.collect()

结果:
这里写图片描述

11.sample

实例1:
scala代码

val a = sc.parallelize(1 to 100, 3)
a.sample(false,0.1,0).collect

结果:
这里写图片描述

注:
sample接受三个参数,第一个参数withReplacement表示是否放回抽样,ture表示有放回,false表示无放回;
第二个参数fraction表示抽样比例;
第三个参数seed表示随机数。
python代码

import time
a = sc.parallelize(range(1 ,100), 3)
a.sample(False,0.1,0).collect()

结果:
这里写图片描述

12.takeSample

实例1:
scala代码

val a = sc.parallelize(1 to 100, 3)
a.takeSample(false, 20,0)

结果:
这里写图片描述
python代码

a = sc.parallelize(range(1 ,100), 3)
a.takeSample(False, 20,0)

结果:
这里写图片描述

13.cache、persist

实例1:
scala代码

val a = sc.parallelize(1 to 10,3).map(x=>x*1.0)
val b = a.map(x=>x*x)

b.cache()  

val avg = b.reduce((x,y) => x+y) / b.count
println(avg)

结果:
这里写图片描述

python代码

import numpy as np
a = sc.parallelize(np.linspace(1.0, 10.0, 10),3)
b = a.map(lambda x: x**2)

b.cache()  # Preserve the actual items of this RDD in memory

avg = b.reduce(lambda x,y:x+y) / b.count
print(avg)

结果:
这里写图片描述

注:缓存RDD结果对于重复迭代的操作非常有用,比如很多机器学习的算法,训练过程需要重复迭代。

Key-Value型Transformation算子将在spark算子实战(二)中介绍,Actions算子将在spark算子实战(三)中介绍


参考博文:
http://blog.youkuaiyun.com/u013980127/article/details/53046760
https://www.cnblogs.com/liuzhongfeng/p/5285613.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值