it-swarm.com.ru

Spark извлекает значения из строки

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

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

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

27
Sam D

Начнем с фиктивных данных:

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 и сохранить ожидаемые типы:

  1. Сопоставление с образцом 

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

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

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

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

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

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

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

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, поскольку имя столбца класса Rating идентично имени столбца «count», сгенерированному Spark во время выполнения.

7
user-asterix

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

Рассмотрим, как выглядит ваш Dataframe, как показано ниже.

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

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

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

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

0
Sarath Avanavu