快速创建两个原始列表的差异列表的方法

时间:2019-07-20 06:41:57

标签: python algorithm numpy

我有两个列表。两者都是数字排序列表。说:

A = [1.1, 5.2, 12.3, 12.6]
B = [2.3, 2.7, 5.2, 11.1, 12.1, 15.6, 16.6]

在这种情况下,我想输出:

result = [[2.3], [0.4, 2.5], [5.9, 1]]

和一个附加列表:

remainder = [3.5, 1]

这是哪里来的?

首先考虑B中连续值之间的差异列表,并在开头添加一个隐式零。

[2.3, 0.4, 2.5, 5.9, 1, 3.5, 1]

我们需要根据A中每个值最接近B中的位置进行拆分。

对于A中的每个数字,B中最接近的值为:

1.1 -> 2.3 -> 2.3
5.2 -> 5.2 -> 2.5
12.3 -> 12.1 -> 1
12.6 -> 12.1 -> 1

其余的进入剩余变量。

我正在寻找一种快速(线性时间)的方式在python中做到这一点。任何帮助,非常感谢。我不介意它是否使用numpy,以较快者为准。


我的尝试:

我试图解决这个问题,但是要通过复杂的途径。首先,我使用以下方式进行映射:

def find_nearest(array, value):
    idx = np.searchsorted(array, value, transformed_remainderside="left")
    if idx > 0 and (idx == len(array) or math.fabs(value - array[idx-1]) < math.fabs(value - array[idx])):
        return array[idx-1]
    else:
        return array[idx]

然后我用它来制作:

[[2.3], [2.7, 5.2], [11.1, 12.1]] and [15.6, 16.6]

然后我做

[[2.3], [0.4, 2.9], [5.9, 6.9]] and [3.5, 4.5]

然后我最终得到[[2.3],[0.4、2.5],[5.9、1]]和[3.5、1]

这很痛苦且容易出错,而且总体上也不是线性时间。


添加的示例

A = [2.3, 2.7, 5.2, 11.1]
B = [2.3, 2.7, 5.2, 11.1]

result = [[2.3], [0.4], [2.5], [5.9]]
remainder = []

2 个答案:

答案 0 :(得分:1)

这可以通过将任务分为两部分来以非常明确的方式完成:匹配最接近的数字并建立范围。

首先,代码线性地遍历两个数组,并为A中的每个数字选择B中最接近的数字。然后,代码将结构转换为所需的相邻数字范围的输出,并滤除不匹配的范围:

import numpy as np

A = [1.1, 5.2, 12.3, 12.6]
B = [2.3, 2.7, 5.2, 11.1, 12.1, 15.6, 16.6]

# This array will hold the closest numbers in A for each number in B
matches = [[] for _ in B]

i = 0
for num in A:
    # Check if the current number in B is the closest to the current one
    # This assumes both arrays are sorted
    while i < len(B) - 1 and abs(num - B[i]) > abs(num - B[i + 1]):
        i += 1
    matches[i].append(num)

# Unite the pairs so each range has a list of matching numbers
matches = [[matches[0]]] + [l1+l2 for l1, l2 in zip(matches[1::2], matches[2::2])]

# Create a list of diffs and pair them into ranges
diffs = (np.array(B[1:]) - np.array(B[:-1])).tolist()
ranges = [[B[0]]] + list(map(list, zip(diffs[::2], diffs[1::2])))

# Output only the ranges that had at least a single match in A
ranges_with_numbers = [num_range for num_range, range_matches in zip(ranges, matches)
                       if len(range_matches) > 0]
remainder = [num_range for num_range, range_matches in zip(ranges, matches)
             if len(range_matches) == 0]

复杂度为 O(n),因为匹配阶段仅扫描每个数组一次,变换阶段也是如此。

答案 1 :(得分:1)

这里是基于[np.searchsorted]-

# https://stackoverflow.com/a/45350318/ Variant for already sorted B
def closest_argmin_sortedB(A, sorted_B):
    L = len(sorted_B)
    sorted_idx = np.searchsorted(sorted_B, A)
    sorted_idx[sorted_idx==L] = L-1
    mask = (sorted_idx > 0) & \
    ((np.abs(A - sorted_B[sorted_idx-1]) < np.abs(A - sorted_B[sorted_idx])) )
    return sorted_idx-mask

A = np.asarray(A)
B = np.asarray(B)
d = np.ediff1d(B,to_begin=B[0])
idx = closest_argmin_sortedB(A,B)
idxf = idx[np.r_[True,idx[:-1]!=idx[1:]]]

p = np.split(d,idxf+1)
res,remainder = p[:-1],p[-1]

在更大的列表上,要实现性能提升,我们可以使用zip进行切片,从而分割数组/列表数据。因此,最后两个步骤可以替换为-

s = np.r_[0,idxf+1,len(d)]
res,remainder = [d[i:j] for (i,j) in zip(s[:-2],s[1:-1])], d[s[-2]:s[-1]]