ODPS Spark PySpark分组排序打序号并自关联(包含中文乱码问题解决)

        在ODPS上用Spark,由于java和scala都需要装SDK和配置POM来编写ODPS Spark脚本(其实就是自己被各版本号弄懵了没装明白),而Dataworks中可以直接在线编写Python资源并提交使用,因此测试了Pyspark。

        数据集操作逻辑:跟最早时候在ODPS上测试使用MR一样,这里习惯性地演示下做分组排序并自关联的处理——将数据集做分组排序并打上row_number,再对rn加个偏移量(这里偏移量统一设置的是固定值1,设置成其他值或者某列的动态值也都可以)进行自关联,最后写入目标表。

        中文乱码问题:由于工作原因,Dataworks中的很多模块比较落后,因此被编码问题困扰了很久。具体表现就是中文插入目标表后是乱码的情况。

        经测试,reload(sys)\sys.setdefaultencoding('utf8')是没用的。。。官方说的添加配置项也是没用的。。。最后想着先无脑decode一下,发现竟然解决了问题,说明ODPS Spark使用的是unicode?最后结论就是注意使用decode,示例如下:

# -*- coding: utf-8 -*-
import sys
from pyspark.sql import SparkSession
from pyspark.sql import functions as F
from pyspark.sql.window import Window
import random

reload(sys)
sys.setdefaultencoding('utf8')

if __name__ == "__main__":
    spark = SparkSession.builder\
        .appName("spark sql")\
        .config("spark.sql.broadcastTimeout", 20 * 60)\
        .config("spark.sql.crossJoin.enabled", True)\
        .config("odps.exec.dynamic.partition.mode", "nonstrict")\
        .config("spark.sql.catalogImplementation", "odps")\
        .getOrCreate()

#创建\导入
    #创建一个可迭代对象
    data = [i for i in range(0, 100)]
    #调用sparkContext将dara转化为一个RDD,并通过map函数生成两个字段
    rdd0 = spark.sparkContext.parallelize(data, 3).map(lambda s: (random.choice(["杭州","宁波","温州"]).decode("utf-8"), s))
    #通过toDF转换成结构化DataFrame
    df0 = rdd0.toDF("city: string, num: int")
    #或者直接通过结构化接口创建DataFrame
    # df0 = spark.createDataFrame()
    #或者读取已有结构化数据
    #sourcetable="project.input_source_table"
    #df0 = spark.sql("select city,num from %s" % sourcetable)

#计算
    #1.分组排序打序号-结构化接口DF操作方式
    #用开窗函数,也可以直接跑SQL会更简单一些,效率上没有差别
    window = Window.partitionBy('city').orderBy(df0.num) #传入df0.num.desc()则为倒序
    df1 = df0.withColumn('rn', F.row_number().over(window))

    #2.分组排序打序号-低级接口RDD操作方式
    #代码思路:
    #groupByKey返回的是RDD[K,Iterator[V]],不熟的话会有点小绕,可以先用Python原生map梳理下转化操作的逻辑:
    #data=[('a',[1,5,4,6,3,2]),('b',[11,15,14,16,13,12])]
    #ans=map(lambda row: list(map(lambda x: (row[0],x[0],x[1]),enumerate(sorted(row[1]),1))),data)
    #没有flatMap则可以使用Python原生reduce测试,比如:reduce(lambda x,y:x+y,ans)
    #因此Spark代码如下:
    rdd1 = rdd0.groupByKey().flatMap(lambda row: map(lambda x: (row[0],x[1],x[0]),enumerate(sorted(row[1]),1)))
    #***以上逻辑引发的思考:
    #groupByKey引起shuffle,那么\
    #大数据量时是否考虑将key包装成样例类(需排序的v作为属性之一,并实现两个相关性运算符接口),\
    #再使用SortShuffleManager进行shuffle(按需调整bypassMergeThreshold配置项)而不使用原生sorted可能效率更好?
    df1_copy = rdd1.toDF("city: string, num: int ,rn: int") #和df1内容上相同
    
    #可对列进行加工
    df = df1.withColumn("city",F.decode(F.format_string("%s市",df1.city),"utf-8"))
    # #数据过滤
    # df = df.filter(df.city=="杭州市")

#关联
    df_right = df.withColumn("rn",df.rn+1)#关联的目标为表自身rn字段偏移1的“新”表
    df = df.join(df_right,["city","rn"],'left')
    df.show(10)
    df = df.toDF("city","num","rn","num2")
    #RDD的关联接口不算复杂,注意需要通过map重新将需关联的字段组成key,在此不做示例

#写出
    #创建目标表
    tablename="project.output_target_table"
    spark.sql("drop table if exists %s" %(tablename))
    spark.sql("create table % s (city string COMMENT '地市',num bigint comment '号',rn bigint comment '序',num2 bigint comment '号2')"%(tablename))
    # df.show(10)
    #写入目标表
    df.write.mode('overwrite').option("encoding","utf-8").insertInto(tablename)
    #临时视图方式写入目标表
    #tmpview=tablename+"_tmp"
    # df.createOrReplaceTempView(tmpview)
    # spark.sql("insert overwrite table %s from %s" %(tablename,tmpview))

        ODPS(DataWorks)中也有提供Spark SQL节点,因此可以直接通过全程写SQL来开发基于Spark引擎的数据处理程序,效率上应该是无差异的(SQL接口也更具安全性)。当进行临时性的、具研究性的、流程较为复杂的处理时可以使用DF或者RDD,实际上有时支持lambda程度越高,解决临时性小问题反而越便捷。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值