0

['2598.95165', '2541.220308', '221068.0401', 'end', '4834.581952', '1056.394859', '3010.609563', '2421.437603', '4619.861889', '746.040504', '268.3881793', '379.3934898', '1252.527752', '11459.88522', '4862.167506', '506.924289', '634.6737389', '496.4679199', '17941.59143', '919.4998935', '7247.610974', '1166.053214', '47360.91508', '855.2426137', '4020.444585', '4469.896904', '2615.874982', '19862.92009', '2379.619573', '1203.268956', '4399.589212', '6838.825864', '1848.407564', '3527.198403', '33976.85042', '818.8722263', '634.6652078', '469.2685928', '4864.830004', '5103.222941', '1011.239929', '829.9915382', '8571.237936', '3301.953656', '14594.47385', '25688.83822', '4024.393045', '4163.775185', '1775.894366', '3682.012227', '3371.092883', '6651.509488', '7906.092773', '7297.133447', 'end', '4566.874299', 'end', '4255.700077', '1857.648393', '11289.48095', '2070.981805', '1817.505094', '1892.256615', '1757.0048', '59458.46328', '778.5755201', '54987.32423', '2245.172711', '722.2619663', '5116.616632', '3427.865861', '17973.07118', '14398.74281', '66313.92115', '11585.24151', '45294.03043', '6524.744077', '25958.80015', '593.3786209', '2899.040703', '85577.21342', '153576.2633', '5852.008444', '563.0265409', '70796.45356', '565.2123689', '6560.030116', '2668.934414', '418.666014', '5216.392132', '760.894589', '8072.957639', '346.5905371', 'end']

I would like to create a function that, given a bin, which is a list (example above), generates averages for the numbers separated by a string 'end'. I am expecting to have 4 averages from the above bin, since there are 4 sets of numbers separated by 4 'end' strings

can someone help me fix my code below which does not seem to work:

def average(bin):
    total = 0.0
    count=0
    for number in bin:       
        if number==float(number):
            total += float(number)
            count+=1
            avg = total/count
        elif number=='end':
            continue

    return avg
2
  • 1
    if number==float(number): will raise a ValueError when testing the string 'end'. Commented Jan 19, 2012 at 22:23
  • Is this the form that you obtain the raw data in? It's possible there's a simpler way. I'll edit my answer below to accomodate. Commented Jan 19, 2012 at 23:59

4 Answers 4

4
def averages(bin):
    li, s, n = [], 0, 0
    for item in bin:
        if item == 'end':
            li.append(s / n)
            s, n = 0, 0
        else:
            s += float(item)
            n += 1
    return li

Or, you can try this one-liner:

    averages = [sum(k) / len(k) for k in [[float(j) for j in i.split() if j] for i in ' '.join(bin).split('end') if i]]
Sign up to request clarification or add additional context in comments.

1 Comment

Your one liner, if stretched out over a couple of lines, would get a +1 from me.
2

Here's a really concise way to compute these averages:

def average(bin):
    start = 0
    while 'end' in bin[start:]:
        stop = bin.index('end', start)
        if stop > start:
            print sum(map(float, bin[start:stop])) / (stop-start)
        start = stop+1

If you want to keep track of your averages, you can store them and return them at the end of the function:

def average(bin):
    results = []
    start = 0
    while 'end' in bin[start:]:
        stop = bin.index('end', start)
        if stop > start:
            results.append(sum(map(float, bin[start:stop])) / (stop-start))
        start = stop+1
    return results

The line "if stop > start:" will prevent a ZeroDivisionError if you have two 'end' markers in a row in your input.

For even more Python magic, you can replace print with yield and your function will become a generator:

def average(bin):
    start = 0
    while 'end' in bin[start:]:
        stop = bin.index('end', start)
        if stop > start:
            yield sum(map(float, bin[start:stop])) / (stop-start)
        start = stop+1

for a in average(bin):
    print a

Comments

1

Your code doesn't work because you are overwriting avg on each pass through the loop. You will need to store the value of avg for each set as a list, or something. You also need to reset count and total whenever you come across an end. Try this:

def average(bin):
    total = 0.0
    count=0
    avgs = []
    for number in bin:       
        if number == 'end':
            avgs.append(avg)
            total = 0.0
            count = 0
        else:
            total += float(number)
            count+=1
            avg = total/count

    return avgs

Note there are better ways to accomplish your goal, but in the interest of understanding I tried to make as few changes to your code as possible.

2 Comments

I think you'll have to swap the if order; float('end') will raise a ValueError otherwise. number == float(number) will also never be true for a string number.
better to move avg = total/count to the first line of the if block
0

Here's a solution (inspired by the others) which also gives the mean of empty sets (NaN) instead of ignoring them.

def average(bin):
  start=0    
  o=[]
  while 'end' in bin[start:]:
    stop = bin.index('end',start)
    s=1.0/(stop-start) if stop>start else float('nan')
    o.append(s*(sum(map(float, bin[start:stop]))))
    start=stop+1
  return o

and the corresponding one liner (based on the one by Cornett) is:

[sum(k) / len(k) if k else float('nan') for k in [
 [float(j) for j in i.split() if j] for i in ' '.join(bin).split('end') if i]]

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.