我有一个包含许多列的spark数据帧'mydataframe'。我试图只在两列上运行kmeans:lat和long(纬度和经度),使用它们作为简单值)。我想基于这两个列提取7个集群,然后我想将集群asignment附加到我的原始数据帧。我试过了:
from numpy import array
from math import sqrt
from pyspark.mllib.clustering import KMeans, KMeansModel
# Prepare a data frame with just 2 columns:
data = mydataframe.select('lat', 'long')
data_rdd = data.rdd # needs to be an RDD
data_rdd.cache()
# Build the model (cluster the data)
clusters = KMeans.train(data_rdd, 7, maxIterations=15, initializationMode="random")
但过了一段时间我收到了错误:
org.apache.spark.SparkException:作业因阶段失败而中止:阶段5191.0中的任务1失败4次,最近失败:阶段5191.0中失去任务1.3(TID 260738,10.19.211.69,执行者1):org .apache.spark.api.python.PythonException:Traceback(最近一次调用最后一次)
我试图分离并重新连接群集。结果相同。我做错了什么?
非常感谢!
答案 0 :(得分:36)
因为,基于another recent question of yours,我猜你正处于Spark集群的第一步(你甚至导入sqrt
& array
,而不使用它们,可能是因为就像在docs example中那样,让我提供一个更普遍的建议,而不是你在这里提出的具体问题(希望也可以让你免于随后再打开3-4个问题,试图让你的群集分配回到您的数据框中)...
自
您的数据已存在于数据框
您希望将群集成员资格附加回初始成员资格 数据帧
您没有理由恢复到RDD并使用(soon to be deprecated)MLlib包;使用(现在推荐的)ML软件包,可以更轻松,优雅,高效地完成工作,该软件包直接与数据框架配合使用。
第0步 - 制作一些与您类似的玩具数据:
spark.version
# u'2.2.0'
df = spark.createDataFrame([[0, 33.3, -17.5],
[1, 40.4, -20.5],
[2, 28., -23.9],
[3, 29.5, -19.0],
[4, 32.8, -18.84]
],
["other","lat", "long"])
df.show()
# +-----+----+------+
# |other| lat| long|
# +-----+----+------+
# | 0|33.3| -17.5|
# | 1|40.4| -20.5|
# | 2|28.0| -23.9|
# | 3|29.5| -19.0|
# | 4|32.8|-18.84|
# +-----+----+------+
第1步 - 汇总您的功能
与大多数ML包不同,Spark ML要求您的输入功能收集在数据帧的单列中,通常命名为features
;它提供了一种具体的方法,VectorAssembler
:
from pyspark.ml.feature import VectorAssembler
vecAssembler = VectorAssembler(inputCols=["lat", "long"], outputCol="features")
new_df = vecAssembler.transform(df)
new_df.show()
# +-----+----+------+-------------+
# |other| lat| long| features|
# +-----+----+------+-------------+
# | 0|33.3| -17.5| [33.3,-17.5]|
# | 1|40.4| -20.5| [40.4,-20.5]|
# | 2|28.0| -23.9| [28.0,-23.9]|
# | 3|29.5| -19.0| [29.5,-19.0]|
# | 4|32.8|-18.84|[32.8,-18.84]|
# +-----+----+------+-------------+
正如可能已经猜到的那样,参数inputCols
用于告诉VectoeAssembler
我们的数据框中的哪些特定列将用作要素。
第2步 - 适合您的KMeans模型
from pyspark.ml.clustering import KMeans
kmeans = KMeans(k=2, seed=1) # 2 clusters here
model = kmeans.fit(new_df.select('features'))
select('features')
这里用于告诉算法数据帧的哪一列用于群集 - 请记住,在上面的步骤1之后,您的原始lat
& long
功能不再直接使用。
第3步 - 将您的初始数据框转换为包含群集分配
transformed = model.transform(new_df)
transformed.show()
# +-----+----+------+-------------+----------+
# |other| lat| long| features|prediction|
# +-----+----+------+-------------+----------+
# | 0|33.3| -17.5| [33.3,-17.5]| 0|
# | 1|40.4| -20.5| [40.4,-20.5]| 1|
# | 2|28.0| -23.9| [28.0,-23.9]| 0|
# | 3|29.5| -19.0| [29.5,-19.0]| 0|
# | 4|32.8|-18.84|[32.8,-18.84]| 0|
# +-----+----+------+-------------+----------+
transformed
数据框的最后一列prediction
显示了群集分配 - 在我的玩具案例中,我最终在群集#0中有4条记录,在群集#1中有1条记录。
您可以使用transformed
语句进一步操纵select
数据框,甚至drop
features
列{已经完成其功能,可能不再需要) ...
希望你现在更接近你真正想要实现的目标。要提取群集统计信息等,another recent answer of mine可能会有所帮助......
答案 1 :(得分:3)
尽管我有其他一般性答案,如果你出于某种原因必须坚持使用MLlib& RDD,这就是使用相同玩具df
导致错误的原因。
当您从数据框中select
列转换为RDD时,结果是行的RDD:
df.select('lat', 'long').rdd.collect()
# [Row(lat=33.3, long=-17.5), Row(lat=40.4, long=-20.5), Row(lat=28.0, long=-23.9), Row(lat=29.5, long=-19.0), Row(lat=32.8, long=-18.84)]
不适合作为MLlib KMeans的输入。您需要map
操作才能实现此目的:
df.select('lat', 'long').rdd.map(lambda x: (x[0], x[1])).collect()
# [(33.3, -17.5), (40.4, -20.5), (28.0, -23.9), (29.5, -19.0), (32.8, -18.84)]
所以,你的代码应该是这样的:
from pyspark.mllib.clustering import KMeans, KMeansModel
rdd = df.select('lat', 'long').rdd.map(lambda x: (x[0], x[1]))
clusters = KMeans.train(rdd, 2, maxIterations=10, initializationMode="random") # works OK
clusters.centers
# [array([ 40.4, -20.5]), array([ 30.9 , -19.81])]