How do you append modified data from a for loop? (Python) - python

Thanks for checking out my question, am using Python 3 by the way, here is the code:)
scale=[1,2,3,4,5,6]
MNP = {'scale degree' : []
}
for index, value in enumerate(scale):
scale.insert(len(scale),scale[0])
del scale[0]
MNP['scale degree'].append(scale[1])
print (MNP['scale degree'])
So, my goal here is append the modified versions of a list I called scale, into a dict I called MNP. The desired output is a list ('scale degree' that takes the original number of a list (such as scale), and moves it the end of the list, at the same time, all the numbers would shift one to the left, this is what is happens if I give the command to print the scale within the function:
for index, value in enumerate(scale):
scale.insert(len(scale),scale[0])
del scale[0]
MNP['scale degree'].append(scale[1])
print (MNP['scale degree'])
[1, 2, 3, 4, 5, 6]
[2, 3, 4, 5, 6, 1]
[3, 4, 5, 6, 1, 2]
[4, 5, 6, 1, 2, 3]
[5, 6, 1, 2, 3, 4]
[6, 1, 2, 3, 4, 5]
Yet when I try to do so I encountered (yes, past tense) a problem with my understanding. See, if I execute the command:
print(scale[1])
Then it actually does print the numbers 2,3,4,5,6,1 . This is why I used the enumerate function, I thought that this may solve the problem. I thought that, maybe the index number determined somehow which list would be printed off, but this is of course is wrong, even though the output is identical, and, it's worth mentioning that am still very much a stargazed newbie. Yet I did some thinking and realized that it's merely printing off the second value of each modified list, the exact same as if I gave it a single command to print off only one version, yet being a disobedient little bugger if I ask it to append the whole blown thing.
Bloody Hell.
So, now, my question is, how do I append the modified version of the scale, and not just a single value?
Appending the scale as a whole does'st work by the way (this was also my first solution), as the output is then:
[[2, 3, 4, 5, 6, 1]]
[[3, 4, 5, 6, 1, 2], [3, 4, 5, 6, 1, 2]]
[[4, 5, 6, 1, 2, 3], [4, 5, 6, 1, 2, 3], [4, 5, 6, 1, 2, 3]]
it keeps going in this fashion until it reaches [1,2,3,4,5,6] * 6
Thanks in advance for any help, also if you may, could you recommend resources so that I can gain a basic understanding of what's playing behind the scenes? There's probably a lot I still don't understand (I mean in relation only to this problem alone, of course, in general am that close to understanding everything there is to know (haha)).
Thanks again
James
P.S how long in general should I wait till I seek help on SE? (For this particular problem, I've been dueling with if for a solid Day 1/2)
P.S.S A thanks to Borodin for pointing that the manipulation of data within a for loop is bad practice. How should this be correctly executed?

Is this the effect you're after?
>>> l = [1,2,3,4]
>>> l.append(list.pop())
[2,3,4,1]
Or if you want it unrolled a little:
>>> l = [1,2,3,4]
>>> e = list.pop()
>>> l.append(e)
[2,3,4,1]
In both cases, we remove the first element from the list and stick it on the end of the list - which I think is what you're after.
Or if you don't want to modify the original list:
>>> l = [1,2,3,4]
>>> l[1:]+[l[0]]
[2,3,4,1]
Here, we're just recombining slices - the original list is unchanged. Notice that I have to put l[0] into a list because that's the way + is implemented for lists - I can't do l[1:] + l[0].

I think this would help. It has rotate(n) which shifts the elements of the list by n places.
import collections
my_list = []
d = collections.deque([1,2,3,4,5])
for i in range(5):
d.rotate(-1)
my_list.append(d)
print my_list
Output:
[deque([1, 2, 3, 4, 5]), deque([1, 2, 3, 4, 5]), deque([1, 2, 3, 4, 5]), deque([1, 2, 3, 4, 5]), deque([1, 2, 3, 4, 5])]

Related

Remove duplicate numbers from a list

I was attempting to remove all duplicated numbers in a list.
I was trying to understand what is wrong with my code.
numbers = [1, 1, 1, 1, 6, 5, 5, 2, 3]
for x in numbers:
if numbers.count(x) >= 2:
numbers.remove(x)
print(numbers)
The result I got was:
[1, 1, 6, 5, 2, 3]
I guess the idea is to write code yourself without using library functions. Then I would still suggest to use additional set structure to store your previous items and go only once over your array:
numbers = [1, 1, 1, 1, 6, 5, 5, 2, 3]
unique = set()
for x in numbers:
if x not in unique:
unique.add(x)
numbers = list(unique)
print(numbers)
If you want to use your code then the problem is that you modify collection in for each loop, which is a big NO NO in most programming languages. Although Python allows you to do that, the problem and solution are already described in this answer: How to remove items from a list while iterating?:
Note: There is a subtlety when the sequence is being modified by the loop (this can only occur for mutable sequences, i.e. lists). An internal counter is used to keep track of which item is used next, and this is incremented on each iteration. When this counter has reached the length of the sequence the loop terminates. This means that if the suite deletes the current (or a previous) item from the sequence, the next item will be skipped (since it gets the index of the current item which has already been treated). Likewise, if the suite inserts an item in the sequence before the current item, the current item will be treated again the next time through the loop. This can lead to nasty bugs that can be avoided by making a temporary copy using a slice of the whole sequence, e.g.,
for x in a[:]:
if x < 0: a.remove(x)
numbers = [1, 1, 1, 1, 6, 5, 5, 2, 3]
Using a shallow copy of the list:
for x in numbers[:]:
if numbers.count(x) >= 2:
numbers.remove(x)
print(numbers) # [1, 6, 5, 2, 3]
Alternatives:
Preserving the order of the list:
Using dict.fromkeys()
print(list(dict.fromkeys(numbers).keys())) # [1, 6, 5, 2, 3]
Using more_itertools.unique_everseen(iterable, key=None):
from more_itertools import unique_everseen
print(list(unique_everseen(numbers))) # [1, 6, 5, 2, 3]
Using pandas.unique:
import pandas as pd
print(pd.unique(numbers).tolist()) # [1, 6, 5, 2, 3]
Using collections.OrderedDict([items]):
from collections import OrderedDict
print(list(OrderedDict.fromkeys(numbers))) # [1, 6, 5, 2, 3]
Using itertools.groupby(iterable[, key]):
from itertools import groupby
print([k for k,_ in groupby(numbers)]) # [1, 6, 5, 2, 3]
Ignoring the order of the list:
Using numpy.unique:
import numpy as np
print(np.unique(numbers).tolist()) # [1, 2, 3, 5, 6]
Using set():
print(list(set(numbers))) # [1, 2, 3, 5, 6]
Using frozenset([iterable]):
print(list(frozenset(numbers))) # [1, 2, 3, 5, 6]
Why don't you simply use a set:
numbers = [1, 1, 1, 1, 6, 5, 5, 2, 3]
numbers = list(set(numbers))
print(numbers)
Before anything, the first advice I can give is to never edit over an array that you are looping. All kinds of wacky stuff happens. Your code is fine (I recommend reading other answers though, there's an easier way to do this with a set, which pretty much handles the duplication thing for you).
Instead of removing number from the array you are looping, just clone the array you are looping in the actual for loop syntax with slicing.
numbers = [1, 1, 1, 1, 6, 5, 5, 2, 3]
for x in numbers[:]:
if numbers.count(x) >= 2:
numbers.remove(x)
print(numbers)
print("Final")
print(numbers)
The answer there is numbers[:], which gives back a clone of the array. Here's the print output:
[1, 1, 1, 6, 5, 5, 2, 3]
[1, 1, 6, 5, 5, 2, 3]
[1, 6, 5, 5, 2, 3]
[1, 6, 5, 5, 2, 3]
[1, 6, 5, 5, 2, 3]
[1, 6, 5, 2, 3]
[1, 6, 5, 2, 3]
[1, 6, 5, 2, 3]
[1, 6, 5, 2, 3]
Final
[1, 6, 5, 2, 3]
Leaving a placeholder here until I figure out how to explain why in your particular case it's not working, like the actual step by step reason.
Another way to solve this making use of the beautiful language that is Python, is through list comprehension and sets.
Why a set. Because the definition of this data structure is that the elements are unique, so even if you try to put in multiple elements that are the same, they won't appear as repeated in the set. Cool, right?
List comprehension is some syntax sugar for looping in one line, get used to it with Python, you'll either use it a lot, or see it a lot :)
So with list comprehension you will iterate an iterable and return that item. In the code below, x represents each number in numbers, x is returned to be part of the set. Because the set handles duplicates...voila, your code is done.
numbers = [1, 1, 1, 1, 6, 5, 5, 2, 3]
nubmers_a_set = {x for x in numbers }
print(nubmers_a_set)
This seems like homework but here is a possible solution:
import numpy as np
numbers = [1, 1, 1, 1, 6, 5, 5, 2, 3]
filtered = list(np.unique(numbers))
print(filtered)
#[1, 2, 3, 5, 6]
This solution does not preserve the ordering. If you need also the ordering use:
filtered_with_order = list(dict.fromkeys(numbers))
Why don't you use fromkeys?
numbers = [1, 1, 1, 1, 6, 5, 5, 2, 3]
numbers = list(dict.fromkeys(numbers))
Output: [1,6,5,2,3]
The flow is as follows.
Now the list is [1, 1, 1, 1, 6, 5, 5, 2, 3] and Index is 0.
The x is 1. The numbers.count(1) is 4 and thus the 1 at index 0 is removed.
Now the numbers list becomes [1, 1, 1, 6, 5, 5, 2, 3] but the Index will +1 and becomes 1.
The x is 1. The numbers.count(1) is 3 and thus the 1 and index 1 is removed.
Now the numbers list becomes [1, 1, 6, 5, 5, 2, 3] but the Index will +1 and becomes 2.
The x will be 6.
etc...
So that's why there are two 1's.
Please correct me if I am wrong. Thanks!
A fancy method is to use collections.Counter:
>>> from collections import Counter
>>> numbers = [1, 1, 1, 1, 6, 5, 5, 2, 3]
>>> c = Counter(numbers)
>>> list(c.keys())
[1, 6, 5, 2, 3]
This method have a linear time complexity (O(n)) and uses a really performant library.
You can try:
from more_itertools import unique_everseen
items = [1, 1, 1, 1, 6, 5, 5, 2, 3]
list(unique_everseen(items))
or
from collections import OrderedDict
>>> items = [1, 1, 1, 1, 6, 5, 5, 2, 3]
>>> list(OrderedDict.fromkeys(items))
[1, 2, 0, 3]
more you can find here
How do you remove duplicates from a list whilst preserving order?

Understanding Logic of Inorder traversal of BST, Python

I'm studying for coding interviews and working with a lot of different data structures.
I'm relatively new to Tree problems and have been doing problems daily to practice.
It's one thing to have formulas committed to memory, and another to truly understand them. When I understand something it is easy to apply that understanding to more difficult problems.
Recursive solutions are a little harder for me to mentally visualize and while intuitively they make sense, I am trying to get a deep understanding of what's happening on the Stack.
I have a tree and want to do in order traversal. No problem.
data = []
def checkBST(root):
if root:
checkBST(root.left)
data.append(root.data)
checkBST(root.right)
print(data)
I created the data variable to print out what is being stored on each pass through the method.
It printed
[]
[1]
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
I am trying to logically look at what is happening and want to know if my Logic is correct.
There are 15 printed results and 7 Nodes. However we get to 15 because there are 8 places checked for Nodes where Node is None. That happens on Nodes 1, 3, 5, 7.
We are checking the left half of the tree before the right.
[]
#nothing stored because we move onto Node 2 as we don't hit the base case.
[1]
#1 stored because Node 1 doesn't have a left value. So we move onto the append call.
[1]
#1 returned because Node 1 doesn't have a right value.
[1, 2]
#2 stored because because we finished checking the left side and moved onto append data.
[1, 2, 3]
#3 is stored because we are calling the in order traversal on the right side of two now.
[1, 2, 3]
#3 is returned again because it doesn't have a root.left
[1, 2, 3]
#3 is returned again because it doesn't have a root.right
[1, 2, 3, 4]
# we hit the append method for 4, now we move onto the in order traversal on the right
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
The right side will be checked just like the left so I didn't write my logic as it'd be redundant.
I just want clarification if I am looking at this problem in the correct format.
Thanks for any help in understanding this!
The comments in the output are not always correct.
The first output ([]) happens when a function call reaches the end. The first call where this happens is when root is node 1 and from there the first recursive call is made. That call will have None as argument, and so it is the first time a call reaches the print statement.
So we have these ongoing calls:
checkBST(4)
checkBST(2) # left child of 4
checkBST(1) # left child of 2
checkBST(None) # left child of 1
print # --> []
When that deepest call finishes, the function with node 1 will append 1 to the data list, and then make the recursive call for the right child, which also is None and [1] is printed.
Here is a visualisation of the process. The columns represent the depth of the recursion, and the rows represent the sequence of events as time progresses (downwards). The last column is reserved for showing what the current value of data is. When it has a yellow background it means it is printed.
Light blue means that code is executed at that recursion depth. Darker blue means the corresponding function call is pending (on the stack), waiting for the nested recursive call to return.
From this image you can also see why the same output is sometimes repeated: it is printed at different recursion levels as the algorithm is backtracking.

what's the meaning of x[:]=y? [duplicate]

This question already has answers here:
What is the difference between slice assignment that slices the whole list and direct assignment?
(4 answers)
Closed 6 years ago.
I tried to understand [:] in the beginning, but I can't find any document mention it. Where is the best place to learn advanced grammar for Python? Google search won't find [:]. But I figured it out at the end. I just wonder where is best place to learn Python 'tricks'.
For example:
def test(x, y):
x[:] = y
#x = y
>>> a = [0.5,0.6]
>>> b = [0.3]
>>> test(a, b)
>>>
>>> print a
[0.3] # [0.5,0.6]
x[:] means the entire sequence. It's basically x[from:to].
Omitting from means, from the beginning until the to.
>>> numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> numbers[:5]
[0, 1, 2, 3, 4]
Omitting to means, from the from until the end.
>>> numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> numbers[5:]
[5, 6, 7, 8, 9]
Omitting both means the entire list.
>>> numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> numbers[:]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Setting numbers[:] means setting the entire list:
>>> numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> numbers[:] = [1, 2, 3, 4]
>>> numbers
[1, 2, 3, 4]
Keep in mind setting numbers[:] changes that list and does not create a new one. The object will still have the same id().
The term you need to search for is slice. x[start:end:step] is the full form, any one can be omitted to use a default value: start defaults to 0, end defaults to the length of the list, and step defaults to 1. So x[:] means exactly the same as x[0:len(x):1]. You can find more information at
the Expression section of the language reference, and section four of the python tutorial might also be helpful.
The notation x[:] is equivalent to x[0:n] where n is len(x). It specifies the range of elements of x from 0 through n-1 inclusive.
When read, a new list, string, or whatever is created, containing the specified range of elements.
When assigned to, the specified range of elements is destructively replaced in the original. Note that this is allowed for lists, but not for strings.

Different ways of Iterating over a list

Can somebody tell me what's the difference between this code:
x = [1, 2, 3, 4, 5, 6, 7]
for i in x[:]:
if i == 5:
x.insert(0, i)
And this code:
x = [1, 2, 3, 4, 5, 6, 7]
for i in x:
if i == 5:
x.insert(0, i)
Why doesn't the second one work? I know it is mentioned in the Python tutorial, but I can't quite understand it.
In the first version, you create a copy (by slicing the list from the beginning to the end), in the second one you're iterating over the original list.
If you iterate over a container, its size can't change during iteration, for good reasons (see below). But since you call x.insert, the size of your list changes.
If you execute the second version, it actually doesn't throw an error immediately, but continues indefinitely, filling the list up with more and more 5s:
Once you're at the list index 4 (and i is therefore 5), you're inserting a 5 at the beginning of the list:
[5, 1, 2, 3, 4, 5, 6, 7]
You then continue in the loop, implicitly increasing your index to 5, which is now again 5, because the whole list got shifted one to the right, due to your insertion, so 5 will be inserted again:
[5, 5, 1, 2, 3, 4, 5, 6, 7]
This goes on "forever" (until there's a MemoryError).

Removing duplicates using set

Basically, I want to do this with iteration from a lst in to a set and the printing it back to list. The problem I get is that I can't iterate through set.add(item). set.add() was perfectly fine when applying one value outside a loop but I can't get it to work inside a loop.
Using this function I am able to remove duplicates.
remove_duplicates(numbers):
lst = []
for i in numbers:
if i not in lst:
lst.append(i)
return lst
However, I want to be able to do something like this.
Here is how far I was able to come.
lst = { }
lsto = [1, 1, 1, 2, 3, 4, 1, 2, 5, 7, 5]
for item in lsto:
lst.add(item)
print(lst)
Thanks in advance!
I think you mean this,
>>> lsto = [1, 1, 1, 2, 3, 4, 1, 2, 5, 7, 5]
>>> list(set(lsto))
[1, 2, 3, 4, 5, 7]
set(lsto) turns the iterable lsto into set which in-turn remove the duplicate elements. By again turning the set to list will give you a list at final.
To match the first logic and keep order you can use an OrderedDict :
from collections import OrderedDict
lsto = [1, 1, 1, 2, 3, 4, 1, 2, 5, 7, 5]
print(OrderedDict().fromkeys(lsto).keys())
[1, 2, 3, 4, 5, 7]
The set by chance gives you the same order but sets are unordered collections so you cannot rely on getting any order.

Categories

Resources