首页 > 解决方案 > Apache Commons Math 中 DBSCAN 的自定义距离度量(v3.1 与 v3.6)

问题描述

我想使用 Apache Commons MathDBSCANClusterer<T extends Clusterable>使用 DBSCAN 算法执行聚类,但使用自定义距离度量,因为我的数据点包含非数值。这似乎在旧版本中很容易实现(请注意,此类的完全限定名称是org.apache.commons.math3.stat.clustering.DBSCANClusterer<T>,而它org.apache.commons.math3.ml.clustering.DBSCANClusterer<T>适用于当前版本),现在已被弃用。在旧版本中,Clusterable将采用 type-param, T,描述被聚类的数据点的类型,并且两点之间的距离将由 的实现来定义Clusterable.distanceFrom(T),例如:

class MyPoint implements Clusterable<MyPoint> {
    private String someStr = ...;
    private double someDouble = ...;

    @Override
    public double distanceFrom(MyPoint p) {
        // Arbitrary distance metric goes here, e.g.:
        double stringsEqual = this.someStr.equals(p.someStr) ? 0.0 : 10000.0;
        return stringsEqual + Math.sqrt(Math.pow(p.someDouble - this.someDouble, 2.0)); 
    }
}

在当前版本中,Clusterable不再参数化。这意味着必须想出一种方法来将一个(可能是非数字的)数据点表示为 adouble[]并从 中返回该表示getPoint(),例如:

class MyPoint implements Clusterable {
    private String someStr = ...;
    private double someDouble = ...;

    @Override
    public double[] getPoint() {
        double[] res = new double[2];
        res[1] = someDouble; // obvious
        res[0] = ...; // some way of representing someStr as a double required
        return res;
    }
}

然后提供一个实现,根据被比较的两个点DistanceMeasure的表示来定义自定义距离函数double[],例如:

class CustomDistanceMeasure implements DistanceMeasure {
    @Override
    public double compute(double[] a, double[] b) {
        // Let's mimic the distance function from earlier, assuming that
        // a[0] is different from b[0] if the two 'someStr' variables were
        // different when their double representations were created.
        double stringsEqual = a[0] == b[0] ? 0.0 : 10000.0;
        return stringsEqual + Math.sqrt(Math.pow(a[1] - b[1], 2.0));
    }
}

我的数据点的形式为(整数、整数、字符串、字符串):

class MyPoint {
    int i1;
    int i2;
    String str1;
    String str2;
}

我想使用一个距离函数/度量,它基本上说“如果str1和/或str2不同MyPoint mpaMyPoint mpb则距离是最大的,否则距离是整数之间的欧几里德距离”,如下面的片段所示:

class Dist {
    static double distance(MyPoint mpa, MyPoint mpb) {
        if (!mpa.str1.equals(mpb.str1) || !mpa.str2.equals(mpb.str2)) {
            return Double.MAX_VALUE;
        }
        return Math.sqrt(Math.pow(mpa.i1 - mpb.i1, 2.0) + Math.pow(mpa.i2 - mpb.i2, 2.0));
    }
}

问题:

  1. 我如何将 a 表示String为 a double,以便在 Apache Commons Math 的当前版本(v3.6.1)中启用上述距离度量?String.hashCode()是不够的,因为哈希码冲突会导致不同的字符串被认为是相等的。这似乎是一个无法解决的问题,因为我本质上是在尝试创建一个从无限字符串集到有限数值集(64bit double)的唯一映射。
  2. 由于(1)似乎不可能,我是否误解了如何使用该库?如果是,我是不是走错路了?
  3. 对于这种距离度量,我唯一的选择是使用不推荐使用的版本吗?如果是,(3a)为什么设计者会选择使库不那么通用?也许有利于速度?也许是为了摆脱class MyPoint implements Clusterable<MyPoint>某些人可能认为设计不佳的自我参照?(我意识到这可能太自以为是了,所以如果是这样,请忽略它)。对于公共数学专家:​​(3b)使用不推荐使用的版本除了前向兼容性之外还有哪些缺点(不推荐使用的版本将在 4.0 中删除)?是不是比较慢?也许甚至不正确?

注意:我知道ELKI在一组 SO 用户中很受欢迎,但它不符合我的需求,因为它是作为命令行和 GUI 工具销售的,而不是包含在第三方应用程序中的 Java 库

您甚至可以将 ELKI 嵌入到您的应用程序中(如果您接受 AGPL-3 许可),但我们目前(还)不建议这样做,因为 API 仍在发生重大变化。[...]

ELKI 并非设计为可嵌入库。它可以使用,但它不是为这种方式设计的。ELKI 有大量的选项和功能,这是有代价的,无论是在运行时(尽管它可以轻松胜过 R 和 Weka,例如!)内存使用,尤其是在代码复杂性方面。

ELKI 是为研究数据挖掘算法而设计的,而不是为了让它们易于包含在任意应用程序中。相反,如果您遇到特定问题,您应该使用 ELKI 找出哪种方法效果好,然后以针对您的问题的优化方式重新实现该方法(甚至可能在 C++ 中,以进一步减少内存和运行时间)。

标签: javadistancedbscanapache-commons-math

解决方案


推荐阅读