Исправляемые значения искры из строки

У меня есть следующий файл данных

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""")

Я пытаюсь преобразовать строки в объекты Rating, но поскольку x (0) возвращает массив, это не работает

val ratings = transactions_with_counts
  .map(x => Rating(x(0).toInt, x(1).toInt, x(2).toInt))

error: значение toInt не является членом Any

Ответ 1

Давайте начнем с некоторых фиктивных данных:

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)

Существует несколько способов получить доступ к значениям Row и сохранить ожидаемые типы:

  • Соответствие шаблону

    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)
    } 
    
  • Типизированные методы get*, такие как getInt, getLong:

    transactions_with_counts.map(
      r => Rating(r.getInt(0), r.getInt(1), r.getLong(2))
    )
    
  • getAs, который может использовать оба имени и индексы:

    transactions_with_counts.map(r => Rating(
      r.getAs[Int]("user_id"), r.getAs[Int]("category_id"), r.getAs[Long](2)
    ))
    

    Он может использоваться для правильного извлечения определенных пользователем типов, включая mllib.linalg.Vector. Очевидно, что доступ по имени требует схемы.

  • Преобразование в статически типизированный Dataset (Spark 1.6+/2.0 +):

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

Ответ 2

Используя наборы данных, вы можете определить рейтинги следующим образом:

case class Rating(user_id: Int, category_id:Int, count:Long)

Класс рейтинга здесь имеет название столбца 'count' вместо 'rating', как было предложено нуль323. Таким образом, рейтинговая переменная присваивается следующим образом:

val transactions_with_counts = transactions.groupBy($"user_id", $"category_id").count

val rating = transactions_with_counts.as[Rating]

Таким образом, вы не столкнетесь с ошибками во время выполнения Spark, потому что Имя столбца класса рейтинга идентично имени столбца "count", сгенерированного Spark во время выполнения.

Ответ 3

Чтобы получить доступ к значению строки Dataframe, вам необходимо использовать rdd.collect для Dataframe с циклом for.

Рассмотрим ваш Dataframe, как показано ниже.

val df = Seq(
      (1,"James"),    
      (2,"Albert"),
      (3,"Pete")).toDF("user_id","name")

Используйте rdd.collect поверх вашего Dataframe. Переменная row будет содержать каждую строку Dataframe типа строки rdd. Чтобы получить каждый элемент из строки, используйте row.mkString(",") который будет содержать значение каждой строки в значениях, разделенных запятыми. Используя функцию split (встроенная функция), вы можете получить доступ к каждому значению rdd строки rdd с индексом.

for (row <- df.rdd.collect)
{   
    var user_id = row.mkString(",").split(",")(0)
    var category_id = row.mkString(",").split(",")(1)       
}

Приведенный выше код выглядит немного больше по сравнению с циклами dataframe.foreach, но вы получите больше контроля над своей логикой, используя приведенный выше код.