处理Spark UDF中的所有列/整行

时间:2018-03-22 17:14:04

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

对于包含字符串和数字数据类型混合的数据框,目标是创建一个新的features列,其中包含所有这些数据的minhash

虽然可以通过执行dataframe.toRDD来完成此操作,但在下一步将简单地将RDD 返回转换为数据框时,这样做会很昂贵。< / p>

那么有一种方法可以按以下方式进行udf

val wholeRowUdf = udf( (row: Row) =>  computeHash(row))

Row当然不是spark sql数据类型 - 所以这不会如图所示。

更新/澄清我意识到创建在withColumn内运行的全行UDF很容易。在spark sql声明中可以使用的内容不太清楚:

val featurizedDf = spark.sql("select wholeRowUdf( what goes here? ) as features 
                              from mytable")

2 个答案:

答案 0 :(得分:4)

  
    

Row当然不是spark sql数据类型 - 所以这不会如图所示。

  

我将展示您可以使用Row将所有列或所选列传递给使用struct inbuilt函数的udf函数

首先我定义一个dataframe

val df = Seq(
  ("a", "b", "c"),
  ("a1", "b1", "c1")
).toDF("col1", "col2", "col3")
//    +----+----+----+
//    |col1|col2|col3|
//    +----+----+----+
//    |a   |b   |c   |
//    |a1  |b1  |c1  |
//    +----+----+----+

然后我定义一个函数,使一行中的所有元素成为一个由, 分隔的字符串(因为你有computeHash函数)

import org.apache.spark.sql.Row
def concatFunc(row: Row) = row.mkString(", ")

然后我在udf函数

中使用它
import org.apache.spark.sql.functions._
def combineUdf = udf((row: Row) => concatFunc(row))

最后,我使用udf函数和withColumn 内置函数调用struct函数,将所选列组合为一列并传递给udf功能

df.withColumn("contcatenated", combineUdf(struct(col("col1"), col("col2"), col("col3")))).show(false)
//    +----+----+----+-------------+
//    |col1|col2|col3|contcatenated|
//    +----+----+----+-------------+
//    |a   |b   |c   |a, b, c      |
//    |a1  |b1  |c1  |a1, b1, c1   |
//    +----+----+----+-------------+

所以你可以看到 Row可以用来将整行作为参数传递

您甚至可以一次传递一行中的所有列

val columns = df.columns
df.withColumn("contcatenated", combineUdf(struct(columns.map(col): _*)))

<强>更新

你也可以使用sql查询实现相同的,你只需要注册udf函数

df.createOrReplaceTempView("tempview")
sqlContext.udf.register("combineUdf", combineUdf)
sqlContext.sql("select *, combineUdf(struct(`col1`, `col2`, `col3`)) as concatenated from tempview")

它会给你与上面相同的结果

现在,如果您不想对列的名称进行硬编码,那么您可以根据需要选择列名并将其设为字符串

val columns = df.columns.map(x => "`"+x+"`").mkString(",")
sqlContext.sql(s"select *, combineUdf(struct(${columns})) as concatenated from tempview")

我希望答案很有帮助

答案 1 :(得分:0)

我提出了一种解决方法:将列名删除到任何现有的spark sql函数中以生成新的输出列:

concat(${df.columns.tail.mkString(",'-',")}) as Features

在这种情况下,数据框中的第一列是目标,并被排除在外。这是这种方法的另一个优点:许多列的实际列表都被操作。

这种方法避免了RDD /数据帧的不必要重组。