Искра MLLib Kmeans из фрейма данных и обратно
Я стремлюсь применить алгоритм кластеризации kmeans к очень большому набору данных с помощью Spark (1.3.1) MLLib. Я вызвал данные из HDFS, используя hiveContext из Spark, и в конечном итоге хотел бы вернуть его туда таким образом - в этом формате
|I.D |cluster |
===================
|546 |2 |
|6534 |4 |
|236 |5 |
|875 |2 |
я запустил следующий код, где "данные" - это фрейм данных двойников и идентификатор для первого столбца.
val parsedData = data.rdd.map(s => Vectors.dense(s.getDouble(1),s.getDouble(2))).cache()
val clusters = KMeans.train(parsedData, 3, 20)
это работает успешно, я застрял теперь отображение кластеров обратно в их соответствующие идентификаторы, в фрейм данных, как описано выше. Я могу преобразовать его в datframe с помощью:
sc.makeRDD(clusters.predict(parsedData).toArray()).toDF()
но это все, что у меня есть. этот пост находится на правильном пути, и этот пост Я думаю, что задает аналогичный вопрос моему.
Я подозреваю, что библиотека labeledPoint необходима. Любые комментарии, ответы будут оценены, ура.
Edit: только что найдено этой в списке пользователей Spark выглядит многообещающе
4 ответов
Я понимаю, что вы хотите получить DataFrame в конце. Я вижу два возможных решения. Я бы сказал, что выбор между ними-дело вкуса.
создать столбец из RDD
очень легко получить пары идентификаторов и кластеров в виде RDD:
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, поэтому вы можете просто zip ids с результатами предсказание.
используйте UDF (пользовательская функция)
Второй метод включает в себя использование clusters.predict
метод как UDF:
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. Это означает, что данные закрытия будут храниться в памяти.
неправильные / неоптимальные решения
использование кластеров.предсказать без вещание
он не будет работать в распределенной конфигурации. Edit: на самом деле это будет работать, я был смущен реализация прогноза для RDD, который использует широковещательные.
sc.makeRDD(clusters.predict(parsedData).toArray()).toDF()
toArray
собирает все данные в драйвере. Это означает, что в распределенном режиме вы будете копировать идентификаторы кластера в один узел.
Я делаю что-то подобное, используя pySpark. Я предполагаю, что вы можете напрямую перевести это в Scala, поскольку нет ничего конкретного python. myPointsWithID-это мой RDD с идентификатором для каждой точки и точкой, представленной в виде массива значений.
# 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)))
из вашего кода, я предполагаю:
-
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
).выбирает
label
столбец вместе с UDF применяется кx1
иx2
столбцы. Поскольку ОДС предскажетclosestCluster
, после этогоresult
будет фреймом данных, состоящим из (label
,closestCluster
)
Дайте мне знать, если этот код работает для вас:
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