modifiedDistance.put(id, maxDist);
}
}
// Second step - compute actual SLOM values
DoubleMinMax slomminmax = new DoubleMinMax();
WritableDataStore<Double> sloms = DataStoreUtil.makeStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC, Double.class);
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.get(neighbor);
cnt++;
}
double slom;
if(cnt > 0) {
// With and without the object itself:
double avgPlus = (sum + modifiedDistance.get(id)) / (cnt + 1);
double avg = sum / cnt;
double beta = 0;
for(DBID neighbor : neighbors) {
final double dist = modifiedDistance.get(neighbor).doubleValue();
if(dist > avgPlus) {
beta += 1;
}
else if(dist < avgPlus) {
beta -= 1;
}
}
// Include object itself
if(!neighbors.contains(id)) {
final double dist = modifiedDistance.get(id).doubleValue();
if(dist > avgPlus) {
beta += 1;
}
else if(dist < avgPlus) {
beta -= 1;
}
}
beta = Math.abs(beta);
// note: cnt == size of N(x), not N+(x)
if(cnt > 1) {
beta = Math.max(beta, 1.0) / (cnt - 1);
}
else {
// Workaround insufficiency in SLOM paper - div by zero
beta = 1.0;
}
beta = beta / (1 + avg);
slom = beta * modifiedDistance.get(id);
}
else {
// No neighbors to compare to - no score.
slom = 0.0;
}
sloms.put(id, slom);
slomminmax.put(slom);
}
Relation<Double> scoreResult = new MaterializedRelation<Double>("SLOM", "slom-outlier", TypeUtil.DOUBLE, sloms, relation.getDBIDs());
OutlierScoreMeta scoreMeta = new BasicOutlierScoreMeta(slomminmax.getMin(), slomminmax.getMax(), 0.0, Double.POSITIVE_INFINITY);
OutlierResult or = new OutlierResult(scoreMeta, scoreResult);
or.addChildResult(npred);
return or;
}