J'ai un ensemble de données de (user, product, review)
, et souhaitons l'introduire dans l'algorithme ALS de mllib.
L'algorithme a besoin que les utilisateurs et les produits soient des nombres, tandis que les miens sont des noms d'utilisateur de chaîne et des SKU de chaîne.
À l'heure actuelle, j'obtiens les utilisateurs et les SKU distincts, puis je leur attribue des ID numériques en dehors de Spark.
Je me demandais s'il y avait une meilleure façon de procéder. La seule approche à laquelle j'ai pensé est d'écrire un RDD personnalisé qui énumère essentiellement de 1 à n
, puis d'appeler Zip sur les deux RDD.
À partir de Spark 1. il existe deux méthodes que vous pouvez utiliser pour résoudre ce problème facilement:
RDD.zipWithIndex
est comme Seq.zipWithIndex
, il ajoute des nombres contigus (Long
). Cela doit d'abord compter les éléments dans chaque partition, donc votre entrée sera évaluée deux fois. Mettez en cache votre RDD d'entrée si vous souhaitez l'utiliser.RDD.zipWithUniqueId
vous donne également des identifiants Long
uniques, mais ils ne sont pas garantis contigus. (Ils ne seront contigus que si chaque partition a le même nombre d'éléments.) L'avantage est que cela n'a pas besoin de connaître quoi que ce soit sur l'entrée, donc cela ne provoquera pas de double évaluation.Pour un exemple d'utilisation similaire, je viens de hacher les valeurs de chaîne. Voir http://blog.cloudera.com/blog/2014/03/why-Apache-spark-is-a-crossover-hit-for-data-scientists/
def nnHash(tag: String) = tag.hashCode & 0x7FFFFF
var tagHashes = postIDTags.map(_._2).distinct.map(tag =>(nnHash(tag),tag))
On dirait que vous faites déjà quelque chose comme ça, bien que le hachage puisse être plus facile à gérer.
Matei a suggéré ici une approche pour émuler zipWithIndex
sur un RDD, ce qui revient à attribuer des ID au sein de chaque partition qui seront uniques au monde: https://groups.google.com/forum/# ! topic/spark-users/WxXvcn2gl1E
Une autre option simple, si vous utilisez des DataFrames et que vous vous préoccupez uniquement de l'unicité, est d'utiliser la fonction MonotonicallyIncreasingID
import org.Apache.spark.sql.functions.monotonicallyIncreasingId
val newDf = df.withColumn("uniqueIdColumn", monotonicallyIncreasingId)
Modifier: MonotonicallyIncreasingID
a été déconseillé et supprimé depuis Spark 2. ; il est désormais connu sous le nom de monotonically_increasing_id
.
Les gens ont déjà recommandé monotonically_increasing_id () , et ont mentionné le problème qu'il crée des Longs, pas des Ints.
Cependant, d'après mon expérience (mise en garde - Spark 1.6) - si vous l'utilisez sur un seul exécuteur (répartition à 1 avant), aucun préfixe d'exécuteur n'est utilisé et le nombre peut être casté en toute sécurité en Int. Évidemment, vous devez avoir moins de lignes Integer.MAX_VALUE.
monotonically_increasing_id () apparaît pour être la réponse, mais ne fonctionnera malheureusement pas pour ALS car il produit des nombres 64 bits et ALS attend les 32 bits (voir mon commentaire ci-dessous la réponse de radek1st pour les deets).
La solution que j'ai trouvée est d'utiliser zipWithIndex () , comme mentionné dans la réponse de Darabos. Voici comment l'implémenter:
Si vous disposez déjà d'un DataFrame à colonne unique avec vos utilisateurs distincts appelés userids
, vous pouvez créer une table de recherche (LUT) comme suit:
# PySpark code
user_als_id_LUT = sqlContext.createDataFrame(userids.rdd.map(lambda x: x[0]).zipWithIndex(), StructType([StructField("userid", StringType(), True),StructField("user_als_id", IntegerType(), True)]))
Maintenant vous pouvez:
Faites de même pour les articles, évidemment.