Spark数据框中的列值比较

时间:2018-08-30 08:44:16

标签: apache-spark apache-spark-sql

我有一个包含大量记录的数据框。在该DF中,一条记录可以重复多次,并且每次更新时,最后更新的字段将具有修改记录的日期。

我们有一组要比较相似ID的行的列。在此比较中,我们要捕获从先前记录更改为当前记录的字段/列,然后在更新记录的“ updated_columns”列中捕获该字段/列。将此第二条记录与第三条记录进行比较,并标识更新的列,并在第三条记录的“ updated_columns”字段中捕获该记录,继续执行相同操作直到该ID的最后一条记录,并对每个具有多个条目的ID执行相同的操作

最初,我们对列进行分组,并从该组列中创建一个哈希,然后与下一行的哈希值进行比较,这有助于我识别具有更新但想要更新的列的记录。

我在这里共享一些数据,这是预期的结果,因此最终数据应该像添加更新的列后的样子(在这里我可以说,使用列Col1,Col2,Col3,col4和Col5进行两行之间的比较):

enter image description here

想要高效地做到这一点。任何人都可以尝试这样的事情。

正在寻求帮助!

〜克里希。

2 个答案:

答案 0 :(得分:1)

可以使用window

想法是按 ID 将数据分组,按 LAST-UPDATED 排序,然后将前一行的值(如果存在)复制到当前行中然后将复制的数据与当前值进行比较。

val data = ... //the dataframe has the columns ID,Col1,Col2,Col3,Col4,Col5,LAST_UPDATED,IS_DELETED

val fieldNames = data.schema.fieldNames.dropRight(1) //1
val columns = fieldNames.map(f => col(f))
val windowspec = Window.partitionBy("ID").orderBy("LAST_UPDATED") //2
def compareArrayUdf() = ... //3

val result = data
  .withColumn("cur", array(columns: _*)) //4
  .withColumn("prev", lag($"cur", 1).over(windowspec)) //5
  .withColumn("updated_columns", compareArrayUdf()($"cur", $"prev")) //6
  .drop("cur", "prev") //7
  .orderBy("LAST_UPDATED")

备注:

  1. 创建所有要比较的字段的列表。除了最后一个( LAST-UPDATED )以外的所有字段都已使用
  2. 创建一个按 ID 分区的窗口,每个分区按 LAST-UPDATED
  3. 排序
  4. 创建一个比较两个数组的udf,并将发现的差异映射到字段名称,代码见下文
  5. 创建一个新列,其中包含所有应比较的值
  6. 创建一个新列,其中包含应该比较的上一个行的所有值(通过使用lag函数)。前一行是具有相同 ID 和最大 LAST-UPDATED 的行,该行小于当前行。该字段可以为空
  7. 比较两个新列,并将结果放入 updated-columns
  8. 删除在步骤3和4中创建的两个中间列

compareArraysUdf

def compareArray(cur: mutable.WrappedArray[String], prev: mutable.WrappedArray[String]): String = {
  if (prev == null || cur == null) return ""
  val res = new StringBuilder
  for (i <- cur.indices) {
    if (!cur(i).contentEquals(prev(i))) {
      if (res.nonEmpty) res.append(",")
      res.append(fieldNames(i))
    }
  }
  res.toString()
}
def compareArrayUdf() = udf[String, mutable.WrappedArray[String], mutable.WrappedArray[String]](compareArray)

答案 1 :(得分:0)

您可以将DataFrame或DataSet与其自身连接,将两行ID相同且左行版本为i且右行版本为{{1 }}。这是一个例子

i+1

然后您可以引用新的DataFrame / DataSet中的列,例如case class T(id: String, version: Int, data: String) val data = Seq(T("1", 1, "d1-1"), T("1", 2, "d1-2"), T("2", 1, "d2-1"), T("2", 2, "d2-2"), T("2", 3, "d2-3"), T("3", 1, "d3-1")) data: Seq[T] = List(T(1,1,d1-1), T(1,2,d1-2), T(2,1,d2-1), T(2,2,d2-2), T(2,3,d2-3), T(3,1,d3-1)) val ds = data.toDS val joined = ds.as("ds1").join(ds.as("ds2"), $"ds1.id" === $"ds2.id" && (($"ds1.version"+1) === $"ds2.version")) $"ds1.data等。

要查找数据从一种版本更改为另一种版本的行,可以执行

$"ds2.data