0

I have a list of this sort :

numbers = [12, None, None, 17, 20, None, 28]

What's the best path to consider in order to fill in None values with appropriate numbers so that the None values get numbers in between the defined values.

Example :

numbers = [12, **14**, **16**, 17, 20, **24**, 28]
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
The Other Guy
  • 576
  • 10
  • 21

2 Answers2

1

Interpolating on the fly to create a linear interpolation:

def interpolate(l):
    last = None
    count = 0
    for num in l:
        if num is None:
            # count the number of gaps
            count += 1
            continue
        if count:
            # fill a gap based on the last seen value and the current
            step = (num - last) / float(count + 1)
            for i in xrange(count):
                yield last + int(round((1 + i) * step))
            count = 0
        last = num
        yield num

Demo:

>>> list(interpolate([12, None, None, 17, 20, None, 28]))
[12, 14, 15, 17, 20, 24, 28]
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
0

I didn't at first see the requirement of the replaced index needing to be in between.

def interpolate(x):
    while None in x:
        indexa = x.index(None)
        indexb = indexa + 1
        while (x[indexb]) is None:
            indexb = indexb + 1
        indexb = indexb - 1
        index = int((indexa + indexb) / 2)
        x[index] = y(index, x)
    return x


def y(j, x):
    a, b, j2 = 0.0, 0.0, j
    while x[j2] is None and j2 > -1:
        j2 = j2 - 1
    a = x[j2]
    while x[j] is None:
        j = j + 1
    b = x[j]
    return int((a + b) / 2)

print(interpolate([12, None, None, 17, 20, None, 28]))
print(interpolate([12, None, None, None, 17, 20, None, 28]))
print(interpolate([12, None, None, None, None, 17]))
nones = lambda i: [None for nothing in range(i)]
for i in range(10):
    print(interpolate([0] + nones(i) + [10]))

Output:

[12, 14, 15, 17, 20, 24, 28]
[12, 13, 14, 15, 17, 20, 24, 28]
[12, 13, 14, 15, 16, 17]
[0, 10]
[0, 5, 10]
[0, 5, 7, 10]
[0, 2, 5, 7, 10]
[0, 2, 5, 7, 8, 10]
[0, 2, 3, 5, 7, 8, 10]
[0, 2, 3, 5, 6, 7, 8, 10]
[0, 1, 2, 3, 5, 6, 7, 8, 10]
[0, 1, 2, 3, 5, 6, 7, 8, 9, 10]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
motoku
  • 1,571
  • 1
  • 21
  • 49
  • 1
    You are interpolating `4` everywhere. Your output is `[1, 4, 2, 4, 3]`, which is *nowhere close* to the expected output. – Martijn Pieters Dec 05 '14 at 18:52
  • @MartijnPieters Is that better? – motoku Dec 05 '14 at 19:28
  • Your variable and function names are also.. rather opaque. You should try and come up with better names than single letters. – Martijn Pieters Dec 05 '14 at 19:38
  • I do not get that error on either python3 pr python2. I get `[12, 14, 14, 17, 20, 24, 28]` for the sample, and `[1, 1, 2, 5, 8, 8, 10, 15, 20]` for my input. – motoku Dec 05 '14 at 19:39
  • Ah, I see now what is going on. I had a different `x` from another test; you are assuming that `x` is a global here. When I explictly set `x` to the sample, it works. Never a good idea. – Martijn Pieters Dec 05 '14 at 19:42
  • You repeatedly fill in the same number for `None` values; if you extend the sample from the OP to `[12, None, None, None, 17, 20, None, 28]` you simply put in `14` three times; that's not really linear interpolation; that's just repeatedly inserting the midpoint of the two values at surrounding the 'gap'. – Martijn Pieters Dec 05 '14 at 19:47
  • You don't need to use a list comprehension to turn the generator result into a list; you can just use `list(z(x))` instead. – Martijn Pieters Dec 05 '14 at 19:48
  • To me, the OP doesn't mention interpolation. – motoku Dec 05 '14 at 19:55
  • The example shown is a linear interpolation result. Cyber pointed out to the OP that [the correct term is interpolation](http://stackoverflow.com/posts/comments/43104174). – Martijn Pieters Dec 05 '14 at 19:56
  • Ok. Now the numbers aren't repeating. – motoku Dec 05 '14 at 20:15
  • Right; the output is still a little.. uneven and you are altering the list in-place, but at least you now have a linear interpolation, of sorts; try `[12, None, None, None, None, 17]` for example; rather than `[12, 13, 14, 15, 16, 17]` you end up with `[12, 12, 13, 14, 15, 17]` for example. – Martijn Pieters Dec 05 '14 at 20:20
  • Solves it more or less. – motoku Dec 05 '14 at 20:33
  • Yup, now at least you get decent output. Your algorithm does do a *lot* of unnecessary scanning though; each time you do an `.index()` the list is scanned from the start to find a `None` value, then you scan forward from there to find the next not-`None` value, replace *one* of the `None` values, then start the whole process again. `list.index()` takes a second argument, the starting index from where to start searching, that could be used here. And if you then scan forward to the next non-`None` value, why not fill in those intermediate values you just skipped at the same time? – Martijn Pieters Dec 05 '14 at 20:50