PySpark数据分析基础:核心数据集RDD常用函数操作一文详解(四)

目录

前言

一、keys()(获取键RDD)

二、leftOuterJoin(左外连接)

三、localCheckpoint(将此RDD标记为本地检查点)

四、lookup

五、map/mapPartitions(遍历操作)

map

mapPartitions

六、mapPartitionsWithIndex(分区遍历操作带Index)

七、mapPartitionsWithSplit(分区遍历操作带Split)

八、mapValues(仅对值进行遍历操作)

 九、max最大

十、mean()均值

十一、meanApprox

十二、min(最小值)

 十三、name

点关注,防走丢,如有纰漏之处,请留言指教,非常感谢


前言

写关于RDD的操作比我想象中的要多很多,不愧是支持整个大数据计算的核心数据集,相当于Pandas的DataFrame了,而且后续的PySpark的DataFrame很多还是需要转为RDD来实现其中的一些接口功能。关于PySpark的Pandas如何实现分布式计算的过程博主还是没有搞清楚故此内容回等我研究透了再写,而使用RDD去进行分布式计算的话,不论是Spark2或者3都可以实现,而且灵活,是大数据分析师必备的能力。

PySpark系列的专栏文章目前的话应该只会比Pandas更多不会更少,可以用PySpark实现的功能太多了,基本上Spark能实现的PySpark都能实现,而且能够实现兼容python其他库,这就给了PySpark极大的使用空间,能够结合大数据集群实现更高效更精确的大数据处理或者预测。如果能够将这些工具都使用的相当熟练的话,那必定是一名优秀的大数据工程师。故2023年这一年的整体学习重心都会集中在这门技术上,当然Pandas以及Numpy的专栏都会更新。我将对PySpark专栏给予极大的厚望,能够实现从Pandas专栏过度到PySpark专栏零跨度学习成本,敬请期待。


此篇文章将主要描述清楚每个RDD的API函数功能用法,了解RDD可用的操作才能最大限度的发挥大数据分布式计算的功能。


一、keys()(获取键RDD)

RDD.keys() → pyspark.rdd.RDD[K]

 返回包含每个元组的键的RDD。

m = sc.parallelize([(1, 2), (3, 4)]).keys()
m.collect()

 

二、leftOuterJoin(左外连接)

RDD.leftOuterJoin(other: pyspark.rdd.RDD[Tuple[K, U]],
                  numPartitions: Optional[int] = None) 
                  → pyspark.rdd.RDD[Tuple[K, Tuple[V, Optional[U]]]]

左外连接。对于自身中的每个元素(k,v),生成的RDD将包含其他元素中的w的所有对(k,(v,w)),或者如果其他元素中没有元素具有键k,则包含对(k、(v,None))。
哈希将生成的RDD划分为给定数量的分区。

x = sc.parallelize([("a", 1), ("b", 4)])
y = sc.parallelize([("a", 2)])
sorted(x.leftOuterJoin(y).collect())
[('a', (1, 2)), ('b', (4, None))]

三、localCheckpoint(将此RDD标记为本地检查点)

使用Spark现有的缓存层将此RDD标记为本地检查点。
该方法适用于希望截断RDD谱系,同时跳过在可靠的分布式文件系统中复制物化数据的昂贵步骤的用户。这对于具有需要定期截断的长谱系的RDD(例如GraphX)非常有用。

本地检查点牺牲了性能的容错性。特别是,检查点数据被写入执行器中的临时本地存储,而不是写入可靠的容错存储。其结果是,如果执行器在计算过程中失败,则检查点数据可能不再可访问,从而导致无法恢复的作业失败。

这与动态分配一起使用是不安全的,动态分配会删除执行器及其缓存块。如果必须同时使用这两个功能,建议将spark.dynamicAllocation.cachedExecutorIdleTimeout设置为高值。
检查未使用通过SparkContext.setCheckpointDir()设置的检查点目录。

四、lookup

RDD.lookup(key: K) → List[V]

 返回key RDD中的值列表。如果RDD具有已知的分区器,则只需搜索键映射到的分区即可有效地完成此操作。

l = range(1000)
rdd = sc.parallelize(zip(l, l), 10)
rdd.lookup(42)  # slow

 

sorted = rdd.sortByKey()
sorted.lookup(42)  # fast

 

rdd2 = sc.parallelize([(('a', 'b'), 'c')]).groupByKey()
list(rdd2.lookup(('a', 'b'))[0])

 

五、map/mapPartitions(遍历操作)

这两个函数在之前在第一篇文章已经详细讲述了,这里在作扩展:

map

map可以对每个RDD内的元素进行逐个操作,一般可以用来生成Pair RDD赋予每个值Key,从而进行groupby或者直接进行partitionby。

sp_df.rdd.map(lambda x: (x.linkid, x))

实现DataFrame的groupby操作。

mapPartitions

对每个RDD生成的分区进行操作,字面上的意思很容易理解,逐个对每个分区进行操作,里面和map一样可以进行嵌套函数操作,关键在于如何将这个函数写好。理论上spark兼容的所有环境能够运行的程序都可以通过mapPartitions的函数导入。

比如我们想利用python pandas的API,就可以直接写个方法:

def f(iterator):
    #row
    #List(row)
    empty_df=pd.DataFrame()
    list_test = []
    result = []    
    list_row=[]
    pre_linkid = 0
    for vlinkid,row in iterator:
        #print(row)
        cur_linkid = row.linkid
        #如果相同就收集
        if  pre_linkid == 0:
            df=pd.DataFrame(row.asDict(),index=[0])
            df_concat=pd.concat([empty_df,df],axis=0,join='outer')
            empty_df=df_concat
        elif pre_linkid == cur_linkid :
            df=pd.DataFrame(row.asDict(),index=[0])
            df_concat=pd.concat([df_concat,df],axis=0,join='outer')
            empty_df=df_concat
        else:
        #不同则处理逻辑
            result=result+algo(df_concat)
            empty_df=pd.DataFrame()
            df=pd.DataFrame(row.asDict(),index=[0])
            df_concat=pd.concat([empty_df,df],axis=0,join='outer')
        pre_linkid = cur_linkid
    if empty_df.shape[0] != 0:
        result=result+algo(df_concat)
    yield result

 这个函数就比较复杂了,但是只要将运算过程封装放入mappartition的函数中,就可以实现分区计算。

 rdd_pari=rdd.mapPartitions(f)

用起来逻辑简单,这样以来也就是进行了分布式(分区)计算了。

六、mapPartitionsWithIndex(分区遍历操作带Index)

RDD.mapPartitionsWithIndex(f: Callable[[int, Iterable[T]],
                           Iterable[U]], 
                           preservesPartitioning: bool = False) 
                           → pyspark.rdd.RDD[U]

 通过将函数应用于此RDD的每个分区,同时跟踪原始分区的索引,返回新的RDD。

rdd = sc.parallelize([('a',1), ('a',2), ('b',3), ('c',4)], 2)
def f(splitIndex, iterator):
    yield  splitIndex
rdd.mapPartitionsWithIndex(f).collect()
[0, 1]

七、mapPartitionsWithSplit(分区遍历操作带Split)

RDD.mapPartitionsWithSplit(f: Callable[[int, Iterable[T]],
                           Iterable[U]], preservesPartitioning: bool = False)
                           → pyspark.rdd.RDD[U]

 通过将函数应用于此RDD的每个分区,同时跟踪原始分区的索引,返回新的RDD。

其实就是和上一个方法一样,返回多了一个索引而已。

八、mapValues(仅对值进行遍历操作)

RDD.mapValues(f: Callable[[V], U])
 → pyspark.rdd.RDD[Tuple[K, U]]

 通过映射函数传递键值对RDD中的每个值,而不改变键;这也保留了原始RDD的分区。

x = sc.parallelize([("a", ["apple", "banana", "lemon"]), ("b", ["grapes"])])
def f(x): return len(x)
x.mapValues(f).collect()

 九、max最大

RDD.max(key: Optional[Callable[[T], S]] = None) 
→ T

 查找此RDD中的最大值

rdd = sc.parallelize([1.0, 5.0, 43.0, 10.0])
rdd.max()

 

十、mean()均值

RDD.mean() → NumberOrArray

 计算此RDD元素的平均值。

sc.parallelize([1, 2, 3]).mean()

 

十一、meanApprox

RDD.meanApprox(timeout: int,
               confidence: float = 0.95)
              → pyspark.rdd.BoundedFloat

 在超时内返回平均值或满足置信度的近似操作

十二、min(最小值)

RDD.min(key: Optional[Callable[[T], S]] = None) → T
rdd = sc.parallelize([2.0, 5.0, 43.0, 10.0])
rdd.min()
2.0

 十三、name

RDD.name() → Optional[str]

 返回此RDD的名称。

RDD.partitionBy(numPartitions: Optional[int], 
                partitionFunc: Callable[[K], int] = <function portable_hash>)
               → pyspark.rdd.RDD[Tuple[K, V]]

 下面几章就是重要算子了。


点关注,防走丢,如有纰漏之处,请留言指教,非常感谢

以上就是本期全部内容。我是fanstuck ,有问题大家随时留言讨论 ,我们下期见

猜你喜欢

转载自blog.csdn.net/master_hunter/article/details/128973338