Python - 将一个数组映射到另一个数组的“比较”简单方法

5
我有一个数组a = [1, 2, 3, 4, 5, 6]b = [1, 3, 5],我想映射a,以便对于a中的每个元素,如果它在b中的元素之间,则将其映射到b的索引,该索引是包含a的上限范围。这可能不是最好的文字解释,但以下是一个例子。
a = 1 -> 0 because a <= first element of b
a = 2 -> 1 because b[0] < 2 <= b[1] and b[1] = 3
a = 3 -> 1 
a = 4 -> 2 because b[1] < 4 <= b[2]

因此,我想要的最终产品是f(a, b) = [0, 1, 1, 2, 2, 2]

我知道我可以使用循环来解决它,但我想知道是否有一种聪明、快速(向量化)的方法可以在pandas/numpy中完成。


这些数组总是有序的吗? - taras
是的,您可以假设它们是有序的。同时也可以假设 b 的每个元素都包含在 a 中(一个更一般的解决方案,没有这个限制会更好,但我认为这会使问题变得更简单)。 - Michael
2个回答

7
使用Python的bisect模块:
from bisect import bisect_left

a = [1, 2, 3, 4, 5, 6]
b = [1, 3, 5]

def f(_a, _b):
  return [bisect_left(_b, i) for i in _a]

print(f(a, b))
bisect — 数组二分算法

该模块提供了维护已排序列表的支持,而无需在每次插入后对列表进行排序。对于具有昂贵比较操作的大型项目列表,这可能比更常见的方法更好。该模块称为bisect,因为它使用基本的二分算法来完成其工作。源代码作为算法的工作示例可能最有用(边界条件已经正确!)。

提供以下函数:

bisect.bisect_left(a, x, lo=0, hi=len(a))

查找xa中的插入点以保持排序顺序。可以使用参数lohi指定应考虑的列表子集;默认情况下,使用整个列表。如果x已经存在于a中,则插入点将位于任何现有条目的前面(左侧)。 如果a已经排序,则返回值适用于用作list.insert()的第一个参数。

返回的插入点i将数组a分成两半,使得all(val < x for val in a[lo:i])为左侧,all(val >= x for val in a[i:hi])为右侧。

参考链接: https://docs.python.org/3/library/bisect.html


2
bisect更快:该解决方案假设列表已排序
a = [1, 2, 3, 4, 5, 6]
b = [1, 3, 5]

inds=[min(bisect_left(b,x),len(b)-1) for x in a]

返回结果

[0, 1, 1, 2, 2, 2]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接