使用 ELKI 和 Mongodb

Posted

技术标签:

【中文标题】使用 ELKI 和 Mongodb【英文标题】:Using ELKI with Mongodb 【发布时间】:2016-01-28 06:47:18 【问题描述】:

通过使用测试用例,我能够看到如何直接从 Java 中使用 ELKI,但现在我想从 MongoDB 读取我的数据,然后使用 ELKI 对地理(经纬度)数据进行聚类。

我只能使用 ELKI 对 CSV 文件中的数据进行聚类。是否可以将 de.lmu.ifi.dbs.elki.database.Database 与 MongoDB 连接?我可以从java调试器中看到de.lmu.ifi.dbs.elki.database.Database中有一个databaseconnection字段。

我查询 MongoDB 为每一行创建 POJO,现在我想使用 ELKI 对这些对象进行聚类。

可以从 MongoDB 读取数据并将其写入 CSV 文件,然后使用 ELKI 读取该 CSV 文件,但我想知道是否有更简单的解决方案。

---------发现_1:

从ELKI - Use List<String> of objects to populate the Database 我发现我需要实现 de.lmu.ifi.dbs.elki.datasource.DatabaseConnection 并专门重写返回 MultiObjectsBundle 实例的 loadData() 方法。

所以我认为我应该用 MultiObjectsBundle 包装一个 POJO 列表。现在我正在查看 MultiObjectsBundle,看起来数据应该保存在列中。为什么列数据类型是 List> 不应该是 List?只是您要聚类的项目列表?

我有点困惑。 ELKI 怎么知道它应该查看 POJO 的 long 和 lat?我在哪里告诉 ELKI 这样做?使用 de.lmu.ifi.dbs.elki.data.type.SimpleTypeInformation?

---------发现_2:

我曾尝试使用 ArrayAdapterDatabaseConnection 并尝试实现 DatabaseConnection。抱歉,我需要用非常简单的术语来理解。

这是我的聚类代码:

    int minPts=3;
    double eps=0.08; 
    double[][] data1 = -0.197574246, 51.49960695, -0.084605692, 51.52128377, -0.120973687, 51.53005939, -0.156876, 51.49313, 
            -0.144228881, 51.51811784, -0.1680743, 51.53430039, -0.170134484,51.52834133,  -0.096440751, 51.5073853, 
            -0.092754157, 51.50597426, -0.122502346, 51.52395143, -0.136039674, 51.51991453, -0.123616824, 51.52994371, 
            -0.127854211, 51.51772703, -0.125979294, 51.52635795, -0.109006325, 51.5216612, -0.12221963, 51.51477076, -0.131161087, 51.52505093 ;


    //      ArrayAdapterDatabaseConnection dbcon = new ArrayAdapterDatabaseConnection(data1);
    DatabaseConnection dbcon = new MyDBConnection();

    ListParameterization params = new ListParameterization();
    params.addParameter(de.lmu.ifi.dbs.elki.algorithm.clustering.DBSCAN.Parameterizer.MINPTS_ID, minPts);
    params.addParameter(de.lmu.ifi.dbs.elki.algorithm.clustering.DBSCAN.Parameterizer.EPSILON_ID, eps);
    params.addParameter(DBSCAN.DISTANCE_FUNCTION_ID, EuclideanDistanceFunction.class);
    params.addParameter(AbstractDatabase.Parameterizer.DATABASE_CONNECTION_ID, dbcon);
    params.addParameter(AbstractDatabase.Parameterizer.INDEX_ID,
            RStarTreeFactory.class);
    params.addParameter(RStarTreeFactory.Parameterizer.BULK_SPLIT_ID, 
            SortTileRecursiveBulkSplit.class);
    params.addParameter(AbstractPageFileFactory.Parameterizer.PAGE_SIZE_ID, 1000);

    Database db = ClassGenericsUtil.parameterizeOrAbort(StaticArrayDatabase.class, params);
    db.initialize();

    GeneralizedDBSCAN dbscan = ClassGenericsUtil.parameterizeOrAbort(GeneralizedDBSCAN.class, params);

    Relation<DoubleVector> rel = db.getRelation(TypeUtil.DOUBLE_VECTOR_FIELD);
    Relation<ExternalID> relID = db.getRelation(TypeUtil.EXTERNALID);

    DBIDRange ids = (DBIDRange) rel.getDBIDs();
    Clustering<Model> result = dbscan.run(db);  

    int i =0;
    for(Cluster<Model> clu : result.getAllClusters()) 
        System.out.println("#" + i + ": " + clu.getNameAutomatic());
        System.out.println("Size: " + clu.size());

        System.out.print("Objects: ");
        for(DBIDIter it = clu.getIDs().iter(); it.valid(); it.advance()) 
           DoubleVector v = rel.get(it);
           ExternalID exID = relID.get(it);
           System.out.print("DoubleVec: ["+v+"]");
           System.out.print("ExID: ["+exID+"]");

           final int offset = ids.getOffset(it);
           System.out.print(" " + offset);
        
        System.out.println();
        ++i;
     

ArrayAdapterDatabaseConnection 产生两个集群,当我设置 epsilon=0.008 dbscan 开始创建集群时,我只需要使用 epsilon 的值。当我设置 epsilon=0.04 时,所有项目都在 1 个集群中。

我也尝试过实现DatabaseConnection:

@Override
public MultipleObjectsBundle loadData()  
    MultipleObjectsBundle bundle = new MultipleObjectsBundle(); 

    List<Station> stations = getStations();
    List<DoubleVector> vecs = new ArrayList<DoubleVector>();
    List<ExternalID> ids = new ArrayList<ExternalID>();

    for (Station s : stations)

        String strID = Integer.toString(s.getId());
        ExternalID i = new ExternalID(strID);
        ids.add(i);     

        double[] st = s.getLongitude(), s.getLatitude();
        DoubleVector dv = new DoubleVector(st); 
        vecs.add(dv);
     

    SimpleTypeInformation<DoubleVector> type = new VectorFieldTypeInformation<>(DoubleVector.FACTORY, 2, 2, DoubleVector.FACTORY.getDefaultSerializer());

    bundle.appendColumn(type, vecs);      
    bundle.appendColumn(TypeUtil.EXTERNALID, ids);
    return bundle;
 

这些 long/lat 与 ID 相关联,我需要将它们链接回此 ID 到值。使用 ID 偏移量(在上面的代码中)是唯一的方法吗?我尝试添加 ExternalID 列,但不知道如何检索特定 NumberVector 的 ExternalID?

在看到Using ELKI's Distance Function 之后,我尝试使用 Elki 的 longLatDistance 但它不起作用,我找不到任何示例来实现它。

【问题讨论】:

我觉得我应该使用这个例子中的关系 elki.dbs.ifi.lmu.de/browser/elki/elki/src/main/java/tutorial/… 但我不知道如何使它适应地理点 其实我觉得这就是我需要的elki.dbs.ifi.lmu.de/releases/release0.4.0/doc/de/lmu/ifi/dbs/…我现在只需要了解如何.. 所以.. 尝试实现 databaseConnection 我需要它以某种方式接受 long 和 lat 你看过例如的来源吗? ArrayAdapterDatabaseConnection? @Anony-Mousse 我已尝试实现 ArrayAdapterDatabaseConnection 请参阅我编辑的问题。谢谢。 【参考方案1】:

数据源的接口称为DatabaseConnection

JavaDoc of DatabaseConnection

您可以实现基于 MongoDB 的接口来获取数据。

接口不复杂,方法单一。

【讨论】:

请参阅我的问题中的编辑。我对如何实现 DatabaseConnection 有点困惑。 您想要一列DoubleVector,因为这是距离函数所需的数据类型。您需要二维数值向量场的类型。 所以在 MultiObjectsBundle List> 列中,对于每个条目,您有 2 个 DoubleVector,一个用于 long,另一个用于 lat SimpleTypeInformation 在哪里发挥作用?我不明白如何使用它? 你想要每个对象一个向量,很明显,还有一个VectorFieldTypeInformation,表明该列是一个二维向量场。 (向量具有 恰好 2 维,而不是像文本向量那样的 0-1000 维)。 数学对象中的向量,而不是Java中的误称Vector(这是一个通用列表)。

以上是关于使用 ELKI 和 Mongodb的主要内容,如果未能解决你的问题,请参考以下文章

使用 ELKI 的距离函数

KMeans 在 ELKI 中的使用,综合示例

构建ELKI当前发展状态的推荐方式是啥?

如何使用带有 ELKI 库的距离矩阵对数据进行聚类?

OPTICS 聚类算法的 ELKI 实现只检测到一个聚类

elki DBSCAN 的 distanceFunction 参数是啥?