2017-10-16 4 views
0

J'ai une trame de données comme ceci:Manipulation Rejoignez OU Spark

+---+---+---+---+---+ 
|AId| A1| A2| A3| A4| 
+---+---+---+---+---+ 
| 1| *| a| b| c| 
| 2| *| *| b| c| 
| 3| c| a| b| c| 
| 4| *| *| *| c| 
| 5| *| *| *| *| 
+---+---+---+---+---+ 

que je voudrais rejoindre sur:

+---+---+---+---+---+----+ 
|BId| B1| B2| B3| B4|Code| 
+---+---+---+---+---+----+ 
| 1| c| a| b| c| AO| 
| 2| b| a| b| c| AS| 
| 3| b| b| b| c| AT| 
| 4| a| d| d| c| BO| 
| 5| d| a| c| b| BS| 
| 6| a| b| b| c| BT| 
| 7| d| d| d| c| CO| 
| 8| d| d| d| d| CS| 
+---+---+---+---+---+----+ 

pour correspondre à une carte d'identité à la règle. Cependant, * est un caractère générique. Cela correspondra à n'importe quoi. Dans l'exemple ci-dessus AId == 1 correspondra à BId 1 et 2, AId == 3 ne correspondra qu'à BId 1, AId == 4 correspondra à tous sauf 5 et 8, et AId == 5 correspondra à tous les 8.

Quelle serait la meilleure façon d'aborder cela? La requête semble chère dans Spark, et de plus Spark n'a pas de bloc opératoire intégré. L'alternative semble faire l'affaire - quand A1-A4 doit définir un drapeau, puis revenir en arrière et rejoindre. Un point délicat est également que les caractères génériques peuvent apparaître 1-4 fois dans la première table, dans n'importe quelle colonne, bien qu'ils n'apparaissent pas dans la seconde.

Répondre

0

Vous pouvez exprimer la condition de jointure comme:

(A1 = * | (A1 = B1)) AND (A2 = * | (A2 = B2)) AND ... AND (AN = * | (AN = BN)) 

Avec l'expression équivalente PySpark peut généré par exemple comme celui-ci

from pyspark.sql.functions import col 
from functools import reduce 
from operator import and_ 

expr = reduce(
    and_, 
    ((col("A{}".format(i)) == "*") | (col("A{}".format(i)) == col("B{}".format(i))) 
    for i in range(1, 5))) 
Column<b'(((((A1 = *) OR (A1 = B1)) AND ((A2 = *) OR (A2 = B2))) AND ((A3 = *) OR (A3 = B3))) AND ((A4 = *) OR (A4 = B4)))'> 

et utilisé avec crossJoin:

a.crossJoin(b).where(expr) 

ou

spark.conf.set("spark.sql.crossJoin.enabled", "true") 

a.join(b, expr) 

Malheureusement cela assez cher, en raison de produit cartésien. Avec un petit nombre de colonnes (4 est probablement un cas limite), vous pouvez essayer de générer un ensemble de colonnes de puissance et de créer des plans optimisés, mais de toute évidence, il ne s'agira pas d'un plus grand nombre de colonnes.