0基础学习PyFlink——用户自定义函数之UDTAF

news2024/9/24 1:29:51

大纲

  • UDTAF
  • TableAggregateFunction的实现
    • 累加器
      • 定义
      • 创建
      • 累加
    • 返回
      • 类型
      • 计算
  • 完整代码

在前面几篇文章中,我们分别介绍了UDF、UDTF和UDAF这三种用户自定义函数。本节我们将介绍最后一种函数:UDTAF——用户自定义表值聚合函数。
在这里插入图片描述

UDTAF

UDTAF函数即具备了UDTF的特点,也具备UDAF的特点。即它可以像《0基础学习PyFlink——用户自定义函数之UDTF》介绍的UDTF那样可以返回任意数量的行作为输,又可以像《0基础学习PyFlink——用户自定义函数之UDAF》介绍的UDAF那样通过聚合的数据(多组)计算出一个值
举一个例子:我们拿到一个学生成绩表,每行包括:

  • 学生姓名
  • 英语成绩
  • 数学成绩
  • 年级

现在我们需要把这张表调整为:

  • 学生姓名
  • 成绩
  • 科目
  • 科目年级平均成绩
  • 年级
    在这里插入图片描述
    将一行中的“英语成绩”和“数学成绩”,拆成“成绩”和“科目”,相当于把一行数据拆解成多行,如上图左侧“张三”只有一行,而右侧有两行“张三”信息。这种拆解操作就需要T类型的用户自定义函数,比如UDTF和UDTAF。
    而我们需要计算一个年级一科的平均成绩,比如1年级英语的平均成绩,则需要按年级聚合之后再做计算。这个就需要A类型的用户自定义函数,比如UDAF和UDTAF。
    同时要满足上述两种技术方案的就是UDTAF。我们先看下主体代码,它和《0基础学习PyFlink——用户自定义函数之UDAF》中的很像。但是有两个重要区别:
  • 要设置成in_streaming_mode模式,否则会报错
  • udtaf要修饰一个对象,而非一个方法;
def calc():
    config = Configuration()
    # write all the data to one file
    config.set_string('parallelism.default', '1')
    env_settings = EnvironmentSettings \
        .new_instance() \
        .in_streaming_mode() \
        .with_configuration(config) \
        .build()
    
    t_env = TableEnvironment.create(env_settings)
    
    row_type_tab_source = DataTypes.ROW([DataTypes.FIELD('name', DataTypes.STRING()), DataTypes.FIELD('english', DataTypes.FLOAT()), DataTypes.FIELD('math', DataTypes.FLOAT()), DataTypes.FIELD('grade', DataTypes.STRING())])
    students_score = [
        ("张三", 80.0, 60.0, "1"),
        ("李四", 75.0, 95.0, "1"),
        ("王五", 90.0, 90.0, "2"),
        ("赵六", 85.0, 70.0, "2"),
        ("孙七", 60.0, 0.0, "3"),
    ]
    tab_source = t_env.from_elements(students_score, row_type_tab_source)
    
    split_class = udtaf(SplitClass())
    tab_source.group_by(col('grade')) \
        .flat_aggregate(split_class) \
        .select(col('*')) \
        .execute().print()

TableAggregateFunction的实现

用于计算的类要继承于TableAggregateFunction,即UDTAF中的TAF。

class SplitClass(TableAggregateFunction):
    _class_keys = ["english", "math"]

我们需要通过get_result_type告诉框架,UDTAF函数返回的是什么类型的数据。一般我们都是构造一个行类型——ROW,然后定义其每个字段的值和类型:

  • name:string类型,用户姓名;
  • score:float类型,考分;
  • avg score:float类型,科目年级平均分数;
  • class:sting类型,科目名称;

累加器

accumulator(累加器)是用于参与计算的中间数据。比如这个案例中,我们会向让accumulator保存拆解后的数据(即一行拆解成多行后的数据),然后再计算各年级每科的平均成绩。

定义

    def get_accumulator_type(self):
        return DataTypes.ARRAY(DataTypes.ROW([DataTypes.FIELD("name", DataTypes.STRING()), DataTypes.FIELD("score", DataTypes.FLOAT()), DataTypes.FIELD("class", DataTypes.STRING())])) 

因为只是为了保存展开的数据,于是我们只用定义均值计算之前的字段:

  • name:string类型,姓名;
  • score:float类型,分数;
  • class:string类型,科目名称;

创建

刚开始时,我们让其是一个空数组,对应上定义中的ARRAY类型。

    def create_accumulator(self):
        return []

累加

我们对科目进行遍历,进行行的拆分。即将(“张三”, 80.0, 60.0, “1”)拆解成(“张三”, 80.0, “english”)和(“张三”, 60.0, “math”)这样的两组数据。

    def accumulate(self, accumulator, row):
        for i in self._class_keys:
            accumulator.append(Row(row["name"], row[i], i))

返回

类型

    def get_result_type(self):
        return DataTypes.ROW([DataTypes.FIELD("name", DataTypes.STRING()), DataTypes.FIELD("score", DataTypes.FLOAT()), DataTypes.FIELD("avg score", DataTypes.FLOAT()), DataTypes.FIELD("class", DataTypes.STRING())])

可以看到result_type(返回类型)和accumulator_type(累加器类型)是不一样的(也可以一样,主要看怎么计算规则)。前者比后者多了“学科年级平均分”(avg score),这就更加接近我们希望获得的最终结果。
这些字段和我们目标字段只差一个grade(年级)。因为原始表中有grade,且我们会通过grade聚类,所以最终我们可以获得这个信息,而不用在这儿定义。
需要注意的是,虽然表值类型函数返回的是一组数据(若干Row),但是这儿只是返回Row的具体定义,而不是ARRAY[Row]。

计算

    def emit_value(self, accumulator):
        rows = []
        for i in self._class_keys: 
            total = 0.0
            student_count = 0
            for y in accumulator:
                # y[2] y[]"class"]
                if i == y[2]:
                    # y[1] y["score"]
                    total = total + y[1]
                    student_count = student_count + 1
            avg_score = total / student_count
            for y in accumulator:
                if i == y[2]:
                    rows.append(Row(y[0], y[1], avg_score, y[2]))
        for x in rows:   
            yield x

这个函数会在最后执行,它会通过累加器中的数据计算“学科年级平均分”,然后构造和“返回类型”一直的Row到rows数组中。最后通过yeild关键字返回一个生成器,我们可以将其看成还是一组Row,即拆解后的结果。

最后我们看下结果

+----+--------------------------------+--------------------------------+--------------------------------+--------------------------------+--------------------------------+
| op |                          grade |                           name |                          score |                      avg score |                          class |
+----+--------------------------------+--------------------------------+--------------------------------+--------------------------------+--------------------------------+
| +I |                              1 |                           张三 |                           80.0 |                           77.5 |                        english |
| +I |                              1 |                           李四 |                           75.0 |                           77.5 |                        english |
| +I |                              1 |                           张三 |                           60.0 |                           77.5 |                           math |
| +I |                              1 |                           李四 |                           95.0 |                           77.5 |                           math |
| +I |                              2 |                           王五 |                           90.0 |                           87.5 |                        english |
| +I |                              2 |                           赵六 |                           85.0 |                           87.5 |                        english |
| +I |                              2 |                           王五 |                           90.0 |                           80.0 |                           math |
| +I |                              2 |                           赵六 |                           70.0 |                           80.0 |                           math |
| +I |                              3 |                           孙七 |                           60.0 |                           60.0 |                        english |
| +I |                              3 |                           孙七 |                            0.0 |                            0.0 |                           math |
+----+--------------------------------+--------------------------------+--------------------------------+--------------------------------+--------------------------------+
10 rows in set

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

完整代码

from pyflink.common import Configuration
from pyflink.table import (EnvironmentSettings, TableEnvironment, Schema)
from pyflink.table.types import DataTypes
from pyflink.table.table_descriptor import TableDescriptor
from pyflink.table.expressions import lit, col
from pyflink.common import Row
from pyflink.table.udf import udf,udtf,udaf,udtaf,TableAggregateFunction
import pandas as pd
from pyflink.table.udf import UserDefinedFunction
from typing import List

class SplitClass(TableAggregateFunction):
    _class_keys = ["english", "math"]

    def emit_value(self, accumulator):
        rows = []
        for i in self._class_keys: 
            total = 0.0
            student_count = 0
            for y in accumulator:
                if i == y[2]:
                    total = total + y[1]
                    student_count = student_count + 1
            avg_score = total / student_count
            for y in accumulator:
                if i == y[2]:
                    rows.append(Row(y[0], y[1], avg_score, y[2]))
        return rows

    def create_accumulator(self):
        return []

    def accumulate(self, accumulator, row):
        for i in self._class_keys:
            accumulator.append(Row(row["name"], row[i], i))

    def get_accumulator_type(self):
        return DataTypes.ARRAY(DataTypes.ROW([DataTypes.FIELD("name", DataTypes.STRING()), DataTypes.FIELD("score", DataTypes.FLOAT()), DataTypes.FIELD("class", DataTypes.STRING())]))  

    def get_result_type(self):
        return DataTypes.ROW([DataTypes.FIELD("name", DataTypes.STRING()), DataTypes.FIELD("score", DataTypes.FLOAT()), DataTypes.FIELD("avg score", DataTypes.FLOAT()), DataTypes.FIELD("class", DataTypes.STRING())])

    
def calc():
    config = Configuration()
    # write all the data to one file
    config.set_string('parallelism.default', '1')
    env_settings = EnvironmentSettings \
        .new_instance() \
        .in_streaming_mode() \
        .with_configuration(config) \
        .build()
    
    t_env = TableEnvironment.create(env_settings)
    
    row_type_tab_source = DataTypes.ROW([DataTypes.FIELD('name', DataTypes.STRING()), DataTypes.FIELD('english', DataTypes.FLOAT()), DataTypes.FIELD('math', DataTypes.FLOAT()), DataTypes.FIELD('grade', DataTypes.STRING())])
    students_score = [
        ("张三", 80.0, 60.0, "1"),
        ("李四", 75.0, 95.0, "1"),
        ("王五", 90.0, 90.0, "2"),
        ("赵六", 85.0, 70.0, "2"),
        ("孙七", 60.0, 0.0, "3"),
    ]
    tab_source = t_env.from_elements(students_score, row_type_tab_source)
    
    split_class = udtaf(SplitClass())
    tab_source.group_by(col('grade')) \
        .flat_aggregate(split_class) \
        .select(col('*')) \
        .execute().print()
    
if __name__ == '__main__':
    calc()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1141147.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

归一化一维时序信号,针对上下幅值波动不均问题

目的:如下图,信号上包络和下包络都有无规律的起伏,如何进行有效归一化,步骤如下: 步骤1. 信号初步归一化 data mapminmax(data,-1,1); 步骤2. 希尔伯特变换获得该时序信号的包络 z hilbert(data);figure;plot(data…

【VUE】ArcoDesign之自定义主题样式和命名空间

前言 Arco Design是什么? Arco Design 是由字节跳动推出的企业级产品的完整设计和开发解决方案前端组件库 官网地址:https://arco.design/同时也提供了一套开箱即用的中后台前端解决方案:Arco Design Pro(https://pro.arco.design/) Arco De…

基于SSM的高校勤工助学系统

基于SSM的高校勤工助学系统的设计与实现~ 开发语言:Java数据库:MySQL技术:SpringSpringMVCMyBatis工具:IDEA/Ecilpse、Navicat、Maven 系统展示 主页 管理员界面 摘要 基于SSM(Spring、SpringMVC、MyBatis&#xff…

560. 和为 K 的子数组(前缀和 + 哈希表)

这道题的思路就是: 前缀和的差值可以表示一个区间内的元素的总和。 所以index1处的前缀和如果为sum,那么前面只要出现过 k - sum 的前缀和就表示,有和为k的子数组存在。 因为,sum - (sum - k) k class Sol…

ATFX汇市:美国9月PCE数据来袭,高通胀问题或已不构成威胁

ATFX汇市:今日20:30,美国商务部将公布9月核心PCE物价指数年率,前值为3.9%,金融机构预期为3.7%,预期将出现小幅下降。核心PCE数据与核心CPI数据的走势共振性较强,9月份核心CPI数据显示,最新值4.1…

【机器学习可解释性】3.部分依赖图

机器学习可解释性 1.模型洞察的价值2.特征重要性排列3.部分依赖图4.SHAP Value5.SHAP Value 高级使用 正文 每个特征怎么样影响预测结果? 部分依赖图 Partial Dependence Plots 虽然特征重要性显示了哪些变量对预测影响最大,但部分依赖图显示了特征如…

CorelDRAW和AI哪个更好用?

设计软件市场中,CorelDRAW和Adobe Illustrator(简称AI)无疑是两大重量级选手。它们各自拥有庞大的用户群和丰富的功能,但究竟哪一个更好用?本文将从多个角度出发,对这两款软件进行全面而深入的比较&#xf…

mysql基本操作命令

1、数据库的分类 mysql:关系型数据库 redis:非关系型数据库 关系型数据库:存储数据的结构是一个二维表格 表:行 列 行:记录,用来描述一个对象的信息 列:字段,用来描述对象的一个…

基于SSM的二手车交易系统

基于SSM的二手车交易系统的设计与实现~ 开发语言:Java数据库:MySQL技术:SpringSpringMVCMyBatis工具:IDEA/Ecilpse、Navicat、Maven 系统展示 主页 登录界面 管理员界面 摘要 基于SSM(Spring、SpringMVC、MyBatis&a…

高效管理文件夹名称:如何批量修改指定多样化的文件夹名称

在文件管理工作中,文件夹名称的管理对于整体的文件管理体系有着至关重要的作用。然而,往往我们会在文件夹名称的管理上遇到一些难题,如:需要修改的文件夹名称多样化,无法一次性满足所有需求。为了解决这个问题&#xf…

米尔AM62x核心板助力新一代工业4.0升级

米尔AM62x核心板 续写AM335x经典 在过去的十几年中,TI Sitara系列推出了很多优秀的处理器,其中在工业、电力、医疗等领域有着广泛应用的AM335x系列处理器,引领工业市场从MCU向MPU演进,帮助产业界从ARM9迅速迁移至高性能Cortex-A…

796. 子矩阵的和(二维前缀和)

题目: 796. 子矩阵的和 - AcWing题库 思路: 1.暴力搜索(搜索时间复杂度为O(n2),很多时候会超时) 2. 前缀和(左上角(二维)前缀和):本题特殊在不是直接求前…

软考系统架构师知识点集锦五:系统可靠性分析与设计

一、考情分析 二、考点精讲 2.1相关基本概念 可靠性:可靠性是软件系统在应用或系统错误面前,在意外或错误使用的情况下维持软件系统的功能特性的基本能力。 可用性:可用性是系统能够正常运行的时间比例。 软件可靠性 ≠ 硬件可靠性 软硬件对比 复杂性:软件复杂性比…

linux下部署nacos(单机、集群)

文章目录 nacos简介单机部署集群部署部署常见问题 官网文档地址:https://nacos.io/zh-cn/docs/deployment.html github地址:https://github.com/alibaba/nacos nacos简介 Nacos,全称阿里巴巴开源的动态服务发现、配置和服务管理平台&#x…

【springBoot】博客系统

SSM版本的博客系统 1. 项目亮点 使用MD5加盐算法进行密码的加密使用Redis持久化存储Session使用拦截器验证用户登录 2. 项目创建 1.项目框架的选择 2. 项目依赖的引入 3. 静态页面的代码文件: program/博客系统(静态页面).rar 叁伍/java语言练习 - 码云 - 开源…

电脑msvcp100.dll丢失了怎么办?详细的5个修复方法

电脑已经成为我们生活和工作中不可或缺的一部分。然而,由于各种原因,其中最常见的就是“缺少xxx.dll文件”,而msvcp100.dll就是其中之一。那么,msvcp100.dll到底是什么?当我们遇到这个问题时,应该如何解决呢…

剑指JUC原理-4.共享资源和线程安全性

共享问题 小故事 老王(操作系统)有一个功能强大的算盘(CPU),现在想把它租出去,赚一点外快 小南、小女(线程)来使用这个算盘来进行一些计算,并按照时间给老王支付费用 …

如何优化工业5G网关的网络信号

工业5G网关,通常是指支持5G网络,具有高速率、低时延、广接入等特点的高性能工业物联网智能网关,这类网关具有强大的设备接入能力、通信协议转换、运算处理能力、联动控制能力,有助于提升工业物联网整体通信效率,实现生…

tooltip实现悬停内容高亮及格式化

一: 通过highlight.js项目实现对json字符串的染色高亮 此项目是jsp文件,并且引用了element-ui/highlight.js的组件,对tooltip中的json文本(理论上支持highlight所支持的所有项目)进行高亮并格式化 二: 实现效果 三: 代码实现 关键点在于成功…

树莓派 qt 调用multimedia、multimediawidgets、serialport、Qchats

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、测试11.命令安装出现错误 二、测试21. 安装 Qt Charts:2. 安装 Qt Multimedia 和 Qt MultimediaWidgets:3. 安装 Qt SerialPort&…