【问题标题】:Python - split list in to sublists based on another listPython - 根据另一个列表将列表拆分为子列表
【发布时间】:2015-12-13 14:49:57
【问题描述】:

我有两个列表:l1 = [0, 0.002, 0.3, 0.5, 0.6, 0.9, 1.3, 1.9]l2 = [0.5, 1.0, 1.5, 2.0]。我想将l1 拆分为子列表,这些子列表定义为l2 的两个索引之间的元素。因此,例如 l1 将等于 [[0,0.002, 0.3], [0.5, 0.6, 0.9], [1.3], [1.9]]

这是我的解决方案:

l3 = []
b=0
for i in l2:
    temp = []
    for p in l1:
        if b <= p < i:
        temp.append(p)
    l3.append(temp)
    b+=0.5

这个解决方案是我代码中的一个巨大瓶颈。有没有更快的方法来做到这一点?

【问题讨论】:

  • 所以这些是桶。这是一个直方图!
  • @PeterWood 或哈希图!或间隔树!这么多的可能性!

标签: python list python-2.7 split


【解决方案1】:

您的列表已排序,因此无需在此处进行双重循环。

以下基于两个列表作为输入生成子列表:

def partition(values, indices):
    idx = 0
    for index in indices:
        sublist = []
        while idx < len(values) and values[idx] < index:
            sublist.append(values[idx])
            idx += 1
        if sublist:
            yield sublist

然后您可以遍历 partition(l1, l2) 以获取单个子列表,或调用 list() 一次性生成整个列表列表:

>>> l1 = [0, 0.002, 0.3, 0.5, 0.6, 0.9, 1.3, 1.9] 
>>> l2 = [0.5, 1.0, 1.5, 2.0]
>>> list(partition(l1, l2))
[[0, 0.002, 0.3], [0.5, 0.6, 0.9], [1.3], [1.9]]

【讨论】:

  • 这不是O(n*m) 像 op 的解决方案吗?会有巨大的性能提升吗?
  • @taesu:OP 是 O(N*M)。
  • 啊,你是对的。感谢您的参与,也感谢您参与 SO。
  • @taesu, index 遍历整个列表,给出 O(n)。现在,在迭代 indices 时,idx 并没有减少,并且 while 循环的每次迭代都会将 idx 递增 1。这给出了最大的 len (values) 迭代,将复杂性提高到 O (n + m)。简而言之,两个列表都被遍历一次。
【解决方案2】:
def split_l(a,b):
    it = iter(b)
    start, sub = next(it), []
    for ele in a:
        if ele >= start:
            yield sub
            sub, start = [], next(it)
        sub.append(ele)
    yield sub

print(list(split_l(l1,l2)))
[[0, 0.002, 0.3], [0.5, 0.6, 0.9], [1.3], [1.9]]

使用 kasras 输入这击败了公认的答案和 numpy 解决方案:

In [14]: l1 = [0, 0.002, 0.3, 0.5, 0.6, 0.9, 1.3, 1.9]*1000

In [15]: l1.sort()

In [16]: l2 = [0.5, 1.0, 1.5, 2.0]

In [17]: timeit list(partition(l1,l2))
1000 loops, best of 3: 1.53 ms per loop

In [18]: timeit list(split_l(l1,l2))
1000 loops, best of 3: 703 µs per loop

In [19]: timeit np.split(l1,np.searchsorted(l1,l2))
1000 loops, best of 3: 802 µs per loop

In [20]: list(split_l(l1,l2))  == list(partition(l1,l2))
Out[20]: True

创建一个本地引用来追加更糟糕:

def split_l(a, b):
    it = iter(b)
    start, sub = next(it), []
    append = sub.append
    for ele in a:
        if start <= ele:
            yield sub
            start, sub = next(it), []
            append = sub.append
        append(ele)
    yield sub

在 numpy 解决方案的时间内运行:

In [47]: l1.sort()

In [48]: timeit list(split_l(l1,l2))
1000 loops, best of 3: 498 µs per loop

In [49]: timeit list(partition(l1,l2))
1000 loops, best of 3: 1.73 ms per loop

In [50]: timeit np.split(l1,np.searchsorted(l1,l2))
1000 loops, best of 3: 812 µs per loop

【讨论】:

    【解决方案3】:

    作为一种快速方式,您可以使用 numpy 非常有效的方式来处理大量列表:

    >>> np.split(l1,np.searchsorted(l1,l2))
    [array([ 0.   ,  0.002,  0.3  ]), array([ 0.5,  0.6,  0.9]), array([ 1.3]), array([ 1.9]), array([], dtype=float64)]
    

    np.searchsorted 将在l1 中找到l2 项目的索引,而l1 保持排序(使用其默认排序),np.split 将根据索引列表拆分您的列表。

    在列表中接受答案的基准是大 1000 倍:

    from timeit import timeit
    
    s1="""
    
    def partition(values, indices):
        idx = 0
        for index in indices:
            sublist = []
            while idx < len(values) and values[idx] < index:
                sublist.append(values[idx])
                idx += 1
            if sublist:
                yield sublist
    
    l1 = [0, 0.002, 0.3, 0.5, 0.6, 0.9, 1.3, 1.9]*1000
    l2 = [0.5, 1.0, 1.5, 2.0]
    list(partition(l1, l2))
    
    """
    
    s2="""
    l1 = [0, 0.002, 0.3, 0.5, 0.6, 0.9, 1.3, 1.9]*1000
    l2 = [0.5, 1.0, 1.5, 2.0]
    np.split(l1,np.searchsorted(l1,l2))
       """
    
    print '1st: ' ,timeit(stmt=s1, number=10000)
    print '2nd : ',timeit(stmt=s2, number=10000,setup="import numpy as np")
    

    结果:

    1st:  17.5872459412
    2nd :  10.3306460381
    

    【讨论】:

    • 您应该真正将l1l2 的创建移出 的timeit 测试(以及定义partition() 函数)。
    • 1000 次重复我得到 1.43 vs 1.1。对于纯 python 实现来说还不错。
    • 啊,有问题。您不能只将l1 乘以1000,因为我的解决方案需要对l1 进行排序。如果您对列表进行正确排序,这对我的情况并没有帮助,因为这会使我的解决方案在产生更多结果时稍微变慢。
    • 回答了我自己的问题,我使用纯python的解决方案更快
    • @PadraicCunningham 10000000000000000 倍! :-D
    【解决方案4】:
    l1 = [0, 0.002, 0.3, 0.5, 0.6, 0.9, 1.3, 1.9]
    
    l2 = [0.5, 1.0, 1.5, 2.0]
    
    
      def partition(values, indices):
    
        temp = []
        p_list = []
    
    
        for j in range(len(indices)):
            for i in range(len(values)):
                if indices[j] > values[i]:
                    temp.append(values[i])
    
            p_list.append(temp)
    
            # added to the partition values are truncated from the list
            values = values[len(temp):]
    
            temp = []
    
        print(p_list)
    

    分区(l1,l2)

    [[0, 0.002, 0.3], [0.5, 0.6, 0.9], [1.3], [1.9]]

    【讨论】:

    • 这和OP的解决方案一样糟糕;你这里有一个 O(N*M) 二次算法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-23
    • 2015-05-13
    • 2020-01-14
    • 2018-10-24
    • 2020-05-02
    • 2013-09-05
    • 1970-01-01
    相关资源
    最近更新 更多