2017-09-05 875 views
0

我有一个使用Spark的VectorAssembler打包成矢量向量的特征列,如下所示。 data是输入数据帧(类型spark.sql.DataFrame)。将特征的Spark向量转换为数组

val featureCols = Array("feature_1","feature_2","feature_3") 
val featureAssembler = new VectorAssembler().setInputCols(featureCols).setOutputCol("features") 
val dataWithFeatures = featureAssembler.transform(data) 

我开发使用ClassifierClassificationModel开发API的自定义分类。 ClassificationModel需要开发一个predictRaw()函数,该函数从模型输出预测标签的向量。

def predictRaw(features: FeaturesType) : Vector 

这个功能是通过该API集和需要一个参数,的FeaturesType特征,并输出向量(这在我的情况下,我要带去是火花DenseVector作为DenseVector延伸Vector性状)。

由于VectorAssembler的包装,features列的类型为Vector,每个元素本身就是一个向量,它是每个训练样本的原始特征的向量。例如:

设有柱 - 型的矢量
[1.0,2.0,3.0] - 元素1,本身的矢量
[3.5,4.5,5.5] - element2的,本身的载体

我需要将这些功能提取到Array[Double]以实现我的predictRaw()逻辑。理想情况下,我想下面的结果,以保持基数:

`val result: Array[Double] = Array(1.0, 3.5, 2.0, 4.5, 3.0, 4.5)` 

即在列优先的顺序,我会变成一个矩阵。

我已经试过:

val array = features.toArray // this gives an array of vectors and doesn't work 

我也试着输入功能作为一个数据帧的对象,而不是一个向量,但API期待一个向量,由于特征从VectorAssembler包装。例如,该功能本身的工作原理,但并不符合API,因为它的预期FeaturesType是矢量,而不是数据帧:

def predictRaw(features: DataFrame) :DenseVector = { 
    val featuresArray: Array[Double] = features.rdd.map(r => r.getAs[Vector](0).toArray).collect 
//rest of logic would go here 
} 

我的问题是features的类型Vector,不DataFrame的。另一种选择可能是打包features作为DataFrame,但我不知道如何做到这一点,而不使用VectorAssembler

所有建议表示感谢,谢谢!我看过Access element of a vector in a Spark DataFrame (Logistic Regression probability vector),但这是在python中,我正在使用Scala。

回答

2

如果你只是想DenseVector转换成数组[双]这与UDF相当简单:

import org.apache.spark.ml.linalg.DenseVector 
val toArr: Any => Array[Double] = _.asInstanceOf[DenseVector].toArray 
val toArrUdf = udf(toArr) 
val dataWithFeaturesArr = dataWithFeatures.withColumn("features_arr",toArrUdf('features)) 

这会给你一个新的列:

|-- features_arr: array (nullable = true) 
| |-- element: double (containsNull = false) 
+0

你好 - 我不知道。如果这些真的做我所需的东西。使用上面的extract_features UDF,我似乎获得了与features列相同的列,如下所示:+ -------------------- + -------- ------------ + |功能| extracted_features | + + -------------------- + 0.016682 ... | [-9.5357,0.01668 ... | + -------------------- + -------------------- + – LucieCBurgess

+0

换句话说,功能列和提取的功能看起来完全一样。我可以像这样得到每个元素:只显示前1行。如果我然后执行以下操作:'val featuresArray1:Array [Double] = temp.rdd.map(r => r.getAs [Double](0))。collect'(使用索引元素1和2)问题如空间不足 – LucieCBurgess

+0

我认为问题是Array给每行3个元素的数组,然后我努力访问它们。我要问一个单独的问题,这更清楚。请看一下,谢谢 – LucieCBurgess

0

这里是一个办法(没有udf)从数据框(字符串,矢量)获取数据框(字符串,数组)。主要想法是使用一个中间RDD投作为一个载体,并利用其指定者方法:

val arrayDF = vectorDF.rdd 
    .map(x => x.getAs[String](0) -> x.getAs[Vector](1).toArray) 
    .toDF("word","array")