Scala 如何用对象变换的方式编写for循环?
我需要在Scala中编写一个带有对象变异的for循环。在机器学习中,当进行聚类(将样本分配到最优分离的组中)时,为了确定集合中的最优组数,聚类算法使用不同的组数运行,为每个组数计算一些误差度量。最佳组数是组数图相对于误差度量的弯头。 在Spark ML库中,一个Scala 如何用对象变换的方式编写for循环?,scala,Scala,我需要在Scala中编写一个带有对象变异的for循环。在机器学习中,当进行聚类(将样本分配到最优分离的组中)时,为了确定集合中的最优组数,聚类算法使用不同的组数运行,为每个组数计算一些误差度量。最佳组数是组数图相对于误差度量的弯头。 在Spark ML库中,一个KMeans对象用于集群,其中组号作为参数传递。因此,我计算误差度量以绘制弯头图,如下所示: var baseClusterer = new KMeans() .setFeaturesCol("sca
KMeans
对象用于集群,其中组号作为参数传递。因此,我计算误差度量以绘制弯头图,如下所示:
var baseClusterer = new KMeans()
.setFeaturesCol("scaledFeatures")
.setPredictionCol("clusters")
.setSeed(0)
2 to 10 map {
baseClusterer = baseClusterer.setK(k)
baseClusterer.fit(scaledDF).computeCost(scaledDF)
}
我必须将clusterer对象声明为一个var,并在每次迭代中对其进行修改。有没有更适合scala的编写方法?您可以避免var这样做:
2 to 10 map { k =>
baseClusterer.setK(k).fit(scaledDF).computeCost(scaledDF)
}
您可以通过以下方式避免var:
2 to 10 map { k =>
baseClusterer.setK(k).fit(scaledDF).computeCost(scaledDF)
}
如果我正确理解您的逻辑,也许您可以使用foldLeft,其中每个循环都将返回修改/更新的对象,如下所示:
val finalClusterer = (2 to 10).foldLeft(baseClusterer) { (accum, elem) =>
val newClusterer = accum.copy(k = k)
newClusterer.fit(scaledDF).computeCost(scaledDF)
}
implicit class ClustererWrapper {
def copy {
...
}
}
这样,您将得到一个“最终聚类器”,在该聚类器中,您始终以基本聚类器为原点进行操作
编辑:我的代码使用baseClusterer作为case类,因此使用copy方法。如果您没有它,因为它似乎是一个java类,那么您可以创建一个充当包装器的隐式类,并在其中定义这样的方法,如下所示:
val finalClusterer = (2 to 10).foldLeft(baseClusterer) { (accum, elem) =>
val newClusterer = accum.copy(k = k)
newClusterer.fit(scaledDF).computeCost(scaledDF)
}
implicit class ClustererWrapper {
def copy {
...
}
}
如果我正确理解您的逻辑,也许您可以使用foldLeft,其中每个循环都将返回修改/更新的对象,如下所示:
val finalClusterer = (2 to 10).foldLeft(baseClusterer) { (accum, elem) =>
val newClusterer = accum.copy(k = k)
newClusterer.fit(scaledDF).computeCost(scaledDF)
}
implicit class ClustererWrapper {
def copy {
...
}
}
这样,您将得到一个“最终聚类器”,在该聚类器中,您始终以基本聚类器为原点进行操作
编辑:我的代码使用baseClusterer作为case类,因此使用copy方法。如果您没有它,因为它似乎是一个java类,那么您可以创建一个充当包装器的隐式类,并在其中定义这样的方法,如下所示:
val finalClusterer = (2 to 10).foldLeft(baseClusterer) { (accum, elem) =>
val newClusterer = accum.copy(k = k)
newClusterer.fit(scaledDF).computeCost(scaledDF)
}
implicit class ClustererWrapper {
def copy {
...
}
}
注:此版本是根据注释从原始版本修改而来
如果您打算在不同的数据上重复这个操作,您可能需要考虑创建一个聚类器列表,然后使用:
val clusterers = (2 to 10).map(k =>
new KMeans()
.setFeaturesCol("scaledFeatures")
.setPredictionCol("clusters")
.setSeed(0)
.setk(k)
)
val costs = clusterers.map(_.fit(scaledDF).computeCost(scaledDF))
但是,请参阅@BogdanVakulenko的答案,以获得重新编写原始版本的好方法
还请注意,使用相同的k
多次使用不同的setSeed
值可能是一个好主意,以避免局部极小值。注意:此版本是根据注释从原始版本修改而来的
如果您打算在不同的数据上重复这个操作,您可能需要考虑创建一个聚类器列表,然后使用:
val clusterers = (2 to 10).map(k =>
new KMeans()
.setFeaturesCol("scaledFeatures")
.setPredictionCol("clusters")
.setSeed(0)
.setk(k)
)
val costs = clusterers.map(_.fit(scaledDF).computeCost(scaledDF))
但是,请参阅@BogdanVakulenko的答案,以获得重新编写原始版本的好方法
还请注意,使用相同的
k
多次使用不同的setSeed
值可能是一个好主意,以避免出现局部极小值。感谢您的回复。KMeans是SparkML库中的scala类。感谢您的回复。KMeans是SparkML库中的scala类。感谢您的回复。这更像scala。我仍然必须将baseClusterer声明为var。我知道经验法则是尽可能地声明val。对象突变看起来不像scala。如果没有对象突变,我就无法编写它,这让我很烦恼,这就是为什么我问这个问题。你可以将baseClusterer声明为val。但是Tim的方法更像scala-way@BogdanVakulenko不,你的答案比蒂姆的好(事实上,他的根本不起作用)。。。我解释了为什么对他的回答发表评论。@Dima,这是否意味着baseCluster将发生变异,并且baseCluster可用结果可用,foreach将更加清楚,因为foreach用于副作用。foreach用于预期副作用。这里的结果是computeCost
返回的值。副作用只是spark库实现的一个不幸产物。感谢您的回复。这更像scala。我仍然必须将baseClusterer声明为var。我知道经验法则是尽可能地声明val。对象突变看起来不像scala。如果没有对象突变,我就无法编写它,这让我很烦恼,这就是为什么我问这个问题。你可以将baseClusterer声明为val。但是Tim的方法更像scala-way@BogdanVakulenko不,你的答案比蒂姆的好(事实上,他的根本不起作用)。。。我解释了为什么对他的回答发表评论。@Dima,这是否意味着baseCluster将发生变异,并且baseCluster可用结果可用,foreach将更加清楚,因为foreach用于副作用。foreach用于预期副作用。这里的结果是computeCost
返回的值。副作用只是spark的库实现的一个不幸产物。(2到10)。map(basecluster.setK)-它将更具伸缩性。我不确定KMeans
是如何实现的,但setK
听起来像是对实际对象进行了变异,而不是复制。如果是这样的话,这种方法最终只会做同样的事情10次。我认为@BogdanVakulenko的答案更好,因为它没有对的实现细节做出假设。setK
只是查找了一下,它确实改变了对象,所以这不起作用@Dima感谢您的评论,我已经更新了答案以反映您的评论,因此现在应该可以正常工作了!(2到10).map(basecluster.setK)-这将是一种更具伸缩性的方式。我不确定KMeans
是如何实现的,但setK
听起来像是对实际对象进行了变异,而不是复制。如果是这样的话,这种方法最终只会做同样的事情10次。我认为@BogdanVakulenko的答案更好,因为它没有对的实现细节做出假设。setK
只是查找了一下,它确实改变了对象,所以这不起作用@迪玛:谢谢你的评论,我已经更新了回复