Это моя проблема, предположим, у меня есть такой вектор:
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, для того, чтобы для размещения неровностей в реальных данных. Таким образом, позволяя мне находить те точки, которые находятся в пределах этой толерантности, на этих регулярных расстояниях.
Возможно, вы немного обойдетесь назад. Вместо того, чтобы пытаться найти толерантность, которая работает (что 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]
Вам необходимо правильно управлять + / -
. Простой способ сделать это:
error=minimum(-idxx%.25,idxx%.25)
res= idxx[error<.05]
# [ 0., 0.24366872, 0.49555099, 0.74743326, 2.24503765, 3.74811773]