首页 > 解决方案 > 使用scala将DataFrame单行转换为Spark中的列

问题描述

我在这里看到了这个问题: Transpose DataFrame without Aggregation in Spark with scala,我想做完全相反的事情。

我有一个单行数据框,其值为字符串、整数、布尔值、数组:

+-----+-------+-----+------+-----+
|col1 | col2  |col3 | col4 |col5 |
+-----+-------+-----+------+-----+
|val1 | val2  |val3 | val4 |val5 |
+-----+-------+-----+------+-----+

我想像这样转置它:

+-----------+-------+
|Columns    | values|
+-----------+-------+
|col1       | val1  |
|col2       | val2  |
|col3       | val3  |
|col4       | val4  |
|col5       | val5  |
+-----------+-------+

我正在使用 Apache Spark 2.4.3 和 Scala 2.11

编辑:值可以是任何类型(int、double、bool、array),而不仅仅是字符串。

标签: scaladataframeapache-sparkpysparktranspose

解决方案


不使用的想法不同arrays_zip(在=> Spark 2.4中可用)]并得到以下...

它将以更简单的方式(和函数)适用于 Spark = flatmap> map2.0 explode...

这里map函数(用于与列)创建一个新的地图列。输入列必须分组为键值对。

案例:数据中的字符串数据类型:

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

val df: DataFrame =Seq((("val1"),("val2"),("val3"),("val4"),("val5"))).toDF("col1","col2","col3","col4","col5")

var columnsAndValues = df.columns.flatMap { c => Array(lit(c), col(c)) }
df.printSchema()

df.withColumn("myMap", map(columnsAndValues:_*)).select(explode($"myMap"))
  .toDF("Columns","Values").show(false)

结果 :

root
 |-- col1: string (nullable = true)
 |-- col2: string (nullable = true)
 |-- col3: string (nullable = true)
 |-- col4: string (nullable = true)
 |-- col5: string (nullable = true)

+-------+------+
|Columns|Values|
+-------+------+
|col1   |val1  |
|col2   |val2  |
|col3   |val3  |
|col4   |val4  |
|col5   |val5  |
+-------+------+

案例:数据中数据类型的混合:

如果您有不同的类型将它们转换为字符串...其余步骤不会改变..

val df1 = df.select(df.columns.map(c => col(c).cast(StringType)): _*)

完整示例:

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

val df = Seq(((2), (3), (true), (2.4), ("val"))).toDF("col1", "col2", "col3", "col4", "col5")
df.printSchema()
/**
  * convert all columns to  to string type since its needed further
  */
val df1 = df.select(df.columns.map(c => col(c).cast(StringType)): _*)
df1.printSchema()
var ColumnsAndValues: Array[Column] = df.columns.flatMap { c => {
    Array(lit(c), col(c))
  }
}

df1.withColumn("myMap", map(ColumnsAndValues: _*))
   .select(explode($"myMap"))
   .toDF("Columns", "Values")
   .show(false)

结果 :

root
 |-- col1: integer (nullable = false)
 |-- col2: integer (nullable = false)
 |-- col3: boolean (nullable = false)
 |-- col4: double (nullable = false)
 |-- col5: string (nullable = true)

root
 |-- col1: string (nullable = false)
 |-- col2: string (nullable = false)
 |-- col3: string (nullable = false)
 |-- col4: string (nullable = false)
 |-- col5: string (nullable = true)

+-------+------+
|Columns|Values|
+-------+------+
|col1   |2     |
|col2   |3     |
|col3   |true  |
|col4   |2.4   |
|col5   |val   |
+-------+------+

推荐阅读