我正在尝试将RDD [Seq [String]]解析为Dataframe。 尽管它是字符串的序列,但它们可以具有更特定的类型,例如Int,Boolean,Double,String等。 例如,一行可能是:
"hello", "1", "bye", "1.1"
"hello1", "11", "bye1", "2.1"
...
另一个执行可能具有不同数量的列。
第一列将始终为String,第二列为int,依此类推,并且将始终采用这种方式。另一方面,一个执行可能包含五个元素,而其他执行可能包含2000个元素,因此取决于执行情况。在每次执行中,都会定义列类型的名称。
要做到这一点,我可以有这样的东西:
//I could have a parameter to generate the StructType dinamically.
def getSchema(): StructType = {
var schemaArray = scala.collection.mutable.ArrayBuffer[StructField]()
schemaArray += StructField("col1" , IntegerType, true)
schemaArray += StructField("col2" , StringType, true)
schemaArray += StructField("col3" , DoubleType, true)
StructType(schemaArray)
}
//Array of Any?? it doesn't seem the best option!!
val l1: Seq[Any] = Seq(1,"2", 1.1 )
val rdd1 = sc.parallelize(l1).map(Row.fromSeq(_))
val schema = getSchema()
val df = sqlContext.createDataFrame(rdd1, schema)
df.show()
df.schema
我根本不希望拥有Seq of Any,但这确实是我拥有的。另一个机会?
另一方面,我认为我有一个类似于CSV的东西,我可以创建一个。使用spark时,有一个库可以读取CSV并返回一个推断类型的数据框。如果我已经有RDD [String],可以调用它吗?
答案 0 :(得分:2)
由于每次执行的列数都会发生变化,所以我建议使用CSV选项,将分隔符设置为空格或其他。这样,spark将为您找出列类型。
由于您提到从HBase读取数据,因此一种方法是将HBase行转换为JSON或CSV,然后将RDD转换为数据框:
val jsons = hbaseContext.hbaseRDD(tableName, scan).map{case (_, r) =>
val currentJson = new JSONObject
val cScanner = r.cellScanner
while (cScanner.advance) {
currentJson.put(Bytes.toString(cScanner.current.getQualifierArray, cScanner.current.getQualifierOffset, cScanner.current.getQualifierLength),
Bytes.toString(cScanner.current.getValueArray, cScanner.current.getValueOffset, cScanner.current.getValueLength))
}
currentJson.toString
}
val df = spark.read.json(spark.createDataset(jsons))
可以对CSV做类似的事情。