Apache Spark : Utilisation des UDF et Currying

Le framework de traitement de données distribué Apache Spark est devenu en peu de temps la référence dans le monde de la datascience. Certaines évolutions ont permis de rendre cette plateforme très attrayante. En particulier l'utilisation de l'API python et de la structure de données dataframe en ont fait un outil de choix pour les data scientists habitués au couple Pandas/Scikit-learn.

UDF : les bases

Aujourd'hui, nous allons nous intéresser à une fonctionnalité très utile pour enrichir la donnée dans un dataset : les User Defined Functions (UDF). Les UDF permettent de créer une nouvelle colonne dans un dataframe qui sera le résultat d'un calcul pouvant utiliser les valeurs d'une (ou plusieurs) colonne(s) existante(s).
Une UDF prend en argument :

  • un objet de type colonne (pyspark.sql.Column)

et retourne :

  • une valeur de type pyspark.sql.types.

Par exemple pour créer une colonne qui indique si le nom d'une personne comporte plus de 10 caractères cela donne :

+------------+------+---------+--------+
|         nom|points|   prenom|lensup10|
+------------+------+---------+--------+
|Abdul-jabbar|  1560|   Kareem|    true|
|      Malone|  1476|     Karl|   false|
|      Jordan|  1072|  Michael|   false|
| Chamberlain|  1045|     Wilt|    true|
|       Oneil|  1117|Shaquille|   false|
+------------+------+---------+--------+

Sur ce modèle, on peut tout à fait créer des UDF qui utilisent des valeurs de plusieurs colonnes. Par exemple, pour regrouper nom et prénom, on obtient :

+------------+------+---------+--------+---------------------+
|nom         |points|prenom   |lensup10|nomprenom            |
+------------+------+---------+--------+---------------------+
|Abdul-jabbar|1560  |Kareem   |true    |Abdul-jabbar - Kareem|
|Malone      |1476  |Karl     |false   |Malone - Karl        |
|Jordan      |1072  |Michael  |false   |Jordan - Michael     |
|Chamberlain |1045  |Wilt     |true    |Chamberlain - Wilt   |
|Oneil       |1117  |Shaquille|false   |Oneil - Shaquille    |
+------------+------+---------+--------+---------------------+

Pour aller plus loin : le currying

Ok, jusqu'ici c'était facile. Maintenant, comment faire lorsque l'on veut ajouter des paramètres à l'UDF qui ne sont pas des valeurs de colonnes ? La solution : le currying.
Le currying vient de la programmation fonctionnelle. Selon wikipedia : "la curryfication désigne la transformation d'une fonction à plusieurs arguments en une fonction à un argument qui retourne une fonction sur le reste des arguments."

Si l'on reprend le premier exemple mais que l'on souhaite vérifier si la longueur du nom est supérieure à une valeur saisie par l'utilisateur. Dans ce cas, il faut passer cette longueur en paramètre. On ne peut pas définir une udf sur la base d'une fonction du type islensupn(name,longueur) car longueur n'est pas de type colonne.
L'approche basée sur le currying est donc la suivante :

+------------+------+---------+--------+---------------------+-------+
|nom         |points|prenom   |lensup10|nomprenom            |lensupn|
+------------+------+---------+--------+---------------------+-------+
|Abdul-jabbar|1560  |Kareem   |true    |Abdul-jabbar - Kareem|true   |
|Malone      |1476  |Karl     |false   |Malone - Karl        |true   |
|Jordan      |1072  |Michael  |false   |Jordan - Michael     |true   |
|Chamberlain |1045  |Wilt     |true    |Chamberlain - Wilt   |true   |
|Oneil       |1117  |Shaquille|false   |Oneil - Shaquille    |false  |
+------------+------+---------+--------+---------------------+-------+

Dans cet exemple, on voit bien que islensupn(longueur) renvoie une UDF qui prend comme argument la colonne "nom".


Voilà, vous êtes maintenant parés pour manipuler les UDF à volonté !!!