web-dev-qa-db-fra.com

Spark extraire des valeurs d'une ligne

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

27
Sam D

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:

  1. 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)
    } 
    
  2. Méthodes get* telles que getInt, getLong:

    transactions_with_counts.map(
      r => Rating(r.getInt(0), r.getInt(1), r.getLong(2))
    )
    
  3. 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.

  4. Conversion en Dataset de type statique (Spark 1.6+/2.0+):

    transactions_with_counts.as[(Int, Int, Long)]
    
54
zero323

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.

7
user-asterix

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.

0
Sarath Avanavu