Как отключить Spark DataFrame без жесткого кодирования имен столбцов в Scala? - PullRequest
0 голосов
/ 28 марта 2020

Предположим, у вас есть

val df = Seq(("Jack", 91, 86), ("Mike", 79, 85), ("Julia", 93, 70)).toDF("Name", "Maths", "Art")

, что дает:

+-----+-----+---+
| Name|Maths|Art|
+-----+-----+---+
| Jack|   91| 86|
| Mike|   79| 85|
|Julia|   93| 70|
+-----+-----+---+

Теперь вы хотите отменить его:

df.select($"Name", expr("stack(2, 'Maths', Maths, 'Art', Art) as (Subject, Score)"))

, что дает:

+-----+-------+-----+
| Name|Subject|Score|
+-----+-------+-----+
| Jack|  Maths|   91|
| Jack|    Art|   86|
| Mike|  Maths|   79|
| Mike|    Art|   85|
|Julia|  Maths|   93|
|Julia|    Art|   70|
+-----+-------+-----+

Пока, черт возьми! А что если вы не знаете список имен столбцов? Что делать, если список имен столбцов длинный или может измениться? Как мы можем избежать жесткого кодирования имен столбцов тупо, как это?

Или даже что-то вроде этого тоже хорошо:

// fake code
df.select($"Name", unpivot(df.columns.diff("Name")) as ("Subject", "Score"))

Почему у нас нет API, как это?

Ответы [ 2 ]

0 голосов
/ 29 марта 2020

Это работает довольно хорошо:

def melt(preserves: Seq[String], toMelt: Seq[String], column: String = "variable", row: String = "value", df: DataFrame) : DataFrame = {
    val _vars_and_vals = array((for (c <- toMelt) yield { struct(lit(c).alias(column), col(c).alias(row)) }): _*)
    val _tmp = df.withColumn("_vars_and_vals", explode(_vars_and_vals))
    val cols = preserves.map(col _) ++ { for (x <- List(column, row)) yield { col("_vars_and_vals")(x).alias(x) }}
    _tmp.select(cols: _*)
}

Источник: Как расплавить Spark DataFrame? Спасибо @ user10938362

0 голосов
/ 28 марта 2020

Используя .mkString мульти разделитель, мы можем создать выражение и использовать его в выражении.

Пример:

df.show()
//+-----+-----+---+
//| Name|Maths|Art|
//+-----+-----+---+
//| Jack|   91| 86|
//| Mike|   79| 85|
//|Julia|   93| 70|
//+-----+-----+---+

//filtering required cols
val cols=df.columns.filter(_.toLowerCase != "name")

//defining alias cols string
val alias_cols="Subject,Score"

//mkString with 3 seperators
val stack_exp=cols.map(x => s"""'${x}',${x}""").mkString(s"stack(${cols.size},",",",s""") as (${alias_cols})""")

df.select($"Name", expr(s"""${stack_exp}""")).show()
//+-----+-------+-----+
//| Name|Subject|Score|
//+-----+-------+-----+
//| Jack|  Maths|   91|
//| Jack|    Art|   86|
//| Mike|  Maths|   79|
//| Mike|    Art|   85|
//|Julia|  Maths|   93|
//|Julia|    Art|   70|
//+-----+-------+-----+
...