如何仅针对行的给定子集有效地选择仅包含值变化的列?

时间:2018-08-07 19:23:58

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

对于上下文,我的最终目标是从非常大的数据框中删除几乎重复的行。这是一些虚拟数据:

+---+--------+----------+---+-------+-------+---+-------+-------+
|key|unique_1|  unique_2|...|col_125|col_126|...|col_414|col_415|
+---+--------+----------+---+-------+-------+---+-------+-------+
|  1|     123|01-01-2000|...|      1|   true|...|    100|     25|
|  2|     123|01-01-2000|...|      0|  false|...|    100|     25|
|  3|     321|12-12-2012|...|      3|   true|...|     99|      1|
|  4|     321|12-12-2012|...|      3|  false|...|     99|      5|
+---+--------+----------+---+-------+-------+---+-------+-------+

在此数据中,来自 unique_1 unique_2 的观察值组合应该是不同的,但并非总是如此。当它们重复时,它们对于绝大多数列具有相同的值,但是在极少数其他列上具有变化。我正在尝试开发一种处理近重复项的策略,但是它很复杂,因为每组近重复项都有一组不同的包含变体的列。

我正在尝试一次查看包含一组几乎重复的变量的列-像这样:

+---+-------+-------+
|key|col_125|col_126|
+---+-------+-------+
|  1|      1|   true|
|  2|     20|  false|
+---+-------+-------+

或者这个:

+---+-------+-------+
|key|col_126|col_415|
+---+-------+-------+
|  3|   true|      1|
|  4|  false|      5|
+---+-------+-------+

我已经通过几种不同的方法成功获得了这一结果。这是我的第一次尝试:

def findColumnsWithDiffs(df: DataFrame): DataFrame = {

    df.columns.foldLeft(df){(a,b) =>
        a.select(b).distinct.count match {
            case 1 => a.drop(b)
            case _ => a
        }
    }
}

val smallFrame = originalData.filter(($"key" === 1) || ($"key" === 2))
val desiredOutput = findColumnsWithDiffs(smallFrame)

这在给我我想要的东西的范围内有效,但是它是如此令人难以置信。上面的函数运行起来要慢大约10倍,然后才能显示smallFrame中的所有数据(而且我认为性能只会随着数据的大小而变差-尽管我尚未检验该假设彻底)。

我认为使用fold代替foldLeft可能会带来一些改进,因此我重新编写了findColumnsWithDiffs函数,如下所示:

def findColumnsWithDiffsV2(df: DataFrame): DataFrame = {

    val colsWithDiffs = df.columns.map(colName => List(colName)).toList.fold(Nil){(a,b) =>
        df.select(col(b(0))).distinct.count match {
            case 1 => a
            case _ => a ++ b
        }
    }

    df.select(colsWithDiffs.map(colName => col(colName)):_*)

}

但是性能是一样的。我还尝试过将每个列映射到它具有的不同值的数量并从那里开始工作,但是性能还是一样。在这一点上,我没有想法。我的直觉是正在为每一列执行过滤器,这就是为什么它是如此之慢的原因,但是我不知道如何验证该理论和/或在我正确的情况下更改为修复它所做的工作。有谁有想法提高我的工作效率?

我目前正在使用spark 2.1.0 / scala 2.11.8

1 个答案:

答案 0 :(得分:0)

所有用于识别不同值的方法都很好,问题在于过滤器的延迟评估。为了提高性能,请在使用smallFrame.cache之前先致电findColsWithDiffs。这样可以将过滤后的数据保存在内存中,这很好,因为一次只能存储几行。