6

假设我有一个x带有数字的列表,以及另一个y带有其他数字的列表。的元素y应该是 的元素x,但由于测量中的噪声,它们有点不同。对于 的每个值,我想找到最接近它y的值。x

我可以用一些循环来做到这一点,并检查每个元素y[i],哪个元素x[j]最小化abs(x[j]-y[i]),但我很确定有一种更简单、更清洁的方法来做到这一点。列表可能很大,所以我在这里寻找有效的代码。

到目前为止我写的代码是:

x_in = [1.1, 2.2, 3, 4, 6.2]
y_in = [0.9, 2, 1.9, 6, 5, 6, 6.2, 0.5, 0, 3.1]
desired_output = [1.1, 2.2, 2.2, 6.2, 4, 6.2, 6.2, 1.1, 1.1, 3]

y_out = []

for y in y_in:
    aux = [abs(l - y) for l in x_in]
    mn,idx = min( (aux[i],i) for i in range(len(aux)) )
    y_out.append(x_in[idx])

>>> y_out == desired_output
True

但我不知道是否有更有效的方法来做到这一点......

编辑:

由于我的无知,我忘记根据收到的评论澄清一些可能相关的内容。

  • x列表已排序
  • x是唯一可以具有相当大尺寸的列表:通常在 500,000 到 1,000,000 个元素之间。y通常会非常小,少于 10 个元素。
4

6 回答 6

2

鉴于x已排序,最有效的方法是使用bisect搜索最接近的值。只需在 x 值之间创建一个中点列表并在这些值上运行 bisect:

In [69]: mid_points = [(x1+x2)/2 for x1, x2 in zip(x[1:], x[:-1])]

In [70]: mid_points
Out[70]: [1.5, 2.5, 3.5, 4.5]

In [72]: [x[bisect.bisect(mid_points, v)] for v in y]
Out[72]: [1, 1, 4, 5, 2]

这将O(Mlog(N)+N)在 `M=len(y), N=len(x) 时运行

(对于python2在计算中做from __future__ import division或使用)float(x1+x2)/2mid_points

于 2018-07-18T21:35:32.520 回答
1

您可以使用 lambda 函数和列表推导快速完成此操作:

[min(x, key=lambda x:abs(x-a)) for a in y]

这将适用于浮点数、整数等。

于 2018-07-18T21:02:36.583 回答
0

我的尝试:

首先,我对 X 数组进行排序(如果尚未排序)。循环遍历每个 y 并计算每个 x 的绝对值,直到该绝对值高于前一个,然后停止 for 循环(因为数组 X 已排序):

x = sorted([1, 2, 3, 4, 5])
y = [1.1, 1.2, 3.6, 6.2, 2.1]

out = []
while y:
    current_value = y.pop()
    current_min = float('inf')
    current_x_value = None
    for v in x:
        temp_min = abs(current_value - v)
        if temp_min < current_min:
            current_min = temp_min
            current_x_value = v
        if temp_min > current_min:  # no need to iterate further, X is sorted
            break
    out.insert(0, current_x_value)
print(out)

输出:

[1, 1, 4, 5, 2]
于 2018-07-18T21:22:55.037 回答
0

所以这是我快速编造的东西,它只是得到了所有的差异,而不是从最小到最大对它们进行排序。取最小的差异,然后从那里开始。

x = [1, 2, 3, 4, 5]
y = [1.1, 1.2, 3.6, 6.2, 2.1]

for y_index in range(len(y)):
    value_and_index= {}
    for x_index in range(len(x)):
        difference= y[y_index]-x[x_index]
        difference= difference*-1 if difference<0 else difference
        value_and_index[difference]= x_index
    y[y_index]= x[value_and_index[sorted(value_and_index.keys())[0]]]

print y # [1, 1, 4, 5, 2]

希望这会有所帮助,快乐的编码!

于 2018-07-18T21:11:41.983 回答
0

如果x已排序,请使用bisect

import bisect 
test_out=[]
max_x=max(x)
min_x=min(x)
for f in y:
    if f>=max_x:
        idx=-1
    elif f<=min_x:
        idx=0
    else:
        idx=bisect.bisect_left(x,f)
        if abs(x[idx-1]-f)<abs(x[idx]-f):
            idx-=1
    test_out.append(x[idx])

>>> test_out==desired_output
True
于 2018-07-18T22:09:34.150 回答
0

有了下一个假设:

  • 结果的顺序无关紧要,

  • 我们正在使用Python 3.3 +。

非常简单的解决方案可能看起来像

from itertools import repeat


def evaluate(expected_values, measurements):
    if not expected_values:
        raise ValueError('Expected values should be a non-empty sequence.')
    expected_values = sorted(expected_values)
    measurements = sorted(measurements)
    expected_iter = iter(expected_values)
    left_value = next(expected_iter)
    try:
        right_value = next(expected_iter)
    except StopIteration:
        # there is only one expected value
        yield from repeat(left_value,
                          len(measurements))
        return
    for evaluated_count, measurement in enumerate(measurements):
        while measurement > right_value:
            try:
                left_value, right_value = right_value, next(expected_iter)
            except StopIteration:
                # rest of the measurements are closer to max expected value
                yield from repeat(right_value,
                                  len(measurements) - evaluated_count)
                return

        def key(expected_value):
            return abs(expected_value - measurement)

        yield min([left_value, right_value],
                  key=key)

对于Python3.3-我们可以替换

yield from repeat(object_, times)

for-loop 类似

for _ in range(times):
    yield object_

测试

>>> x_in = [1.1, 2.2, 3, 4, 6.2]
>>> y_in = [0.9, 2, 1.9, 6, 5, 6, 6.2, 0.5, 0, 3.1, 7.6, 10.4]
>>> y_out = list(evaluate(x_in, y_in))
>>> y_out
[1.1, 1.1, 1.1, 2.2, 2.2, 3, 4, 6.2, 6.2, 6.2, 6.2, 6.2]
于 2018-07-18T22:18:39.980 回答