it-swarm.com.ru

Извлечение `Seq [(String, String, String)]` из плагина DataFrame

У меня есть искра DF со строками Seq[(String, String, String)]. Я пытаюсь сделать что-то вроде flatMap с этим, но все, что я делаю, в итоге бросает

Java.lang.ClassCastException: org.Apache.spark.sql.catalyst.expressions.GenericRowWithSchema не может быть приведен к scala.Tuple3

Я могу просто взять одну или несколько строк из DF

df.map{ r => r.getSeq[Feature](1)}.first

возвращается 

Seq[(String, String, String)] = WrappedArray([ancient,jj,o], [olympia_greece,nn,location] .....

и тип данных СДР кажется правильным. 

org.Apache.spark.rdd.RDD[Seq[(String, String, String)]]

Схема дф это

root
 |-- article_id: long (nullable = true)
 |-- content_processed: array (nullable = true)
 |    |-- element: struct (containsNull = true)
 |    |    |-- lemma: string (nullable = true)
 |    |    |-- pos_tag: string (nullable = true)
 |    |    |-- ne_tag: string (nullable = true)

Я знаю, что эта проблема связана с тем, что spark sql рассматривает строки RDD как org.Apache.spark.sql.Row, хотя они идиотски говорят, что это Seq[(String, String, String)]. Есть связанный вопрос (ссылка ниже), но ответ на этот вопрос не работает для меня. Я также недостаточно знаком со спарком, чтобы понять, как превратить его в рабочее решение.

Строки Row[Seq[(String, String, String)]] или Row[(String, String, String)] или Seq[Row[(String, String, String)]] или что-то еще более безумное.

Я пытаюсь сделать что-то вроде

df.map{ r => r.getSeq[Feature](1)}.map(_(1)._1)

который, кажется, работает, но на самом деле не

df.map{ r => r.getSeq[Feature](1)}.map(_(1)._1).first

выдает вышеуказанную ошибку. Так как же мне (например) получить первый элемент второго кортежа в каждой строке?

Кроме того, ПОЧЕМУ было спроектировано для этой цели, кажется глупым утверждать, что что-то относится к одному типу, хотя на самом деле это не так и не может быть преобразовано в заявленный тип.


Связанный вопрос: Исключение GenericRowWithSchema при приведении ArrayBuffer к HashSet в DataFrame в RDD из таблицы Hive

Связанный отчет об ошибке: http://search-hadoop.com/m/q3RTt2bvwy19Dxuq1&subj=ClassCastException+when+extracting+and+collecting+DF+array+column+type

11
Matti Lyra

Ну, это не означает, что это кортеж. Он утверждает, что это struct, который отображается на Row:

import org.Apache.spark.sql.Row

case class Feature(lemma: String, pos_tag: String, ne_tag: String)
case class Record(id: Long, content_processed: Seq[Feature])

val df = Seq(
  Record(1L, Seq(
    Feature("ancient", "jj", "o"),
    Feature("olympia_greece", "nn", "location")
  ))
).toDF

val content = df.select($"content_processed").rdd.map(_.getSeq[Row](0))

Точные правила отображения вы найдете в Руководстве по программированию Spark SQL .

Поскольку Row не совсем красивая структура, вы, вероятно, захотите сопоставить ее с чем-то полезным:

content.map(_.map {
  case Row(lemma: String, pos_tag: String, ne_tag: String) => 
    (lemma, pos_tag, ne_tag)
})

или же:

content.map(_.map ( row => (
  row.getAs[String]("lemma"),
  row.getAs[String]("pos_tag"),
  row.getAs[String]("ne_tag")
)))

Наконец, немного более краткий подход с Datasets:

df.as[Record].rdd.map(_.content_processed)

или же 

df.select($"content_processed").as[Seq[(String, String, String)]]

хотя в данный момент это кажется слегка глючным.

Существует важное отличие первого подхода (Row.getAs) и второго (Dataset.as). Первый извлекает объекты как Any и применяет asInstanceOf. Последний использует кодеры для преобразования между внутренними типами и желаемым представлением.

14
zero323
object ListSerdeTest extends App {

  implicit val spark: SparkSession = SparkSession
    .builder
    .master("local[2]")
    .getOrCreate()


  import spark.implicits._
  val myDS = spark.createDataset(
    Seq(
      MyCaseClass(mylist = Array(("asd", "aa"), ("dd", "ee")))
    )
  )

  myDS.toDF().printSchema()

  myDS.toDF().foreach(
    row => {
      row.getSeq[Row](row.fieldIndex("mylist"))
        .foreach {
          case Row(a, b) => println(a, b)
        }
    }
  )
}

case class MyCaseClass (
                 mylist: Seq[(String, String)]
               )

Приведенный выше код является еще одним способом работы с вложенной структурой. По умолчанию Spark Encoder будет кодировать TupleX, делая их вложенными структурами, поэтому вы видите это странное поведение. и, как говорили другие в комментарии, вы не можете просто выполнить getAs[T](), поскольку это просто приведение (x.asInstanceOf[T]), поэтому вы получите исключения во время выполнения. 

0
linehrr