Отбор приблизительно регулярных выборок из нерегулярно разнесенного вектора

1

Это моя проблема, предположим, у меня есть такой вектор:

import numpy as np
idxx = np.array([0.        , 0.07665982, 0.24366872, 0.49555099, 0.74743326,
       1.07871321, 1.58247775, 2.24503765, 2.58179329, 3.16221766,
       3.74811773, 4.1615332 , 4.58042437, 5.33059548])

Меня интересует фильтрация только тех значений, которые приблизительно равны 0.25, 0.5 и 1.0. Начните с 0.25 чтобы продемонстрировать, что я ищу, чтобы фильтр возвращался:

np.array([0.24366872, 0.49555099, 0.74743326,
       1.07871321, 1.58247775, 2.24503765, 2.58179329,
       3.74811773, 4.58042437, 5.33059548])

Здесь остаются только значения, которые приблизительно равны кратным 0.25. На практике мне также необходимо сохранить первую запись 0.0, но удалили ее для этой демонстрации.

Если я использую 0.5 тогда мой результат будет выглядеть так:

np.array([0.49555099, 1.07871321, 1.58247775, 2.58179329, 4.58042437])

Мои первоначальные попытки были:

import math
for i in idxx:
    g = 0.25
    k = i % g
    if math.isclose(k, g, rel_tol=0.5):
        print('This is reasonably close: ', i, '\n\t for modulus k == ', k, '\n')

Все еще требует от меня сделать очень много настроек (и я все еще не могу отфильтровать то, что хочу), поэтому мне интересно, есть ли у кого-то лучший способ сделать это правильно?

В сущности, я хочу выбрать те нерегулярные точки (например, 0.24366872) на "регулярно" разнесенную сетку (например, с интервалом 0,25), но где каждая точка на регулярно разнесенной сетке имеет некоторый допуск, например, + / - 0,05, для того, чтобы для размещения неровностей в реальных данных. Таким образом, позволяя мне находить те точки, которые находятся в пределах этой толерантности, на этих регулярных расстояниях.

Теги:
numpy

2 ответа

1
Лучший ответ

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

Вот подход, не связанный с циклом, который расточительно относится к памяти, поскольку он создает полный idxx.size idxx.size -by- n, где n - размер вашей сетки:

def grid_filter(idxx, spacing):
    # use idxx[0] instead of idxx.min() if idxx is sorted
    g0 = np.floor(idxx.min() / spacing) * spacing
    # use idxx[-1] instead of idxx.max() if idxx is sorted
    g1 = np.ceil(idxx.max() / spacing) * spacing
    # turn the grid into a column vector for broadcasting
    n = np.round((g1 - g0) / spacing) + 1
    grid = np.linspace(g0, g1, n).reshape(-1, 1)

    # compute the absolute distance to each point and
    # get the index of the point nearest each grid point:
    # rows are grid points, columns data points
    indices = np.abs(grid - idxx).argmin(axis=1)
    # post-process to ensure that a data point only matches one grid point
    indices = np.unique(indices)

    # apply the result
    return idxx[indices]

grid - idxx массив - grid - idxx. Вероятно, это не проблема. Результат grid_filter(idxx, 0.25):

[ 0. 0.24366872 0.49555099 0.74743326 1.07871321 1.58247775 2.24503765 2.58179329 3.16221766 3.74811773 4.1615332 4.58042437 5.33059548]

Если вы недовольны результатом 3.16 и 4.16 в результате, вы можете сделать допуск 1/3 spacing или что-то подобное и работать с ним:

def tolerance_filter(idxx, spacing, tolerance):
    deltas = (idxx % spacing)
    deltas = np.minimum(deltas, spacing - deltas)
    candidates = deltas <  tolerance * spacing
    return idxx[candidates]

Это решение фактически делает то, что вы хотите, и полностью векторизовано. tolerance_filter(idxx, 0.25, 0.33) возвращает

[ 0. 0.07665982 0.24366872 0.49555099 0.74743326 1.07871321 1.58247775 2.24503765 2.58179329 3.74811773 4.58042437 5.33059548]

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

tolerance_filter(grid_filter(idxx, 0.25), 0.25, 0.33)

В этот момент вы можете сделать что-то еще лучше: сначала присоедините каждый элемент массива к ближайшей точке сетки, как в первой части. Затем сделайте некоторые адаптивные вещи. Например, получите стандартное отклонение остатков и отбросьте что-нибудь выше, скажем, 3-сигма от номинала:

def sigma_filter(idxx, spacing, nsigma):
    deltas = (idxx % spacing)
    deltas[deltas > 0.5 * spacing] -= spacing
    sigma = np.std(deltas)
    candidates = (np.abs(deltas) <= nsigma * sigma)
    return idxx[candidates]
  • 0
    Это красиво. Но теперь мне любопытно, потому что у вас есть две точки в ваших результатах для 0,25, которые я не считаю (и это субъективно, учитывая мои знания предметной области) «близкими» к обычным точкам сетки, а именно: 3.16221766 и 4.1615332 (их модуль по отношению к 0,25 слишком высок). Вы можете видеть, что я не включил их в мой идеализированный ответ на вопрос. Как мы учтем это в вашем подходе?
  • 0
    Я добавил обновление
0

Вам необходимо правильно управлять + / -. Простой способ сделать это:

error=minimum(-idxx%.25,idxx%.25)
res= idxx[error<.05]
# [ 0.,  0.24366872,  0.49555099,  0.74743326,  2.24503765, 3.74811773]
  • 0
    isclose прекрасно справляется. Вопрос заключается в неточном определении толерантности

Ещё вопросы

Сообщество Overcoder
Наверх
Меню