ユーザ定義の集約関数
強く型付けされたデータセットと弱く型付けされたデータフレームのような、関連する集計機能を提供する COUNT()、COUNTDISTINCT()、AVG()、MAX()、分()。また、ユーザーが独自のカスタム集計関数を設定することができます。
弱いユーザ定義集約関数型
継承によってUserDefinedAggregateFunctionユーザー定義集計関数を達成しました。次のショーカスタムの平均年齢は、集計関数を要求します。
まず、クラスのカスタム集計関数を作成し、それは抽象クラスUserDefinedAggregateFunctionを継承し、その抽象メソッドを実装します。
パッケージsparksql.udf インポートorg.apache.spark.sql.Rowの インポートorg.apache.spark.sql.expressions。{MutableAggregationBuffer、UserDefinedAggregateFunction} インポートorg.apache.spark.sql.types。{データ型、DoubleType、LongType、StructType} クラスMyAvgは延びUserDefinedAggregateFunctionが{ // 输入数据结构 オーバーライド DEF inputSchemaを:StructType = { 新しい StructType()(追加。" 年齢" 、LongType) } // 缓冲区数据结构 オーバーライド bufferSchema DEF:StructType = { 新しいStructType() .add (「合計」、LongTypeは)(.add " COUNT " 、LongType) } // 出力データを入力し 、オーバーライドデータ型=:DEFデータ型をDoubleType //が安定している、同じ入力が常に同じ出力与える オーバーライド DEF DETERMINISTIC:=ブールをtrueに / * * *バッファの初期化は、次の要件: *適用すること契約万一、2つのバッファをONマージ機能BEの必要がありますだけで初期 *初期バッファリターン自体、IE * `マージ(initialBuffer、initialBuffer)` `万一等しいinitialBuffer * * / オーバーライド DEF初期化(緩衝液:MutableAggregationBuffer):単位= { バッファ(0)= 0L ; 緩衝液(1)= 0L ; } // データを更新する方法バッファ到達 オーバーライド DEF更新(緩衝液:MutableAggregationBuffer、INPUT:行)単位= { バッファ(0)= buffer.getLong(0) input.getLong +(0 ) (緩衝液1)buffer.getLong =(1)+ 1L } // 二つのバッファをマージする方法 // にバッファ1 `バックバッファ更新された値マージ二バッファ凝集ストア オーバーライド DEFマージ(バッファ1 :MutableAggregationBuffer、バッファ2:行)単位= { バッファ1( 0)= buffer1.getLong(0)+ buffer2.getLong(0 ) バッファ1(1)= buffer1.getLong(1)+ buffer2.getLongは、(1 ) } / * この[[UserDefinedAggregateFunction]の最終結果を計算します与えられたに基づいて 集約バッファ。 * / オーバーライド DEF評価(バッファ:行):任意= { buffer.getLong(0).toDouble / buffer.getLong(1 ) } }
その後、テストするには、SQL文の抽象クラスを使用して、インスタンスを抽象クラスのインスタンスを作成し、登録します。
パッケージsparksql.udf インポートorg.apache.spark.SparkConfの インポートorg.apache.spark.sql {DATAFRAME、SparkSession}。 オブジェクトDEMO1 { DEFメイン(引数:配列[文字列]):単位 = { // 创建SparkConf()并设置アプリ名称 ヴァルCONF = 新。SparkConf()setAppName(" sparlsql ").setMaster(" ローカル[*] " ) ヴァルスパーク = 。SparkSession.builder()設定(CONF).getOrCreate() ヴァルuserDF:データフレーム =火花.read.json(" E:/idea/spark3/in/user.json " ) userDF.createOrReplaceTempView("ユーザー" ) // 集約関数のインスタンスを作成 ヴァルmyavg = 新新myavg() // 登録集約関数 spark.udf.register(" udfavg "myavg) // 集約関数 spark.sql(" SELECT udfavg(年齢)からユーザー" ).SHOW } }
次のように印刷結果は以下のとおりです。
+ ---------- +
| myavg(年齢)|
+ ---------- +
| 21.0 |
+ ---------- +
ユーザー定義型を強く集約関数
継承によって強い型付けのカスタム集計関数へのアグリゲータ、同じ平均賃金。
まず、抽象クラスで抽象メソッドの実装を特徴とするカスタム集約クラスと継承アグリゲータを作成すると、
パッケージsparksql.udf 輸入org.apache.spark.sql {エンコーダ、エンコーダ}。 輸入org.apache.spark.sql.expressions.Aggregator ケース クラスUserBeanの(名前:文字列、年齢:ロング) ケース クラスバッファ(VARの合計:ロング、VaRの数:ロング) クラスMyAvg2はアグリゲータ[UserBeanの、バッファ、ダブル] {延び // 定义バッファ的初始值 // この集約のためのAゼロ値を。特性を満足することが任意B +ゼロ= B オーバーライド DEFゼロ:緩衝液=バッファ(0、0 ) / * 計算するときルール定義データ到着 新しい値を生成するために2つの値を結合します。パフォーマンスのために、機能は変更することが'B'と それを返す代わりにBのための新しいオブジェクトを構築します。 * / オーバーライド(:緩衝液:UserBeanのB)=バッファDEF減らす{ b.sum = b.sum + a.age b.count = b.count + 1リットル B } // 合并 オーバーライドバッファ、B2:DEFマージ(B1を:緩衝液):緩衝液= { b1.sum = b1.sum + b2.sum b1.count = b1.count + b2.count B1 } // 最终的结果 オーバーライド DEFフィニッシュ(還元:緩衝液):ダブル= { reduction.sum。 toDouble / reduction.count } オーバーライド bufferEncoder DEF:エンコーダ[緩衝液] = Encoders.product オーバーライド outputEncoder DEF:エンコーダ[二] = Encoders.scalaDouble }
アグリゲータジェネリック型には、以下の意味を有します:
以下の場合:入力データ型
BUF:バッファのデータ型
OUT:出力データタイプ
入力タイプ@tparam について集計。 * @tparam BUF還元の中間の値のタイプ。 * 最終的な出力結果のタイプをOUT @tparam。 * @since 1.6。0 * / 抽象 クラスアグリゲータ[-IN、BUF、OUT]はシリアライズを{延び
その後、カスタムクラスのインスタンスを作成し、彼はこのときの選択方法で使用TypedColumnタイプに変換:
パッケージsparksql.udf インポートorg.apache.spark.SparkConfの インポートorg.apache.spark.sql {データフレーム、データセット、SparkSession、TypedColumn}。 オブジェクトDEMO2 { DEFメイン(引数:配列[文字列]):単位 = { // 创建SparkConf()并设置アプリ名称 ヴァルCONF = 新しい SparkConf()。setAppName(" sparlsql ").setMaster(" ローカル[*] " ) ヴァルスパーク = SparkSession.builder()設定(CONF).getOrCreate() ヴァルuserDF :データフレーム = spark.read.json(" E:/idea/spark3/in/user.json " ) 輸入spark.implicits._ userDSヴァル:データセット[UserBeanの] = userDF AS [UserBeanの] // クラスのインスタンスを作成MyAvg2 ヴァルmyavg2 = 新しい新しいMyAvg2() // この場合は、変換TypedColumnタイプの一例である TypedColumn [UserBeanの、ダブル:ヴァルudfavg ] = myavg2 .toColumn.name(" myavg " ) // 利用 userDS。SELECT (udfavg).SHOW spark.stop() } }
ピット:
彼は数値型に会ったときspark.read.jsonウェイにデータフレームを作成し、システムが自動的にBIGINTとして扱われます。あなたはint型に入れ、これらの変数に従っている場合は、例外がスローされます。
スレッド「メイン」org.apache.spark.sql.AnalysisExceptionの例外:それは切り捨てることと、最大intにBIGINTから `age`をキャストすることはできません。
たとえば、この場合、クラスUserBeanのを宣言します。
ケースの クラス UserBeanの(名前:文字列、年齢:INT)
このようにデータフレームとデータセットを作成します。
ヴァルuserDF:DATAFRAME = spark.read.json( "E:/idea/spark3/in/user.json" ) インポートspark.implicits._ ヴァルuserDS:データセット[UserBeanの] = userDF.as [UserBeanの】
例外が発生します。
だから、長い間使用したり、受信BIGINT。