【问题标题】:How to replace string values in one column with actual column values from other columns in the same dataframe? Part 2如何用同一数据框中其他列的实际列值替换一列中的字符串值?第2部分
【发布时间】:2019-12-13 00:51:31
【问题描述】:

我在一列中有一些字符串值,我想用其他列中的值替换该列中的子字符串,并将所有加号替换为空格(如下所示)。

我有这些 List[String] 映射,它们是动态传递的,mapFrommapTo 应该在索引中相关。

描述值:mapFrom: ["Child", "ChildAge", "ChildState"]

列名:mapTo: ["name", "age", "state"]

输入示例:

name, age, state, description
tiffany, 10, virginia, Child + ChildAge + ChildState
andrew, 11, california, ChildState + Child + ChildAge
tyler, 12, ohio, ChildAge + ChildState + Child

预期结果:

name, age, state, description
tiffany, 10, virginia, tiffany 10 virginia
andrew, 11, california, california andrew 11
tyler, 12, ohio, 12 ohio tyler

如何使用 Spark Scala 实现这一目标?

当我从这里尝试解决方案时:How to replace string values in one column with actual column values from other columns in the same dataframe?

输出变成了

name, age, state, description
tiffany, 10, virginia, tiffany tiffanyAge tiffanyState
andrew, 11, california, andrewState andrew andrewAge
tyler, 12, ohio, tylerAge tylerState tyler

【问题讨论】:

  • 第二行你怎么知道ChildState + Child + Child哪个是年龄和名字,你怎么知道的?
  • 这是一个错字对不起
  • 我假设这里还有一个错字:tyler, 12, ohio, ChildAge + ChildState + ChildName,这应该是tyler, 12, ohio, ChildAge + ChildState + Child,对吗?
  • 看起来mapFrom中的ChildName实际上是Child,而输入中的所有ChildName实际上只有Child。我编辑了问题以反映这一点,如果有问题请告诉我。

标签: scala dataframe apache-spark


【解决方案1】:

我会使用 map 而不是内置的 Spark 函数。
不是最干净的,但可行的解决方案

val data = Seq(
  ("tiffany", 10, "virginia", "ChildName + ChildAge + ChildState"),
  ("andrew", 11, "california", "ChildState + ChildName + ChildAge"),
  ("tyler", 12, "ohio", "ChildAge + ChildState + ChildName")
).toDF("name", "age", "state", "description")

定义编码器转换的架构

val schema = StructType(Seq(
  StructField("name", StringType),
  StructField("age", IntegerType),
  StructField("state", StringType),
  StructField("description", StringType)
))
val encoder = RowEncoder(schema)

逻辑本身

val res = data.map(row => {
  val desc = row.getAs[String]("description").replaceAll("\\s+", "").split("\\+")
  val sb = new StringBuilder()
  val map = desc.zipWithIndex.toMap.map(_.swap)

  map(0) match {
    case "ChildState" => sb.append(row.getAs[String]("state")).append(" ")
    case "ChildAge" => sb.append(row.getAs[Int]("age")).append(" ")
    case "ChildName" => sb.append(row.getAs[String]("name")).append(" ")
  }

  map(1) match {
    case "ChildState" => sb.append(row.getAs[String]("state")).append(" ")
    case "ChildAge" => sb.append(row.getAs[Int]("age")).append(" ")
    case "ChildName" => sb.append(row.getAs[String]("name")).append(" ")
  }

  map(2) match {
    case "ChildState" => sb.append(row.getAs[String]("state")).append(" ")
    case "ChildAge" => sb.append(row.getAs[Int]("age")).append(" ")
    case "ChildName" => sb.append(row.getAs[String]("name")).append(" ")
  }

  Row(row.getAs[String]("name"), row.getAs[Int]("age"), row.getAs[String]("state"), sb.toString())
}) (encoder)

结果

res.show(false)
+-------+---+----------+---------------------+
|name   |age|state     |description          | 
+-------+---+----------+---------------------+
|tiffany|10 |virginia  |tiffany 10 virginia  |
|andrew |11 |california|california andrew 11 |
|tyler  |12 |ohio      |12 ohio tyler        |
+-------+---+----------+---------------------+

【讨论】:

    【解决方案2】:

    这里的问题是由于包含Child 的描述。这是ChildAgeChildState 的子序列。由于使用了正则表达式,这意味着Child 部分将被替换为导致奇怪输出的名称,例如tiffanyAgetiffanyState(请注意,这里的Child 部分被名称替换)。

    在不改变输入的情况下,有两种简单的解决方案:

    1. 更改Child 的正则表达式以使用前瞻:

      val mapFrom = List("Child(?= )", "ChildAge", "ChildState") :+ " \\+ "
      

      这只会在后面有空格时匹配Child

    2. Child 放在列表的最后。这意味着ChildAgeChildState 将首先匹配:

      val mapFrom = List("ChildAge", "ChildState", "Child") :+ " \\+ "
      

    第一种方案的完整解决方案:

    val mapFrom = List("Child(?= )", "ChildAge", "ChildState") :+ " \\+ "
    val mapTo = List("name", "age", "state").map(col) :+ lit(" ")
    val mapToFrom = mapFrom.zip(mapTo)
    
    val df2 = mapToFrom.foldLeft(df){case (df, (from, to)) => 
      df.withColumn("description", regexp_replace($"description", lit(from), to))
    }
    

    【讨论】:

      猜你喜欢
      • 2019-10-03
      • 1970-01-01
      • 2023-01-24
      • 1970-01-01
      • 2020-03-08
      • 1970-01-01
      • 1970-01-01
      • 2018-04-23
      • 2019-08-08
      相关资源
      最近更新 更多