Spark API 之 map、mapPartitions、mapValues、flatMap、flatMapValues详解

本文详细介绍了Spark中的Resilient Distributed Dataset (RDD)及其核心API,包括map、mapPartitions、mapValues、flatMap、flatMapValues、reduce、reduceByKey等函数的应用场景及使用方法,并通过实例演示了如何利用这些API进行数据处理。

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

Spark API  之 map、mapPartitions、mapValues、flatMap、flatMapValues详解

http://spark.apache.org/docs/latest/api/python/pyspark.html

1、创建一个RDD变量,通过help函数,查看相关函数定义和例子:

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

>>> a
ParallelCollectionRDD[21] at parallelize at PythonRDD.scala:475
>>> help(a.map)
Help on RDD in module pyspark.rdd object:

class RDD(__builtin__.object)
 |  A Resilient Distributed Dataset (RDD), the basic abstraction in Spark.
 |  Represents an immutable, partitioned collection of elements that can be
 |  operated on in parallel.
 |
 |  Methods defined here:
 |
 |  __add__(self, other)
 |      Return the union of this RDD and another one.
 |
 |      >>> rdd = sc.parallelize([1, 1, 2, 3])
 |      >>> (rdd + rdd).collect()
 |      [1, 1, 2, 3, 1, 1, 2, 3]
RDD是什么?

RDD是Spark中的抽象数据结构类型,任何数据在Spark中都被表示为RDD。从编程的角度来看,RDD可以简单看成是一个数组。和普通数组的区别是,RDD中的数据是分区存储的,这样不同分区的数据就可以分布在不同的机器上,同时可以被并行处理。因此,Spark应用程序所做的无非是把需要处理的数据转换为RDD,然后对RDD进行一系列的变换和操作从而得到结果。

2、map(function) 

map是对RDD中的每个元素都执行一个指定的函数来产生一个新的RDD。任何原RDD中的元素在新RDD中都有且只有一个元素与之对应。

map(self, f, preservesPartitioning=False) method of pyspark.rdd.RDD instance
    Return a new RDD by applying a function to each element of this RDD.
    >>> rdd = sc.parallelize(["b", "a", "c"])
    >>> sorted(rdd.map(lambda x: (x, 1)).collect())
    [('a', 1), ('b', 1), ('c', 1)]

3、mapPartitions(function) 

map()的输入函数是应用于RDD中每个元素,而mapPartitions()的输入函数是应用于每个分区

mapPartitions是map的一个变种。map的输入函数是应用于RDD中每个元素,而mapPartitions的输入函数是应用于每个分区,也就是把每个分区中的内容作为整体来处理的。

mapPartitions(self, f, preservesPartitioning=False) method of pyspark.rdd.RDD instance
    Return a new RDD by applying a function to each partition of this RDD.

    >>> rdd = sc.parallelize([1, 2, 3, 4], 2)
    >>> def f(iterator): yield sum(iterator)
    >>> rdd.mapPartitions(f).collect()
    [3, 7]

4、mapValues(function) 

原RDD中的Key保持不变,与新的Value一起组成新的RDD中的元素。因此,该函数只适用于元素为KV对的RDD。
mapValues(self, f) method of pyspark.rdd.RDD instance
    Pass each value in the key-value pair RDD through a map function
    without changing the keys; this also retains the original RDD's
    partitioning.

    >>> x = sc.parallelize([("a", ["apple", "banana", "lemon"]), ("b", ["grapes"])])
    >>> def f(x): return len(x)
    >>> x.mapValues(f).collect()
    [('a', 3), ('b', 1)]
5、flatMap(function) 

与map类似,区别是原RDD中的元素经map处理后只能生成一个元素,而原RDD中的元素经flatmap处理后可生成多个元素

Help on method flatMap in module pyspark.rdd:

flatMap(self, f, preservesPartitioning=False) method of pyspark.rdd.RDD instance
    Return a new RDD by first applying a function to all elements of this
    RDD, and then flattening the results.

    >>> rdd = sc.parallelize([2, 3, 4])
    >>> sorted(rdd.flatMap(lambda x: range(1, x)).collect())
    [1, 1, 1, 2, 2, 3]
    >>> sorted(rdd.flatMap(lambda x: [(x, x), (x, x)]).collect())
    [(2, 2), (2, 2), (3, 3), (3, 3), (4, 4), (4, 4)]

6、flatMapValues(function)

flatMapValues类似于mapValues,不同的在于flatMapValues应用于元素为KV对的RDD中Value。每个一元素的Value被输入函数映射为一系列的值,然后这些值再与原RDD中的Key组成一系列新的KV对。

flatMapValues(self, f) method of pyspark.rdd.RDD instance
    Pass each value in the key-value pair RDD through a flatMap function
    without changing the keys; this also retains the original RDD's
    partitioning.

    >>> x = sc.parallelize([("a", ["x", "y", "z"]), ("b", ["p", "r"])])
    >>> def f(x): return x
    >>> x.flatMapValues(f).collect()
    [('a', 'x'), ('a', 'y'), ('a', 'z'), ('b', 'p'), ('b', 'r')]

7、reduce函数

reduce将RDD中元素两两传递给输入函数,同时产生一个新的值,新产生的值与RDD中下一个元素再被传递给输入函数直到最后只有一个值为止。

reduce(self, f) method of pyspark.rdd.RDD instance
    Reduces the elements of this RDD using the specified commutative and
    associative binary operator. Currently reduces partitions locally.

    >>> from operator import add
    >>> sc.parallelize([1, 2, 3, 4, 5]).reduce(add)
    15
    >>> sc.parallelize((2 for _ in range(10))).map(lambda x: 1).cache().reduce(add)
    10
    >>> sc.parallelize([]).reduce(add)
    Traceback (most recent call last):
        ...
    ValueError: Can not reduce() empty RDD

例子如下:

>>> from operator import add
>>> b.collect()
[1, 2, 3, 4, 5, 6]
>>> b.reduce(add)   # 引入内置函数
21
>>> b.reduce(lambda a,b:a+b)    # lambda自定义的匿名函数
21

8、reduceByKey函数

顾名思义,reduceByKey就是对元素为KV对的RDD中Key相同的元素的Value进行reduce,因此,Key相同的多个元素的值被reduce为一个值,然后与原RDD中的Key组成一个新的KV对。

Help on method reduceByKey in module pyspark.rdd:

reduceByKey(self, func, numPartitions=None, partitionFunc=<function portable_hash>) method of pyspark.rdd.RDD instance
    Merge the values for each key using an associative and commutative reduce function.

    This will also perform the merging locally on each mapper before
    sending results to a reducer, similarly to a "combiner" in MapReduce.

    Output will be partitioned with C{numPartitions} partitions, or
    the default parallelism level if C{numPartitions} is not specified.
    Default partitioner is hash-partition.

    >>> from operator import add
    >>> rdd = sc.parallelize([("a", 1), ("b", 1), ("a", 1)])
    >>> sorted(rdd.reduceByKey(add).collect())
    [('a', 2), ('b', 1)]
参考:http://homepage.cs.latrobe.edu.au/zhe/ZhenHeSparkRDDAPIExamples.html
<think>好的,用户想了解Spark中几个常见算子的使用场景和区别,包括map、filter、flatMap、groupByKey和repartition。首先,我需要回忆这些算子的基本功能,然后比较它们的区别,并结合引用中的信息来支持解释。 首先,mapmapPartitions的区别用户之前可能提到过,引用[1]和[3]都提到了mapPartitions更适用于粗粒度处理,这样减少函数调用开销。但用户现在的问题是关于map、filter这些基础算子的区别,所以需要先分别说明每个算子的用途。 map是一对一的转换,每个元素都会被处理,生成一个新的元素。比如将每个数加1,适合逐元素处理的情况。而filter则是过滤,返回满足条件的元素组成的RDD,如选出大于5的数。flatMap则是每个元素生成多个输出,可能变成0个、1个或多个元素,比如分词后每个单词生成一个元素。 接下来是groupByKey,它属于shuffle操作,将相同key的value分组,但可能存在性能问题,因为shuffle开销大。相比之下,reduceByKey会在shuffle前进行合并,效率更高,但用户没问到这点,不过可能需要提及groupByKey的使用场景,比如需要全部分组数据时才使用。 然后是repartition,引用[2]和[4]提到repartition和coalesce的区别,repartition会进行全量shuffle,而coalesce可能只合并分区,避免shuffle。用户需要知道repartition的使用场景,比如数据倾斜或调整分区数时,但要注意shuffle的成本。 现在需要结合引用中的例子,比如引用[3]中的filter示例,引用[4]中的groupByKey和repartition的说明。要确保每个算子的解释都有引用支持,并正确标注来源。 另外,用户可能希望了解这些算子在什么情况下使用,比如何时用map而不是flatMap,或者何时避免groupByKey。需要强调性能差异,比如shuffle操作的影响,以及如何选择合适算子优化任务。 总结结构时,应该先分别介绍每个算子,然后对比它们之间的区别和使用场景,最后给出常见问题的解答,比如性能优化建议。同时要确保引用标注正确,如mapPartitions在引用[1]中,repartition在引用[2]和[4]中,filter在引用[3]和[4]中。</think>### Spark 常用算子使用场景及区别解析 --- #### 一、基础转换算子对比 1. **map** - **功能**:一对一转换元素,输入一个元素生成一个元素 - **场景**:数据清洗、格式转换、数值计算 - **示例**:将数值列表全部加 1 ```scala val rdd = sc.parallelize(List(1,2,3)) rdd.map(_ + 1) // 结果:List(2,3,4) ``` 2. **filter** - **功能**:过滤符合条件的元素 - **场景**:数据筛选、噪声数据剔除 - **示例**:保留大于 5 的数据 ```scala rdd.filter(_ > 5) // 如输入List(3,6,8) => 输出List(6,8) [^3] ``` 3. **flatMap** - **功能**:一对多转换,输入一个元素生成多个元素(或零个) - **场景**:文本分词、嵌套结构展开 - **示例**:拆分句子为单词 ```scala rdd.flatMap(_.split(" ")) // "hello world" => ["hello","world"] ``` --- #### 二、Shuffle 类算子对比 4. **groupByKey** - **功能**:按 Key 分组聚合,生成 `(K, Iterable[V])` - **场景**:需要全量分组数据时使用(如统计用户行为序列) - **缺点**:直接触发 Shuffle 且不预聚合,性能低于 `reduceByKey` - **示例**:统计用户点击次数 ```scala rdd.groupByKey().mapValues(_.sum) // 效率较低 [^4] ``` 5. **repartition** - **功能**:通过全量 Shuffle 重新分区 - **场景**:解决数据倾斜、调整并行度 - **对比**:`coalesce` 仅合并分区(默认无 Shuffle),`repartition` 强制 Shuffle [^2] - **示例**:从 100 分区改为 200 分区 ```scala rdd.repartition(200) ``` --- #### 三、核心区别总结 | 算子 | 数据粒度 | 是否触发 Shuffle | 典型场景 | |-------------|-----------|------------------|-------------------------| | **map** | 单元素 | 否 | 逐元素转换 | | **flatMap** | 单元素 | 否 | 结构展开 | | **filter** | 单元素 | 否 | 数据过滤 | | **groupByKey** | Key | 是 | 全量分组(慎用) | | **repartition** | 分区 | 是 | 数据重分布 | --- #### 四、优化建议 1. **优先选择 mapPartitions** 批量处理分区数据(如数据库连接),减少函数调用次数 [^1] 2. **避免 groupByKey** 使用 `reduceByKey` 或 `aggregateByKey` 预聚合减少 Shuffle 数据量 3. **谨慎使用 repartition** 优先尝试 `coalesce` 减少分区数以避免 Shuffle [^2] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值