* @param spatial Spatial Relation to use.
* @param relation Relation to use.
* @return Outlier detection result
*/
public OutlierResult run(Database database, Relation<N> spatial, Relation<O> relation) {
final NeighborSetPredicate npred = getNeighborSetPredicateFactory().instantiate(spatial);
DistanceQuery<O, D> distFunc = getNonSpatialDistanceFunction().instantiate(relation);
WritableDoubleDataStore modifiedDistance = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_TEMP);
// calculate D-Tilde
for(DBID id : relation.iterDBIDs()) {
double sum = 0;
double maxDist = 0;
int cnt = 0;
final DBIDs neighbors = npred.getNeighborDBIDs(id);
for(DBID neighbor : neighbors) {
if(id.equals(neighbor)) {
continue;
}
double dist = distFunc.distance(id, neighbor).doubleValue();
sum += dist;
cnt++;
maxDist = Math.max(maxDist, dist);
}
if(cnt > 1) {
modifiedDistance.putDouble(id, ((sum - maxDist) / (cnt - 1)));
}
else {
// Use regular distance when the d-tilde trick is undefined.
// Note: this can be 0 when there were no neighbors.
modifiedDistance.putDouble(id, maxDist);
}
}
// Second step - compute actual SLOM values
DoubleMinMax slomminmax = new DoubleMinMax();
WritableDoubleDataStore sloms = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC);
for(DBID id : relation.iterDBIDs()) {
double sum = 0;
int cnt = 0;
final DBIDs neighbors = npred.getNeighborDBIDs(id);
for(DBID neighbor : neighbors) {
if(neighbor.equals(id)) {
continue;
}
sum += modifiedDistance.doubleValue(neighbor);