Обработка столбцов Spark SQL

У меня есть набор данных, который ниже столбцов.

df.show();

+--------+---------+---------+---------+---------+
|  Col1  |  Col2   | Expend1 | Expend2 | Expend3 |
+--------+---------+---------+---------+---------+
| Value1 | Cvalue1 |     123 |    2254 |      22 |
| Value1 | Cvalue2 |     124 |    2255 |      23 |
+--------+---------+---------+---------+---------+

Я хочу, чтобы это было изменено на формат ниже, используя некоторые соединения, куб или любые операции.

1.

    +--------+---------+------+
    | Value1 | Cvalue1 |  123 |
    | Value1 | Cvalue1 | 2254 |
    | Value1 | Cvalue1 |   22 |
    | Value1 | Cvalue1 |  124 |
    | Value1 | Cvalue1 | 2255 |
    | Value1 | Cvalue1 |   23 |
    +--------+---------+------+

или лучше, если этот формат

2.

+--------+---------+---------+------+
| Value1 | Cvalue1 | Expend1 |  123 |
| Value1 | Cvalue1 | Expend2 | 2254 |
| Value1 | Cvalue1 | Expend3 |   22 |
| Value1 | Cvalue1 | Expend1 |  124 |
| Value1 | Cvalue1 | Expend2 | 2255 |
| Value1 | Cvalue1 | Expend3 |   23 |
+--------+---------+---------+------+

Могу ли я достичь этого выше двух возможных форматов. Если В случае № 1, могу ли я получить имя столбца последнего значения, будь то Expend1, Expend 2 или Expend3.


person Garry Steve    schedule 27.02.2018    source источник


Ответы (4)


Функции map, а затем explode могут использоваться:

val data = List(
  ("Value1", "Cvalue1", 123, 2254, 22),
  ("Value1", "Cvalue2", 124, 2255, 23)
)
val df = data.toDF("Col1", "Col2", "Expend1", "Expend2", "Expend3")

// action 
val unpivotedColumns = List("Expend1", "Expend2", "Expend3")
val columnMapping = unpivotedColumns.foldLeft(new ArrayBuffer[Column]())((acc, current) => {
  acc += lit(current)
  acc += col(current)
})
val mapped = df.select($"Col1", $"Col2", map(columnMapping: _*).alias("result"))
val result = mapped.select($"Col1", $"Col2", explode($"result"))
result.show(false)

Результат:

+------+-------+-------+-----+
|Col1  |Col2   |key    |value|
+------+-------+-------+-----+
|Value1|Cvalue1|Expend1|123  |
|Value1|Cvalue1|Expend2|2254 |
|Value1|Cvalue1|Expend3|22   |
|Value1|Cvalue2|Expend1|124  |
|Value1|Cvalue2|Expend2|2255 |
|Value1|Cvalue2|Expend3|23   |
+------+-------+-------+-----+
person pasha701    schedule 27.02.2018


Вы можете преобразовать в array для трех столбцов и explode его

import org.apache.spark.sql.functions._

df.withColumn("Expand", explode(array("Expand1", "Expand2", "Expand3")))
  .drop("Expand1", "Expand2", "Expand3")

Чтобы сохранить значение столбца, вы можете сделать, как показано ниже

  data.withColumn("Expand1", concat_ws(":", lit("Expand1"), $"Expand1"))
      .withColumn("Expand2", concat_ws(":", lit("Expand2") , $"Expand2"))
      .withColumn("Expand3", concat_ws(":", lit("Expand3") , $"Expand3"))
      .withColumn("Expand", explode(array("Expand1", "Expand2", "Expand3")))
      .drop("Expand1", "Expand2", "Expand3")
      .withColumn("ExpandColumn", split($"Expand", ":")(0))
      .withColumn("Expand", split($"Expand", ":")(1))
      .drop("Expand1", "Expand2", "Expand3")
    .show(false)

Я надеюсь, что это было полезно

person koiralo    schedule 27.02.2018
comment
Это работает, спасибо, Шанакар, но могу ли я узнать имя элемента столбца Expand для конкретной строки, будь то Expend1, Expend2 или Expend3 - person Garry Steve; 27.02.2018
comment
Вы можете использовать функцию стека, чтобы получить имя столбца, или добавить к нему имя столбца и разделить позже - person koiralo; 27.02.2018

Используя функцию udf, вы можете получить второй требуемый фрейм данных.

val columns = df.select("Expend1","Expend2","Expend3").columns
import org.apache.spark.sql.functions._
def arrayStructUdf = udf((columnNames: collection.mutable.WrappedArray[String], columnValues: collection.mutable.WrappedArray[String]) => columnNames.zip(columnValues).map(x => (x._1, x._2)).toArray)

Затем просто вызовите udf функцию, drop три дополнительных столбца, затем explode новый сформированный столбец и, наконец, select нужные столбцы.

df.withColumn("new", arrayStructUdf(array(columns.map(x => lit(x)): _*), array(columns.map(col): _*)))
    .drop("Expend1","Expend2","Expend3")
    .withColumn("new", explode(col("new")))
    .select("Col1","Col2", "new.*")

У вас должен быть второй требуемый фрейм данных

+------+-------+-------+----+
|Col1  |Col2   |_1     |_2  |
+------+-------+-------+----+
|Value1|Cvalue1|Expend1|123 |
|Value1|Cvalue1|Expend2|2254|
|Value1|Cvalue1|Expend3|22  |
|Value1|Cvalue2|Expend1|124 |
|Value1|Cvalue2|Expend2|2255|
|Value1|Cvalue2|Expend3|23  |
+------+-------+-------+----+
person Ramesh Maharjan    schedule 27.02.2018