Spark DataFrame 列的合并与拆分

1 DataFrame列数据的合并

例如:我们有如下数据,想要将三列数据合并为一列,并以“,”分割

1
2
3
4
5
6
7
+----+---+-----------+
|name|age| phone|
+----+---+-----------+
|Ming| 20|15552211521|
|hong| 19|13287994007|
| zhi| 21|15552211523|
+----+---+-----------+

1.1 使用map方法重写

使用map方法重写就是将DataFrame使用map取值之后,然后使用toSeq方法转成Seq格式,最后使用Seq的foldLeft方法拼接数据,并返回,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
//方法1:利用map重写
val separator = ","
df.map(_.toSeq.foldLeft("")(_ + separator + _).substring(1)).show()

/**
* +-------------------+
* | value|
* +-------------------+
* |Ming,20,15552211521|
* |hong,19,13287994007|
* | zhi,21,15552211523|
* +-------------------+
*/

1.2 使用内置函数concat_ws

合并多列数据也可以使用SparkSQL的内置函数concat_ws()

1
2
3
4
5
6
7
8
9
10
11
12
13
//方法2: 使用内置函数 concat_ws
import org.apache.spark.sql.functions._
df.select(concat_ws(separator, $"name", $"age", $"phone").cast(StringType).as("value")).show()

/**
* +-------------------+
* | value|
* +-------------------+
* |Ming,20,15552211521|
* |hong,19,13287994007|
* | zhi,21,15552211523|
* +-------------------+
*/

1.3 使用自定义UDF函数

自己编写UDF函数,实现多列合并

1
2
3
4
5
6
7
8
9
//方法3:使用自定义UDF函数

// 编写udf函数
def mergeCols(row: Row): String = {
row.toSeq.foldLeft("")(_ + separator + _).substring(1)
}

val mergeColsUDF = udf(mergeCols _)
df.select(mergeColsUDF(struct($"name", $"age", $"phone")).as("value")).show()

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package com.hollysys.spark.sql

import org.apache.spark.sql.{Row, SparkSession}
import org.apache.spark.sql.types.StringType

/**
* DataFrame 合并列
*/
object MergeColsTest {
def main(args: Array[String]): Unit = {
val spark = SparkSession
.builder()
.appName(this.getClass.getSimpleName)
.master("local")
.getOrCreate()

//从内存创建一组DataFrame数据
import spark.implicits._
val df = Seq(("Ming", 20, 15552211521L), ("hong", 19, 13287994007L), ("zhi", 21, 15552211523L))
.toDF("name", "age", "phone")
df.show()
/**
* +----+---+-----------+
* |name|age| phone|
* +----+---+-----------+
* |Ming| 20|15552211521|
* |hong| 19|13287994007|
* | zhi| 21|15552211523|
* +----+---+-----------+
*/
//方法1:利用map重写
val separator = ","
df.map(_.toSeq.foldLeft("")(_ + separator + _).substring(1)).show()

/**
* +-------------------+
* | value|
* +-------------------+
* |Ming,20,15552211521|
* |hong,19,13287994007|
* | zhi,21,15552211523|
* +-------------------+
*/
//方法2: 使用内置函数 concat_ws
import org.apache.spark.sql.functions._
df.select(concat_ws(separator, $"name", $"age", $"phone").cast(StringType).as("value")).show()

/**
* +-------------------+
* | value|
* +-------------------+
* |Ming,20,15552211521|
* |hong,19,13287994007|
* | zhi,21,15552211523|
* +-------------------+
*/
//方法3:使用自定义UDF函数

// 编写udf函数
def mergeCols(row: Row): String = {
row.toSeq.foldLeft("")(_ + separator + _).substring(1)
}

val mergeColsUDF = udf(mergeCols _)
df.select(mergeColsUDF(struct($"name", $"age", $"phone")).as("value")).show()

/**
* /**
* * +-------------------+
* * | value|
* * +-------------------+
* * |Ming,20,15552211521|
* * |hong,19,13287994007|
* * | zhi,21,15552211523|
* * +-------------------+
**/
*/
}
}

2 DataFrame列数据的拆分

上面我们将DataFrame的多列数据合并为一列如下所示,有时候我们也需要将单列数据,以某种拆分规则,拆分为多列。下面提供几种将一列拆分为多列的方法。

1
2
3
4
5
6
7
+-------------------+
| value|
+-------------------+
|Ming,20,15552211521|
|hong,19,13287994007|
| zhi,21,15552211523|
+-------------------+

2.1 使用内置函数split,然后遍历添加列

该方法,先利用内置函数split将单列的数据拆分,然后遍历使用getItem(角标)方法获取拆分后的数据,依次使用withColumn方法添加新列,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//方法1: 使用内置函数split,然后遍历添加列
val separator = ","
lazy val first = df.first()

val numAttrs = first.toString().split(separator).length
val attrs = Array.tabulate(numAttrs)(n => "col_" + n)
//按指定分隔符拆分value列,生成splitCols列
var newDF = df.withColumn("splitCols", split($"value", separator))
attrs.zipWithIndex.foreach(x => {
newDF = newDF.withColumn(x._1, $"splitCols".getItem(x._2))
})
newDF.show()
/**
* +-------------------+--------------------+-----+-----+-----------+
* | value| splitCols|col_0|col_1| col_2|
* +-------------------+--------------------+-----+-----+-----------+
* |Ming,20,15552211521|[Ming, 20, 155522...| Ming| 20|15552211521|
* |hong,19,13287994007|[hong, 19, 132879...| hong| 19|13287994007|
* | zhi,21,15552211523|[zhi, 21, 1555221...| zhi| 21|15552211523|
* +-------------------+--------------------+-----+-----+-----------+
*/

2.2 使用UDF函数创建多列数据,然后合并

该方法是使用udf函数,生成多个列,然后合并到原来的数据。该方法参考了VectorDisassembler(与spark ml官网提供的VectorAssembler相反),这是一个第三方的spark ml向量拆分算法,该方法github地址:https://github.com/jamesbconner/VectorDisassembler。
代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//方法2:使用udf函数创建多列,然后合并
val attributes: Array[Attribute] = {
val numAttrs = first.toString().split(separator).length
//生成attributes
Array.tabulate(numAttrs)(i => NumericAttribute.defaultAttr.withName("value" + "_" + i))
}
//创建多列数据
val fieldCols = attributes.zipWithIndex.map(x => {
val assembleFunc = udf {
str: String =>
str.split(separator)(x._2)
}
assembleFunc(df("value").cast(StringType)).as(x._1.name.get, x._1.toMetadata())
})
//合并数据
df.select(col("*") +: fieldCols: _*).show()

/**
* +-------------------+-------+-------+-----------+
* | value|value_0|value_1| value_2|
* +-------------------+-------+-------+-----------+
* |Ming,20,15552211521| Ming| 20|15552211521|
* |hong,19,13287994007| hong| 19|13287994007|
* | zhi,21,15552211523| zhi| 21|15552211523|
* +-------------------+-------+-------+-----------+
*/

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package com.hollysys.spark.sql

import org.apache.spark.ml.attribute.{Attribute, NumericAttribute}
import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.types.StringType

/**
* 拆分列
*/
object SplitColTest {
def main(args: Array[String]): Unit = {
val spark = SparkSession
.builder()
.appName(this.getClass.getSimpleName)
.master("local")
.getOrCreate()

//从内存中创建DataFrame
import spark.implicits._
val df = Seq("Ming,20,15552211521", "hong,19,13287994007", "zhi,21,15552211523")
.toDF("value")
df.show()

/**
* +-------------------+
* | value|
* +-------------------+
* |Ming,20,15552211521|
* |hong,19,13287994007|
* | zhi,21,15552211523|
* +-------------------+
*/

import org.apache.spark.sql.functions._
//方法1: 使用内置函数split,然后遍历添加列
val separator = ","
lazy val first = df.first()

val numAttrs = first.toString().split(separator).length
val attrs = Array.tabulate(numAttrs)(n => "col_" + n)
//按指定分隔符拆分value列,生成splitCols列
var newDF = df.withColumn("splitCols", split($"value", separator))
attrs.zipWithIndex.foreach(x => {
newDF = newDF.withColumn(x._1, $"splitCols".getItem(x._2))
})
newDF.show()

/**
* +-------------------+--------------------+-----+-----+-----------+
* | value| splitCols|col_0|col_1| col_2|
* +-------------------+--------------------+-----+-----+-----------+
* |Ming,20,15552211521|[Ming, 20, 155522...| Ming| 20|15552211521|
* |hong,19,13287994007|[hong, 19, 132879...| hong| 19|13287994007|
* | zhi,21,15552211523|[zhi, 21, 1555221...| zhi| 21|15552211523|
* +-------------------+--------------------+-----+-----+-----------+
*/

//方法2:使用udf函数创建多列,然后合并
val attributes: Array[Attribute] = {
val numAttrs = first.toString().split(separator).length
//生成attributes
Array.tabulate(numAttrs)(i => NumericAttribute.defaultAttr.withName("value" + "_" + i))
}
//创建多列数据
val fieldCols = attributes.zipWithIndex.map(x => {
val assembleFunc = udf {
str: String =>
str.split(separator)(x._2)
}
assembleFunc(df("value").cast(StringType)).as(x._1.name.get, x._1.toMetadata())
})
//合并数据
df.select(col("*") +: fieldCols: _*).show()

+-------------------+-------+-------+-----------+
| value|value_0|value_1| value_2|
+-------------------+-------+-------+-----------+
|Ming,20,15552211521| Ming| 20|15552211521|
|hong,19,13287994007| hong| 19|13287994007|
| zhi,21,15552211523| zhi| 21|15552211523|
+-------------------+-------+-------+-----------+

}
}
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2018-2020 丁振莹
  • 访问人数: | 浏览次数:

你的每一分支持,是我努力下去的最大的力量 ٩(๑❛ᴗ❛๑)۶

支付宝
微信