dmitri
dmitri

Reputation: 3294

Padding or truncating a Python list

I'd like to truncate or pad a list. E.g. for size 4:

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

I can see a couple of ways:

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

Or a shorter, but less efficient:

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

Is there a more elegant way of doing this?

Upvotes: 18

Views: 5562

Answers (7)

thefourtheye
thefourtheye

Reputation: 239683

You can use itertools module to make it completely lazy, like this

>>> 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]

Here, we chain the actual sequence with the infinite repeater with the filler value. And then we slice the chained iterator to size.

So, when if the sequence has lesser number of elements than size, chain will start consuming the repeat. If the sequence has at least size elements, then chain will not even have to use the repeat.

The main advantage of this method is that, the complete trimmed or padded list is not created in memory, unless asked for it. So, if all you are going to do is to iterate it, then you can simply iterate it like this

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

Or, if you want to use it with another trimmed or padded list, then you can still do that without creating an actual list, like this

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

Laziness Rocks ;-)

Upvotes: 24

fixxxer
fixxxer

Reputation: 16164

To append -

Append zeroes till your list reaches the length you need:

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]

Ignore the Nones

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

To truncate

Use splicing:

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

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

Upvotes: 1

hlkstuv_23900
hlkstuv_23900

Reputation: 882

Just a trivial solution. Unpythonic.

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]

Upvotes: 1

Vincent
Vincent

Reputation: 13423

In-place version:

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

Copy version:

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

Upvotes: 7

Frerich Raabe
Frerich Raabe

Reputation: 94549

I think your original version is not only very straightforward but also the most efficient one posted so far. I stored all answers given here in separate files (each of which exposing a 'trimmer' function) and then tested them for both padding as well as truncating. Here are the results:

$ python --version
Python 2.7.6

Padding a list of 100 elements to 200 elements:

$ 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

Truncating a list of 100 elements to 50 elements:

$ 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

Upvotes: 4

Kasravnd
Kasravnd

Reputation: 107347

You can use 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]

Upvotes: 4

dting
dting

Reputation: 39307

Slicing using an index greater than the length of a list just returns the entire list.

Multiplying a list by a negative value returns an empty list.

That means the function can be written as:

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]: []

Upvotes: 21

Related Questions