Joel Banks
Joel Banks

Reputation: 151

Some for loop number decreasing when they should be increasing while iterating through a list

I've been looking at my code for a while and I'm just stuck as to where I messed up, so maybe one of you can help.

What my for loop should do is: It iterates through a long list of times. It averages the first 100 times, and sets that as a value. This part works. What it should do next is add one to t (the value i'm using for my for loop) so it will average the 1st and 101st time together, if this average is less than .1 seconds faster, add the t value to a list of x values, then, set that value as the new average to beat. If it's not .1 lower, we increase t and try again until it works.

Here's my code, and I'll walk you through what it means to make it more readable

for t in times:
    t = int(t)
    sumset = sum(times[t:t + 100])
    avgset = (int(round(float(sumset/100), 3) * 10)) /10
    if t + 100 > len(times):
        break
    elif (avgset) <= firstavg - .1:
        avglist.append(avgset)
        firstavg -= .1
        xlist.append(t)
        print(t)
        print("avgset is "+str(avgset))
        print("should decrease by .1 " + str(math.ceil(firstavg * 10) / 10))
        tlist.append(t)
        t += 1
    else:
        t += 1

I'll explain it here.

for t in times:
    t = int(t)
    sumset = sum(times[t:t + 100])
    avgset = (int(round(float(sumset/100), 3) * 10)) /10

for each value in the my list called times, we take the value and make sure it's an int, I do this because earlier I was getting an indexing problem saying it wasn't an int. Sumset gets the sum of the first 100 times that we need, and avgset turns it into an average, multiplies it by 10, uses int to chop off the decimal, and divide by ten to get a tenth value.

Ex

12.34 * 10 = 123.4, int(123.4) = 123, 123 / 10 is 12.3.

Then here

if t + 100 > len(times):
    break

We make sure there are 100 values left to iterate through, if not we end the loop.

On this big chunk

elif (avgset) <= firstavg - .1:
    avglist.append(avgset)
    firstavg -= .1
    xlist.append(t)
    print(t)
    print("avgset is "+str(avgset))
    print("should decrease by .1 " + str(math.ceil(firstavg * 10) / 10))
    tlist.append(t)
    t += 1

We check: if the set is <= to the first average - .1, we append that set of averages to a list of lowering averages. Then we decrease the first avg, and append the value of t to a list that will make up our x-values. What it should do, is produce me a list of x values where each value corresponds to a decrease of .1 from the original average (t: t +100) where t is 0. And we get a y-list (which would be avglist) which is each decrease of .1. I'm not sure where I messed up, so if someone could point me in the right direction I would really appreciate it, thanks!

Upvotes: 0

Views: 1203

Answers (1)

mosegui
mosegui

Reputation: 722

In my opinion, there are multiple things to address in your code:

1) The main and most important is that you are mixing up the elements in your list (floats) with their indices, i.e. their positions in the list. What you want is to iterate over the indices, not over the elements themselves. What I mean is that given the list:

my_list = [5.67, 4.23, 7.88, 9.5]

the indices of [5.67, 4.23, 7.88, 9.5] are respectively: 0,1,2,3. Python wants to have integer number to iterate because it interpretes these numbers as the position of the elements in the list, regardless of their value. And positions, obviously, always need to be integers, i.e. you are either the 4th or the 5th, not the 4.23th. However, this DOES NOT mean that the values of the elements themselves need to be integers. For accounting for this difference there is the python builtin function enumerate():

>>> for index, value in enumerate([5.67, 4.23, 7.88, 9.5]):
...    print (index, '->', value)
...    
0 -> 5.67
1 -> 4.23
2 -> 7.88
3 -> 9.5
>>>

this is the reason for which you needed to convert your values (not the indices) to integers, and make the trick of multiplying and dividing by 10 for not losing the 0.1 resolution that you use to compare. You can forget about all that.

2) You do not really need to check in each iteration wheter there are still 100 elements left in the list or not. It suffices to iterate until the -100th element:

for index, time in enumerate(times[:-100]):

and it will automatically stop at the -100th. However, when you do it, remember you want to use always index as the iterator variable, not time. Moreover, in another for loop you might use in some other case, if you need to check whether some condition is fulfilled to process the current element, and if not skip to the next one, you should use continue instead of break:

for index, time in enumerate(times):
    if index+100 > len(times):
        continue

continue gets you out of the if statement and brings you to the for loop, ready to iterate with the next element. break will break the for loop and stop the iteration.

3) At the end of each of your iterations you have a

elif (...):
    ...
    t += 1
else:
    t += 1

this is wrong in many ways:

3.1) first because you are inside an iterator, and t refers to the variable you are using for iterating. You do not need at all to tell the iterator to sum 1 to the iteration variable at the end of each iteration. Doing that is its one job. It knows.

3.2) Supposing that would be any other control variable inside the loop that you indeed need to manually increase by one, you are repeating code lines. You basically get the same effect if you remove the else clause and remove the indent of the last line of the elif clause:

elif (...):
    ...

t += 1

so the algorithm will fall into t +=1 regardles of whether the elif clause is satisfied or not.

3.3) This is related to the above bullet 1): In your particular case and since you are wrongly using t to iterate (as discussed above), by doing t += 1 you are modifying the list you iterate over, i.e. you are altering the input data.

Taking all this into account, one possible way to roughly implement your code could be:

import numpy as np

times = 100*np.random.rand(150)

list_of_avgs = [np.sum(times[:100])/100.]

for index, element in enumerate(times[:-100]):

    avg = np.sum(times[index:index+100])/100.

    if avg + 0.1 <= list_of_avgs[-1]:
        list_of_avgs.append(avg)
    else:
        continue

 print (list_of_avgs)

which results into (input data is radomly generated):

[49.779866192794358, 49.594673775689778, 49.4409179407875, 
49.304759324340424, 49.106580355542434, 48.651419303532919, 
48.505888846346672, 47.834645246733295, 47.300679740055074, 
46.956253292222293, 46.598627928361239, 46.427709019922297]

Cheers and good luck!

D.

Upvotes: 1

Related Questions