我在HDFS上以制表符分隔的文件中有一些数据,如下所示:
label | user_id | feature
------------------------------
pos | 111 | www.abc.com
pos | 111 | www.xyz.com
pos | 111 | Firefox
pos | 222 | www.example.com
pos | 222 | www.xyz.com
pos | 222 | IE
neg | 333 | www.jkl.com
neg | 333 | www.xyz.com
neg | 333 | Chrome
我需要对其进行转换,为每个user_id创建一个特征向量,以训练org.apache.spark.ml.classification.NaiveBayes
模型。
我目前的做法主要是以下几点:
踢球者就是这个...... 数据已经按user_id预先排序。什么是利用它的最佳方式?我很难想到可能会发生多少不必要的工作。
如果有一些代码有助于理解我当前的方法,这就是地图的本质:
val featurization = (vals: (String,Iterable[Row])) => {
// create a Seq of all the feature indices
// Note: the indexing was done in a previous step not shown
val seq = vals._2.map(x => (x.getDouble(1).toInt,1.0D)).toSeq
// create the sparse vector
val featureVector = Vectors.sparse(maxIndex, seq)
// convert the string label into a Double
val label = if (vals._2.head.getString(2) == "pos") 1.0 else 0.0
(label, vals._1, featureVector)
}
d.rdd
.groupBy(_.getString(1))
.map(featurization)
.toDF("label","user_id","features")
答案 0 :(得分:1)
如果我的磁盘上的数据保证按照用于组聚合或缩减的密钥进行预排序,那么Spark有什么方法可以利用它?
这取决于。如果您应用的操作可以从映射端聚合中受益,那么您可以通过预分类数据获得相当多的收益而无需在代码中进一步干预。共享相同密钥的数据应位于相同的分区上,并且可以在随机播放之前在本地聚合。
不幸的是,在这种特殊情况下它并没有多大帮助。即使你启用了地图边聚合(groupBy(Key)
没有使用,所以你需要自定义实现)或聚合特征向量(你会在我对{{的答案中找到一些例子) 3}})没有太大的收获。你可以在这里和那里保存一些工作,但你仍然需要在节点之间传输所有索引。
如果你想获得更多,你将不得不做更多的工作。我可以看到两种可以利用现有订单的基本方法:
使用自定义Hadoop输入格式仅生成完整记录(标签,ID,所有功能),而不是逐行读取数据。如果您的数据每个ID都有固定的行数,您甚至可以尝试使用NLineInputFormat
并应用mapPartitions
来汇总记录。
这绝对是更详细的解决方案,但Spark中不需要额外的改组。
照常读取数据,但使用groupBy
的自定义分区程序。据我所知,使用rangePartitioner
应该可以正常工作但确保您可以尝试以下程序:
mapPartitionsWithIndex
查找每个分区的最小/最大ID。 groupBy(Key)
这可能是更友好的解决方案,但至少需要一些改组。如果要移动的预期记录数量很少(<<#records-per-partition),您甚至可以使用mapPartitions
和broadcast
*来处理此问题,尽管分区可能更有用且更便宜实践。
*您可以使用与此类似的方法:How to define a custom aggregation function to sum a column of Vectors?