Есть ли хороший способ сделать этот тип добычи?

Я пытаюсь найти точки, наиболее близкие по пространству в направлениях X и Y (набор данных образца, приведенный в конце), и я хочу посмотреть, есть ли более разумные подходы к этому, чем мой тривиальный (и непроверенный) подход. Сюжет этих точек в пространстве выглядит примерно так: я пытаюсь найти множество точек, помеченных внутри ящиков, то есть результат, который я ищу, представляет собой набор групп:

Group 1: (1,23), (2,23), (3,23)...
Group 2: (68,200), (68,201), (68,203), (68,204), (68,100), (68,101), (68,101)...

enter image description here

Для горизонтальных полос я думаю, что могу просто пойти и использовать маленькие скользящие окна размером, например, 5 или 10 (что действительно нужно определить из глобальной информации, размер которой будет давать максимальные сгруппированные точки, но я все еще исследуя хороший подход) и поиск смежных точек, потому что перерыв больше не будет считаться горизонтальной полосой.

Я предполагаю, что тот же подход работает и для вертикальных полос, но не во всех случаях, потому что существует тонкая разница в горизонтальной и вертикальной полосах: точки должны казаться близкими к горизонтальной группе, но они могут появляться в любом месте рассматриваемая часть вертикальной полосы. Соблюдайте большую вертикальную полосу на рисунке. Поэтому я предполагаю, что могу просто искать точки с одинаковой координатой x (в данном случае x = 68), чтобы дать мне много очков.

Помимо этого тривиального решения, я не могу придумать ничего умного, что можно сделать здесь, поскольку эта проблема кажется мне обманчиво простой. Я что-то упустил? Оказывается ли это на какой-то известный класс проблем, и если да, есть ли хороший и масштабируемый подход для достижения этого?

Пример набора данных:

1,23
1,23
2,23
3,23
4,23
5,23
6,23
7,23
8,23
9,23
10,23
11,23
12,23
13,23
14,23
15,23
16,23
10,33
11,33
12,33
13,33
14,33
15,33
16,33
17,33
18,33
19,33
2,28
2,28
3,28
34,75
34,76
34,76
34,77
34,78
34,79
34,80
34,81
34,82
34,83
34,75
34,76
34,76
34,77
34,78
34,79
34,80
34,81
400,28
400,28
400,28
68,200
68,201
68,203
68,204
68,100
68,101
68,103
68,104

Ответы

Ответ 1

Это немного поздно, но эта проблема беспокоила меня в течение некоторого времени. я был уверен, что его можно решить с помощью смешанных методов целочисленного/линейного программирования и попросил помощи в этом вопросе: Идентификация кластеров столбцов и строк с линейным программированием

Однако, получив ответ, я понял, что ваша проблема в по крайней мере, насколько я понимаю, настолько прост (когда он создан как программа ограничения) что вы можете решить его тривиально с помощью простой программы (которую вы уже знал). Другими словами, программирование ограничений было бы крутым способом решения это, но, по крайней мере, с подходом, который я нашел, даст вам тот же ответ как нечто гораздо более простое.

Я объясню ниже свои рассуждения, как я буду реализовывать его с ограничением пакет решений, а затем дать окончательный, тривиальный алгоритм.

Решение смешанного целочисленного программирования

Самая важная деталь - разница между горизонтальной и вертикальной групп. Насколько я могу видеть, все, что выравнивается вертикально, может быть в той же группы. Но горизонтальные группы разные - компоненты должны быть близки вместе.

Самая трудная часть решения проблемы с ограничениями - это найти способ описать пределы таким образом, чтобы решатель мог понять. Я не буду входите в подробности здесь, но решатели разочаровывают. К счастью, я подумайте, что есть способ сделать это здесь, и рассмотреть горизонтальные соседи: если в строке есть N точек, то мы имеем N-1 множества соседей (например, с 4 точками A B C и D есть три пары AB, BC и CD).

Для каждой пары мы можем дать оценку, которая представляет собой количество пробелов между ними (S_i), масштабированный некоторым фактором K, и флаг (F_i), который равен 0 или 1. Если пара находятся в одной и той же горизонтальной группе, тогда мы устанавливаем флаг в 1, иначе равна нулю.

Очень важно видеть, что множество флагов для всех пар полностью определяет решение. Мы можем запускать любую строку, помещая пары с флагом 1 в одной и той же горизонтальной группе и начиная новую горизонтальную группу время флага равно 0. Тогда мы можем взять все горизонтальные группы размером 1 и преобразовать их в вертикальные группы: любая точка, которая не находится в горизонтальной группе должен быть в вертикальной группе (даже если это вертикальная группа только одного).

Итак, все, что нам нужно сейчас, - это способ выразить оптимальное решение в терминах флаги. Я предлагаю свести к минимуму:

sum(1 - F_i) + sum(K * S_i * F_i)

У этого есть два члена. Первая - это сумма "один минус флаг" для каждого пара. Флаг равен 1, когда точки находятся в одной и той же горизонтальной группе и 0 в противном случае. Таким образом, минимизация этого значения совпадает с тем, что мы хотим, чтобы возможно, несколько горизонтальных групп. Если бы это было единственное ограничение, то мы может установить его в ноль, сделав все F_i 1, сделав все пары в строке членов той же группы.

Но второй термин не позволяет нам выбирать такое экстремальное решение. Это наказывает группы с пробелами. Если пара находится в одной группе, но разделенных пробелами S_i, тогда у нас есть "штраф" K * S_i.

Итак, у нас есть компромисс. Мы хотим горизонтальные группы, но нам не нужны пробелы. Окончательное решение будет зависеть от K - если оно велико, то мы не будем включать любые пробелы в горизонтальных группах. Но по мере того, как оно уменьшается, мы начнем поэтому, пока он не будет очень маленьким (стремится к нулю), мы помещаем все подряд в одной группе.

Чтобы использовать это, вы можете выбрать K, вычислить S_i и ввести выражение выше в систему ограничений. Затем система выберет F_i, чтобы свести к минимуму выражение. Наконец, вы преобразуете F_i в шаблон групп, просматривая каждую строку, как описано выше, и затем группируйте одиночные точки по вертикали.

Аналитическое решение

ОК, круто. На этом этапе у нас есть способ выразить проблему, которую мы можем дать к механизму ограничения.

Но это тривиально решать! Нам не нужен механизм принуждения stinkin 'to to решим это - мы можем просто взглянуть на выражение:

sum(1 - F_i) + sum(K * S_i * F_i)

Две суммы находятся по тем же парам, поэтому мы можем переместить все в сумму:

sum(1 - F_i + K * S_i * F_i)
sum(1 + F_i * (K * S_i - 1))

И затем извлеките константу (N здесь общее количество пар):

N + sum(F_i * (K * S_i - 1))

Теперь обратите внимание, что каждый член в сумме является независимым (и аддитивным). Поэтому для каждого мы хотим минимальное значение. У нас есть два варианта:

  • если F_i равно 0, тогда весь член равен 0.

  • в противном случае F_i равно 1, а член K * S_i - 1.

Таким образом, лучший выбор зависит от того, больше ли K * S_i 1. Если K * S_i больше 1, то наименьшее значение этого слова равно 0, а F_i должно быть 0. В противном случае второй выбор выше отрицателен, а F_i должен быть одним.

Тривиальный алгоритм

Что это значит? Это означает, что для каждой пары мы можем просто взглянуть на количество пробелов, S_i. Если это больше, чем 1 / K, то две точки должны быть в отдельных группах. В противном случае они должны находиться в одной группе.

Итак, все эти причудливые математики и оптимизация и ограничения и мужество сводится к: насколько далеко друг от друга находятся две точки в соседних парах? Если они ближе, чем некоторые отсечки, помещают их в одну и ту же горизонтальную группу. В противном случае поместите их в отдельные группы.

Итак, вот, наконец, ваш алгоритм:

choose some cut-off value, X
place each point in its own, singleton, horizontal group
for each row with more than one point:
    for each neighbouring pair in the row:
        if the space between the pair is less than X:
            join into a single horizontal group
for each column:
    join any remaining singleton groups into a single vertical group

Заключение

  • Вы можете использовать методы программирования ограничений для решения этой проблемы, но такие методы ограничены решениями, которые описывают систему в "правильном" (обычно, линейном) способе.

  • Самый простой такой подход, который я могу найти, эквивалентен тривиальному, прямому алгоритм, который делит точки в строке на горизонтальные группы в зависимости от количество пробелов между ними.

  • Все это зависит от целой груды предположений о том, что вы хотели, что, конечно, быть чрезмерно упрощенными, или просто неправильно.

Ответ 2

Вы можете попробовать использовать модуль кластера. Он содержит реализацию алгоритма кластеризации K-средних. Вы можете настроить аргумент функции getclusters, чтобы изменить количество желаемых кластеров.

s = '''
1,23
1,23
2,23
...
68,101
68,103
68,104
'''

from cluster import *

ll = [tuple(map(int,each.split(','))) for each in s.split()]

#horizontal 
cl = HierarchicalClustering(ll, lambda x,y: abs(x[0]-y[0]))

for c in cl.getlevel(1):
    print c

#vertical
cl = HierarchicalClustering(ll, lambda x,y: abs(x[1]-y[1]))

for c in cl.getlevel(1):
    print c