Handling floats and integers in Median


#1

It seems like my code doesn't work for lists with odd lengths and this is the error:
"Oops, try again. median([6, 8, 12, 2, 23]) returned 12.5 instead of 8"

It has something to do with while loop but I can't see where's the problem.

def median(x):
    n=[]
    while len(x)!=0:
        n.append(min(x))
        x.remove(min(x))
    if len(x)%2!=0:
        r=len(x)/2
        return n[r]
    else:
        p=len(x)/2-1
        y=len(x)/2
        z=n[p]+n[y]
        return z/2.0

#2

When I first discovered this I thought, cool! After a good long while something I'd read began to sink in that made it not so cool, anymore. Fudging code is what it is, when you consider that 2 is a counting number, 2.0 is not. 2 is 1/2 of 4. 2.0 is not; it's 1/2 of 4.0.

When a counting number is in the denominator, and we expect our quotient to be a float, then set the numerator to float. Avoid fudging the denominator. It's a mistake to think this is good coding. It is not. Implicit typing of values is a slippery slope that can be avoided by making everything perfectly clear. Be explicit.

    return float(n[p] + n[y]) / 2

as an example.

Okay, now on to the real question, and the real answers. If we do not wish to mutate the list given to the function we can clone it with a shallow slice:

    y = x[:]

Anything we do to y won't affect x which is in global scope and may have a particular order that is meant to be preserved. x is now out of the picture.

Next we sort, and let's cache the length, and the floor value of half this,

    y.sort()
    u = len(y)
    n = u / 2

I didn't have to specify floor() since u is an integer and integer quotients are always floor values. Float quotients are floats, even when we use floor division...

    a = 3.5
    b = a // 2
    print b    # 1.0

Given the three data objects we have above, y, u, and n we have enough to work with.

    if u % 2 != 0:
        return y[n]
    else:
        return float(y[n-1] + y[n]) / 2

We've essentially done everything you did, with one exception, which I've written twice in this post. That one change and your code should work, too. No need to replace it.

    z = float( ... )
    return z / 2

I would suggest one other change, though,

while len(x) > 0:

#3

Now the real problem surfaces. Remember the while loop used for sorting? Well it reduced x to nothing. That means len(x) is always 0 after the while loop.

p => -1
y => 0
z = float( 23 + 2 ) / 2 => 12.5

Change that to len(n) and things should start to work.

def median(x):
    n=[]
    while len(x) > 0:
        n.append(min(x))
        x.remove(min(x))        
    if len(n) % 2 != 0:
        r = len(n) / 2
        return n[r]
    else:
        p = len(n) / 2 - 1
        y = len(n) / 2
        z = float(n[p] + n[y])
        return z / 2

#4

Thank you so much for your explanations. They helped a lot.