J'ai joué avec la conversion de RDD en DataFrames et vice-versa. Tout d'abord, j'avais un RDD de type (Int, Int) appelé dataPair. J'ai ensuite créé un objet DataFrame avec des en-têtes de colonne en utilisant:
val dataFrame = dataPair.toDF(header(0), header(1))
Ensuite, je l'ai reconverti d'un DataFrame en un RDD en utilisant:
val testRDD = dataFrame.rdd
qui renvoie un RDD de type org.Apache.spark.sql.Row (pas (Int, Int)). Ensuite, je voudrais le reconvertir en RDD en utilisant .toDF mais j'obtiens une erreur:
error: value toDF is not a member of org.Apache.spark.rdd.RDD[org.Apache.spark.sql.Row]
J'ai essayé de définir un schéma de type Data (Int, Int) pour testRDD, mais j'obtiens des exceptions de non-correspondance de type:
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
^
J'ai déjà importé
import sqlContext.implicits._
Pour créer un DataFrame à partir d'un RDD de lignes, vous avez généralement deux options principales:
1) Vous pouvez utiliser toDF()
qui peut être importé par import sqlContext.implicits._
. Cependant, cette approche ne fonctionne que pour les types de RDD suivants:
RDD[Int]
RDD[Long]
RDD[String]
RDD[T <: scala.Product]
(source: Scaladoc de l'objet SQLContext.implicits
)
La dernière signature signifie en fait qu'elle peut fonctionner pour un RDD de tuples ou un RDD de classes de cas (car les tuples et les classes de cas sont des sous-classes de scala.Product).
Donc, pour utiliser cette approche pour un RDD[Row]
, Vous devez le mapper sur un RDD[T <: scala.Product]
. Cela peut être fait en mappant chaque ligne à une classe de cas personnalisée ou à un tuple, comme dans les extraits de code suivants:
val df = rdd.map({
case Row(val1: String, ..., valN: Long) => (val1, ..., valN)
}).toDF("col1_name", ..., "colN_name")
ou
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")
Le principal inconvénient de cette approche (à mon avis) est que vous devez définir explicitement le schéma du DataFrame résultant dans la fonction de carte, colonne par colonne. Peut-être que cela peut être fait par programme si vous ne connaissez pas le schéma à l'avance, mais les choses peuvent devenir un peu compliquées. Donc, alternativement, il y a une autre option:
2) Vous pouvez utiliser createDataFrame(rowRDD: RDD[Row], schema: StructType)
, qui est disponible dans l'objet SQLContext . Exemple:
val df = oldDF.sqlContext.createDataFrame(rdd, oldDF.schema)
Notez qu'il n'est pas nécessaire de définir explicitement une colonne de schéma. Nous réutilisons l'ancien schéma de DF, qui est de la classe StructType
et peut être facilement étendu. Cependant, cette approche n'est parfois pas possible et, dans certains cas, peut être moins efficace que la première.
J'espère que c'est plus clair qu'auparavant. À votre santé.