Apache Spark:从行中提取值的问题

Apache Spark: Issues with Extracting Values from Row

我在使用 Spark 中的行 class 时遇到了很多问题。在我看来 Row class 是一个真正设计糟糕的 class。从 Row 中提取值应该并不比从 Scala 列表中提取值更难;但实际上,您必须知道列的确切类型才能提取它。你甚至不能把列变成字符串;对于像 Spark 这样的伟大框架来说,这有多荒谬?在现实世界中,在大多数情况下,您并不知道列的确切类型,除此之外,在许多情况下,您有几十个或数百个列。下面是一个示例,向您展示我一直在获取的 ClassCastExceptions。

有没有人有任何解决方案可以轻松地从行中提取值?

scala> val df = List((1,2),(3,4)).toDF("col1","col2")
df: org.apache.spark.sql.DataFrame = [col1: int, col2: int]


scala> df.first.getAs[String]("col1")
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
  ... 56 elided

scala> df.first.getAs[Int]("col1")
res12: Int = 1

scala> df.first.getInt(0)
res13: Int = 1

scala> df.first.getLong(0)
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long
  at scala.runtime.BoxesRunTime.unboxToLong(BoxesRunTime.java:105)
  at org.apache.spark.sql.Row$class.getLong(Row.scala:231)
  at org.apache.spark.sql.catalyst.expressions.GenericRow.getLong(rows.scala:165)
  ... 56 elided

scala> df.first.getFloat(0)
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Float
  at scala.runtime.BoxesRunTime.unboxToFloat(BoxesRunTime.java:109)
  at org.apache.spark.sql.Row$class.getFloat(Row.scala:240)
  at org.apache.spark.sql.catalyst.expressions.GenericRow.getFloat(rows.scala:165)
  ... 56 elided

scala> df.first.getString(0)
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
  at org.apache.spark.sql.Row$class.getString(Row.scala:255)
  at org.apache.spark.sql.catalyst.expressions.GenericRow.getString(rows.scala:165)
  ... 56 elided 

Spark 是一个开源项目不喜欢可以自行修改不要因为没有得到想要的就消极。有很多选择。 Spark 已尽可能灵活。

或者您可以执行以下操作

df.first.get(0).toString
//res0: String = 1
df.first.get(0).toString.toLong
//res1: Long = 1
df.first.get(0).toString.toFloat
//res2: Float = 1.0

df.first.getAs[Int]("col1").toString
//res0: String = 1
df.first.getAs[Int]("col1").toLong
//res1: Long = 1
df.first.getAs[Int]("col1").toFloat
//res2: Float = 1.0

我再说一遍,如果您对提供的 api 不满意,您可以随时扩展现有的 api 并实现您的或创建您自己的 api

这是为了类型安全。如果您不确定列类型,只需将其视为字符串即可,这对大多数情况都适用。 (这里是 Java 例子)

yourDataSet.foreach(row -> {
            log.info(row.getAs("yourColumnname").toString());
        });