我有一个DataFrame source
,并且想根据另一个称为blacklist
的DataFrame中的条件过滤出条目。 source
必须匹配至少一个blacklist
条目才能被过滤掉。 blacklist
中的列条件/条目由AND
链接。 NULL
中的blacklist
值保留为通配符,这意味着相应的属性可以具有任何值以匹配条件。
一个简化的示例:
source
:
| id | age | color |
|----|-----|-------|
| 1 | 28 | blue |
| 2 | 25 | blue |
| 3 | 15 | red |
| 4 | 20 | red |
| 5 | 27 | green |
| 6 | 30 | green |
blacklist
:
| age | color |
|------|-------|
| 25 | blue |
| NULL | red |
| 30 | NULL |
output
:
| id | age | color |
|----|-----|-------|
| 1 | 28 | blue |
| 5 | 27 | green |
相应的数据框:
val source = Seq((1, 28, "blue"), (2, 25, "blue"), (3, 15, "red"), (4, 20, "red"), (5, 27, "green"), (6, 30, "green")).toDF("id", "age", "color")
val blacklist = Seq((Some(25), Some("blue")), (None, Some("red")), (Some(30), None)).toDF("age", "color")
有关真实数据的更多信息:
source
包含100亿个条目blacklist
包含20万条5列的条目我的方法(使用Spark 2.3):
val joinCondition = (source("age") <=> blacklist("age") || blacklist("age").isNull) && (source("color") <=> blacklist("color") || blacklist("color").isNull)
val dataToRemove = source.join(broadcast(blacklist), joinCondition).select(source("id"), source("age"), source("color"))
val output = source.except(dataToRemove)
问题与疑问:
上面的代码段正常工作。但是,对于真正的大量数据,我在运行时间方面存在性能问题。您是否看到解决此黑名单问题的更好方法?
我还考虑在驱动程序中创建一个较大的过滤条件,然后执行source.filter(theBigCondition)
。这样做的好处是不需要连接。但是,即使黑名单较小,我也遇到了有关Catalyst Optimizer的问题。
您有什么想法?
答案 0 :(得分:1)
您加入广播的方法可能是解决此问题的最佳方法。
首先尝试了解哪个部分花费了这么长时间。 可能是这部分:
val joinedDf = data.join(broadcast(blacklist))
因此,我的第一个嫌疑犯将是10 B数据框中的偏斜数据。而且由于您的黑色DF很小,在这种情况下,“ Salty Join”将非常有用。
算法基础:
通过选择数字1-N
来进行咸加入。比将较小DF中的每一行乘以N。对于N=3
:
黑名单之前:
| age | color |
|------|-------|
| 25 | blue |
| 30 | NULL |
黑名单之后:
| salt | age | color |
|------|------|-------|
| 1 | 25 | blue |
| 2 | 25 | blue |
| 3 | 25 | blue |
| 1 | 25 | red |
| 2 | 25 | red |
| 3 | 25 | red |
对于每行较大的DF,请在1-N之间添加一个随机数:
| salt | age | color |
|------|------|-------|
| 3 | 25 | blue |
| 2 | 27 | green |
| 1 | 25 | blue |
| 3 | 45 | red |
比添加盐列成为联接的一部分
saltedData.join(brodcast(saltedBlacklist), Seq("salt","age","color"))
现在我们可以看到,在大型DF中,我们有重复项(25,蓝色),但是由于它们的盐分不同,它们将分配给更多机器。
咸连接的想法是获得更大的熵。如果我们的联接列中的数据确实存在偏差,那么工作人员之间的分配将很差。通过添加盐分,我们可以使小df倍N的数据膨胀,但是通过在现在包含“ salt”列的新联接列中获得更好的熵,我们可以获得更好的分布。
答案 1 :(得分:0)
让我回答我自己的问题。
在本地测试中,我发现except
非常昂贵。在source
数据中添加一种标记,然后在该接缝处进行筛选以加快速度。
val blacklistWithFlag = blacklist.withColumn("remove", lit(true))
val markedSource = source.join(broadcast(blacklistWithFlag), joinCondition, "left_outer").select(source("id"), source("age"), source("color"), blacklistWithFlag("remove"))
val output = markedSource.filter(col("remove").isNull).drop("remove")
此方法仅需要1个阶段,而不需要上述4个阶段。