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")
J'utilise Spark 2.0.
Le problème que vous rencontrez peut être divisé comme suit:
LabeledPoint
X.1. Conversion de vos notes en données LabeledPoint
X
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")
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*/)