spark学习记录(十二、Spark UDF&UDAF&开窗函数)

一、UDF&UDAF 

public class JavaExample {
    public static void main(String[] args) {
        SparkConf conf = new SparkConf();
        conf.setMaster("local");
        conf.setAppName("udf");
        JavaSparkContext sc = new JavaSparkContext(conf);
        SQLContext sqlContext = new SQLContext(sc);
        JavaRDD<String> parallelize = sc.parallelize(Arrays.asList("Sam", "Tom", "Jetty", "Tom", "Jetty"));
        JavaRDD<Row> rowRDD = parallelize.map(new Function<String, Row>() {
            public Row call(String s) throws Exception {
                return RowFactory.create(s);
            }
        });

        /**
         * 动态创建Schema方式加载DF
         */
        List<StructField> fields = new ArrayList<StructField>();
        fields.add(DataTypes.createStructField("name", DataTypes.StringType, true));
        StructType schema = DataTypes.createStructType(fields);

        Dataset<Row> dataFrame = sqlContext.createDataFrame(rowRDD, schema);
        dataFrame.registerTempTable("user");

        /**
         * 根据UDF函数参数的个数决定是实现哪个UDF   UDF1,UDF2...
         * UDF1表示传一个参数,StrLen(name)
         */
        sqlContext.udf().register("StrLen", new UDF1<String, Integer>() {
            public Integer call(String s) throws Exception {
                return s.length();
            }
        }, DataTypes.IntegerType);

        sqlContext.sql("select name,StrLen(name) as length from user").show();

        /**
         * 注册一个UDAF函数,实现统计相同的值得个数
         */
        sqlContext.udf().register("StringCount", new UserDefinedAggregateFunction() {
            /**
             * 指定输入字段的字段及类型
             */
            @Override
            public StructType inputSchema() {
                return DataTypes.createStructType(
                        Arrays.asList(DataTypes.createStructField("name",
                                DataTypes.StringType, true)));
            }
            /**
             * 指定UDAF函数计算后返回的结果类型
             */
            @Override
            public DataType dataType() {
                return DataTypes.IntegerType;
            }
            /**
             * 确保一致性一般用true,用以标记针对给定的一组输入,UDAF总是生成相同的结果
             */
            @Override
            public boolean deterministic() {
                return true;
            }
            /**
             * 可以认为一个一个的将组内的字段值传递出来显现拼接的逻辑
             * buffer.update(0)获取的是上一次聚合的值
             * 相当于map的combiner,combiner就是对每一个map task的处理结果进行一次小聚合
             * 大聚合发生在reduce端
             * 这里既是:在进行聚合的时候,每当有新的值进来,对分组后的聚合
             */
            @Override
            public void update(MutableAggregationBuffer buffer, Row input) {
                buffer.update(0,buffer.getInt(0)+1);
            }
            /**
             * 在进行聚合操作的时候所要处理的数据的结果的类型
             */
            @Override
            public StructType bufferSchema() {
                return DataTypes.createStructType(
                                Arrays.asList(DataTypes.createStructField("bf", DataTypes.IntegerType, true)));
            }
            /**
             * 合并 update操作,可能是针对一个分组内的部分数据,在某个节点上发生的 但是可能一个分组内的数据,
             * 会分布在多个节点上处理
             * 此时就要用merge操作,将各个节点上分布式拼接好的串,合并起来
             * buffer1.getInt(0) : 大聚和的时候 上一次聚合后的值
             * buffer2.getInt(0) : 这次计算传入进来的update的结果
             * 这里即是:最后在分布式节点完成后需要进行全局级别的Merge操作
             */
            @Override
            public void merge(MutableAggregationBuffer buffer1, Row buffer2) {
                buffer1.update(0, buffer1.getInt(0) + buffer2.getInt(0));
            }
            /**
             * 初始化一个内部的自己定义的值,在Aggregate之前每组数据的初始化结果
             */
            @Override
            public void initialize(MutableAggregationBuffer buffer) {
                buffer.update(0, 0);
            }
            /**
             * 最后返回一个和DataType的类型要一致的类型,返回UDAF最后的计算结果
             */
            @Override
            public Object evaluate(Row buffer) {
                return buffer.getInt(0);
            }
        });

        sqlContext.sql("select name ,StringCount(name) as number from user group by name").show();

        sc.stop();
    }
}

二、开窗函数

例:驾驶第一列为日期,第二类为类别,第三类为价格。统计每个类别赚的最多的三次。

数据:https://download.csdn.net/download/qq_33283652/10904792

/**
 * row_number() 开窗函数是按照某个字段分组,然后取另一字段的前几个的值,相当于 分组取topN
 * 如果SQL语句里面使用到了开窗函数,那么这个SQL语句必须使用HiveContext来执行,HiveContext默认情况下在本地无法创建。
 * 开窗函数格式:
 * row_number() over (partitin by XXX order by XXX desc)
 */
public class JavaExample {
    public static void main(String[] args) {
        SparkConf conf = new SparkConf();
        conf.setAppName("windowfun");
        conf.set("spark.sql.shuffle.partitions","1");
        JavaSparkContext sc = new JavaSparkContext(conf);
        HiveContext hiveContext = new HiveContext(sc);
        hiveContext.sql("use spark");
        hiveContext.sql("drop table if exists sales");
        hiveContext.sql("create table if not exists sales (riqi string,leibie string,jine Int) "
                + "row format delimited fields terminated by '\t'");
        hiveContext.sql("load data local inpath '/root/test/sales' into table sales");
        /**
         * 开窗函数格式:
         * 【 rou_number() over (partitin by XXX order by XXX) 】
         */
        Dataset<Row> result = hiveContext.sql("select riqi,leibie,jine "
                + "from ("
                + "select riqi,leibie,jine,"
                + "row_number() over (partition by leibie order by jine desc) rank "
                + "from sales) t "
                + "where t.rank<=3");
        result.write().mode(SaveMode.Overwrite).saveAsTable("sales_result");
        sc.stop();
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spark SQL中的自定义函数(UDFUDAF、UDTF)是用户自己定义的函数,可以用于对数据进行处理和转换。下面是一些自定义函数的实例: 1. UDF(User-Defined Function):用户自定义函数,可以将一个或多个输入参数转换为输出值。例如,我们可以定义一个UDF来计算两个数的和: ``` import org.apache.spark.sql.functions.udf val sumUDF = udf((a: Int, b: Int) => a + b) val df = Seq((1, 2), (3, 4)).toDF("a", "b") df.select(sumUDF($"a", $"b")).show() ``` 2. UDAF(User-Defined Aggregate Function):用户自定义聚合函数,可以对一组数据进行聚合操作,例如求和、平均值等。例如,我们可以定义一个UDAF来计算一组数的平均值: ``` import org.apache.spark.sql.expressions.MutableAggregationBuffer import org.apache.spark.sql.expressions.UserDefinedAggregateFunction import org.apache.spark.sql.types._ class AvgUDAF extends UserDefinedAggregateFunction { // 输入数据类型 def inputSchema: StructType = StructType(StructField("value", DoubleType) :: Nil) // 聚合缓冲区数据类型 def bufferSchema: StructType = StructType( StructField("sum", DoubleType) :: StructField("count", LongType) :: Nil ) // 输出数据类型 def dataType: DataType = DoubleType // 是否是确定性的 def deterministic: Boolean = true // 初始化聚合缓冲区 def initialize(buffer: MutableAggregationBuffer): Unit = { buffer(0) = 0.0 buffer(1) = 0L } // 更新聚合缓冲区 def update(buffer: MutableAggregationBuffer, input: Row): Unit = { buffer(0) = buffer.getDouble(0) + input.getDouble(0) buffer(1) = buffer.getLong(1) + 1L } // 合并聚合缓冲区 def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = { buffer1(0) = buffer1.getDouble(0) + buffer2.getDouble(0) buffer1(1) = buffer1.getLong(1) + buffer2.getLong(1) } // 计算最终结果 def evaluate(buffer: Row): Any = { buffer.getDouble(0) / buffer.getLong(1) } } val avgUDAF = new AvgUDAF() val df = Seq(1.0, 2.0, 3.0, 4.0).toDF("value") df.agg(avgUDAF($"value")).show() ``` 3. UDTF(User-Defined Table-Generating Function):用户自定义表生成函数,可以将一个或多个输入参数转换为一个表。例如,我们可以定义一个UDTF来将一个字符串拆分成多个单词: ``` import org.apache.spark.sql.Row import org.apache.spark.sql.expressions.UserDefinedFunction import org.apache.spark.sql.functions.{explode, udf} import org.apache.spark.sql.types._ class SplitUDTF extends UserDefinedFunction { // 输入数据类型 def inputSchema: StructType = StructType(StructField("value", StringType) :: Nil) // 输出数据类型 def dataType: DataType = ArrayType(StringType) // 是否是确定性的 def deterministic: Boolean = true // 计算结果 def apply(value: Row): Any = { value.getString(0).split(" ") } } val splitUDTF = udf(new SplitUDTF(), ArrayType(StringType)) val df = Seq("hello world", "spark sql").toDF("value") df.select(explode(splitUDTF($"value"))).show() ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值