web-dev-qa-db-fra.com

Comment préparer des données au format LibSVM à partir de DataFrame?

Je veux faire le format libsvm, alors j'ai créé dataframe au format souhaité, mais je ne sais pas comment convertir au format libsvm Le format est comme indiqué sur la figure. J'espère que le type de libsvm souhaité est élément utilisateur: rating . Si vous savez quoi faire dans la situation actuelle:

val ratings = sc.textFile(new File("/user/ubuntu/kang/0829/rawRatings.csv").toString).map { line =>
     val fields = line.split(",")
      (fields(0).toInt,fields(1).toInt,fields(2).toDouble)
}
val user = ratings.map{ case (user,product,rate) => (user,(product.toInt,rate.toDouble))}
val usergroup = user.groupByKey 

val data =usergroup.map{ case(x,iter) => (x,iter.map(_._1).toArray,iter.map(_._2).toArray)}

val data_DF = data.toDF("user","item","rating")

DATAFRAME FIGURE

J'utilise Spark 2.0.

12
Data diaboli

Le problème que vous rencontrez peut être divisé comme suit:

  • Conversion de vos notes (je crois) en données LabeledPointX.
  • Enregistrement de X au format libsvm.

1. Conversion de vos notes en données LabeledPointX

Considérons les évaluations brutes suivantes:

val rawRatings: Seq[String] = Seq("0,1,1.0", "0,3,3.0", "1,1,1.0", "1,2,0.0", "1,3,3.0", "3,3,4.0", "10,3,4.5")

Vous pouvez gérer ces notations brutes comme un matrice de liste de coordonnées (COO) .

Spark implémente une matrice distribuée soutenue par un RDD de ses entrées: CoordinateMatrix où chaque entrée est un tuple de (i: Long, j: Long, valeur: Double).

Remarque: Un CoordinateMatrix doit être utilisé uniquement lorsque les deux dimensions de la matrice sont énormes et que la matrice est très clairsemée. (ce qui est généralement le cas des évaluations d'utilisateur/d'élément.)

import org.Apache.spark.mllib.linalg.distributed.{CoordinateMatrix, MatrixEntry}
import org.Apache.spark.rdd.RDD

val data: RDD[MatrixEntry] = 
      sc.parallelize(rawRatings).map {
            line => {
                  val fields = line.split(",")
                  val i = fields(0).toLong
                  val j = fields(1).toLong
                  val value = fields(2).toDouble
                  MatrixEntry(i, j, value)
            }
      }

Convertissons maintenant ce RDD[MatrixEntry] en CoordinateMatrix et extrayons les lignes indexées:

val df = new CoordinateMatrix(data) // Convert the RDD to a CoordinateMatrix
                .toIndexedRowMatrix().rows // Extract indexed rows
                .toDF("label", "features") // Convert rows

2. Enregistrement des données LabeledPoint au format libsvm format

Depuis Spark 2.0, vous pouvez le faire avec DataFrameWriter. Créons un petit exemple avec quelques données factices LabeledPoint (vous pouvez également utiliser le DataFrame créé précédemment):

import org.Apache.spark.mllib.linalg.Vectors
import org.Apache.spark.mllib.regression.LabeledPoint
val pos = LabeledPoint(1.0, Vectors.dense(1.0, 0.0, 3.0))
val neg = LabeledPoint(0.0, Vectors.sparse(3, Array(0, 2), Array(1.0, 3.0)))

val df = Seq(neg,pos).toDF("label","features")

Malheureusement, nous ne pouvons toujours pas utiliser directement DataFrameWriter car, bien que la plupart des composants de pipeline prennent en charge la rétrocompatibilité pour le chargement, certains DataFrames et pipelines existants dans les versions Spark antérieures à 2.0, contenant des colonnes vectorielles ou matricielles, peuvent nécessiter une migration vers la nouvelle étincelle. ml types de vecteurs et matrices. 

Les utilitaires permettant de convertir les colonnes DataFrame de mllib.linalg à ml.linalg (et vice versa) sont disponibles dans org.Apache.spark.mllib.util.MLUtils.. Dans notre cas, nous devons procéder comme suit (pour les données factices et les DataFrame de step 1.)

import org.Apache.spark.mllib.util.MLUtils
// convert DataFrame columns
val convertedVecDF = MLUtils.convertVectorColumnsToML(df)

Sauvegardons maintenant le DataFrame:

convertedVecDF.write.format("libsvm").save("data/foo")

Et nous pouvons vérifier le contenu des fichiers: 

$ cat data/foo/part*
0.0 1:1.0 3:3.0
1.0 1:1.0 2:0.0 3:3.0

EDIT: Dans la version actuelle de spark (2.1.0), il n'est pas nécessaire d'utiliser le package mllib. Vous pouvez simplement sauvegarder les données LabeledPoint au format libsvm comme ci-dessous:

import org.Apache.spark.ml.linalg.Vectors
import org.Apache.spark.ml.feature.LabeledPoint
val pos = LabeledPoint(1.0, Vectors.dense(1.0, 0.0, 3.0))
val neg = LabeledPoint(0.0, Vectors.sparse(3, Array(0, 2), Array(1.0, 3.0)))

val df = Seq(neg,pos).toDF("label","features")
df.write.format("libsvm").save("data/foo")
14
eliasah

Afin de convertir un existant en un DataSet typé, je suggère ce qui suit: Utilisez la classe de cas suivante:

case class LibSvmEntry (
   value: Double,
   features: L.Vector)

Vous pouvez utiliser la fonction map pour la convertir en entrée LibSVM, comme ceci: df.map[LibSvmEntry](r: Row => /* Do your stuff here*/)

0
Elior Malul