Scala 从spark数据帧提取'Seq[(String,String,String)]`

Scala 从spark数据帧提取'Seq[(String,String,String)]`,scala,apache-spark,dataframe,apache-spark-sql,Scala,Apache Spark,Dataframe,Apache Spark Sql,我有一个spark DF,它有一行Seq[(String,String,String)]。我试着用它来做一个平面图,但我做的任何事情都会以失败告终 java.lang.ClassCastException:org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema无法强制转换为scala.Tuple3 我可以从DF中选取一行或多行,很好 df.map{ r => r.getSeq[Feature](1)}.first 返回

我有一个spark DF,它有一行
Seq[(String,String,String)]
。我试着用它来做一个平面图,但我做的任何事情都会以失败告终

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] .....
RDD的数据类型似乎是正确的

org.apache.spark.rdd.rdd[Seq[(String,String,String)]]

df的模式是

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)]
。有一个相关的问题(下面的链接),但这个问题的答案对我来说并不适用。我对spark也不太熟悉,不知道如何将其转化为有效的解决方案

行是
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
抛出上述错误。那么,我应该如何(例如)在每一行上获取第二个元组的第一个元素呢

同样为什么spark被设计为这样做,声称某事物属于某一类型而事实上它不是并且不能转换为所声称的类型似乎是愚蠢的


相关问题:


相关bug报告:

好吧,它并没有声称它是一个元组。它声称它是一个映射到
行的
结构

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))
您将在中找到精确的映射规则

由于
的结构并不完美,您可能需要将其映射到一些有用的内容:

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

最后,使用
数据集
的更简洁的方法是:

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

虽然这似乎是有点车在这一刻

第一种方法(
Row.getAs
)和第二种方法(
Dataset.as
)有着重要的区别。前者将对象提取为
Any
,并将
应用为
的实例。后者使用编码器在内部类型和所需表示之间进行转换

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默认编码器将对TupleX进行编码,使其成为嵌套结构,这就是您看到这种奇怪行为的原因。正如其他人在评论中所说,你不能只做
getAs[t]()
,因为它只是一个cast(
x.asInstanceOf[t]
),因此会给你运行时异常

我可以问一下是谁否决了这个问题以及为什么吗?相关的bug报告是meRight的解决方案,我不知道我基本上可以将内容作为任何东西要求回来,并且只会在运行时进行检查(我确信文档中的某个地方提到了这一点)
df.select($“content\u processed”).map(u.getSeq[(String,String)](0))
也会返回一些内容,但实际上不会运行。我假设这不是从Cassandra提取数据的产物,而是数据帧固有的东西?是的<代码>行
只是
任意
的集合。在
getSeq
和其他方法中发生的所有事情都相当于
(第(i)行:Any)
df.select($"content_processed").as[Seq[(String, String, String)]]
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)]
               )