Пользовательская метрика расстояния для DBSCAN в Apache Commons Math (v3.1 против v3.6)
Я хочу использовать Apache Commons Math's DBSCANClusterer<T extends Clusterable>
выполнить кластеризацию, используя алгоритм DBSCAN, но с пользовательской метрикой расстояния, поскольку мои точки данных содержат нечисловые значения. Кажется, что это было легко достижимо в старой версии (обратите внимание, что полное имя этого класса org.apache.commons.math3.stat.clustering.DBSCANClusterer<T>
тогда как это org.apache.commons.math3.ml.clustering.DBSCANClusterer<T>
для текущей версии), которая теперь устарела. В старой версии Clusterable
взял бы тип-параметр, T
описание типа точек данных, которые кластеризуются, и расстояние между двумя точками будет определяться реализацией Clusterable.distanceFrom(T)
Например:
class MyPoint implements Clusterable<MyPoint> {
private String someStr = ...;
private double someDouble = ...;
@Override
public double distanceFrom(MyPoint p) {
// Arbitrary distance metric goes here, e.g.:
double stringsEqual = this.someStr.equals(p.someStr) ? 0.0 : 10000.0;
return stringsEqual + Math.sqrt(Math.pow(p.someDouble - this.someDouble, 2.0));
}
}
В текущем выпуске Clusterable
больше не параметризован. Это означает, что нужно придумать способ представления своих (потенциально не числовых) точек данных как double[]
и вернуть это представление из getPoint()
Например:
class MyPoint implements Clusterable {
private String someStr = ...;
private double someDouble = ...;
@Override
public double[] getPoint() {
double[] res = new double[2];
res[1] = someDouble; // obvious
res[0] = ...; // some way of representing someStr as a double required
return res;
}
}
А затем обеспечить реализацию DistanceMeasure
который определяет пользовательскую функцию расстояния с точки зрения double[]
представления двух сравниваемых точек, например:
class CustomDistanceMeasure implements DistanceMeasure {
@Override
public double compute(double[] a, double[] b) {
// Let's mimic the distance function from earlier, assuming that
// a[0] is different from b[0] if the two 'someStr' variables were
// different when their double representations were created.
double stringsEqual = a[0] == b[0] ? 0.0 : 10000.0;
return stringsEqual + Math.sqrt(Math.pow(a[1] - b[1], 2.0));
}
}
Мои точки данных имеют форму (целое, целое, строка, строка):
class MyPoint {
int i1;
int i2;
String str1;
String str2;
}
И я хочу использовать функцию / метрику расстояния, которая по существу говорит "если str1
и / или str2
отличаются для MyPoint mpa
а также MyPoint mpb
максимальное расстояние, в противном случае это евклидово расстояние между целыми числами ", как показано в следующем фрагменте:
class Dist {
static double distance(MyPoint mpa, MyPoint mpb) {
if (!mpa.str1.equals(mpb.str1) || !mpa.str2.equals(mpb.str2)) {
return Double.MAX_VALUE;
}
return Math.sqrt(Math.pow(mpa.i1 - mpb.i1, 2.0) + Math.pow(mpa.i2 - mpb.i2, 2.0));
}
}
Вопросы:
- Как я представляю
String
какdouble
чтобы включить вышеуказанную метрику расстояния в текущем выпуске (v3.6.1) Apache Commons Math?String.hashCode()
недостаточно, поскольку коллизии хеш-кода могут привести к тому, что разные строки будут считаться равными. Это кажется неразрешимой проблемой, поскольку я по сути пытаюсь создать уникальное отображение из бесконечного набора строк в конечный набор числовых значений (64 битdouble
). - Поскольку (1) кажется невозможным, я неправильно понимаю, как использовать библиотеку? Если да, разве я ошибся?
- Является ли моей единственной альтернативой использование устаревшей версии для такого типа метрики расстояния? Если да, (3а), почему дизайнеры решили сделать библиотеку менее общей? Возможно, в пользу скорости? Возможно, чтобы избавиться от самореференции в
class MyPoint implements Clusterable<MyPoint>
что некоторые могут считать плохим дизайном? (Я понимаю, что это может быть слишком самоуверенным, поэтому, пожалуйста, не обращайте на это внимания, если это так). Для экспертов по математике: (3b) какие недостатки есть в использовании устаревшей версии, кроме прямой совместимости (устаревшая версия будет удалена в 4.0)? Это медленнее? Возможно, даже неправильно?
Примечание. Мне известно об ELKI, который, по-видимому, популярен среди пользователей SO, но он не соответствует моим потребностям, поскольку продается как инструмент командной строки и графического интерфейса пользователя, а не как библиотека Java для включения в сторонние приложения.:
Вы даже можете встроить ELKI в свое приложение (если вы принимаете лицензию AGPL-3), но в настоящее время мы (пока) не рекомендуем это делать, поскольку API по-прежнему существенно меняется. [...]
ELKI не предназначен для встраиваемой библиотеки. Его можно использовать, но он не предназначен для такого использования. У ELKI есть множество опций и функциональных возможностей, и это имеет свою цену, как во время выполнения (хотя он может легко превзойти использование памяти R и Weka, например!), Так и, в частности, в сложности кода.
ELKI был разработан для исследования алгоритмов интеллектуального анализа данных, а не для облегчения их включения в произвольные приложения. Вместо этого, если у вас есть конкретная проблема, вы должны использовать ELKI, чтобы выяснить, какой подход работает хорошо, а затем переопределить этот подход оптимизированным образом для вашей проблемы (возможно, даже в C++, чтобы еще больше уменьшить память и время выполнения).