day04_匹配类标签

day04_匹配类标签

一、性别标签开发(掌握)

1、需求分析

1- 性别的四级标签ID=4

2- 读取标签配置数据,获得性别的四级标签rule规则
inType=Hive##nodes=up01:9083##table=dwd.dwd_mem_member_union_i##selectFields=zt_id,sex##range=all

业务数据存储介质类型inType: Hive数仓
业务数据存储介质连接信息nodes: up01:9083
业务数据具体存储的库和表信息table: dwd.dwd_mem_member_union_i
计算性别标签涉及的业务数据字段selectFields: zt_id,sex
计算性别标签涉及的业务数据范围range: all计算分析所有的数据

3-rule规则进行解析,得到类的实例对象

4- 根据解析后的rule规则,读取对应的业务数据
zt_id,sex
139040,2
196941,1
202025,0

5- 从标签配置数据中获取五级标签配置数据,pid=四级标签ID
id,name,rule
5,,1
6,,2
7,未知,0

6- 开始Spark代码,将业务数据与五级标签配置数据进行关联,给用户打上性别五级标签

7- 将标签结果数据输出到ElasticSearch的user_profile_tags索引中

如何给用户打上性别五级标签:
在这里插入图片描述

2、代码开发

from pyspark.sql import SparkSession
import os
import pyspark.sql.functions as F
from tags.utils.rule_parse_util import RuleParse

os.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python3'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python3'

if __name__ == '__main__':
    # 1- 创建SparkSession对象
    spark = SparkSession.builder\
        .appName("sex_tag_v1")\
        .master("local[*]") \
        .config("spark.sql.shuffle.partitions",3)\
        .config("spark.sql.warehouse.dir", "hdfs://up01:8020/user/hive/warehouse") \
        .config("hive.metastore.uris", "thrift://up01:9083") \
        .enableHiveSupport()\
        .getOrCreate()

    # 2- 性别的四级标签ID
    four_tag_id = 4

    # 3- 读取标签配置表数据
    all_tag_df = spark.read.jdbc(
        url="jdbc:mysql://192.168.88.166:3306/tags_info?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false",
        table="tbl_basic_tag",
        sql={'user': 'root', 'password': '123456'}
    )

    # 4- 读取对应的性别四级标签配置中的rule规则内容
    rule_str = all_tag_df.where(f"id={four_tag_id}").first().rule

    # 5- 解析rule规则,得到实例对象
    rule_obj = RuleParse.parse_rule(rule_str)

    # 6- 根据解析好的rule规则,读取对应的业务数据
    hive_sql = "select " + rule_obj.selectFields + " from " + rule_obj.table + " where 1=1 "
    business_df = spark.sql(hive_sql)

    # 7- 根据四级标签ID,得到五级标签配置数据
    five_tag_df = all_tag_df.where(f"pid={four_tag_id}").select("id","rule")

    # 8- 将业务数据与五级标签配置数据进行关联,给用户打上五级标签
    result_df = business_df.join(five_tag_df,how="left",on=business_df["sex"]==five_tag_df["rule"])\
        .select(
            business_df["zt_id"].alias("user_id"),
            five_tag_df["id"].alias("tags_id_times")
        )
    result_df.show()
    result_df.printSchema()

    # 9- 将结果数据输出到ElasticSearch中
    result_df.write.format("es").mode("append") \
        .option("es.nodes", "192.168.88.166:9200") \
        .option("es.resource", "user_profile_tags") \
        .option("es.mapping.id", "user_id") \
        .option("es.write.operation", "upsert") \
        .save()

    # 10- 释放资源
    spark.stop()

运行结果截图:
在这里插入图片描述

二、Python基础回顾(复习)

1、闭包和装饰器

闭包的语法要求(必须要满足):

1- 函数嵌套定义

2- 外部函数返回内部函数的名称

举例:

def outer_func():
    
    def inner_func():
        pass
    
    return inner_func

装饰器

1- 作用:在不改变原始函数内容和函数调用的基础上,对函数功能进行增强

2- 格式要求:装饰器是一个特殊的闭包。在满足闭包语法的基础上,还需要在外部函数的形参这个地方,有且只能有一个形参,该形参用来接收被增强/修饰的函数

举例:

def outer_func(old_func):
    
    def inner_func():
        # 增强代码
        result = old_func()
        # 增强代码
        return result
    
    return inner_func

3- 通用装饰器

def outer_func(old_func):
    
    def inner_func(*args, **kwargs):
        # 增强代码
        result = old_func(*args, **kwargs)
        # 增强代码
        return result
    
    return inner_func

4- 装饰器本身需要参数

def wrapper_func(装饰器自己需要用的参数):
    
    def outer_func(old_func):
    
        def inner_func(*args, **kwargs):
            # 增强代码
            result = old_func(*args, **kwargs)
            # 增强代码
            return result
    
    	return inner_func
  
    return outer_func

案例代码:

import time

def wrapper(nums):

    def outer_fun(fun_name):

        def inner_fun(*args,**kwargs):
            start_time = time.time()
            result = fun_name(*args,**kwargs)
            cost_time = time.time() - start_time
            print("函数运行耗时(秒)",round(cost_time,nums))
            return result

        return inner_fun

    return outer_fun

@wrapper(3)
def myfun(start,end):
    result = 0
    for i in range(start,end):
        result += i
    return result

if __name__ == '__main__':
    print(myfun(1, 1000000))

2、容器数据类型

  • List
    • 元素可以重复
    • 数据有序。写入顺序和输出顺序是一致
    • 数据可以修改
    • 数据类型不要求统一。但是实际使用一般存放相同类型的数据
    • 支持索引。也就是支持for和while循环遍历
  • Tuple
    • 数据可以重复
    • 数据有序。写入顺序和输出顺序是一致
    • 数据不可以修改
    • 数据类型不要求统一。但是实际使用一般存放相同类型的数据
    • 支持索引。也就是支持for和while循环遍历
  • Dict字典
    • key不可以重复,value没有任何要求
    • 可以通过key来修改对应value的数据内容
    • 不可变数据类型可以作为key。value没有任何要求
    • 不支持索引。支持for循环遍历,但是不支持while循环
  • Set集合
    • 元素不可以重复。会自动进行数据去重
    • 元素是无须的
    • 数据可以修改
    • 数据类型不要求统一。但是实际使用一般存放相同类型的数据
    • 不支持索引。支持for循环遍历,但是不支持while循环
  • string
    • 数据可以重复
    • 数据有序。写入顺序和输出顺序是一致
    • 数据不可以修改
    • 支持索引。也就是支持for和while循环遍历
  • 可变和不可变数据类型:在内存地址值不变的情况下,是否允许修改对应的数据内容
    • 可变数据类型:List、Set、Dict
    • 不可变数据类型:int、float、bool、string、Tuple

3、面向对象

1- 面向对象和面向过程的区别
	1.1- 编码方式: 拿到需求以后,面向过程需要将每一步都分析出来然后再写代码;面向对象先根据需求考虑需要涉及到多少个类,接着在写代码的过程中逐步细化类内部的细节
	1.2- 适用场景: 
		中大型项目: 面向对象
		中小型项目: 面向过程
		
		
2- 方法和属性的区别
	方法: 类具备的功能。例如:吃喝拉撒
	属性: 类具备的特征。例如:高矮胖瘦
	
	
3- 面向对象中方法的分类
	
	3.1- 实例方法: 第一个参数是self,不需要我们传值,由Python底层自动进行传参。在类的内部一般是通过self调用;类的外部是通过类的实例对象进行调用。
	
	3.2- 魔法方法: Python内部定义好的,并且方法名称的前后有两个下划线。魔法方法一般会被自动调用
		__init__: 用来对类的实例对象进行初始化操作,在创建类的实例对象的时候自动被调用
		__str__: 通过print打印实例对象的时候自动被调用。必须要有返回值,返回值类型必须是string
		__del__: 程序运行结束或者del删除对象的时候被自动调用。一般是用来资源回收
		
	3.3- 类方法: 定义类方法的时候需要在上面增加@classmethod装饰器。第一个参数是cls,不需要我们传值,由Python底层自动进行传参。类的内部和外部使用【类名称.类方法】。
	
	3.4- 静态方法: 定义静态方法的时候需要在上面增加@staticmethod装饰器。类的内部和外部使用【类名称.类方法】。
	
	
4- 权限
	4.1- 私有权限: 在方法和属性的名称前面加两个下划线就能够变成私有权限
		4.1.1- 分类: 私有方法和私有属性
		4.1.2- 访问: 
			a、类的内部:self.私有方法、self.私有属性
			b、类的外部:正常情况外部不能使用。但是可以通过【实例名称._类名__私有方法/属性】暴力访问
		
	4.2- 公共权限: 方法和属性正常取名,前面没有两个下划线
		4.2.1- 分类: 公共方法和公共属性
		4.2.2- 访问: 
			a、类的内部:self.公共方法、self.公共属性
			b、类的外部:实例名称.方法/属性
		
5- 属性
	5.1- 实例属性: 属于每个实例对象。在__init__魔法方法中定义和初始化
	5.2- 类属性: 属于类的,每个实例对象都共有的属性。在类的里面方法的外面定义。
	
	
6- 继承
	6.1- 继承的分类:
		6.1.1- 单继承: class B(A)
		6.1.2- 多继承: class C(A,B)
		6.1.3- 多层继承: class B(A)、class C(B)
		
	6.2- 子类能够继承父类的公共方法。继承以后能够重写父类的方法
class Test:
    def __init__(self):
        self.__name = "张三"

    def __fun(self):
        print("这是私有方法")

if __name__ == '__main__':
    obj = Test()

    # 类的外部不能直接方法私有权限
    # print(obj.__name)
    # print(obj.__fun())

    # 但是可以通过暴力的形式方法,语法:类的实例对象名称._类名称__私有属性/私有方法
    print(obj._Test__name)
    obj._Test__fun()

三、匹配类标签代码重构(掌握)

1、为什么要重构

一般在大公司中,会有高级开发/架构师级别的人在项目开发初始阶段,从上帝视角对整个项目进行规划,抽取封装一些公共代码,形成整个项目的大体框架,如项目中的公共模块,工具类等……或者在项目开发初始阶段没有做合理的系统的规划,只是完成了基本的功能。

重构的主要目标和原则包括:
1- 提高代码的可读性: 通过重构,代码的逻辑和结构变得更加直观,方便其他开发人员理解和维护。
2- 减少代码重复: 通过识别和消除重复的代码段,减少冗余代码,使代码更加简洁。
3- 提高代码的可维护性: 重构后的代码结构更加清晰,便于修改和扩展,从而减少维护成本。
4- 提高代码的性能: 虽然重构的主要目的是改善代码质量,但有时通过优化代码结构也能提升性能。

重构思路

标签实施流程:
一样 1- 创建Spark执行环境,也就是创建SparkSession顶级对象
一样 2- 读取标签配置数据: 从MySQL表中读取4级和5级标签配置内容
一样 3- 解析rule规则: 从4级标签中获取rule规则,并且进行规则解析,解析得到业务数据存储位置信息
一样 4- 读取Hive中的业务数据: 根据rule规则读取Hive中存储的业务数据
一样 5- 过滤5级标签: 从4级和5级标签内容中过滤出5级标签内容,也就是pid=4级标签的ID
不一样 6- 标签计算: 给用户打上5级标签。也就是将业务数据和5级标签关联起来
一样 7- 结果数据存储: 将用户标签结果数据存储到ES中 
一样 8- 释放资源

在这里插入图片描述

2、基类代码

  • 面向过程
from pyspark.sql import SparkSession
import os
import pyspark.sql.functions as F
from tags.utils.rule_parse_util import RuleParse

os.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python3'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python3'


# 1- 创建SparkSession对象
def create_spark(app_name,partitions):
    spark = SparkSession.builder \
        .appName(app_name) \
        .master("local[*]") \
        .config("spark.sql.shuffle.partitions", partitions) \
        .config("spark.sql.warehouse.dir", "hdfs://up01:8020/user/hive/warehouse") \
        .config("hive.metastore.uris", "thrift://up01:9083") \
        .enableHiveSupport() \
        .getOrCreate()

    return spark

# 3- 读取标签配置表数据
def read_all_tag(spark):
    all_tag_df = spark.read.jdbc(
        url="jdbc:mysql://192.168.88.166:3306/tags_info?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false",
        table="tbl_basic_tag",
        sql={'user': 'root', 'password': '123456'}
    )

    return all_tag_df

# 4- 读取对应的性别四级标签配置中的rule规则内容
# 5- 解析rule规则,得到实例对象
def read_and_parse_rule(all_tag_df,four_tag_id):
    rule_str = all_tag_df.where(f"id={four_tag_id}").first().rule
    rule_obj = RuleParse.parse_rule(rule_str)
    return rule_obj

# 6- 根据解析好的rule规则,读取对应的业务数据
def read_hive_data(rule_obj,spark):
    hive_sql = "select " + rule_obj.selectFields + " from " + rule_obj.table + " where 1=1 "
    business_df = spark.sql(hive_sql)
    return business_df

# 7- 根据四级标签ID,得到五级标签配置数据
def read_five_tag(all_tag_df,four_tag_id):
    five_tag_df = all_tag_df.where(f"pid={four_tag_id}").select("id", "rule")
    return five_tag_df

# 9- 将结果数据输出到ElasticSearch中
def write_2_es(result_df):
    result_df.write.format("es").mode("append") \
        .option("es.nodes", "192.168.88.166:9200") \
        .option("es.resource", "user_profile_tags") \
        .option("es.mapping.id", "user_id") \
        .option("es.write.operation", "upsert") \
        .save()


if __name__ == '__main__':
    # 1- 创建SparkSession对象
    spark = create_spark("sex_tag_v1",3)

    # 2- 性别的四级标签ID
    four_tag_id = 4

    # 3- 读取标签配置表数据
    all_tag_df = read_all_tag(spark)

    # 4- 读取对应的性别四级标签配置中的rule规则内容
    # 5- 解析rule规则,得到实例对象
    rule_obj = read_and_parse_rule(all_tag_df,four_tag_id)

    # 6- 根据解析好的rule规则,读取对应的业务数据
    business_df = read_hive_data(rule_obj,spark)

    # 7- 根据四级标签ID,得到五级标签配置数据
    five_tag_df = read_five_tag(all_tag_df,four_tag_id)

    # 8- 将业务数据与五级标签配置数据进行关联,给用户打上五级标签
    result_df = business_df.join(five_tag_df,how="left",on=business_df["sex"]==five_tag_df["rule"])\
        .select(
            business_df["zt_id"].alias("user_id"),
            five_tag_df["id"].alias("tags_id_times")
        )
    result_df.show()
    result_df.printSchema()

    # 9- 将结果数据输出到ElasticSearch中
    write_2_es(result_df)

    # 10- 释放资源
    spark.stop()
  • 面向对象
from pyspark.sql import SparkSession
import os
import pyspark.sql.functions as F
from tags.utils.rule_parse_util import RuleParse
import abc

os.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python3'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python3'

# metaclass=abc.ABCMeta这种写法不是继承的意思,而是表示该类是一个抽象类
class AbstractTagBase(metaclass=abc.ABCMeta):
    # 1- 创建SparkSession对象
    def create_spark(self, app_name, partitions):
        spark = SparkSession.builder \
            .appName(app_name) \
            .master("local[*]") \
            .config("spark.sql.shuffle.partitions", partitions) \
            .config("spark.sql.warehouse.dir", "hdfs://up01:8020/user/hive/warehouse") \
            .config("hive.metastore.uris", "thrift://up01:9083") \
            .enableHiveSupport() \
            .getOrCreate()

        return spark

    # 3- 读取标签配置表数据
    def read_all_tag(self, spark):
        all_tag_df = spark.read.jdbc(
            url="jdbc:mysql://192.168.88.166:3306/tags_info?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false",
            table="tbl_basic_tag",
            sql={'user': 'root', 'password': '123456'}
        )

        return all_tag_df

    # 4- 读取对应的性别四级标签配置中的rule规则内容
    # 5- 解析rule规则,得到实例对象
    def read_and_parse_rule(self, all_tag_df, four_tag_id):
        rule_str = all_tag_df.where(f"id={four_tag_id}").first().rule
        rule_obj = RuleParse.parse_rule(rule_str)
        return rule_obj

    # 6- 根据解析好的rule规则,读取对应的业务数据
    def read_hive_data(self, rule_obj, spark):
        hive_sql = "select " + rule_obj.selectFields + " from " + rule_obj.table + " where 1=1 "
        business_df = spark.sql(hive_sql)
        return business_df

    # 7- 根据四级标签ID,得到五级标签配置数据
    def read_five_tag(self, all_tag_df, four_tag_id):
        five_tag_df = all_tag_df.where(f"pid={four_tag_id}").select("id", "rule")
        return five_tag_df

    # 8- 打标签的抽象方法,由子类继承以后进行具体实现
    """
        @abc.abstractmethod标记一个方法为抽象方法。能够实现强制要求子类进行方法的具体实现
    """
    @abc.abstractmethod
    def mark_tag(self,business_df, five_tag_df):
        pass

    # 9- 将结果数据输出到ElasticSearch中
    def write_2_es(self, result_df):
        result_df.write.format("es").mode("append") \
            .option("es.nodes", "192.168.88.166:9200") \
            .option("es.resource", "user_profile_tags") \
            .option("es.mapping.id", "user_id") \
            .option("es.write.operation", "upsert") \
            .save()

    def execute(self,app_name,partitions,four_tag_id):
        # 1- 创建SparkSession对象
        spark = self.create_spark(app_name, partitions)

        # 3- 读取标签配置表数据
        all_tag_df = self.read_all_tag(spark)

        # 4- 读取对应的性别四级标签配置中的rule规则内容
        # 5- 解析rule规则,得到实例对象
        rule_obj = self.read_and_parse_rule(all_tag_df, four_tag_id)

        # 6- 根据解析好的rule规则,读取对应的业务数据
        business_df = self.read_hive_data(rule_obj, spark)

        # 7- 根据四级标签ID,得到五级标签配置数据
        five_tag_df = self.read_five_tag(all_tag_df, four_tag_id)

        # 8- 将业务数据与五级标签配置数据进行关联,给用户打上五级标签
        result_df = self.mark_tag(business_df,five_tag_df)

        # 9- 将结果数据输出到ElasticSearch中
        self.write_2_es(result_df)

        # 10- 释放资源
        spark.stop()

3、重构代码

3.1 年龄段标签重构

import os
import pyspark.sql.functions as F
from tags.base.abstract_tag_base import AbstractTagBase

os.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python3'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python3'

class AgeTag(AbstractTagBase):
    def mark_tag(self,business_df, five_tag_df):
        # 8.1- 将业务数据中的birthday_date删除中横杠
        new_business_df = business_df.withColumn("birthday_date", F.regexp_replace("birthday_date", "-", ""))
        # new_business_df.show()

        # 8.2- 将五级标签配置数据中的rule规则按照中横杠切分得到start和end
        new_five_tag_df = five_tag_df.select(
            "id",
            F.split("rule", "-")[0].alias("start"),
            F.split("rule", "-")[1].alias("end")
        )
        # new_five_tag_df.show()

        # 8.3- 将处理后的数据进行join关联,同时进行数据过滤,给用户打上五级标签
        result_df = new_business_df.join(new_five_tag_df).where(
            "birthday_date between start and end"
        ).select(
            new_business_df["zt_id"].alias("user_id"),
            new_five_tag_df["id"].alias("tags_id_times")
        )

        return result_df

if __name__ == '__main__':
    tag_obj = AgeTag()
    tag_obj.execute("age_tag",3,15)

3.2 性别标签重构

import os
from tags.base.abstract_tag_base import AbstractTagBase

os.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python3'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python3'

class SexTag(AbstractTagBase):
    def mark_tag(self,business_df, five_tag_df):
        result_df = business_df.join(five_tag_df, how="left", on=business_df["sex"] == five_tag_df["rule"]) \
            .select(
            business_df["zt_id"].alias("user_id"),
            five_tag_df["id"].alias("tags_id_times")
        )
        return result_df

if __name__ == '__main__':
    tag_obj = SexTag()
    tag_obj.execute("sex_tag",3,4)

1、开发国籍、政治面貌、婚姻状况等标签

需要注意的是数据的处理:国籍的数据是中文,跟标签的rule数据类型不同;婚姻状况数据中有为空的需要处理,可以作为未婚来处理;政治面貌数据中有4种类型“1团员、2党员、3群众、4其他党派”,其中团员属于标签中的群众。

2、尝试进行新旧标签合并功能的开发

1- 在父类代码中将之前存储的用户画像标签结果数据读取出来

2- 要基于pandas写UDF实现合并新旧标签的功能

3-将合并后的结果输出到ES中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十六ᵛᵃᵉ

停船靠岸_愿君通关

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值