将每个列表编号四舍五入到另一个列表中最接近的编号

Posted

技术标签:

【中文标题】将每个列表编号四舍五入到另一个列表中最接近的编号【英文标题】:Round each number of list to most near number in another list 【发布时间】:2018-07-18 20:52:27 【问题描述】:

假设我有一个带有数字的列表x,另一个带有其他数字的列表yy 的元素应该是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

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

编辑:

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

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

【问题讨论】:

xy 是多长时间?循环和检查将是多项式复杂度,这不是很好。如果性能很重要,您可以使用区间树来获得更好的性能。 一种直接的方法是对两个数组进行排序,然后逐步遍历x,直到找到比y 中的当前元素大的元素e,然后取两者中的较近者(e 或执行它的元素)。从x 中的那个位置继续,直到处理完所有y,有点像mergesort。 @user3483203 我已将我的尝试添加到问题中。 巨大有多大?我希望 wim 的区间树能够扩展得最好,但它需要很多设置 当您说 “列表可能很大” 时,您是指长度 X、Y 还是两者兼而有之?无论如何,两个列表是插入错误的数据结构。改用两棵树(或堆)。然后这两个结构都将默认排序,并且可以很容易地找到它们的(前身和后继)邻居。其余的都是微不足道的。 【参考方案1】:

鉴于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 或在 mid_points 计算中使用 float(x1+x2)/2

【讨论】:

这真的很诙谐,但我刚刚尝试过,但我没有得到问题示例(第二个)的预期结果。最后一个元素应该是 3,你的脚本返回 4。 我发现了错误。由于x 有两个整数,所以当您执行(3+4)/2 时,您会得到3,而不是3.5。如果您执行转换,您将获得所需的结果,并且您的代码在其他答案中明显优于我的代码和其余代码。谢谢。 @Tendero 猜你使用的是 Python 2。你可以使用 from __future__ import division 来避免这种情况。 这将比可能的列表中的 1x 慢【参考方案2】:

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

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

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

【讨论】:

我不知道。有什么建设性的批评吗? 它与 OP 已有的相同,因此“没有用”。 一个干净、可读的实际代码答案与他对“一些循环”的描述“不同”。 这个问题明确要求更高的效率,而这个答案提供了同样的复杂性。更短的代码很好,但有点错过了 IMO 的重点。 这里有两件事:1) OP 的原始文本中从未提及效率。检查编辑历史。 2) “Easier”和“cleaner”是两个明确的要求。【参考方案3】:

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

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]

希望对您有所帮助,祝您编码愉快!

【讨论】:

【参考方案4】:

我的尝试:

首先我对 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]

【讨论】:

最好对 both 数组进行排序,然后用 two 移动迭代器逐步遍历它们。【参考方案5】:

下一个假设:

结果顺序无关紧要,

我们正在使用 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]

【讨论】:

【参考方案6】:

如果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

【讨论】:

idx=bisect.bisect_left(x,f) 可能会返回 0,然后下一行的索引会意外回绕。 那个案子是由上面的f&lt;min_x 处理的,我认为 dups 在左边,不是吗? 不。它必须是f&lt;=min_x 嗯。当我阅读bisect.left 时,它声明all(val &lt; x for val in a[lo:i]) 代表bisect.leftall(val &lt;= x for val in a[lo:i]) 代表bisect.right(或bisect.bisect - 相同)。我想elif f&lt;=min_x: 修复了,不是吗?感谢关注! 用简单的英语来说,二分法返回插入新元素的位置的索引。如果出现平局,bisect_left 会将其插入到相等元素的左侧,bisect_right 将插入到右侧。 f&lt;=min_x 防止在索引 0 处出现平局的可能性,所以是的,它修复了。

以上是关于将每个列表编号四舍五入到另一个列表中最接近的编号的主要内容,如果未能解决你的问题,请参考以下文章

CSS:有没有办法在每个列表元素之前垂直对齐数字/项目符号?

CSS:有没有办法在每个列表元素之前垂直对齐数字/项目符号?

在套接字上发送包含曲目编号,标题,艺术家,专辑,流派和持续时间的歌曲列表?

使用列表推导对数据框列表中的数据框进行编号

如何将项目和按钮添加为编号列表

如何将编号列表切片为子列表