web-dev-qa-db-ja.com

RDD [Row]をDataFrameに戻す方法

私は、RDDをDataFrameに変換したり、元に戻したりして遊んでいます。最初に、dataPairと呼ばれるタイプ(Int、Int)のRDDがありました。次に、以下を使用して列ヘッダーを持つDataFrameオブジェクトを作成しました。

val dataFrame = dataPair.toDF(header(0), header(1))

次に、次を使用して、DataFrameからRDDに変換し直しました。

val testRDD = dataFrame.rdd

タイプorg.Apache.spark.sql.RowのRDDを返します(not(Int、Int))。次に、.toDFを使用してRDDに変換したいのですが、エラーが発生します:

error: value toDF is not a member of org.Apache.spark.rdd.RDD[org.Apache.spark.sql.Row]

TestRDDのデータ型(Int、Int)のスキーマを定義しようとしましたが、型の不一致の例外が発生します。

error: type mismatch;
found   : org.Apache.spark.rdd.RDD[org.Apache.spark.sql.Row]
required: org.Apache.spark.rdd.RDD[Data]
    val testRDD: RDD[Data] = dataFrame.rdd
                                       ^

私はすでにインポートしました

import sqlContext.implicits._
10
TheElysian

行のRDDからDataFrameを作成するには、通常2つの主なオプションがあります。

1)import sqlContext.implicits._でインポートできるtoDF()を使用できます。ただし、このアプローチは次のタイプのRDDでのみ機能します。

  • RDD[Int]
  • RDD[Long]
  • RDD[String]
  • RDD[T <: scala.Product]

(ソース: ScaladocSQLContext.implicitsオブジェクトの)

最後の署名は、実際には、タプルのRDDまたはケースクラスのRDDで機能できることを意味します(タプルとケースクラスはscala.Productのサブクラスであるため)。

したがって、このアプローチをRDD[Row]に使用するには、RDD[T <: scala.Product]にマップする必要があります。これは、次のコードスニペットのように、各行をカスタムケースクラスまたはタプルにマッピングすることで実行できます。

val df = rdd.map({ 
  case Row(val1: String, ..., valN: Long) => (val1, ..., valN)
}).toDF("col1_name", ..., "colN_name")

または

case class MyClass(val1: String, ..., valN: Long = 0L)
val df = rdd.map({ 
  case Row(val1: String, ..., valN: Long) => MyClass(val1, ..., valN)
}).toDF("col1_name", ..., "colN_name")

このアプローチの主な欠点は(私の意見では)、マップ関数で結果のDataFrameのスキーマを列ごとに明示的に設定する必要があることです。スキーマを事前に知らなければ、これはプログラムで実行できるかもしれませんが、少し面倒になります。したがって、代わりに、別のオプションがあります:


2)createDataFrame(rowRDD: RDD[Row], schema: StructType)を使用できます。これは SQLContext オブジェクトで使用できます。例:

val df = oldDF.sqlContext.createDataFrame(rdd, oldDF.schema)

スキーマ列を明示的に設定する必要がないことに注意してください。古いDFのスキーマを再利用します。これはStructTypeクラスであり、簡単に拡張できます。ただし、このアプローチは不可能な場合があり、場合によっては最初のアプローチよりも効率が低下する可能性があります。

以前よりもはっきりしていることを願っています。乾杯。

22
Daniel de Paula