J'ai le dataframe suivant
val transactions_with_counts = sqlContext.sql(
"""SELECT user_id AS user_id, category_id AS category_id,
COUNT(category_id) FROM transactions GROUP BY user_id, category_id""")
J'essaie de convertir les lignes en objets d'évaluation, mais comme x(0) renvoie un tableau, cela échoue.
val ratings = transactions_with_counts
.map(x => Rating(x(0).toInt, x(1).toInt, x(2).toInt))
error: valeur toInt n'est pas membre de Any
Commençons par quelques données factices:
val transactions = Seq((1, 2), (1, 4), (2, 3)).toDF("user_id", "category_id")
val transactions_with_counts = transactions
.groupBy($"user_id", $"category_id")
.count
transactions_with_counts.printSchema
// root
// |-- user_id: integer (nullable = false)
// |-- category_id: integer (nullable = false)
// |-- count: long (nullable = false)
Il existe plusieurs façons d'accéder aux valeurs Row
et de conserver les types attendus:
Correspondance de modèle
import org.Apache.spark.sql.Row
transactions_with_counts.map{
case Row(user_id: Int, category_id: Int, rating: Long) =>
Rating(user_id, category_id, rating)
}
Méthodes get*
telles que getInt
, getLong
:
transactions_with_counts.map(
r => Rating(r.getInt(0), r.getInt(1), r.getLong(2))
)
getAs
méthode pouvant utiliser à la fois des noms et des index:
transactions_with_counts.map(r => Rating(
r.getAs[Int]("user_id"), r.getAs[Int]("category_id"), r.getAs[Long](2)
))
Il peut être utilisé pour extraire correctement les types définis par l'utilisateur, y compris mllib.linalg.Vector
. Évidemment, l'accès par nom nécessite un schéma.
Conversion en Dataset
de type statique (Spark 1.6+/2.0+):
transactions_with_counts.as[(Int, Int, Long)]
En utilisant des jeux de données, vous pouvez définir des évaluations comme suit:
case class Rating(user_id: Int, category_id:Int, count:Long)
La classe Rating a ici un nom de colonne 'count' au lieu de 'rating' comme suggéré par zéro323. Ainsi, la variable de notation est attribuée comme suit:
val transactions_with_counts = transactions.groupBy($"user_id", $"category_id").count
val rating = transactions_with_counts.as[Rating]
Ainsi, vous ne rencontrerez aucune erreur d'exécution dans Spark car votre nom de colonne de classe Rating est identique au nom de colonne "count" généré par Spark lors de l'exécution.
Pour accéder à une valeur d'une ligne de Dataframe , vous devez utiliser rdd.collect
of Dataframe with for loop.
Considérez votre Dataframe comme ci-dessous.
val df = Seq(
(1,"James"),
(2,"Albert"),
(3,"Pete")).toDF("user_id","name")
Utilisez rdd.collect
en plus de votre Dataframe . La variable row
contiendra chaque ligne de Dataframe de type rdd
. Pour obtenir chaque élément d'une ligne, utilisez row.mkString(",")
qui contiendra la valeur de chaque ligne sous forme de valeurs séparées par des virgules. En utilisant la fonction split
(fonction intégrée), vous pouvez accéder à chaque valeur de colonne de rdd
rangée avec index.
for (row <- df.rdd.collect)
{
var user_id = row.mkString(",").split(",")(0)
var category_id = row.mkString(",").split(",")(1)
}
Le code ci-dessus semble un peu plus gros comparé aux boucles dataframe.foreach
, mais vous obtiendrez un meilleur contrôle de votre logique en utilisant ce code.