Explicação detalhada da função da janela de faísca (janela)

Endereço do github do projeto: bitcarmanlee fácil-algoritmo-entrevista-e-prática
muitas vezes traz mensagens privadas aos alunos ou deixa mensagens para fazer perguntas relacionadas, número V bitcarmanlee. Colegas da estrela no github, dentro do escopo de minha habilidade e tempo, farei o possível para ajudá-los a responder a perguntas relacionadas e fazer progressos juntos.

1. Por que você precisa de uma função de janela

Antes de 1.4, o Spark SQL oferecia suporte a dois tipos de funções para calcular um único valor de retorno. O primeiro é uma função interna ou função UDF, eles pegam o valor em uma única linha como entrada e geram um único valor de retorno para cada linha de entrada. A outra são funções de agregação, normalmente SUM, MAX, AVG, que operam em um grupo de dados de linha e calculam um valor de retorno para cada grupo.

As duas funções mencionadas acima são amplamente utilizadas na prática, mas ainda há um grande número de operações que não podem ser expressas apenas por esses tipos de funções. Um dos cenários mais comuns é que muitas vezes você precisa operar em um conjunto de linhas e ainda retornar um valor para cada linha de entrada.Os dois métodos acima são inúteis. Por exemplo, é muito difícil calcular a média móvel, calcular a soma cumulativa ou acessar o valor da linha que aparece antes da linha atual. Felizmente, após 1.4, o Spark SQL fornece funções de janela para compensar as deficiências acima.

O núcleo da função da janela é "Quadro", ou nós o chamamos diretamente de quadro, um quadro é uma série de dados multilinhas ou muitos grupos. Então, podemos basicamente agrupá-los para atender às funções que as funções comuns acima não podem completar. Para ver o aplicativo específico com clareza, examinamos diretamente o exemplo. Falar é barato, mostre-me o código.

2. Construir o conjunto de dados

Para facilitar o teste, primeiro construímos um conjunto de dados

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()
 }

Depois de executar o método de teste acima localmente, a saída é a seguinte

+------+-------+-----+
|  name|subject|score|
+------+-------+-----+
|  lili|     ml|   90|
|  lucy|     ml|   85|
|cherry|     ml|   80|
| terry|     ml|   85|
| tracy|     cs|   82|
|  tony|     cs|   86|
|   tom|     cs|   75|
+------+-------+-----+

Estrutura de dados concluída

3. Veja as classificações em grupos

Um cenário frequentemente utilizado é: é preciso verificar a classificação de cada major, esse é um problema típico de agrupamento, ou seja, quando a função janela mostra seus talentos.

Uma janela precisa definir três partes:

1. Problema de agrupamento, como agrupar linhas? Ao selecionar os dados da janela, isso só tem efeito sobre os dados do grupo
2. Classificação, como classificar? Ao selecionar os dados da janela, eles serão primeiro classificados de acordo com o método especificado
3. Seleção de quadros, com base no comportamento atual, como selecionar as linhas adjacentes?

Em comparação com as três partes acima, a sintaxe da função de janela é geralmente:

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)] )

Entre eles,
window_func é a função de janela
over, o que significa que esta é uma função de janela de
partição por corresponde ao agrupamento, ou seja, de acordo com qual agrupamento de colunas,
ordenar por corresponde à classificação e quais colunas de classificação de
linhas correspondem à seleção de quadro correspondente .

O window_func no spark inclui as três categorias a seguir:
1. A função de classificação (função de classificação) inclui classificação, dense_rank, row_number, percent_rank, ntile, etc. Vamos examiná-la com exemplos mais tarde.
2. As funções analíticas incluem cume_dist, lag, etc.
3. As funções de agregação (funções de agregação) são nossos max, min, sum, avg comumente usados, etc.

Volte para os requisitos acima e verifique a classificação de cada um dos principais

  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()
  }

O código acima é executado e os resultados são os seguintes

+------+-------+-----+----+
|  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|
+------+-------+-----+----+

Concentre-se na parte da janela:

rank() over (partition by subject order by score desc) as rank

A função rank () significa obter a classificação de cada linha no grupo, partição por assunto significa agrupar por assunto e ordem por pontuação desc significa classificar por pontuação e em ordem inversa, de modo que você possa obter a classificação de cada estudante neste curso!

Row_number e dense_rank também são funções de janela relacionadas à classificação. Vamos ver a diferença entre eles por meio de exemplos:

  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|
+------+-------+-----+----------+

Por meio do exemplo acima, não é difícil ver a diferença entre os três: a
classificação gera números de sequência descontínuos. O exemplo acima é 1,2,2,4. Este
dense_rank gera números de sequência contínuos. O exemplo acima é 1,2, 2,3. Este tipo de
row_number, como o nome sugere, gera um número de linha. O exemplo acima é 1,2,3,4.
Não se preocupe com a definição da função, basta olhar o exemplo acima para entender!

4. Ver quantis

Vejamos outro exemplo, queremos verificar o quantil de uma determinada pessoa na profissão, o que devemos fazer?
Neste momento, a função cume_dist pode ser usada.
O método de cálculo desta função é: o número de linhas no grupo que é menor ou igual ao valor da linha atual / o número total de linhas no grupo

Ainda olha o código

    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()

Combinando o código de inicialização de dados anterior com a lógica sql acima, o resultado final é o seguinte:

+------+-------+-----+------------------+
|  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|
+------+-------+-----+------------------+

Pode-se verificar que os requisitos acima são perfeitamente atendidos.

5. Use DataFrame API para completar a consulta da janela

O exemplo acima usa a API SqlContext. No DataFrame, existem APIs correspondentes para completar a consulta. O método específico também é muito simples. Você pode usar a API DataFrame para chamar o método over () nas funções suportadas, como rank ( ). over (...)

Pegue o requisito anterior como exemplo. Se quisermos ver a classificação dos alunos nos cursos, use a API DataFrame da seguinte maneira:

  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()
  }

O resultado é o seguinte:

+------+-------+-----+----+
|  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|
+------+-------+-----+----+

Acho que você gosta

Origin blog.csdn.net/bitcarmanlee/article/details/113617901
Recomendado
Clasificación