web-dev-qa-db-fra.com

Calcul de la similitude en cosinus entre toutes les lignes d'une trame de données dans pyspark

J'ai un ensemble de données contenant des travailleurs avec leurs informations démographiques comme l'âge, le sexe, l'adresse, etc. et leurs lieux de travail. J'ai créé un RDD à partir de l'ensemble de données et l'ai converti en DataFrame.

Il existe plusieurs entrées pour chaque ID. Par conséquent, j'ai créé un DataFrame qui ne contenait que l'ID du travailleur et les différents bureaux où il/elle avait travaillé.

    |----------|----------------|
    | **ID**    **Office_Loc**  |
    |----------|----------------|
    |   1      |Delhi, Mumbai,  |
    |          | Gandhinagar    |
    |---------------------------|
    |   2      | Delhi, Mandi   | 
    |---------------------------|
    |   3      |Hyderbad, Jaipur|
    -----------------------------

Je veux calculer la similitude cosinus entre chaque travailleur avec tous les autres travailleurs en fonction de leur emplacement de bureau ".

Donc, j'ai parcouru les lignes du DataFrame, en récupérant une seule ligne du DataFrame:

myIndex = 1
values = (ID_place_df.rdd.zipWithIndex()
            .filter(lambda ((l, v), i): i == myIndex)
            .map(lambda ((l,v), i): (l, v))
            .collect())

puis en utilisant la carte

    cos_weight = ID_place_df.select("ID","office_location").rdd\
  .map(lambda x: get_cosine(values,x[0],x[1]))

pour calculer la similitude en cosinus entre la ligne extraite et l'ensemble du DataFrame.

Je ne pense pas que mon approche soit bonne puisque j'itère à travers les lignes du DataFrame, cela va à l'encontre du but de l'utilisation de spark. Y a-t-il une meilleure façon de le faire dans pyspark? Veuillez aviser.

8
Abhinav Choudhury

Vous pouvez utiliser le package mllib pour calculer le L2 norme du TF-IDF de chaque ligne. Multipliez ensuite le tableau avec lui-même pour obtenir la similitude cosinus en tant que produit scalaire de deux par deux L2normes:

1. RDD

rdd = sc.parallelize([[1, "Delhi, Mumbai, Gandhinagar"],[2, " Delhi, Mandi"], [3, "Hyderbad, Jaipur"]])
  • Calculer TF-IDF:

    documents = rdd.map(lambda l: l[1].replace(" ", "").split(","))
    
    from pyspark.mllib.feature import HashingTF, IDF
    hashingTF = HashingTF()
    tf = hashingTF.transform(documents)
    

Vous pouvez spécifier le nombre d'entités dans HashingTF pour réduire la matrice d'entités (moins de colonnes).

    tf.cache()
    idf = IDF().fit(tf)
    tfidf = idf.transform(tf)
  • Calculer L2norme:

    from pyspark.mllib.feature import Normalizer
    labels = rdd.map(lambda l: l[0])
    features = tfidf
    
    normalizer = Normalizer()
    data = labels.Zip(normalizer.transform(features))
    
  • Calculez la similitude du cosinus en multipliant la matrice par elle-même:

    from pyspark.mllib.linalg.distributed import IndexedRowMatrix
    mat = IndexedRowMatrix(data).toBlockMatrix()
    dot = mat.multiply(mat.transpose())
    dot.toLocalMatrix().toArray()
    
        array([[ 0.        ,  0.        ,  0.        ,  0.        ],
               [ 0.        ,  1.        ,  0.10794634,  0.        ],
               [ 0.        ,  0.10794634,  1.        ,  0.        ],
               [ 0.        ,  0.        ,  0.        ,  1.        ]])
    

    OU: Utilisation d'un produit cartésien et de la fonction dot sur les tableaux numpy:

    data.cartesian(data)\
        .map(lambda l: ((l[0][0], l[1][0]), l[0][1].dot(l[1][1])))\
        .sortByKey()\
        .collect()
    
        [((1, 1), 1.0),
         ((1, 2), 0.10794633570596117),
         ((1, 3), 0.0),
         ((2, 1), 0.10794633570596117),
         ((2, 2), 1.0),
         ((2, 3), 0.0),
         ((3, 1), 0.0),
         ((3, 2), 0.0),
         ((3, 3), 1.0)]
    

2. DataFrame

Comme vous semblez utiliser des cadres de données, vous pouvez utiliser le spark mlpackage à la place:

import pyspark.sql.functions as psf
df = rdd.toDF(["ID", "Office_Loc"])\
    .withColumn("Office_Loc", psf.split(psf.regexp_replace("Office_Loc", " ", ""), ','))
  • Calculer TF-IDF:

    from pyspark.ml.feature import HashingTF, IDF
    hashingTF = HashingTF(inputCol="Office_Loc", outputCol="tf")
    tf = hashingTF.transform(df)
    
    idf = IDF(inputCol="tf", outputCol="feature").fit(tf)
    tfidf = idf.transform(tf)
    
  • Calculer L2 norme:

    from pyspark.ml.feature import Normalizer
    normalizer = Normalizer(inputCol="feature", outputCol="norm")
    data = normalizer.transform(tfidf)
    
  • Produit de matrice de calcul:

    from pyspark.mllib.linalg.distributed import IndexedRow, IndexedRowMatrix
    mat = IndexedRowMatrix(
        data.select("ID", "norm")\
            .rdd.map(lambda row: IndexedRow(row.ID, row.norm.toArray()))).toBlockMatrix()
    dot = mat.multiply(mat.transpose())
    dot.toLocalMatrix().toArray()
    

    OU: en utilisant une jointure et un UDF pour la fonction dot:

    dot_udf = psf.udf(lambda x,y: float(x.dot(y)), DoubleType())
    data.alias("i").join(data.alias("j"), psf.col("i.ID") < psf.col("j.ID"))\
        .select(
            psf.col("i.ID").alias("i"), 
            psf.col("j.ID").alias("j"), 
            dot_udf("i.norm", "j.norm").alias("dot"))\
        .sort("i", "j")\
        .show()
    
        +---+---+-------------------+
        |  i|  j|                dot|
        +---+---+-------------------+
        |  1|  2|0.10794633570596117|
        |  1|  3|                0.0|
        |  2|  3|                0.0|
        +---+---+-------------------+
    

Ce didacticiel répertorie différentes méthodes pour multiplier les matrices à grande échelle: https://labs.yodas.com/large-scale-matrix-multiplication-with-pyspark-or-how-to-match-two-large-datasets -of-company-1be4b1b2871e

19
MaFF