Python: Control Flow - python

An issue with my existing code. Code goes:
example_dic = {'name': 'jim','value': 4}
list_of_dic = [example_dic,dic2,dic3,...]
empty_list = [] #will be filled with multiple dictionaries all in same format/same keys
key_sum = sum(blah['value'] for blah in empty_list) #tested this with a filled in "list_of_dic", works as expected
if not empty_list or key_sum < arbitrary_value:
for things in list_of_dic[:]:
if case1:
empty_list.append(things)
list_of_dic.remove(things)
elif case2:
empty_list.append(things)
list_of_dic.remove(things)
else:
pass
Problem is that key_sum does not get updated ever even though things are being appended onto empty_list. As I said in the comments, I know the key_sum line works because I tried it by filling in the list of dictionaries with random stuff first.
What I want is that items will keep being added onto list_of_dic only while key_sum < arbitrary. If for example I want key_sum < 20, if the next item causes key_sum >= 20, I do not want it to be added at all, not simply break and end after it's already been added. I also do not want the code to end there, if there is a list of 10 items and the 1st one has value = 22 I don't want the whole thing to stop, I want it to keep going through the rest, adding items on until it cannot add anymore that wouldn't cause key_sum >= 20.
Simpler answer would be, is there any other language which doesn't require such unnecessary complication for what seems like a very simple task?

There are a couple of issues with this. One is that your code assumes that key_sum gets automatically updated when you change empty_list, but that's not the case. It just gets calculated once. You'll need to recalculate key_sum on every iteration, or if you're really worried about efficiency, increment the key_sum every time you append to empty_list. It also seems like you want to check the value of key_sum on every iteration of your for loop, rather than only after you've iterated over the entire list_of_dic.
The second issue is that you're removing items from list_of_dic while you iterate over it. This has undefined behavior in Python, and generally results in certain elements of your iterable being skipped over. Instead, you need to iterate over a copy of the list.
Summarizing the changes:
for things in list_of_dic[:]: # Iterate over a copy of list_of_dic
do_append = False
if case1:
do_append = True
elif case2:
do_append = True
if do_append:
if (key_sum + things['value']) >= arbitrary_value:
continue
empty_list.append(things)
list_of_dic.remove(things)
key_sum += things['value']

Related

the code is giving me a number from the list instead of the mode

in one of my work i need to find the mode a list called "dataset" using no modual or function that would find the mode by itself.
i tried to make it so it can output the mode or the list of modes depending on the list of numbers. I used 2 for loops so the first number of the list checks each number of the list including its self to see how many numbers of its self there is, for example if my list was 123415 it would say there is 2 ones, and it does this for all the numbers of the list. the number with the most counts would be the mode. The bottom section of the code where the if elif and else is, there is where it checks if the number has the most counts by comparing with the other numbers of the list checking if it has more numbers or the same as the previous biggest number.
I've tried to change the order of the codes but i'm still confused why it is doing this error
pop_number = []
pop_amount = 0
amount = 0
for i in range(len(dataset)):
for x in dataset:
if dataset[i] == x:
amount += 1
if amount>pop_amount:
pop_amount = amount
pop_number = []
pop_number.append(x)
amount = 0
elif amount==pop_amount:
pop_amount = amount
if x not in pop_number:
pop_number.append(x)
pop_amount = amount
amount = 0
else:
continue
print(pop_number)
i expected the output to be the mode of the list or the list of modes but it came up with the last number from the list
As this is apparently homework, I will present a sketch, not working code.
Observe that a dict in Python can hold key-value mappings.
Let the numbers in the input list be the keys, and the values the number of times they occur. Going over the list, use each item as the key for the dict, and add one to the value (starting at 0 -- defaultdict(int) is good for this). If the result is bigger than any previous maximum, remember this key.
Since you want to allow for more than one mode value, the variable which remembers the maximum key should be a list; but since you have a new maximum, replace the old list with a list containing just this key. If another value also reaches the maximum, add it to the list. (That's the append method.)
(See how this is if bigger than maximum so far and then else if equal to maximum so far and then otherwise there is no need to do anything.)
When you have looped over all items in the input list, the list of remembered keys is your result.
Go back and think about what variables you need already before the loop. The maximum so far should be defined but guaranteed to be smaller than any value you will see -- it makes sense to start this at 0 because as soon as you see one key, it will have a bigger count than zero. And the keys you want to remember can start out as an empty list.
Now think about how you would test this. What happens if the input list is empty? What happens if the input list contains just the same number over and over? What happens if every item on the input list is unique? Can you think of other corner cases?
Without using any module or function that will specifically find the mode itself, you can do that with much less code. Your code will work with a little more effort, I highly suggest you to try to solve the problem on your own logic, but meanwhile let me show you how to take the help of all the built-in data structures in Python List, Tuples, Dictionaries and Sets within 7-8 lines. Also there is unzipping at the end (*). I will suggest you to look these up, when you get time.
lst = [1,1,1,1,2,2,2,3,3,3,3,3,3,4,2,2,2,5,5,6]
# finds the unique elements
unique_elems = set(lst)
# creates a dictionary with the unique elems as keys and initializes the values to 0
count = dict.fromkeys(unique_elems,0)
# gets the frequency of each element in the lst
for elem in unique_elems:
count[elem] = lst.count(elem)
# finds max frequency
max_freq = max(count.values())
# stores list of mode(s)
modes = [i for i in count if count[i] == max_freq]
# prints mode(s), I have used unzipping here so that in case there is one mode,
# you don't have to print ugly [x]
print(*modes)
Or if you want to go for the shortest (I really shouldn't be making such bold claims in StackOverflow), then I guess this will be it (even though, writing short codes for the sake of it is discouraged)
lst = [1,1,1,1,2,2,2,3,3,3,3,3,3,4,2,2,2,5,5,6]
freq_dist = [(i, lst.count(i)) for i in set(lst)]
[print(i,end=' ') for i,j in freq_dist if j==max(freq_dist, key=lambda x:x[1])[1]]
And if you just want to go bonkers and say goodbye to loops (Goes without saying, this is ugly, really ugly):
lst = [1,1,1,1,2,2,2,3,3,3,3,3,3,4,2,2,2,5,5,6]
unique_elems = set(lst)
freq_dist = list(map(lambda x:(x, lst.count(x)), unique_elems))
print(*list(map(lambda x:x[0] if x[1] == max(freq_dist,key = lambda y: y[1])[1] else '', freq_dist)))

Looping for loop in Python

I have the following code.
for idx in range(len(networks)):
net_ = networks[idx]
lastId=0
for layerUptID in range(len(net_[1])):
retNet,lastId=cn_.UpdateTwoConvLayers(deepcopy(net_),lastId)
networks.append(retNet)
if(lastId==-1):
break
networks has only one net at the beginning.
After running the line retNet,lastId=cn_.UpdateTwoConvLayers(deepcopy(net_),lastId), I have additional six nets and appended to networks.
So after this lastId ==-1, go back to first for loop with len(networks) is 7.
For the next idx, idx=1 and continue.
Then, len(networks) is 13. Then go back to first for loop.
After this, the first for loop breaks.
I am expecting to continue for idx is 2, but it breaks.
What could be the issue?
If you try using a WHILE loop instead of FOR loop, the break statement would be check if the loop is on the last item in 'networks' collection.
This way the network length would be calculated in each loop iteration
For starters: Iterating, or looping, over the list (or data) you're editing is bad practice. Keep that in mind while coding.
This means if you plan to edit what you're looping on, in your case networks, then you're going to have a bad time looping over it. I would advise to break it up into two code parts:
The first part creates a new list of whatever it is you want WHILE looping.
The second part replaces the list you've used to generate what you wanted.
Another thing which could go wrong is net_[i] may not be set up for some i, and you're trying to access it here:
for layerUptID in range(len(net_[1])):
What if there is nothing in net_[1]?
To avoid these errors, usually verifying your data is a great way to start. If it is not null, then proceed, otherwise, print an error.
This is what I can think of. Hope it helps.
If I understood correctly your problem is that you've added new elements to networks, i.e. have increased length of networks and expect that for-loop will pick up this changes, well it's not, let's look at following snippet
elements = [1]
indices = range(len(elements))
for index in indices:
print('index is', index)
elements.append(2)
print('elements count is', len(elements))
print('indices count is', len(indices))
outputs are
index is 0
elements count is 2
indices count is 1
so as we can see despite the fact that length of elements list has changed, range object which is used in for-loop has not. This happens because len returns int object which are immutable, so when you change list length its length becomes different object and range function has no idea about this changes.
Finally, we can use while loop here like
while networks:
net_ = networks.pop()
lastId = 0
for layerUptID in range(len(net_[1])):
retNet, lastId = cn_.UpdateTwoConvLayers(deepcopy(net_), lastId)
networks.append(retNet)
if lastId == -1:
break

python: "while (len(list1)>0) or (len(list2)>0)" doesn't work

Im taking the Google Python Course and I am solving the below problem:
Given two lists sorted in increasing order, create and return a merged >list of all the elements in sorted order. You may modify the passed in >lists. Ideally, the solution should work in "linear" time, making a single >pass of both lists.
And my solution is below:
def linear_merge(list1, list2):
# +++your code here+++
a = []
while (len(list1)>0) or (len(list2)>0):
if list1[-1] > list2[-1]:
a.append(list1.pop(-1))
elif list1[-1] < list2[-1]:
a.append(list2.pop(-1))
else:
a.append(list1.pop(-1))
a.append(list2.pop(-1))
#Have to force check
if (len(list1)==0):
break
if (len(list2)==0):
break
if len(list1)>0:
res = (a+list1)
return res[::-1]
else:
res = (a+list2)
return res[::-1]
My issue is even though I check if both lists are empty, I get a list index out of range error. I have to force check if either list is empty at the end of the while loop to prevent the error from occurring.
Why is the while loop failing to correctly see that one of the lists is empty? I am new to python and I am looking for some clarification on why this happens.
My issue is even though I check if both lists are empty...
No, you are checking if either one of them is not empty.
Change this:
while (len(list1)>0) or (len(list2)>0)
To this:
while (len(list1)>0) and (len(list2)>0)
And at the end of the while loop continue working on the one list which is still not empty.

Trying to create a sorting algorithm from scratch in Python

I'm taking a course on programming (I'm a complete beginner) and the current assignment is to create a Python script that sorts a list of numbers in ascending order without using built-in functions like "sorted".
The script I started to come up with is probably laughably convoluted and inefficient, but I'd like to try to make it work eventually. In other words, I don't want to just copy someone else's script that's better.
Also, as far as I can tell, this script (if it ever functioned) would probably just put things in a NEW order that wouldn't necessarily be ascending. This is just a start, though, and hopefully I'll fix that later.
Anyway, I've run in to several problems with the current incarnation, and the latest is that it just runs forever without printing anything.
So here it is (with hashes explaining what I was trying to accomplish). If someone could look over it and tell me why the code does not match my explanations of what each block is supposed to do, that would be great!
# The numbers to be inputted, could be anything
numList = [1, 25, 5, 6, 17, 4]
# The final (hopefully sorted) list
numSort = []
# The index
i = 0
# Run the loop until you run out of numbers
while len(numList) != 0:
# If there's only one value left in numList,
# attach it to the end of numSort.
# (Variable 'basket' is just for transporting numbers)
if len(numList) == 1:
basket = numList.pop()
numSort.append(basket)
# The rest of the elifs are supposed to compare values
# that sit next to each other.
# If there's still a number in numList after 'i'
# and 'i' is smaller than that next number
# then pop 'i' and attach it to the end of numSort
elif numList[i+1] != numList[-1] and numList[i] < numList[i+1]:
basket = numList.pop(i)
numSort.append(basket)
# If there's NOT a number after 'i'
# then compare 'i' to the first number in the list instead.
elif numList[i+1] == numList[-1] and numList[i] < numList[0]:
basket = numList.pop(i)
numSort.append(basket)
# If 'i' IS the last number in the list
# and has nothing to compare itself to,
# Then start over and go through it again
# from the beginning
elif numList [i+1] == numList[-1]:
i = 0
# If 'i' is not at the end of numList yet
# and 'i' is NOT smaller than the next number
# and there are still numbers left
# then move on to the next pair
# and continue comparing and moving numbers
else:
i = i+1
# Hopefully these will be in ascending order eventually.
print(numSort)
Here is a simple way to sort your list with a classic loop :
myList = [2,99,0,56,8,1]
for i,value in enumerate(myList):
for j,innerValue in enumerate(myList):
if value < innerValue: #for order desc use '>'
myList[j],myList[i]=myList[i],myList[j]
print(myList)
The Algorithm behind this code is :
fix one value of the list and compare it with the rest
if it is smallest then switch the index of two values
I hope this will help you
Your conditions are essentially:
If there is only one number in the list
The current number is less than the next, which is not equal to the last number
The current number is less than the first number, and the next number is equal to the last number
The next number is equal to the last number
If you trace out the code by hand you will see how in many cases, none of these will evaluate to true and your "else" will be executed and i will be incremented. Under these conditions, certain numbers will never be removed from your original list (none of your elifs will catch them), i will increment until the next number is equal to the last number, i will be reset to zero, repeat. You are stuck in an infinite loop. You will need to update your if-statement in such a way that all numbers will eventually be caught by a block other than your final elif.
On a separate note, you are potentially comparing a number to only one number in the current list before appending it to the "sorted" list. You will likely need to compare the number you want to add to your "sorted" list and find its proper place in THAT list rather than merely appending.
You should also consider finding the end of list using a method more like
if i == len(numList) - 1
This will compare the index to the length of the list rather than comparing more values in the list which are not necessarily relevant to the order you are trying to create.

Python: Adding element to list while iterating

I know that it is not allowed to remove elements while iterating a list, but is it allowed to add elements to a python list while iterating. Here is an example:
for a in myarr:
if somecond(a):
myarr.append(newObj())
I have tried this in my code and it seems to work fine, however I don't know if it's because I am just lucky and that it will break at some point in the future?
EDIT: I prefer not to copy the list since "myarr" is huge, and therefore it would be too slow. Also I need to check the appended objects with "somecond()".
EDIT: At some point "somecond(a)" will be false, so there can not be an infinite loop.
EDIT: Someone asked about the "somecond()" function. Each object in myarr has a size, and each time "somecond(a)" is true and a new object is appended to the list, the new object will have a size smaller than a. "somecond()" has an epsilon for how small objects can be and if they are too small it will return "false"
Why don't you just do it the idiomatic C way? This ought to be bullet-proof, but it won't be fast. I'm pretty sure indexing into a list in Python walks the linked list, so this is a "Shlemiel the Painter" algorithm. But I tend not to worry about optimization until it becomes clear that a particular section of code is really a problem. First make it work; then worry about making it fast, if necessary.
If you want to iterate over all the elements:
i = 0
while i < len(some_list):
more_elements = do_something_with(some_list[i])
some_list.extend(more_elements)
i += 1
If you only want to iterate over the elements that were originally in the list:
i = 0
original_len = len(some_list)
while i < original_len:
more_elements = do_something_with(some_list[i])
some_list.extend(more_elements)
i += 1
well, according to http://docs.python.org/tutorial/controlflow.html
It is not safe to modify the sequence
being iterated over in the loop (this
can only happen for mutable sequence
types, such as lists). If you need to
modify the list you are iterating over
(for example, to duplicate selected
items) you must iterate over a copy.
You could use the islice from itertools to create an iterator over a smaller portion of the list. Then you can append entries to the list without impacting the items you're iterating over:
islice(myarr, 0, len(myarr)-1)
Even better, you don't even have to iterate over all the elements. You can increment a step size.
In short: If you'are absolutely sure all new objects fail somecond() check, then your code works fine, it just wastes some time iterating the newly added objects.
Before giving a proper answer, you have to understand why it considers a bad idea to change list/dict while iterating. When using for statement, Python tries to be clever, and returns a dynamically calculated item each time. Take list as example, python remembers a index, and each time it returns l[index] to you. If you are changing l, the result l[index] can be messy.
NOTE: Here is a stackoverflow question to demonstrate this.
The worst case for adding element while iterating is infinite loop, try(or not if you can read a bug) the following in a python REPL:
import random
l = [0]
for item in l:
l.append(random.randint(1, 1000))
print item
It will print numbers non-stop until memory is used up, or killed by system/user.
Understand the internal reason, let's discuss the solutions. Here are a few:
1. make a copy of origin list
Iterating the origin list, and modify the copied one.
result = l[:]
for item in l:
if somecond(item):
result.append(Obj())
2. control when the loop ends
Instead of handling control to python, you decides how to iterate the list:
length = len(l)
for index in range(length):
if somecond(l[index]):
l.append(Obj())
Before iterating, calculate the list length, and only loop length times.
3. store added objects in a new list
Instead of modifying the origin list, store new object in a new list and concatenate them afterward.
added = [Obj() for item in l if somecond(item)]
l.extend(added)
You can do this.
bonus_rows = []
for a in myarr:
if somecond(a):
bonus_rows.append(newObj())
myarr.extend( bonus_rows )
Access your list elements directly by i. Then you can append to your list:
for i in xrange(len(myarr)):
if somecond(a[i]):
myarr.append(newObj())
make copy of your original list, iterate over it,
see the modified code below
for a in myarr[:]:
if somecond(a):
myarr.append(newObj())
I had a similar problem today. I had a list of items that needed checking; if the objects passed the check, they were added to a result list. If they didn't pass, I changed them a bit and if they might still work (size > 0 after the change), I'd add them on to the back of the list for rechecking.
I went for a solution like
items = [...what I want to check...]
result = []
while items:
recheck_items = []
for item in items:
if check(item):
result.append(item)
else:
item = change(item) # Note that this always lowers the integer size(),
# so no danger of an infinite loop
if item.size() > 0:
recheck_items.append(item)
items = recheck_items # Let the loop restart with these, if any
My list is effectively a queue, should probably have used some sort of queue. But my lists are small (like 10 items) and this works too.
You can use an index and a while loop instead of a for loop if you want the loop to also loop over the elements that is added to the list during the loop:
i = 0
while i < len(myarr):
a = myarr[i];
i = i + 1;
if somecond(a):
myarr.append(newObj())
Expanding S.Lott's answer so that new items are processed as well:
todo = myarr
done = []
while todo:
added = []
for a in todo:
if somecond(a):
added.append(newObj())
done.extend(todo)
todo = added
The final list is in done.
Alternate solution :
reduce(lambda x,newObj : x +[newObj] if somecond else x,myarr,myarr)
Assuming you are adding at the last of this list arr, You can try this method I often use,
arr = [...The list I want to work with]
current_length = len(arr)
i = 0
while i < current_length:
current_element = arr[i]
do_something(arr[i])
# Time to insert
insert_count = 1 # How many Items you are adding add the last
arr.append(item_to_be inserted)
# IMPORTANT!!!! increase the current limit and indexer
i += 1
current_length += insert_count
This is just boilerplate and if you run this, your program will freeze because of infinite loop. DO NOT FORGET TO TERMINATE THE LOOP unless you need so.

Categories

Resources