Apache spark 从数据帧触发MLLib Kmeans,然后再返回
我的目标是使用Spark(1.3.1)MLLib将kmeans聚类算法应用于非常大的数据集。我使用Spark的hiveContext调用了HDFS中的数据,并最终希望以这种格式将其放回那里Apache spark 从数据帧触发MLLib Kmeans,然后再返回,apache-spark,k-means,Apache Spark,K Means,我的目标是使用Spark(1.3.1)MLLib将kmeans聚类算法应用于非常大的数据集。我使用Spark的hiveContext调用了HDFS中的数据,并最终希望以这种格式将其放回那里 |I.D |cluster | =================== |546 |2 | |6534 |4 | |236 |5 | |875 |2 | 我已经运行了下面的代
|I.D |cluster |
===================
|546 |2 |
|6534 |4 |
|236 |5 |
|875 |2 |
我已经运行了下面的代码,其中“data”是一个双精度的数据帧,并且是第一列的ID
val parsedData = data.rdd.map(s => Vectors.dense(s.getDouble(1),s.getDouble(2))).cache()
val clusters = KMeans.train(parsedData, 3, 20)
这成功地运行了,我现在被困在上面描述的数据帧中,将集群映射回它们各自的ID。我可以通过以下方式将其转换为datframe:
sc.makeRDD(clusters.predict(parsedData).toArray()).toDF()
但这就是我所能做到的。在正确的轨道上,我想他问了一个与我类似的问题
我怀疑需要标签点库。如果您有任何意见和答案,我们将不胜感激,干杯
编辑:刚刚在Spark用户列表中找到,看起来很有前途我正在使用pySpark做类似的事情。我猜您可以直接将其转换为Scala,因为没有特定于python的内容。myPointsWithID是我的RDD,每个点都有一个ID,该点表示为一个值数组
# Get an RDD of only the vectors representing the points to be clustered
points = myPointsWithID.map(lambda (id, point): point)
clusters = KMeans.train(points,
100,
maxIterations=100,
runs=50,
initializationMode='random')
# For each point in the original RDD, replace the point with the
# ID of the cluster the point belongs to.
clustersBC = sc.broadcast(clusters)
pointClusters = myPointsWithID.map(lambda (id, point): (id, clustersBC.value.predict(point)))
我知道您希望在最后获得数据帧。我看到两种可能的解决办法。我要说的是,两者之间的选择是品味的问题 从RDD创建列 以RDD的形式获得成对的ID和集群非常容易:
val idPointRDD = data.rdd.map(s => (s.getInt(0), Vectors.dense(s.getDouble(1),s.getDouble(2)))).cache()
val clusters = KMeans.train(idPointRDD.map(_._2), 3, 20)
val clustersRDD = clusters.predict(idPointRDD.map(_._2))
val idClusterRDD = idPointRDD.map(_._1).zip(clustersRDD)
然后从中创建数据帧
val idCluster = idClusterRDD.toDF("id", "cluster")
它之所以有效,是因为映射不会改变RDD中数据的顺序,这就是为什么您可以只压缩ID和预测结果
使用自定义函数(用户定义函数)
第二种方法涉及使用聚类。预测方法作为自定义项:
val bcClusters = sc.broadcast(clusters)
def predict(x: Double, y: Double): Int = {
bcClusters.value.predict(Vectors.dense(x, y))
}
sqlContext.udf.register("predict", predict _)
现在我们可以使用它向数据中添加预测:
val idCluster = data.selectExpr("id", "predict(x, y) as cluster")
请记住,Spark API不允许取消UDF注册。这意味着闭包数据将保存在内存中
错误/非最佳解决方案
- 使用clusters.predict而不广播
它在分布式设置中不起作用。编辑:实际上它会工作的,我被它搞糊涂了,它使用广播
sc.makeRDD(clusters.predict(parsedData.toArray()).toDF()
toArray
收集驱动程序中的所有数据。这意味着在分布式模式下,您将把集群ID复制到一个节点中。请告知此代码是否适用于您:
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.clustering._
val rows = data.rdd.map(r => (r.getDouble(1),r.getDouble(2))).cache()
val vectors = rows.map(r => Vectors.dense(r._1, r._2))
val kMeansModel = KMeans.train(vectors, 3, 20)
val predictions = rows.map{r => (r._1, kMeansModel.predict(Vectors.dense(r._1, r._2)))}
val df = predictions.toDF("id", "cluster")
df.show
根据您的代码,我假设:
data
是一个具有三列的数据框(label:Double
、x1:Double
和x2:Double
)
- 您需要
KMeans.predict
使用x1
和x2
进行集群分配closestCluster:Int
- 结果数据帧的格式应为(
label:Double
,closestCluster:Int
)
下面是一个简单的示例应用程序,其中一些玩具数据遵循假定的模式:
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.clustering.KMeans
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.sql.functions.{col, udf}
case class DataRow(label: Double, x1: Double, x2: Double)
val data = sqlContext.createDataFrame(sc.parallelize(Seq(
DataRow(3, 1, 2),
DataRow(5, 3, 4),
DataRow(7, 5, 6),
DataRow(6, 0, 0)
)))
val parsedData = data.rdd.map(s => Vectors.dense(s.getDouble(1),s.getDouble(2))).cache()
val clusters = KMeans.train(parsedData, 3, 20)
val t = udf { (x1: Double, x2: Double) => clusters.predict(Vectors.dense(x1, x2)) }
val result = data.select(col("label"), t(col("x1"), col("x2")))
重要的部分是最后两行
创建一个UDF(用户定义函数),该函数可直接应用于数据帧列(在本例中为两列x1
和x2
)
选择标签
列以及应用于x1
和x2
列的UDF。由于UDF将预测closestCluster
,因此在此之后结果将是一个由(label
,closestCluster
)组成的数据帧