私は、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._
行の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
クラスであり、簡単に拡張できます。ただし、このアプローチは不可能な場合があり、場合によっては最初のアプローチよりも効率が低下する可能性があります。
以前よりもはっきりしていることを願っています。乾杯。