Skip to content

Commit

Permalink
[ctrlLib] added DBSCAN clustering
Browse files Browse the repository at this point in the history
  • Loading branch information
pattacini committed Jul 11, 2018
1 parent 41c2983 commit f752d61
Show file tree
Hide file tree
Showing 4 changed files with 277 additions and 3 deletions.
6 changes: 4 additions & 2 deletions src/libraries/ctrlLib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ set(folder_source src/math.cpp
src/minJerkCtrl.cpp
src/optimalControl.cpp
src/neuralNetworks.cpp
src/outliersDetection.cpp)
src/outliersDetection.cpp
src/clustering.cpp)

set(folder_header include/iCub/ctrl/math.h
include/iCub/ctrl/filters.h
Expand All @@ -28,7 +29,8 @@ set(folder_header include/iCub/ctrl/math.h
include/iCub/ctrl/minJerkCtrl.h
include/iCub/ctrl/optimalControl.h
include/iCub/ctrl/neuralNetworks.h
include/iCub/ctrl/outliersDetection.h)
include/iCub/ctrl/outliersDetection.h
include/iCub/ctrl/clustering.h)

if(ICUB_USE_GSL)
set(folder_source ${folder_source} src/functionEncoder.cpp)
Expand Down
5 changes: 4 additions & 1 deletion src/libraries/ctrlLib/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
2018-07-11 Ugo Pattacini <ugo.pattacini@iit.it>
* clustering: added DBSCAN algorithm

2018-06-07 Ugo Pattacini <ugo.pattacini@iit.it>
* ctrlLib/parallelPID: enforce zero output of a disabled integrator
* parallelPID: enforce zero output of a disabled integrator

2018-02-19 Ugo Pattacini <ugo.pattacini@iit.it>
* re-licesing to BSD3
Expand Down
99 changes: 99 additions & 0 deletions src/libraries/ctrlLib/include/iCub/ctrl/clustering.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright (C) 2006-2018 Istituto Italiano di Tecnologia (IIT)
* Copyright (C) 2006-2010 RobotCub Consortium
* All rights reserved.
*
* This software may be modified and distributed under the terms
* of the BSD-3-Clause license. See the accompanying LICENSE file for
* details.
*/

/**
* \defgroup clustering Clustering
*
* @ingroup ctrlLib
*
* Classes for clustering.
*
* \author Ugo Pattacini
*
*/

#ifndef __CLUSTERING_H__
#define __CLUSTERING_H__

#include <map>
#include <set>

#include <yarp/os/Property.h>
#include <yarp/sig/Vector.h>

namespace iCub
{

namespace ctrl
{

/**
* \ingroup clustering
*
* Abstract class for clustering.
*/
class Clustering
{
public:
/**
* Cluster the provided data.
* @param data contains points to be clustered.
* @param options contains clustering options.
* @return clusters as a mapping between classes and the sets of
* elements indexes wrt the original data;
*/
virtual std::map<size_t,std::set<size_t>> cluster(const std::vector<yarp::sig::Vector> &data,
const yarp::os::Property &options) = 0;

/**
* Virtual destructor.
*/
virtual ~Clustering() { }
};


/**
* \ingroup clustering
*
* Data clustering based on DBSCAN algorithm.
*
* @note This implementation is based on the code available at
* https://github.com/gyaikhom/dbscan.
*/
class DBSCAN : public Clustering
{
public:
/**
* Cluster the provided data.
* @param data contains points to be clustered.
* @param options contains clustering options. The available
* options are: "epsilon" representing the
* proximity sensitivity; "minpts" representing
* the minimum number of neighbours.
* @return clusters as a mapping between classes and the sets of
* elements indexes wrt the original data;
*/
std::map<size_t,std::set<size_t>> cluster(const std::vector<yarp::sig::Vector> &data,
const yarp::os::Property &options) override;

/**
* Virtual destructor.
*/
virtual ~DBSCAN() { }
};

}

}

#endif



170 changes: 170 additions & 0 deletions src/libraries/ctrlLib/src/clustering.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/*
* Copyright (C) 2006-2018 Istituto Italiano di Tecnologia (IIT)
* Copyright (C) 2006-2010 RobotCub Consortium
* All rights reserved.
*
* This software may be modified and distributed under the terms
* of the BSD-3-Clause license. See the accompanying LICENSE file for
* details.
*/

#include <memory>
#include <vector>
#include <yarp/math/Math.h>
#include <iCub/ctrl/clustering.h>

using namespace std;
using namespace yarp::os;
using namespace yarp::sig;
using namespace yarp::math;
using namespace iCub::ctrl;

namespace iCub {
namespace ctrl {
namespace dbscan {
enum class PointType {
unclassified=-1,
noise=-2
};

struct Data_t {
const vector<Vector> &points;
map<size_t,set<size_t>> &table;
vector<int> ids;
Data_t(const vector<Vector> &points_,
map<size_t,set<size_t>> &table_) :
points(points_), table(table_) {
ids.assign(points.size(),(int)PointType::unclassified);
}
};

struct Node_t {
size_t index;
shared_ptr<Node_t> next;
};

struct Epsilon_neighbours_t {
size_t num_members;
shared_ptr<Node_t> head;
weak_ptr<Node_t> tail;
};

/**********************************************************************/
shared_ptr<Node_t> create_node(const size_t index)
{
shared_ptr<Node_t> node(new Node_t());
node->index=index;
node->next=nullptr;
return node;
}

/**********************************************************************/
void append(const size_t index, shared_ptr<Epsilon_neighbours_t> &en)
{
shared_ptr<Node_t> node=create_node(index);
if (en->head==nullptr)
{
en->head=node;
en->tail=node;
}
else
{
en->tail.lock()->next=node;
en->tail=node;
}
en->num_members++;
}

/**********************************************************************/
shared_ptr<Epsilon_neighbours_t> get_epsilon_neighbours(const size_t index,
Data_t &augData,
const double epsilon)
{
shared_ptr<Epsilon_neighbours_t> en(new Epsilon_neighbours_t());
for (size_t i=0; i<augData.points.size(); i++)
{
if ((i!=index) && (norm(augData.points[index]-augData.points[i])<=epsilon))
{
append(i,en);
}
}
return en;
}

/**********************************************************************/
void spread(const size_t index, shared_ptr<Epsilon_neighbours_t> &seeds,
const size_t id, Data_t &augData, const double epsilon,
const size_t minpts)
{
shared_ptr<Epsilon_neighbours_t> spread=get_epsilon_neighbours(index,augData,epsilon);
if (spread->num_members>=minpts)
{
for (shared_ptr<Node_t> node=spread->head; node!=nullptr; node=node->next)
{
if ((augData.ids[node->index]==(int)PointType::noise) ||
(augData.ids[node->index]==(int)PointType::unclassified))
{
if (augData.ids[node->index]==(int)PointType::unclassified)
{
append(node->index,seeds);
}
augData.ids[node->index]=(int)id;
augData.table[id].insert(node->index);
}
}
}
}

/**********************************************************************/
bool expand(const size_t index, const size_t id, Data_t &augData,
const double epsilon, const size_t minpts)
{
shared_ptr<Epsilon_neighbours_t> seeds=get_epsilon_neighbours(index,augData,epsilon);
if (seeds->num_members<minpts)
{
augData.ids[index]=(int)PointType::noise;
return false;
}
else
{
augData.ids[index]=(int)id;
for (shared_ptr<Node_t> h=seeds->head; h!=nullptr; h=h->next)
{
augData.ids[h->index]=(int)id;
}
for (shared_ptr<Node_t> h=seeds->head; h!=nullptr; h=h->next)
{
spread(h->index,seeds,id,augData,epsilon,minpts);
}
return true;
}
}
}
}
}


/**********************************************************************/
map<size_t,set<size_t>> DBSCAN::cluster(const vector<Vector> &data,
const Property &options)
{
double epsilon=options.check("epsilon",Value(1.0)).asDouble();
size_t minpts=(size_t)options.check("minpts",Value(2)).asInt();

map<size_t,set<size_t>> table;
dbscan::Data_t augData(data,table);

size_t id=0;
for (size_t i=0; i<augData.points.size(); i++)
{
if (augData.ids[i]==(int)dbscan::PointType::unclassified)
{
if (dbscan::expand(i,id,augData,epsilon,minpts))
{
id++;
}
}
}
return table;
}

0 comments on commit f752d61

Please sign in to comment.