'Python: forward fill the data in a list

I have a list named x, I would like to fill the zero data with previous value, which means:

x = [x[t]=x[t-1] if x[t] == 0.0 for t in range(1,len(x)-2)]

But it displayed: SyntaxError: invalid syntax

I'm wondering where is wrong with my code? Thanks a lot.



Solution 1:[1]

Here's a list comprehension to do what you require:

x = [xi if xi or i==0 else x[i-1]
     for i, xi in enumerate(x)]

Solution 2:[2]

It's your assignment x[t] = x[t-1]. Instead just use a for loop:

for t in range(1, len(x)-1):
    if x[t] == 0:
        x[t] = x[t-1]

Although it would probably be considered more Pythonic to use enumerate to do this:

for idx, val in enumerate(x):
    if idx==0: continue # skip the first element
    if val == 0:
        x[idx] = x[idx-1]

# DEMO

In [1]: x = [1,0,3,0,4,0,5,0]

In [2]: for idx,val in enumerate(x):
   ...:     if idx==0: continue
   ...:     if val == 0:
   ...:         x[idx] = x[idx-1]
   ...:

In [3]: x
Out[3]: [1, 1, 3, 3, 4, 4, 5, 5]

You could also make this work with a list comp by implementing a pairwise iterator

from itertools import tee

def pairwise(iterable):
    a,b = tee(iterable)
    next(b) # advance one iterator
    return zip(a,b)

x = [x[0]] + [val if val else lastval for lastval,val in pairwise(x)]

We need to specifically add the first element since the pairwise iterator skips it. Alternatively we could define pairwise differently, e.g.

def pairwise(iterable):
    iterable = itertools.chain([None], iterable)
    a,b = itertools.tee(iterable)
    next(b)
    return zip(a,b)

x = [val if val else lastval for lastval,val in pairwise(x)]
# ta-da!

Solution 3:[3]

For full forward and backward filling (backwards in case non found before), the following will give you a filled element even if the element before it is zero:

#     ?        ?        ?  ?  ?     ?
x = [ 0, 1, 2, 0, 3, 5, 0, 0, 0, 9, 0 ]
y = []
for i,e in enumerate(x):
    if e == 0:
        # search left, get first non zero
        for left_e in reversed(x[:i]):
            if left_e != 0:
                e = left_e
                break
    # backward fill if all elements on the left are zeros 
    if e == 0:
        # search right, get first non zero
        for right_e in x[i+1:]:
            if right_e != 0:
                e = right_e
                break
    y.append(e)
print(y)
# [1, 1, 2, 2, 3, 5, 5, 5, 5, 9, 9]

If you want forward filling with looking only at one the previous element then Alex's answers suffice.

You can also use a simpler method next():

x = [ 0, 1, 2, 0, 3, 5, 0, 0, 0, 9, 0 ]
y = []
for i,e in enumerate(x):
    if e == 0:
        e = next((item for item in x[i:] if item != 0), e)
        e = next((item for item in reversed(x[:i]) if item != 0), e)
    y.append(e)

print(y)
# [1, 1, 2, 2, 3, 5, 5, 5, 5, 9, 9]

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Alex Martelli
Solution 2
Solution 3