Хобрук: Ваш путь к мастерству в программировании

Эффективно находить соседей с разрезами и возвращать индекс

У меня есть много точек на плоскости x,y длиной около 10000, каждая точка (x,y) имеет собственный радиус r. Этот небольшой набор данных — лишь крошечный уголок всего моего набора данных. У меня есть интересующая точка (x1,y1), я хочу найти ближайшую точку вокруг (x1,y1) в пределах 1 и выполнить условие, что расстояние между (x,y) и (x1,y1) меньше r. Я хочу вернуть индекс этих хороших точек, а не сами хорошие точки.

import numpy as np
np.random.seed(2000)
x = 20.*np.random.rand(10000)
y = 20.*np.random.rand(10000)
r = 0.3*np.random.rand(10000)
x1 = 10.  ### (x1,y1) is an interest point 
y1 = 12.
def index_finder(x,y,r,x1,y1):
    idx = (abs(x - x1) < 1.) & (abs(y - y1) < 1.)  ### This cut will probably cut 90% of the data
    x_temp = x[idx]   ### but if I do like this, then I lose the track of the original index
    y_temp = y[idx]
    dis_square = (x_temp - x1)*(x_temp - x1) + (y_temp - y1)*(y_temp - y1)
    idx1 = dis_square < r*r    ### after this cut, there are only a few left 
    x_good = x_temp[idx1]
    y_good = y_temp[idx1]

В этой функции я могу найти хорошие точки вокруг (x1,y1), но не индекс этих хороших точек. ОДНАКО мне нужен ИСХОДНЫЙ индекс, потому что ИСХОДНЫЙ индекс используется для извлечения других данных, связанных с координатой (x,y). Как я уже упоминал, образец набора данных — это лишь крошечная часть всего моего набора данных, я буду вызывать вышеуказанную функцию около 1 000 000 раз для всего моего набора данных, поэтому эффективность вышеупомянутой функции index_finder также является соображением.

Есть мысли по такой задаче?


  • Как вы используете index_finder для всех этих точек? Вы используете его в цикле или просто так? 18.09.2017
  • Я буду использовать эту функцию внутри цикла, потому что у меня много таких интересных точек, как (x1,y1). Сама эта функция может избежать любого цикла. И этот набор данных составляет только 1/1000 всего моего набора данных. 18.09.2017

Ответы:


1

Подход №1

Мы могли бы просто индексировать первую маску с ее собственной маской для выбора замаскированных значений True Places из второго этапа, например так:

idx[idx] = idx1

Таким образом, idx будет иметь окончательные допустимые замаскированные значения/места с хорошими значениями, соответствующие исходному массиву x и y, т.е. -

x_good = x[idx]
y_good = y[idx]

Затем эту маску можно использовать для индексации других массивов, как указано в вопросе.


Подход № 2

В качестве другого подхода мы могли бы использовать два условных оператора, таким образом создавая с ними две маски. Наконец, объедините их с AND-ing, чтобы получить комбинированную маску, которую можно проиндексировать в массивы x и y для окончательных выходных данных. Нам не нужно будет получать фактические индексы таким образом, так что это еще одно преимущество.

Следовательно, реализация -

X = x-x1
Y = y-y1
mask1 = (np.abs(X) < 1.) & (np.abs(Y) < 1.)
mask2 = X**2 + Y*2 < r**2
comb_mask = mask1 & mask2

x_good = x[comb_mask]
y_good = y[comb_mask]

Если по какой-то причине вам все еще нужны соответствующие индексы, просто сделайте -

comb_idx = np.flatnonzero(comb_mask)

Если вы выполняете эти операции для разных пар x1 и y1 для одного и того же набора данных x и y, я бы предложил использовать broadcasting для векторизации его через все эти парные наборы данных x1, y1, как показано в this post.

18.09.2017
  • Спасибо за Ваш ответ. Я предполагаю, что эта реализация будет немного менее эффективной. Я также хочу ускорить его, потому что у меня будет большой цикл около 1 000 000 раз для вызова этой функции. 18.09.2017
  • @HuanianZhang Немного менее эффективен, чем что? 18.09.2017
  • Я думаю, это будет немного менее эффективно, чем моя реализация. Потому что он вычисляет только около 10% данных во втором разрезе. Но недостаток моей реализации в том, что она не может вернуть index. 18.09.2017
  • Для ускорения (потому что у меня 1 миллион петель) я разделил два разреза. Я проверю вашу реализацию, чтобы увидеть, сколько времени это займет. 18.09.2017
  • Спасибо за ваше время и помощь. Я попробовал два ваших подхода. Интересно, что второй подход более эффективен. У меня есть результаты и использование времени для обоих, как показано ниже: [1575 3709 5706] time used for approach 1 is 0.000797033309937 [1575 3709 5706] time used for approach 2 is 0.000538110733032. Это совершенно неожиданно. 18.09.2017
  • @HuanianZhang Вероятно, индексация: x[idx] убивает первую. Кроме того, мы могли бы использовать power вместо самоумножения, обновлено. 18.09.2017

  • 2

    numpy.whereпохоже сделано для поиска индексов

    векторизованная норма calc + np.where() может быть быстрее, чем цикл

    sq_norm = (x - x1)**2 + (y - y1)**2  # no need to take 10000 sqrt
    idcs = np.where(sq_norm < 1.)
    
    len(idcs[0])
    Out[193]: 69
    
    np.stack((idcs[0], x[idcs], y[idcs]), axis=1)[:5]
    Out[194]: 
    array([[  38.        ,    9.47165956,   11.94250173],
           [  39.        ,    9.6966941 ,   11.67505453],
           [ 276.        ,   10.68835317,   12.11589316],
           [ 288.        ,    9.93632584,   11.07624915],
           [ 344.        ,    9.48644057,   12.04911857]])
    

    норма calc тоже может включать массив r, 2-й шаг?

    r_sq_norm = (x[idcs] - x1)**2 + (y[idcs] - y1)**2 - r[idcs]**2
    r_idcs = np.where(r_sq_norm < 0.)
    
    idcs[0][r_idcs]
    Out[11]: array([1575, 3476, 3709], dtype=int64)
    

    возможно, вы захотите рассчитать время двухэтапного теста по сравнению с включением r в 1-й векторизованный расчет нормы?

    sq_norm = (x - x1)**2 + (y - y1)**2 - r**2
    idcs = np.where(sq_norm < 0.)
    
    idcs[0]
    Out[13]: array([1575, 3476, 3709], dtype=int64)
    
    18.09.2017

    3

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

    def index_finder(x,y,r,x1,y1):
        idx = np.nonzero((abs(x - x1) < 1.) & (abs(y - y1) < 1.))  #numerical, not boolean
        mask = (x[idx] - x1)*(x[idx] - x1) + (y[idx] - y1)*(y[idx] - y1) < r*r
        idx1 = [i[mask] for i in idx]
        x_good = x_temp[idx1]
        y_good = y_temp[idx1]
    

    теперь idx1 - это индексы, которые вы хотите извлечь.

    В общем, более быстрый способ сделать это - использовать scipy.spatial.KDTree

    from scipy.spatial import KDTree
    
    xy = np.stack((x,y))
    kdt = KDTree(xy)
    kdt.query_ball_point([x1, y1], r)
    

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

    x1y1 = np.stack((x1, y1)) #`x1` and `y1` are arrays of coordinates.
    kdt.query_ball_point(x1y1, r)
    

    ТАКЖЕ НЕПРАВИЛЬНО: если у вас разные расстояния для каждой точки, вы можете сделать следующее:

    def query_variable_ball(kdtree, x, y, r):
        out = []
        for x_, y_, r_ in zip(x, y, r):
            out.append(kdt.query_ball_point([x_, y_], r_)
        return out
    
    xy = np.stack((x,y))
    kdt = KDTree(xy)
    query_variable_ball(kdt, x1, y1, r)
    

    EDIT 2: Это должно работать с разными значениями r для каждой точки.

    from scipy.spatial import KDTree
    
    def index_finder_kd(x, y, r, x1, y1):  # all arrays
        xy = np.stack((x,y), axis = -1)
        x1y1 = np.stack((x1, y1), axis = -1)
        xytree = KDTree(xy)
        d, i = xytree.query(x1y1, k = None, distance_upper_bound = 1.)
        good_idx = np.zeros(x.size, dtype = bool)
        for idx, dist in zip(i, d):
            good_idx[idx] |= r[idx] > dist
        x_good = x[good_idx]
        y_good = y[good_idx]
        return x_good, y_good, np.flatnonzero(good_idx)
    

    Это очень медленно только для одной пары (x1, y1), поскольку для заполнения KDTree требуется некоторое время. Но если у вас миллионы пар, это будет гораздо быстрее.

    (Я предположил, что вы хотите объединить все хорошие точки в данных (x, y) для всех (x1, y1), если вы хотите их по отдельности, также возможно использовать аналогичный метод, удаляя элементы i[j] в зависимости от того, является ли d[j] < r[i[j]])

    18.09.2017
  • Разве index_finder #2 не совпадает с тем, что я предлагаю в своем посте в начале? 18.09.2017
  • Да. Не заметил, потому что сразу перешел к подходу №2. 18.09.2017
  • Если это не звучит слишком оскорбительно, не могли бы вы удалить эту часть? Два поста с одинаковым содержанием выглядят не слишком хорошо :) 18.09.2017
  • Не уверен, почему, если мы собираемся получить полные ответы, но ладно. 18.09.2017
  • Ценить это . 18.09.2017
  • Кажется, KDTree работает неправильно. Потому что здесь r - это массив, а не число. Но спасибо. 19.09.2017
  • Подождите, это неправильно. Сколько (x1, y1) пар вы планируете коллировать? 19.09.2017
  • Новые материалы

    React on Rails
    Основное приложение Reverb - это всеми любимый монолит Rails. Он отлично обслуживает наш API и уровень просмотра трафика. По мере роста мы добавляли больше интерактивных элементов..

    Что такое гибкие методологии разработки программного обеспечения
    Что представляют собой гибкие методологии разработки программного обеспечения в 2023 году Agile-методологии разработки программного обеспечения заключаются в следующем: И. Введение A...

    Ториго  — революция в игре Го
    Наш следующий вызов против ИИ и для ИИ. Сможет ли он победить людей в обновленной игре Го? Обратите внимание, что в следующей статье AI означает искусственный интеллект, а Goban  —..

    Простое развертывание моделей с помощью Mlflow — Упаковка классификатора обзоров продуктов NLP от HuggingFace
    Как сохранить свои модели машинного обучения в формате с открытым исходным кодом с помощью MLFlow, чтобы позже получить возможность легкого развертывания. Сегодня модели упаковки имеют несколько..

    Математика и интуиция - Часть 1
    У каждой математической формулы есть доказательство. Часто эти доказательства слишком сложно понять, поскольку многие из них основаны на индукции, некоторые - на очень сложных наблюдениях, а..

    Раскрытие возможностей НЛП: часть речевой маркировки и ее проблемы
    В сфере обработки естественного языка (NLP) маркировка частей речи (POS) выступает в качестве фундаментального метода, позволяющего компьютерам понимать и анализировать человеческий язык на..

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