Spark生成包含(SQL LIKE)字符串的列名列表

时间:2019-01-11 00:36:27

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

以下是一种简单的语法,可以使用SQL Like功能在特定列中搜索字符串。

<div name="block-0">
<div name="block-1">
<div name="block-2">
<div name="block-3">

问题是我该如何抓取每列NAME中包含特定字符串的列,并生成一个新列,其中包含每一行的那些“列名”。 >

到目前为止,这是我采用的方法,但是由于无法在UDF中使用spark-sql“ Like”函数而陷入困境。

val dfx = df.filter($"name".like(s"%${productName}%"))

这是示例输出。 请注意,这里只有3列,但在实际工作中,我将读取多个表,这些表可以包含动态列数。

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

import spark.implicits._
val df1 = Seq(
  (0, "mango", "man", "dit"), 
  (1, "i-man", "man2", "mane"),
  (2, "iman", "mango", "ho"),
  (3, "dim",  "kim", "sim")
).toDF("id", "col1", "col2", "col3")

val df2 = df1.columns.foldLeft(df1) {
  (acc: DataFrame, colName: String) =>
    acc.withColumn(colName, concat(lit(colName + "="), col(colName)))
}

val df3 = df2.withColumn("merged_cols", split(concat_ws("X",  df2.columns.map(c=> col(c)):_*), "X"))

1 个答案:

答案 0 :(得分:1)

可以通过在列上使用foldLeftwhenotherwise来完成此操作:

val e = "%man%"

val df2 = df1.columns.foldLeft(df.withColumn("merged_cols", lit(""))){(df, c) => 
    df.withColumn("merged_cols", when(col(c).like(e), concat($"merged_cols", lit(s"$c,"))).otherwise($"merged_cols"))}
  .withColumn("merged_cols", expr("substring(merged_cols, 1, length(merged_cols)-1)"))

所有满足条件e的列都将附加到merged_cols列中的字符串中。请注意,该列必须存在才能使第一个附加项起作用,因此在发送到foldLeft时会将其添加到数据帧中(包含空字符串)。

代码的最后一行只是删除了最后添加的额外,。如果您希望将结果作为数组,只需添加.withColumn("merged_cols", split($"merged_cols", ","))即可。


另一种方法是改用UDF。当处理许多列时,这可能是首选方法,因为foldLeft将创建多个数据帧副本。这里使用了正则表达式(而不是像SQL这样的SQL,因为它可以对整个列进行操作)。

val e = ".*man.*"

val concat_cols = udf((vals: Seq[String], names: Seq[String]) => {
  vals.zip(names).filter{case (v, n) => v.matches(e)}.map(_._2)
})

val df2 = df.withColumn("merged_cols", concat_cols(array(df.columns.map(col(_)): _*), typedLit(df.columns.toSeq)))

注意typedLit可以在Spark 2.2+版本中使用,而在使用旧版本时,请使用array(df.columns.map(lit(_)): _*)