我正在尝试对一些地理空间数据进行聚类,之前我曾尝试使用WEKA库。 我找到了此benchmarking,并决定尝试ELKI。
尽管建议不使用ELKI作为Java库(假设维护不如UI),我将它合并到我的应用程序中,我可以说我很开心关于结果。它用于存储数据的结构远比Weka使用的结构更有效,并且它可以选择使用空间索引这一事实绝对是一个优势。
然而,当我将Weka's DBSCAN的结果与来自ELKI's DBSCAN的结果进行比较时,我有点疑惑。我会接受不同的实现可以给出略有不同的结果,但是这些差异使得我认为算法存在问题(可能是我的代码)。在两种算法中,簇的数量和它们的几何形状是非常不同的。
为了记录,我使用的是最新版本的ELKI(0.6.0),我用于模拟的参数是:
minpts = 50 小量= 0.008
我编写了两个DBSCAN函数(用于Weka和ELKI),其中"入口点"是带有点的csv,"输出"对于它们两者也是相同的:计算一组点的凹壳(每个簇一个)的函数。由于将csv文件读入ELKI"数据库"相对简单,我认为我的问题可能是:
a)在算法的参数化中; b)阅读结果(最有可能)。
参数化DBSCAN不会带来任何挑战,我使用了两个强制参数,我之前通过UI测试过:
ListParameterization params2 = new ListParameterization();
params2.addParameter(de.lmu.ifi.dbs.elki.algorithm.clustering.DBSCAN.Parameterizer.MINPTS_ID, minPoints);
params2.addParameter(de.lmu.ifi.dbs.elki.algorithm.clustering.DBSCAN.Parameterizer.EPSILON_ID, epsilon);
阅读结果更具挑战性,因为我不完全理解存储集群的结构的组织;我的想法是遍历每个集群,获取点列表,并将其传递给计算凹壳的函数,以生成多边形。
ArrayList<Clustering<?>> cs = ResultUtil.filterResults(result, Clustering.class);
for (Clustering<?> c : cs) {
System.out.println("clusters: " + c.getAllClusters().size());
for (de.lmu.ifi.dbs.elki.data.Cluster<?> cluster : c.getAllClusters()) {
if (!cluster.isNoise()){
Coordinate[] ptList=new Coordinate[cluster.size()];
int ct=0;
for (DBIDIter iter = cluster.getIDs().iter(); iter.valid(); iter.advance()) {
ptList[ct]=dataMap.get(DBIDUtil.toString(iter));
++ct;
}
//there are no "empty" clusters
assertTrue(ptList.length>0);
GeoPolygon poly=getBoundaryFromCoordinates(ptList);
if (poly.getCoordinates().getGeometryType()==
"Polygon"){
try {
out.write(poly.coordinates.toText()+"\n");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else
System.out.println(
poly.getCoordinates().getGeometryType());
}//!noise
}
}
我注意到&#34;噪音&#34;作为一个集群出现了,所以我忽略了这个集群(我不想画它)。 我不确定这是否是阅读群集的正确方法,因为我没有找到很多例子。我也有一些问题,但我还没有找到答案:
任何可能指向正确方向的评论,或任何代码建议迭代ELKI的DBSCAN结果集都会非常受欢迎!我在我的代码中也使用了ELKI的OPTICSxi,我对这些结果有甚至更多的问题,但我想我会将其保存到另一篇文章中。
答案 0 :(得分:3)
这主要是@ Anony-Mousse的后续行动,他给出了一个非常完整的答案。
getTopLevelClusters()
和getAllClusters()
对DBSCAN执行相同的操作,因为DBSCAN不会生成分层群集。isNoise()==true
作为单个对象处理聚类可能是处理噪声的最佳方法。我们的OPTICSXi
实现返回的集群也是不相交的,但是您应该将所有子集群的成员视为外部集群的一部分。对于凸包,一种有效的方法是首先计算子群的凸包;然后为父母计算附加物体上的凸包+所有孩子的凸包点。RangeDBIDs
方法对于静态数据库来说非常简洁。与动态数据库一起使用的干净方法是具有标识对象的附加关系。使用CSV文件作为输入时,您只需添加一个包含标签的非数字列,而不是依赖于行编号。 object123
。从逻辑的角度来看,这是最好的方法 - 如果您希望能够识别对象,请为它们提供唯一的标识符。 ; - )答案 1 :(得分:2)
如果你注意它们的分配方式,那么访问ELKI的DBIDs
是有效的。
对于静态数据库,getDBIDs()
将返回一个RangeDBIDs
对象,它可以为您提供数据库的偏移量。这非常可靠。但是如果你总是重新开始你的过程,那么无论如何都会确定性地分配DBIDs
(只有在使用MiniGUI时,如果你重新开始工作,它们会有所不同!)
这也比DBIDUtil.toString
更有效。
DBSCAN结果不是分层结构,因此每个群集都应该是顶级群集。
至于Weka,它有时会自动标准化。那么epsilon值将会失真。对于地理数据,无论如何我更喜欢大地距离,纬度和经度上的欧几里德距离没有意义。
检查Wekas代码的这一部分:"norm" function, used by EuclideanDataObject。这个 看起来好像Wekas DBSCAN强制对数据集进行规范化!尝试将数据缩放到[0:1](我很确定在ELKI中有一个过滤器),如果之后结果相同吗?
从这段代码片段来看,我会责怪Weka。上面的代码对我来说也看起来有点低效。过滤器方法使IMHO比数据对象中的强制过滤更有意义。