プロジェクトのgithubアドレス:bitcarmanlee easy-algorithm-interview-and-practiceには、
多くの場合、学生にプライベートメッセージを送信したり、関連する質問をするためのメッセージを残したりします。V番号bitcarmanlee。githubのスターのクラスメートは、私の能力と時間の範囲内で、あなたが関連する質問に答え、一緒に進歩するのを助けるために最善を尽くします。
1.なぜウィンドウ関数が必要なのですか
1.4より前のバージョンでは、SparkSQLは単一の戻り値を計算するために2種類の関数をサポートしていました。1つは組み込み関数またはUDF関数で、単一行の値を入力として受け取り、入力行ごとに単一の戻り値を生成します。もう1つは集計関数で、通常はSUM、MAX、AVGで、行データのグループを操作し、各グループの戻り値を計算します。
上記の2つの機能は実際に広く使われていますが、これらの機能だけでは表現できない操作がまだたくさんあります。最も一般的なシナリオの1つは、行のセットを操作し、それでも各入力行の値を返す必要がある場合です。上記の2つの方法は役に立ちません。たとえば、移動平均の計算、累積合計の計算、または現在の行の前に表示される行の値へのアクセスは非常に困難です。幸い、1.4以降、SparkSQLは上記の欠点を補うためのウィンドウ関数を提供します。
ウィンドウ関数の中核は「フレーム」です。これを直接フレームと呼びます。フレームは一連の複数行データ、または多数のグループです。そうすれば、基本的にこれらをグループ化して、上記の通常の機能では完了できない機能を満たすことができます。特定のアプリケーションを明確に確認するために、例を直接見ていきます。話は安いです、コードを見せてください。
2.データセットを作成します
テストを容易にするために、最初にデータセットを作成します
import org.apache.spark.SparkConf
import org.apache.spark.sql.{Row, SparkSession}
import org.apache.spark.sql.functions._
def test() = {
val sparkConf = new SparkConf().setMaster("local[2]")
val spark = SparkSession.builder().config(sparkConf).getOrCreate()
val data = Array(("lili", "ml", 90),
("lucy", "ml", 85),
("cherry", "ml", 80),
("terry", "ml", 85),
("tracy", "cs", 82),
("tony", "cs", 86),
("tom", "cs", 75))
val schemas = Seq("name", "subject", "score")
val df = spark.createDataFrame(data).toDF(schemas: _*)
df.show()
}
上記のテストメソッドをローカルで実行すると、出力は次のようになります。
+------+-------+-----+
| name|subject|score|
+------+-------+-----+
| lili| ml| 90|
| lucy| ml| 85|
|cherry| ml| 80|
| terry| ml| 85|
| tracy| cs| 82|
| tony| cs| 86|
| tom| cs| 75|
+------+-------+-----+
データ構造が完成しました
3.グループでランキングを表示する
よく使用されるシナリオは次のとおりです。各メジャーのランキングを確認する必要があります。これは典型的なグループ化の問題です。つまり、ウィンドウ関数がその才能を示している場合です。
ウィンドウは3つの部分を定義する必要があります。
1.グループ化の問題、行をグループ化する方法は?ウィンドウデータを選択すると、グループ内のデータにのみ有効になります
。2。並べ替え、どのように並べ替えますか?ウィンドウデータを選択するとき、最初に指定された方法に従ってソートされます。3
。フレームの選択、現在の動作に基づいて、周囲の行を選択する方法は?
上記の3つの部分と比較すると、ウィンドウ関数の構文は一般的に次のとおりです。
window_func(args) OVER ( [PARTITION BY col_name, col_name, ...] [ORDER BY col_name, col_name, ...] [ROWS | RANGE BETWEEN (CURRENT ROW | (UNBOUNDED |[num]) PRECEDING) AND (CURRENT ROW | ( UNBOUNDED | [num]) FOLLOWING)] )
その中で、
window_funcはウィンドウ関数
overです。つまり、これは
グループ化に対応するウィンドウ関数パーティションです。つまり、どの列のグループ化、
order byは並べ替えに対応し、どの列の並べ替え
行は対応するフレーム選択に対応します。 。
sparkのwindow_funcには、次の3つのカテゴリが含まれ
ます。1。ランキング関数(ランキング関数)には、rank、dense_rank、row_number、percent_rank、ntileなどが含まれます。後で例を挙げて見ていきます。
2.分析関数には、cume_dist、lagなどが含まれます。
3.集計関数(集計関数)は、一般的に使用されるmax、min、sum、avgなどです。
上記の要件に戻り、各メジャーのランキングを確認してください
def test() = {
val sparkConf = new SparkConf().setMaster("local[2]")
val spark = SparkSession.builder().config(sparkConf).getOrCreate()
val sqlContext = spark.sqlContext
val data = Array(("lili", "ml", 90),
("lucy", "ml", 85),
("cherry", "ml", 80),
("terry", "ml", 85),
("tracy", "cs", 82),
("tony", "cs", 86),
("tom", "cs", 75))
val schemas = Seq("name", "subject", "score")
val df = spark.createDataFrame(data).toDF(schemas: _*)
df.createOrReplaceTempView("person_subject_score")
val sqltext = "select name, subject, score, rank() over (partition by subject order by score desc) as rank from person_subject_score";
val ret = sqlContext.sql(sqltext)
ret.show()
}
上記のコードが実行され、結果は次のようになります
+------+-------+-----+----+
| name|subject|score|rank|
+------+-------+-----+----+
| tony| cs| 86| 1|
| tracy| cs| 82| 2|
| tom| cs| 75| 3|
| lili| ml| 90| 1|
| lucy| ml| 85| 2|
| terry| ml| 85| 2|
|cherry| ml| 80| 4|
+------+-------+-----+----+
ウィンドウ部分に焦点を当てます。
rank() over (partition by subject order by score desc) as rank
rank()関数は、グループ内の各行のランクを取得することを意味し、サブジェクトごとにパーティション化することはサブジェクトごとにグループ化することを意味し、スコアによる順序は、スコアごとに逆の順序で並べ替えることを意味します。これにより、それぞれのランクを取得できます。この専攻の学生!
Row_numberとdense_rankも、並べ替えに関連するウィンドウ関数です。例を使用して、これらの違いを見てみましょう。
def test() = {
val sparkConf = new SparkConf().setMaster("local[2]")
val spark = SparkSession.builder().config(sparkConf).getOrCreate()
val sqlContext = spark.sqlContext
val data = Array(("lili", "ml", 90),
("lucy", "ml", 85),
("cherry", "ml", 80),
("terry", "ml", 85),
("tracy", "cs", 82),
("tony", "cs", 86),
("tom", "cs", 75))
val schemas = Seq("name", "subject", "score")
val df = spark.createDataFrame(data).toDF(schemas: _*)
df.createOrReplaceTempView("person_subject_score")
val sqltext = "select name, subject, score, rank() over (partition by subject order by score desc) as rank from person_subject_score";
val ret = sqlContext.sql(sqltext)
ret.show()
val sqltext2 = "select name, subject, score, row_number() over (partition by subject order by score desc) as row_number from person_subject_score";
val ret2 = sqlContext.sql(sqltext2)
ret2.show()
val sqltext3 = "select name, subject, score, dense_rank() over (partition by subject order by score desc) as dense_rank from person_subject_score";
val ret3 = sqlContext.sql(sqltext3)
ret3.show()
}
+------+-------+-----+----+
| name|subject|score|rank|
+------+-------+-----+----+
| tony| cs| 86| 1|
| tracy| cs| 82| 2|
| tom| cs| 75| 3|
| lili| ml| 90| 1|
| lucy| ml| 85| 2|
| terry| ml| 85| 2|
|cherry| ml| 80| 4|
+------+-------+-----+----+
+------+-------+-----+----------+
| name|subject|score|row_number|
+------+-------+-----+----------+
| tony| cs| 86| 1|
| tracy| cs| 82| 2|
| tom| cs| 75| 3|
| lili| ml| 90| 1|
| lucy| ml| 85| 2|
| terry| ml| 85| 3|
|cherry| ml| 80| 4|
+------+-------+-----+----------+
+------+-------+-----+----------+
| name|subject|score|dense_rank|
+------+-------+-----+----------+
| tony| cs| 86| 1|
| tracy| cs| 82| 2|
| tom| cs| 75| 3|
| lili| ml| 90| 1|
| lucy| ml| 85| 2|
| terry| ml| 85| 2|
|cherry| ml| 80| 3|
+------+-------+-----+----------+
上記の例を通して、3つの違いを確認することは難しくありません:
rankは不連続なシーケンス番号を生成します。上記の例は1,2,2,4です。この
dense_rankは連続したシーケンス番号を生成します。上記の例は1,2、 2,3。この種の
row_numberは、その名前が示すように、行番号を生成します。上記の例は1,2,3,4です。
関数の定義について心配する必要はありません。上記の例を見て理解してください。
4.分位数を表示する
別の例を見てみましょう。職業の特定の人の分位数を確認したいのですが、どうすればよいですか?
この時点で、cume_dist関数を使用できます。
この関数の計算方法は次のとおりです。現在の行の値以下のグループ内の行数/グループ内の行の総数
まだコードを見てください
val sqltext5 = "select name, subject, score, cume_dist() over (partition by subject order by score desc) as cumedist from person_subject_score";
val ret5 = sqlContext.sql(sqltext5)
ret5.show()
前のデータ初期化コードを上記のSQLロジックと組み合わせると、最終的な結果は次のようになります。
+------+-------+-----+------------------+
| name|subject|score| cumedist|
+------+-------+-----+------------------+
| tony| cs| 86|0.3333333333333333|
| tracy| cs| 82|0.6666666666666666|
| tom| cs| 75| 1.0|
| lili| ml| 90| 0.25|
| lucy| ml| 85| 0.75|
| terry| ml| 85| 0.75|
|cherry| ml| 80| 1.0|
+------+-------+-----+------------------+
上記の要件が完全に満たされていることがわかります。
5. DataFrameAPIを使用してウィンドウクエリを完了します
上記の例では、SqlContext APIを使用しています。DataFrameには、クエリを完了するための対応するAPIがあります。特定のメソッドも非常に単純です。DataFrameAPIを使用して、rank(などのサポートされている関数でover()メソッドを呼び出すことができます。 ).over(…)
前の要件を例として取り上げます。専攻の学生のランキングを表示する場合は、次のようにDataFrameAPIを使用します。
def test() = {
val sparkConf = new SparkConf().setMaster("local[2]")
val spark = SparkSession.builder().config(sparkConf).getOrCreate()
val data = Array(("lili", "ml", 90),
("lucy", "ml", 85),
("cherry", "ml", 80),
("terry", "ml", 85),
("tracy", "cs", 82),
("tony", "cs", 86),
("tom", "cs", 75))
val schemas = Seq("name", "subject", "score")
val df = spark.createDataFrame(data).toDF(schemas: _*)
df.createOrReplaceTempView("person_subject_score")
val window = Window.partitionBy("subject").orderBy(col("score").desc)
val df2 = df.withColumn("rank", rank().over(window))
df2.show()
}
出力は次のとおりです。
+------+-------+-----+----+
| name|subject|score|rank|
+------+-------+-----+----+
| tony| cs| 86| 1|
| tracy| cs| 82| 2|
| tom| cs| 75| 3|
| lili| ml| 90| 1|
| lucy| ml| 85| 2|
| terry| ml| 85| 2|
|cherry| ml| 80| 4|
+------+-------+-----+----+