Алгоритм размещения узлов на окружности с учетом их расстояния до друг друга - PullRequest
2 голосов
/ 28 августа 2009

У меня следующая проблема. У меня есть области мозга и корреляции между ними. Области мозга, которые я знаю расстояния. Теперь мы ожидаем, что корреляции будут отрицательно коррелировать с расстоянием между областями мозга. Поэтому, когда мы увеличиваем расстояние, корреляция падает до нуля. Ожидается, что это на 1 / D ^ 2.

Я хочу визуализировать свою матрицу корреляции для проверки отклонений. У меня уже есть несколько других реализаций, таких как визуализация корреляционной матрицы Тайюна и простая двумерная диаграмма рассеяния с кривой 1 / D ^ 2 в виде синей линии.

Далее я хочу получить что-то, основанное на корреляционных кругах .

Области мозга, для которых я создал класс Node. Так что мои области мозга - это узлы. Я подражаю корреляции с краями. Мои края имеют sourceNode и destinationNode, а также корреляцию и расстояние, поэтому я могу связать их с правильным Node. Расстояние и корреляция необходимы для поиска в таблице (обратная связь с regionID, regionName и т. Д.).

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

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

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

Какие-нибудь советы по этому вопросу? Моя математика не так уж велика;). В настоящее время я гуглю на кругах, узлах, весах ..

Примечание

Если у вас есть другие яркие идеи для визуализации матрицы, обязательно напишите мне в личку об этом или прокомментируйте здесь:).

Ответы [ 3 ]

3 голосов
/ 28 августа 2009

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

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

alt text

import pylab as px
import numpy as nx
import numpy.random as rand
rand.seed(1)
rt2 = nx.sqrt(2)

N = 10 # number of brain regions

# make brain region locations r=1
regions = []
s = 2.
while len(regions)<N:
    p = 2*s*rand.rand(2)-s
    if nx.sqrt(px.dot(p,p))<s:
        regions.append(p)
regions = nx.array(regions)

#px.figure()
px.subplot(2,2,1)
for i in range(len(regions)):
    px.text(regions[i,0], regions[i,1], `i`, fontsize=15)
px.xlim(-1.1*s, 1.1*s)
px.ylim(-1.1*s, 1.1*s)
px.title("inital positions")

# precalc distance matrix for future comparisons
dm = nx.zeros((N,N), dtype=nx.float)
for i in range(N):
    for j in range(N):
        dm[i,j] = nx.sqrt(nx.sum((regions[i,:]-regions[j,:])**2))

def randomize_on_circle(n):
    """return array of n random angles"""
    return 2*nx.pi*rand.rand(n)

def cost_fcn(d_target, d_actual): # cost for distances not matching
    return abs(d_target-d_actual)

def calc_cost(angles):
    """calc cost for the given arrangement    """
    c = 0.
    for i in range(N-1):
        for j in range(i, N):
            # sqrt(...) is distance between two pts on a circle (I think)
            c += cost_fcn(dm[j, i], rt2*nx.sqrt(1-nx.cos(angles[i]-angles[j])))
    return c

def optimize_step(a, shift=2*nx.pi/360):
    """try shifting all points a bit cw and ccw, and return the most beneficial"""
    max_benefit, ref_cost = None, None
    best_i, best_shift = None, None
    for imove in range(N): # loop through the regions and try moving each one
        cost0 = calc_cost(a)
        for da in (shift, -shift):
            a_temp = nx.array(a)
            a_temp[imove] += da
            cost = calc_cost(a_temp)
            benefit = cost0 - cost  # benefit if moving lowers the cost
            if max_benefit is None or benefit > max_benefit:
                max_benefit, best_i, best_shift, ref_cost = benefit, imove, da, cost
    return max_benefit, best_i, best_shift, ref_cost       

lowest_cost, best_angles = None, None
cost_initials, cost_plateaus = [], []
for i in range(30):  # loop though 20 randomized placements on the circle
    angles = randomize_on_circle(N)
    costs = []
    benefits = []
    # optimize each original arrangement by shifting placements one-by-one in small steps
    count_benefits_neg = 0
    count_total, max_total = 0, 2000
    while count_benefits_neg < 10: # better to do a variable step size
        b, i, s, c = optimize_step(angles)
        angles[i] += s
        costs.append(c)
        benefits.append(b)
        if b < 0:
            count_benefits_neg += 1
        count_total += 1
        if count_total > max_total:
            print count_total, b, costs[-20:], benefits[-20]
            raise "not finding an equilibrium"
    if lowest_cost is None or c < lowest_cost:
        lowest_cost = c
        best_angles = nx.array(angles)
        cost_graph = costs[:]
        benefit_graph = nx.array(benefits)
    cost_plateaus.append(c)
    cost_initials.append(costs[0])

px.subplot(2, 2, 2)
px.plot(cost_graph, 'o') # make sure the cost is leveling off
px.title("cost evoloution of best")
px.subplot(2, 2, 3)
px.plot(cost_initials, 'o')
px.plot(cost_plateaus, 'd')
px.title("initial and final costs")

px.subplot(2, 2, 4)
for i in range(len(best_angles)):
    px.text(nx.cos(best_angles[i]), nx.sin(best_angles[i]), `i`, fontsize=15)
px.xlim(-1.2, 1.2)
px.ylim(-1.2, 1.2)
px.title("positioned on circle")

px.show()

Интересно, что это, кажется, привело к тому, что далекие вещи были далекими, а близкие - близкими, но с испорченными средними приказами, так что, возможно, это будет делать то, что вы хотите? (Это также иллюстрирует фундаментальную проблему при переходе от 2D к 1D. Например, на окружности 4 хочет быть дальше от 9, но это не может быть сделано без приближения к другим числам, тогда как в 2D это может просто выйди в сторону.)

Возможно, вы захотите изменить cost_fnc, который определяет штраф за то, что расстояния между точками на окружности не совпадают с расстоянием от 2D-схемы. Это может помочь изменить затраты на большие ошибки (скажем, на квадратичные) или подчеркнуть стоимость правильного большого расстояния, скажем, d_target*(abs(d_actual-d_target)) и т. Д., Может помочь.

Кроме того, изменение размера круга относительно размера двумерных данных сильно изменит внешний вид, и вы, вероятно, захотите обвести круг немного меньше, чем данные, как я сделал здесь, так как это расширит точки вокруг круга больше. (Здесь кружок имеет R = 1, поэтому просто масштабируйте данные соответствующим образом.) Также обратите внимание, что количественная оценка стоимости будет не очень значимой, поскольку лучшие схемы никогда не достигают очень низких затрат, поскольку некоторые регионы никогда не могут быть так далеко друг от друга, как в 2D данных.

Точка запуска нескольких случайных запусков заключается в том, что развивающееся устройство может застрять в локальных минимумах. Этот метод кажется полезным: расчеты помогают правильно определить расстояние и снизить затраты (график № 3, синие точки = начальная случайная величина, ромбы = локальный минимум), и некоторые первоначальные схемы помогают гораздо больше, чем другие, поэтому стоит попробовать несколько начальных договоренностей. Кроме того, поскольку некоторые из них, по-видимому, достигают 15, это дает некоторую уверенность в том, что эта схема может быть репрезентативной.

2 голосов
/ 28 августа 2009

Я предлагаю вам использовать алгоритм минимизации энергии , чтобы расположить узлы на круге таким образом, чтобы минимизировать что-то вроде квадрата (расстояние по кругу - расстояние в мозге). Затем утолщите края, как вы описываете, и визуализация должна быть завершена.

1 голос
/ 28 августа 2009

Возможно, что GraphViz имеет несколько ссылок на алгоритмы. Кроме того, вы можете получить ваши данные в формате, который принимает GraphViz, и выполнить их через него.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...