【问题标题】:Padding or truncating a Python list填充或截断 Python 列表
【发布时间】:2015-08-09 03:09:14
【问题描述】:

我想截断或填充列表。例如。对于 4 号:

[1,2,3] -> [1,2,3,0]
[1,2,3,4,5] -> [1,2,3,4]

我可以看到几种方法:

def trp(l, n):
    """ Truncate or pad a list """
    r = l[:n]
    if len(r) < n:
        r.extend([0] * (n - len(r)))
    return r

或者更短但效率更低的:

map(lambda x, y: x if x else 0, m[0:n], [0] * n)

有没有更优雅的方式来做到这一点?

【问题讨论】:

    标签: python list python-2.7


    【解决方案1】:

    你可以使用itertools模块让它完全懒惰,像这样

    >>> from itertools import repeat, chain, islice
    >>> def trimmer(seq, size, filler=0):
    ...     return islice(chain(seq, repeat(filler)), size)
    ... 
    >>> list(trimmer([1, 2, 3], 4))
    [1, 2, 3, 0]
    >>> list(trimmer([1, 2, 3, 4, 5], 4))
    [1, 2, 3, 4]
    

    在这里,我们将实际序列与具有filler 值的无限中继器链接起来。然后我们将链式迭代器切片为size

    因此,如果序列的元素数量少于sizechain 将开始使用repeat。如果序列至少有size 元素,那么chain 甚至不必使用repeat

    这种方法的主要优点是,除非请求,否则不会在内存中创建完整的修剪或填充列表。所以,如果你要做的只是迭代它,那么你可以像这样简单地迭代它

    >>> for item in trimmer([1, 2, 3, 4, 5], 4):
    ...     print(item * 2)
    ...     
    ... 
    2
    4
    6
    8
    

    或者,如果您想将它与另一个修剪或填充的列表一起使用,那么您仍然可以在不创建实际列表的情况下这样做,就像这样

    >>> for item in chain(trimmer([1, 2, 3], 4), trimmer([1, 2, 3, 4, 5], 4)):
    ...     print(item, item * 2)
    ...     
    ... 
    1 2
    2 4
    3 6
    0 0
    1 2
    2 4
    3 6
    4 8
    

    懒惰摇滚 ;-)

    【讨论】:

    • 而且,更好的是,这样你可以只返回islice 并让调用者决定他是否需要一个列表或者可以一直保持完全懒惰。
    • @abarnert 啊,这实际上是我的想法 :-) 这就是我在答案中写“完全懒惰”的原因。感谢您指出。
    • 这比 OP 给出的 trp 定义要慢得多(慢 2 倍 - 3 倍)。
    • @FrerichRaabe 鉴于 OP 要求提供更 优雅 的解决方案,我并不认为这是一个问题。此外,您必须考虑到这是惰性的,这在某些情况下可能意味着它甚至没有得到评估。
    【解决方案2】:

    使用大于列表长度的索引进行切片只会返回整个列表。

    将一个列表乘以一个负值会返回一个空列表。

    也就是说函数可以写成:

    def trp(l, n):
        return l[:n] + [0]*(n-len(l))
    
    trp([], 4)
    [0, 0, 0, 0]
    
    trp([1,2,3,4], 4)
    [1, 2, 3, 4]
    
    trp([1,2,3,4,5], 4)
    [1, 2, 3, 4]
    
    trp([1,2,3], 4)
    [1, 2, 3, 0]
    

    In [1]: a = [1,2,3]
    
    In [2]: a[:4]
    Out[2]: [1, 2, 3]
    
    In [3]: [0]*0
    Out[3]: []
    
    In [4]: [0]*-1
    Out[4]: []
    

    【讨论】:

      【解决方案3】:

      就地版本:

      l[n:] = [0] * (n - len(l))
      

      复制版本:

      l[:n] + [0] * (n - len(l))
      

      【讨论】:

        【解决方案4】:

        你可以使用numpy.pad

        >>> def trp(a,n):
        ...    diff=n-len(a)
        ...    if diff >0:
        ...         return np.lib.pad(l2,(0,diff),'constant', constant_values=(0))
        ...    else :
        ...         return a[:n]
        ... 
        
        >>> l1=[1, 2, 3, 4, 5]
        >>> l2=[1, 2, 3]
        >>> trp(l2,4)
        array([1, 2, 3, 0])
        >>> trp(l1,4)
        [1, 2, 3, 4]
        

        【讨论】:

        • Numpy 对于这样的事情来说是非常过分的,除非 OP 已经在使用它。
        • @SnakesandCoffee 是的,它只是一种选择! ;) 还有np.pad 更灵活,建议收费很多!
        【解决方案5】:

        我认为您的原始版本不仅非常简单,而且是迄今为止发布的最有效的版本。我将此处给出的所有答案存储在单独的文件中(每个文件都公开一个“修剪器”功能),然后测试它们的填充和截断。结果如下:

        $ python --version
        Python 2.7.6
        

        将 100 个元素的列表填充到 200 个元素:

        $ for VERSION in dmtri1 dmtri2 thefourtheye dting; do echo -n "$VERSION: "; python -m timeit -s "from $VERSION import trimmer; l = range(100)" -- 'list(trimmer(l, 200))'; done
        dmtri1: 100000 loops, best of 3: 2.9 usec per loop
        dmtri2: 10000 loops, best of 3: 27.1 usec per loop
        thefourtheye: 100000 loops, best of 3: 5.78 usec per loop
        dting: 100000 loops, best of 3: 2.69 usec per loop
        

        将 100 个元素的列表截断为 50 个元素:

        $ for VERSION in dmtri1 dmtri2 thefourtheye dting; do echo -n "$VERSION: "; python -m timeit -s "from $VERSION import trimmer; l = range(100)" -- 'list(trimmer(l, 50))'; done
        dmtri1: 1000000 loops, best of 3: 0.832 usec per loop
        dmtri2: 100000 loops, best of 3: 8.27 usec per loop
        thefourtheye: 100000 loops, best of 3: 2.62 usec per loop
        dting: 1000000 loops, best of 3: 1.29 usec per loop
        

        【讨论】:

        • Vincent 的 in-place 版本会更快,但这并不重要。
        【解决方案6】:

        只是一个简单的解决方案。 非pythonic。

        def f(a):
            length_a = len(a)
            limit = 4
            if length_a > limit:
              a = a[:limit]
            else:
              for i in xrange(0,limit - length_a):
                a.append(0)
            return a
        
        >>> a = [1,2,3,4,5,6,7,7,8,8]
        >>> b = [1]
        >>> c = [1,2]
        >>> f(a)
        [1, 2, 3, 4]
        >>> f(b)
        [1, 0, 0, 0]
        >>> f(c)
        [1, 2, 0, 0]
        

        【讨论】:

          【解决方案7】:

          追加 -

          添加零直到您的列表达到您需要的长度:

          In [31]: x
          Out[31]: [1, 2, 3, 0]
          
          In [32]: [x.append(0) for i in range(10 - len(x))]
          Out[32]: [None, None, None, None, None, None]
          

          忽略Nones

          In [33]: x
          Out[33]: [1, 2, 3, 0, 0, 0, 0, 0, 0, 0]
          

          截断

          使用拼接:

          In [19]: x
          Out[19]: [1, 2, 3, 0, 1, 2, 3, 4]
          
          In [20]: x[:4]
          Out[20]: [1, 2, 3, 0]
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-12-28
            • 1970-01-01
            • 1970-01-01
            • 2018-01-17
            • 2023-03-15
            • 2012-06-02
            • 2020-03-02
            • 1970-01-01
            相关资源
            最近更新 更多