Only looping through even numbers - python

I am new to programming so forgive me if this is basic.
Below is part of my code for extracting Home and Away odds from many different bookmakers for a basketball match.
If I just loop through every element in the range, I am left with home and away odds all in one column, however, I want a separate column for home and away odds.
num_page_items = len(odds)
for i in range(num_page_items):
Home = (odds[2*i].text)
Away = (odds[2*i-1].text)
print(Home,Away)
My desired outcome is to display the home team odds next to the away team odds separated by a comma.
My code achieves this, however I believe it is not a great way of doing it as it technically runs into an error at the end (even though all of the data I'm after still displays).
How can I clean this up?
Thanks

You can create an iterable with a generator expression to extract the text attribute from the odd objects, and then pair them by zipping the iterable with itself, so that you can iterate through it to unpack Home and Away:
i = (odd.text for odd in odds)
for Home, Away in zip(i, i):
print(Home, Away)

num_page_items = len(odds)
for i in range(0, num_page_items, 2):
Home = odds[i].text # Starts from 0, goes till num_page_items, incrementing by 2 (even indices)
for i in range(1, num_page_items, 2):
Away = odds[i].text # Starts from 1, goes till num_page_items, incrementing by 2 (odd indices)
You run into an IndexError, since you access an element at index 2*i, where i iterates over values from 0 to the size of the list. For eg, if size is 10, you will end up trying to access odds[2*i] for i = 5, 6, 7... , the index of which is out of bounds

Another way to go is to do a check inside the for-loop whether i is odd or even. Just a work around of Shobhit Verma's answer.
num_page_items = len(odds)
for i in range(num_page_items):
if i % 2 == 0: #This is a sanity check to see if i is odd or even
Home = odds[i].text
else:
Away = odds[i].text

The itemgetter in the standard module operator is designed to select list contents. Fast, suitable for regular selection.
from operator import itemgetter
odds=[1,2,3,4,5,6]
num_page_items = len(odds)
getHome = itemgetter(*range(0,num_page_items,2))
getAway = itemgetter(*range(1,num_page_items,2))
Home = getHome(odds)
Away = getAway(odds)
print(Home)
print(Away)
Output:
(1, 3, 5)
(2, 4, 6)

Related

Python Find the mean school assignment - What is a loop?

I have been working on this assignment for about 2 weeks and have nothing done. I am a starter at coding and my teacher is really not helping me with it. She redirects me to her videos that I have to learn from every time and will not directly tell or help me on how I can do it. Here are the instructions to the assignment (said in a video, but made it into text.
Find the mean
Create a program that finds the mean of a list of numbers.
Iterate through it, and instead of printing each item, you want to add them together.
Create a new variable inside of that, that takes the grand total when you add things together,
And then you have to divide it by the length of your array, for python/java script you’ll need to use the method that lets you know the length of your list.
Bonus point for kids who can find the median, to do that you need to sort your list and then you need to remove items from the right and the left until you only have one left
All you’re doing is you need to create a variable that is your list
Create another variable that is a empty one at the moment and be a number
Iterate through your list and add each of the numbers to the variable you created
Then divide the number by the number of items that you had in the list.
Here's what I've done so far.
num = [1, 2, 3, 4, 5, 6];
total = 0;
total = (num[0] + total)
total = (num[1] + total)
total = (num[2] + total)
total = (num[3] + total)
total = (num[4] + total)
total = (num[5] + total)
print(total)
However, she tells me I need to shorten down the total = (num[_] + total) parts into a loop. Here is how she is telling me to do a loop in one of her videos.
for x in ____: print(x)
or
for x in range(len(___)): print (x+1, ____[x])
there is also
while i < len(___):
print(___[i])
i = i + 1
I really don't understand how to do this so explain it like I'm a total noob.
First of all, loops in python are of two types.
while: a while loop executes the code in a body until a condition is true. For example:
i = 0
while(i < 5):
i = i + 1
executes i = i + 1 until i < 5 is true, meaning that when i will be equal to 5 the loop will terminate because its condition becomes false.
for: a for loop in python iterates over the items of any sequence, from the first to the last, and execute its body at each iteration.
Note: in both cases, by loop body I mean the indented code, in the example above the body is i = i + 5.
Iterating over a list. You can iterate over a list:
Using an index
As each position of the array is indexed with a positive number from 0 to the length of the array minus 1, you can access the positions of the array with an incremental index. So, for example:
i = 0
while i < len(arr):
print(arr[i])
i = i + 1
will access arr[0] in the first iteration, arr[1] in the second iteration and so on, up to arr[len(arr)-1] in the last iteration. Then, when i is further incremented, i = len(arr) and so the condition in the while loop (i < arr[i]) becomes false. So the loop is broken.
Using an iterator
I won't go in the details of how an iterator works under the surface since it may be too much to absorb for a beginner. However, what matters to you is the following. In Python you can use an iterator to write the condition of a for loop, as your teacher showed you in the example:
for x in arr:
print(x)
An iterator is intuitively an object that iterates over something that has the characteristic of being "iterable". Lists are not the only iterable elements in python, however they are probably the most important to know. Using an iterator on a list allows you to access in order all the elements of the list. The value of the element of the list is stored in the variable x at each iteration. Therefore:
iter 1: x = arr[0]
iter 2: x = arr[1]
...
iter len(arr)-1: x = arr[len(arr)-1]
Once all the elements of the list are accessed, the loop terminates.
Note: in python, the function range(n) creates an "iterable" from 0 to n-1, so the for loop
for i in range(len(arr)):
print(arr[i])
uses an iterator to create the sequence of values stored in i and then i is in turn used on the array arr to access its elements positionally.
Summing the elements. If you understand what I explained to you, it should be straightforward to write a loop to sum all the elements of a list. You initialize a variable sum=0 before the loop. Then, you add the element accessed as we saw above at each iteration to the variable sum. It will be something like:
sum = 0
for x in arr:
sum = sum + x
I will let you write an equivalent code with the other two methods I showed you and do the other points of the assignment by yourself. I am sure that once you'll understand how it works you'll be fine. I hope to have answered your question.
She wants you to loop through the list.
Python is really nice makes this easier than other languages.
I have an example below that is close to what you need but I do not want to do your homework for you.
listName = [4,8,4,7,84]
for currentListValue in listName:
#Do your calculating here...
#Example: tempVar = tempVar + (currentListValue * 2)
as mentioned in the comments w3schools is a good reference for python.

IndexError: list index out of range - sometimes it runs and some times it is not

i have a situation that i am not sure how to solve.
sometimes when i run the program it's running perfectly and sometimes it says
list index out of range
the code:
hashtag_list = ['urban', 'hipster', 'retro']
for hashtag in hashtag_list:
tag = randint(1,3)
driver.get('https://www.instagram.com/explore/tags/' + hashtag_list[tag] + '/')
sleep(5)
any suggestions? - it's not the whole code tho but i figured it's all i need. - please let me know if any other information is needed
Python list are zeroed indexed. That means the first index of a list starts from 0. So the indexes for hastag_list are 0, 1, 2. However, randint(1,3) is inclusive, so tag will sometimes randomly be assigned 3.
It should start to become clear what the problem is. Since the maximum index of hashtag_list is 2, and tag is sometimes 3, then sometimes you will be indexing hashtag_list out of range.
The simple solution would be to use randint(0, 2) rather than randint(1, 3). However, a cleaner solution would be to use random.choice to choose a random option from hashtag_list:
from random import choice
# ...
hashtag_list = ['urban', 'hipster', 'retro']
for hashtag in hashtag_list:
tag = choice()
driver.get('https://www.instagram.com/explore/tags/' + hashtag_list[tag] + '/')
sleep(5)
randint(a, b) produces a random integer in the range [a, b] inclusive, those sometimes when it doesn't work it's producing 3, and 3 is beyond the list. Change it to randint(0, 2), because in Python (and many other languages), a list with n elements' indices start from 0 and end at n - 1. Thus:
# elements: ['urban', 'hipster', 'retro']
# indices: 0 1 2
Change
tag = randint(1,3)
to
tag = randint(0,2)

While loop gets ignored

Hej Everyone,
The idea of the script is to grab image links from a catalogue page of my company's website and change them to image links with a higher resolution and filter for image format, where the variable to filter for is found in the link itself, in this case the capital P. Afterwards a csv is generated with the links.
The transformation, filtering and writing to csv works fine, but my problem is that I don't want all the 80 products, I only want 8 to be in the list nl.
The links list contains elements like this one https://rndr.mywebsite.com/media/catalog/product/seo-cache/x386/19/95/19-95-101P/How-Hard-You-Hit-Butcher-Billy-Premium-Poster.jpg
NOTE: variables ratio and creatives (inputnumber-1) are defined by commandline input. Just assume that the input was ratio = P and creatives = 9-1.
NOTE2: For quicker testing, the links list has a limit of 15 elements by now.
nl= []
string1= "https://rndr.mywebsite.com/media/catalog/product/cache/x800/"
string2= ".jpg"
while len(nl) <= creatives:
for index in range(len(links)):
if "P" in "".join(links[index].split("/", 12)[10]) and "P" in ratio:
print("YEAH", len(nl))
nl.extend([string1 + "/".join(links[index].split("/", 11)[8:11]) + string2])
else:
print ("Ups", len(nl))
print (nl)
The actual output is
('YEAH', 0)
('YEAH', 1)
('YEAH', 2)
('YEAH', 3)
('Ups', 4)
('YEAH', 4)
('YEAH', 5)
('Ups', 6)
('YEAH', 6)
('YEAH', 7)
('YEAH', 8)
('YEAH', 9)
('YEAH', 10)
('YEAH', 11)
('YEAH', 12)
[https://rndr.mywebsite.com/media/catalog/product/cache/x800/19/95/19-95-101P.jpg, transformed-link2,...,transformed-link12]
As you can see the filtering and transforming works fine, but it should stop after having 9 links in the list nl.
As mentioned by Coldspeed, in the inner loop you're adding a whole batch of items to nl, thus overshooting the limit. To fix it, you could get rid of the while loop and do this instead:
for index in range(len(links)):
if "P" in "".join(links[index].split("/", 12)[10]) and "P" in ratio:
print("YEAH", len(nl))
nl.append(string1 + "/".join(links[index].split("/", 11)[8:11]) + string2)
if len(nl) > creatives:
break
else:
print ("Ups", len(nl))
Adding a couple of print statements like this can help you figure out exactly what is going on:
while len(nl) <= creatives:
print('outer loop')
for index in range(len(links)):
print('inner loop')
...
You've got a nested loop here. What happens is, inside the inner loop, the condition for the outer loop is not checked, until the inner loop has finished iterating. What you'd need to do is put an explicit break inside the inner loop.
Look at this answer for a solution. :)
You're doing a for loop inside the while loop. The while loop will only check its condition upon finishing the first for loop, by which point you've already looped over every element in links.
E.g.
i = 0
while i < 10:
for z in range(20):
i = z
print(i)
will print all the way to 19, because the precondition for the while loop will only be checked when the inner for loop finishes.

Controlling the flow of for loop in python

I'm trying to solve a musical chairs problem. The list_of_people is the list which contains the position of the people in the circle. The steps variable is the length of the song. So at each steps an element from list_of_people. At the end there should be only one element.I'm trying to solve this by using a simple for loop. I am deleting the elements in the loop at each steps that is at a count. I have two requirements: 1.)When I delete an element I want to step back one position in the loop from my current position. So, when I delete, I set the integer to the previous element, so the next time it should start from that position. But that's not working
2.) When the last element is reached i want to restart the for loop from the first position.
I know there is a provision in python itertools.cycle for cyclic iteration, but the problem in that is the len() function is not available and i'm breaking my for loop by checking the length of list_of_people
count=0
list_of_people = list(range(1,inputlength+1))
for integer in list_of_people:
if count==steps:
print("Element removed: "+str(integer))
#Getting the previous index
previous = list_of_people.index(integer)-1;
#deleting the current element
del list_of_people[list_of_people.index(integer)]
#Setting the index to previous element, THIS IS NOT WORKING!
#Need some help here!!!!!
integer = list_of_people[previous]
count=0
if len(list_of_people) < 2:#This is the breaking condition
break
#I need some help here!!!
#I need to restart the for loop from the first position
if list_of_people.index(integer)==len(list_of_people)-1:
#Set the loop index to zero
count+=1
print("The remaining element: "+str(list_of_people[0]))
Can anyone please help me on this? If there are any mistakes in the code, please forgive me I'm new to python.
It is extremely unwise to try to delete things from a list while you're iterating over it. Instead, build a new list, for example with a list comprehension:
list_of_people = [p for i, p in enumerate(list_of_people) if (i + 1) % steps]
An quick example:
>>> people = range(10)
>>> [p for i, p in enumerate(people, 1) if i % 3]
[0, 1, 3, 4, 6, 7, 9]
Based on your clarifying comments, I think what you want is simply:
def musical_chairs(people, steps):
index = 0
people = list(people)
while len(people) > 1:
index = (index + steps) % len(people)
del people[index]
index -= 1
return people
Demo:
>>> musical_chairs(range(1, 11), 4)
[6]

How to determine the sum of a group of integers without using recursion

This is my first post on Stack Overflow, and I'm hoping that it'll be a good one.
This is a problem that I thought up myself, and now I'm a bit embarrassed to say, but it's beating the living daylights out of me. Please note that this is not a homework exercise, scout's honor.
Basically, the program takes (as input) a string made up of integers from 0 to 9.
strInput = '2415043'
Then you need to break up that string of numbers into smaller groups of numbers, until eventually, the sum of those groups give you a pre-defined total.
In the case of the above string, the target is 289.
iTarget = 289
For this example, there are two correct answers (but most likely only one will be displayed, since the program stops once the target has been reached):
Answer 1 = 241, 5, 043 (241 + 5 + 043 = 289)
Answer 2 = 241, 5, 0, 43 (241 + 5 + 0 + 43 = 289)
Note that the integers do not change position. They are still in the same order that they were in the original string.
Now, I know how to solve this problem using recursion. But the frustrating part is that I'm NOT ALLOWED to use recursion.
This needs to be solved using only 'while' and 'for' loops. And obviously lists and functions are okay as well.
Below is some of the code that I have so far:
My Code:
#Pre-defined input values, for the sake of simplicity
lstInput = ['2','4','1','5','0','4','3'] #This is the kind of list the user will input
sJoinedList = "".join(lstInput) #sJoinedList = '2415043'
lstWorkingList = [] #All further calculuations are performed on lstWorkingList
lstWorkingList.append(sJoinedList) #lstWorkingList = ['2415043']
iTarget = 289 #Target is pre-defined
-
def SumAll(_lst): #Adds up all the elements in a list
iAnswer = 0 #E.g. lstEg = [2,41,82]
for r in _lst: # SumAll(lstEg) = 125
iAnswer += int(r)
return(iAnswer)
-
def AddComma(_lst):
#Adds 1 more comma to a list and resets all commas to start of list
#E.g. lstEg = [5,1001,300] (Note only 3 groups / 2 commas)
# AddComma(lstEg)
# [5,1,0,001300] (Now 4 groups / 3 commas)
iNoOfCommas = len(_lst) - 1 #Current number of commas in list
sResetString = "".join(_lst) #Make a string with all the elements in the list
lstTemporaryList = []
sTemp = ""
i = 0
while i < iNoOfCommas +1:
sTemp += sResetString[i]+',' #Add a comma after every element
i += 1
sTemp += sResetString[i:]
lstTemporaryList = sTemp.split(',') #Split sTemp into a list, using ',' as a separator
#Returns list in format ['2', '415043'] or ['2', '4', '15043']
return(lstTemporaryList)
return(iAnswer)
So basically, the Pseudo-code will look something like this:
Pseudo-Code:
while SumAll(lstWorkingList) != iTarget: #While Sum != 289
if(len(lstWorkingList[0]) == iMaxLength): #If max possible length of first element is reached
AddComma(lstWorkingList) #then add a new comma / group and
Reset(lstWorkingList) #reset all the commas to the beginning of the list to start again
else:
ShiftGroups() #Keep shifting the comma's until all possible combinations
#for this number of comma's have been tried
#Otherwise, Add another comma and repeat the whole process
Phew! That was quite a mouthfull .
I have worked through the process that the program will follow on a piece of paper, so below is the expected output:
OUTPUT:
[2415043] #Element 0 has reached maximum size, so add another group
#AddComma()
#Reset()
[2, 415043] #ShiftGroups()
[24, 15043] #ShiftGroups()
[241, 5043] #ShiftGroups()
#...etc...etc...
[241504, 3] #Element 0 has reached maximum size, so add another group
#AddComma()
#Reset()
[2, 4, 15043] #ShiftGroups()
[2, 41, 5043] #ShiftGroups()
#etc...etc...
[2, 41504, 3] #Tricky part
Now here is the tricky part.
In the next step, the first element must become 24, and the other two must reset.
#Increase Element 0
#All other elements Reset()
[24, 1, 5043] #ShiftGroups()
[24, 15, 043] #ShiftGroups()
#...etc...etc
[24, 1504, 3]
#Increase Element 0
#All other elements Reset()
[241, 5, 043] #BINGO!!!!
Okay. That is the basic flow of the program logic. Now the only thing I need to figure out, is how to get it to work without recursion.
For those of you that have been reading up to this point, I sincerely thank you and hope that you still have the energy left to help me solve this problem.
If anything is unclear, please ask and I'll clarify (probably in excruciating detail X-D).
Thanks again!
Edit: 1 Sept 2011
Thank you everyone for responding and for your answers. They are all very good, and definitely more elegant than the route I was following.
However, my students have never worked with 'import' or any data-structures more advanced than lists. They do, however, know quite a few list functions.
I should also point out that the students are quite gifted mathematically, many of them have competed and placed in international math olympiads. So this assignment is not beyond the scope of
their intelligence, perhaps only beyond the scope of their python knowledge.
Last night I had a Eureka! moment. I have not implemented it yet, but will do so over the course of the weekend and then post my results here. It may be somewhat crude, but I think it will get the job done.
Sorry it took me this long to respond, my internet cap was reached and I had to wait until the 1st for it to reset. Which reminds me, happy Spring everyone (for those of you in the Southern Hempisphere).
Thanks again for your contributions. I will choose the top answer after the weekend.
Regards!
A program that finds all solutions can be expressed elegantly in functional style.
Partitions
First, write a function that partitions your string in every possible way. (The following implementation is based on http://code.activestate.com/recipes/576795/.) Example:
def partitions(iterable):
'Returns a list of all partitions of the parameter.'
from itertools import chain, combinations
s = iterable if hasattr(iterable, '__getslice__') else tuple(iterable)
n = len(s)
first, middle, last = [0], range(1, n), [n]
return [map(s.__getslice__, chain(first, div), chain(div, last))
for i in range(n) for div in combinations(middle, i)]
Predicate
Now, you'll need to filter the list to find those partitions that add to the desired value. So write a little function to test whether a partition satisfies this criterion:
def pred(target):
'Returns a function that returns True iff the numbers in the partition sum to iTarget.'
return lambda partition: target == sum(map(int, partition))
Main program
Finally, write your main program:
strInput = '2415043'
iTarget = 289
# Run through the list of partitions and find partitions that satisfy pred
print filter(pred(iTarget), partitions(strInput))
Note that the result is calculated in a single line of code.
Result: [['241', '5', '043'], ['241', '5', '0', '43']]
Recursion isn't the best tool for the job anyways. itertools.product is.
Here's how I search it:
Imagine the search space as all the binary strings of length l, where l is the length of your string minus one.
Take one of these binary strings
Write the numbers in the binary string in between the numbers of your search string.
2 4 1 5 0 4 3
1 0 1 0 1 0
Turn the 1's into commas and the 0's into nothing.
2,4 1,5 0,4 3
Add it all up.
2,4 1,5 0,4 3 = 136
Is it 289? Nope. Try again with a different binary string.
2 4 1 5 0 4 3
1 0 1 0 1 1
You get the idea.
Onto the code!
import itertools
strInput = '2415043'
intInput = map(int,strInput)
correctOutput = 289
# Somewhat inelegant, but what the heck
JOIN = 0
COMMA = 1
for combo in itertools.product((JOIN, COMMA), repeat = len(strInput) - 1):
solution = []
# The first element is ALWAYS a new one.
for command, character in zip((COMMA,) + combo, intInput):
if command == JOIN:
# Append the new digit to the end of the most recent entry
newValue = (solution[-1] * 10) + character
solution[-1] = newValue
elif command == COMMA:
# Create a new entry
solution.append(character)
else:
# Should never happen
raise Exception("Invalid command code: " + command)
if sum(solution) == correctOutput:
print solution
EDIT:
agf posted another version of the code. It concatenates the string instead of my somewhat hacky multiply by 10 and add approach. Also, it uses true and false instead of my JOIN and COMMA constants. I'd say the two approaches are equally good, but of course I am biased. :)
import itertools
strInput = '2415043'
correctOutput = 289
for combo in itertools.product((True, False), repeat = len(strInput) - 1):
solution = []
for command, character in zip((False,) + combo, strInput):
if command:
solution[-1] += character
else:
solution.append(character)
solution = [int(x) for x in solution]
if sum(solution) == correctOutput:
print solution
To expand on pst's hint, instead of just using the call stack as recursion does, you can create an explicit stack and use it to implement a recursive algorithm without actually calling anything recursively. The details are left as an exercise for the student ;)

Categories