如何更新数组列?

时间:2018-06-01 07:01:21

标签: scala apache-spark apache-spark-sql

我知道我们可以替换数据框列中的值,并使用以下方法返回包含更新值的新数据框:

dataframe.withColumn("col1",when(col("col1").equalTo("this"),"that").otherwise(col("make")))

但这会在需要的地方更改整个列值。

现在我有一个稍微复杂的数据框:

|        colleagues|   name|

|[guy1, guy2, guy3]|Thisguy|
|[guy4, guy5, guy6]|Thatguy|
|[guy7, guy8, guy9]|Someguy|

我在这里有一个同事'包含数组的列。我想替换任何数组的特定元素,例如,而不是' guy2'在第一行我想要' guy10'在我的新数据框中 我怎样才能做到这一点?请帮忙。

1 个答案:

答案 0 :(得分:1)

简介

在提供最终解决方案之前有几个问题需要解答(例如在更换一些之后对colleagues数组中元素的排序),但我不想拖延太长时间。让我们来看看解决这类问题的常用方法。

解决方案

由于colleagues列是一个数组列(并且Spark在查询行时非常有效),因此您应首先explode(或posexplode)它。使用每个数组元素行,您可以进行必要的更改,最后collect_list可以返回数组列。

  

爆炸(e:列):列   为给定数组或映射列中的每个元素创建一个新行。

     

posexplode(e:列):列   为具有给定数组或映射列中位置的每个元素创建一个新行。

让我们使用以下names数据集:

val names = Seq((Array("guy1", "guy2", "guy3"), "Thisguy")).toDF("colleagues", "name")
scala> names.show
+------------------+-------+
|        colleagues|   name|
+------------------+-------+
|[guy1, guy2, guy3]|Thisguy|
+------------------+-------+
scala> names.printSchema
root
 |-- colleagues: array (nullable = true)
 |    |-- element: string (containsNull = true)
 |-- name: string (nullable = true)

我们explode,进行更改,最后collect_list

val elements = names.withColumn("elements", explode($"colleagues"))
scala> elements.show
+------------------+-------+--------+
|        colleagues|   name|elements|
+------------------+-------+--------+
|[guy1, guy2, guy3]|Thisguy|    guy1|
|[guy1, guy2, guy3]|Thisguy|    guy2|
|[guy1, guy2, guy3]|Thisguy|    guy3|
+------------------+-------+--------+

这就是Spark SQL可以轻松处理的内容。让我们使用regexp_replace(什么?regexp?!现在你有两个问题:))。

val replaced = elements.withColumn("replaced", regexp_replace($"elements", "guy2", "guy10"))
scala> replaced.show
+------------------+-------+--------+--------+
|        colleagues|   name|elements|replaced|
+------------------+-------+--------+--------+
|[guy1, guy2, guy3]|Thisguy|    guy1|    guy1|
|[guy1, guy2, guy3]|Thisguy|    guy2|   guy10|
|[guy1, guy2, guy3]|Thisguy|    guy3|    guy3|
+------------------+-------+--------+--------+

最后,让我们按初始数组列进行分组并使用collect_list分组功能。

val solution = replaced
  .groupBy($"colleagues" as "before")
  .agg(
    collect_list("replaced") as "after",
    first("name") as "name")
scala> solution.show
+------------------+-------------------+-------+
|            before|              after|   name|
+------------------+-------------------+-------+
|[guy1, guy2, guy3]|[guy1, guy10, guy3]|Thisguy|
+------------------+-------------------+-------+

替代解决方案

用户定义函数(UDF)

或者,您也可以编写一个自定义的用户定义函数,但这不会像上面的解决方案那样受益于许多优化,因此我不推荐它(并且只会在请求时显示)。

自定义逻辑运算符

最好的方法是编写一个自定义逻辑运算符(LogicalPlan)来完成所有这些并参与优化,但避免交换(由groupBy引入)。然而,这是一个相当先进的Spark开发,我还没有完成。