在C#中实现快速DBSCAN

时间:2015-10-28 03:37:02

标签: c# algorithm scikit-learn cluster-analysis dbscan

我尝试使用kd-tree在C#中实现DBSCAN。我按照以下方式执行: http://www.yzuzun.com/2015/07/dbscan-clustering-algorithm-and-c-implementation/

public class DBSCANAlgorithm
{
    private readonly Func<PointD, PointD, double> _metricFunc;


    public DBSCANAlgorithm(Func<PointD, PointD, double> metricFunc)
    {
        _metricFunc = metricFunc;
    }

    public void ComputeClusterDbscan(ScanPoint[] allPoints, double epsilon, int minPts, out HashSet<ScanPoint[]> clusters)
    {
        clusters = null;
        var allPointsDbscan = allPoints.Select(x => new DbscanPoint(x)).ToArray();

        var tree = new KDTree.KDTree<DbscanPoint>(2);
        for (var i = 0; i < allPointsDbscan.Length; ++i)
        {
            tree.AddPoint(new double[] { allPointsDbscan[i].ClusterPoint.point.X, allPointsDbscan[i].ClusterPoint.point.Y }, allPointsDbscan[i]);
        }

        var C = 0;
        for (int i = 0; i < allPointsDbscan.Length; i++)
        {
            var p = allPointsDbscan[i];
            if (p.IsVisited)
                continue;
            p.IsVisited = true;

            DbscanPoint[] neighborPts = null;
            RegionQuery(tree, p.ClusterPoint.point, epsilon, out neighborPts);
            if (neighborPts.Length < minPts)
                p.ClusterId = (int)ClusterIds.NOISE;
            else
            {
                C++;
                ExpandCluster(tree, p, neighborPts, C, epsilon, minPts);
            }
        }
        clusters = new HashSet<ScanPoint[]>(
            allPointsDbscan
                .Where(x => x.ClusterId > 0)
                .GroupBy(x => x.ClusterId)
                .Select(x => x.Select(y => y.ClusterPoint).ToArray())
            );

        return;
    }

    private void ExpandCluster(KDTree.KDTree<DbscanPoint> tree, DbscanPoint p, DbscanPoint[] neighborPts, int c, double epsilon, int minPts)
    {
        p.ClusterId = c;
        for (int i = 0; i < neighborPts.Length; i++)
        {
            var pn = neighborPts[i];
            if (!pn.IsVisited)
            {
                pn.IsVisited = true;
                DbscanPoint[] neighborPts2 = null;
                RegionQuery(tree, pn.ClusterPoint.point, epsilon, out neighborPts2);
                if (neighborPts2.Length >= minPts)
                {
                    neighborPts = neighborPts.Union(neighborPts2).ToArray();
                }
            }
            if (pn.ClusterId == (int)ClusterIds.UNCLASSIFIED)
                pn.ClusterId = c;
        }
    }

    private void RegionQuery(KDTree.KDTree<DbscanPoint> tree, PointD p, double epsilon, out DbscanPoint[] neighborPts)
    {
        int totalCount = 0;
        var pIter = tree.NearestNeighbors(new double[] { p.X, p.Y }, 10, epsilon);
        while (pIter.MoveNext())
        {
            totalCount++;
        }
        neighborPts = new DbscanPoint[totalCount];
        int currCount = 0;
        pIter.Reset();
        while (pIter.MoveNext())
        {
            neighborPts[currCount] = pIter.Current;
            currCount++;
        }

        return;
    }
}

//Dbscan clustering identifiers
public enum ClusterIds
{
    UNCLASSIFIED = 0,
    NOISE = -1
}

//Point container for Dbscan clustering
public class DbscanPoint
{
    public bool IsVisited;
    public ScanPoint ClusterPoint;
    public int ClusterId;

    public DbscanPoint(ScanPoint point)
    {
        ClusterPoint = point;
        IsVisited = false;
        ClusterId = (int)ClusterIds.UNCLASSIFIED;
    }
}

,并修改regionQuery(P, eps)以调用kd-tree的最近邻函数。为此,我将kd-sharp库用于C#,这是最快的kd-tree实现之一。

然而,当给定大约20000个2d点的数据集时,其性能在40s的范围内,与DBSCAN的scikit-learn python实现相比,DBSCAN给出相同的参数,大约需要2s。

由于此算法适用于我正在编写的C#程序,因此我无法使用C#。因此,我想知道在优化算法方面我还缺少什么?

0 个答案:

没有答案