web-dev-qa-db-fra.com

Filtre basé sur un autre RDD dans Spark

Je souhaite ne garder que les employés pour lesquels un identifiant de service est référencé dans le deuxième tableau. 

Employee table
LastName    DepartmentID
Rafferty    31
Jones   33
Heisenberg  33
Robinson    34
Smith   34

Department table
DepartmentID
31  
33  

J'ai essayé le code suivant qui ne fonctionne pas:

employee = [['Raffery',31], ['Jones',33], ['Heisenberg',33], ['Robinson',34], ['Smith',34]]
department = [31,33]
employee = sc.parallelize(employee)
department = sc.parallelize(department)
employee.filter(lambda e: e[1] in department).collect()

Py4JError: An error occurred while calling o344.__getnewargs__. Trace:
py4j.Py4JException: Method __getnewargs__([]) does not exist

Des idées? J'utilise Spark 1.1.0 avec Python. Cependant, j'accepterais une réponse Scala ou Python.

12
poiuytrez

Dans ce cas, ce que vous souhaitez réaliser est de filtrer au niveau de chaque partition les données contenues dans la table department: Ce serait la solution de base:

val dept = deptRdd.collect.toSet
val employeesWithValidDeptRdd = employeesRdd.filter{case (employee, d) => dept.contains(d)}

Si les données de votre service sont volumineuses, une variable de diffusion améliorera les performances en transmettant les données une fois à tous les nœuds au lieu de les sérialiser avec chaque tâche.

val deptBC = sc.broadcast(deptRdd.collect.toSet)
val employeesWithValidDeptRdd = employeesRdd.filter{case (employee, d) => deptBC.value.contains(d)}

Bien que l’utilisation de la jointure fonctionne, c’est une solution très coûteuse, car elle nécessite un brassage distribué des données (byKey) pour réaliser la jointure. Étant donné que l'exigence est un filtre simple, l'envoi des données à chaque partition (comme indiqué ci-dessus) donnera de bien meilleures performances.

22
maasg

J'ai finalement mis en place une solution utilisant une jointure. J'ai dû ajouter une valeur 0 au département pour éviter une exception de Spark:

employee = [['Raffery',31], ['Jones',33], ['Heisenberg',33], ['Robinson',34], ['Smith',34]]
department = [31,33]
# invert id and name to get id as the key
employee = sc.parallelize(employee).map(lambda e: (e[1],e[0]))
# add a 0 value to avoid an exception
department = sc.parallelize(department).map(lambda d: (d,0))

employee.join(department).map(lambda e: (e[1][0], e[0])).collect()

output: [('Jones', 33), ('Heisenberg', 33), ('Raffery', 31)]
10
poiuytrez

Filtrer plusieurs valeurs dans plusieurs colonnes:

Si vous extrayez des données d'une base de données (base de données Hive ou SQL de type SQL par exemple) et devez filtrer sur plusieurs colonnes, il peut s'avérer plus simple de charger la table avec le premier filtre. RDD (plusieurs petites itérations est la méthode recommandée de la programmation Spark):

{
    import org.Apache.spark.sql.Hive.HiveContext
    val hc = new HiveContext(sc)

    val first_data_filter = hc.sql("SELECT col1,col2,col2 FROM tableName WHERE col3 IN ('value_1', 'value_2', 'value_3)")
    val second_data_filter = first_data_filter.filter(rdd => rdd(1) == "50" || rdd(1) == "20")
    val final_filtered_data = second_data_filter.filter(rdd => rdd(0) == "1500")

}

Bien sûr, vous devez connaître un peu vos données pour filtrer sur les bonnes valeurs, mais cela fait partie du processus d'analyse.

0
WaveRider

pour le même exemple ci-dessus, je souhaite ne conserver que les employés qui contiennent ou dans un ID de département référencé dans le deuxième tableau. ou "dans", Je veux dire 33 est "dans" 334 et 335

employee = [['Raffery',311], ['Jones',334], ['Heisenberg',335], ['Robinson',34], ['Smith',34]]
department = [31,33]
employee = sc.parallelize(employee)
department = sc.parallelize(department)
0
yechiel