.append() adding 2 values and deleting 1 previous value - python

Hi I've been trying to figure out why in this code 'permutations.append(lst)' is not only appending twice, but also deleted the first term in the 'permutations'.
def permute(lst):
permutations = []
permutations.append(lst)
flag = True
while flag:
k = len(lst) - 2
while k>=0:
if lst[k] < lst[k+1]:
break
k -= 1
else:
flag = False
break
l = len(lst) - 1
while l>k:
if lst[k] < lst[l]:
break
l-=1
lst[k], lst[l] = lst[l], lst[k]
lst = lst[:k+1] + lst[len(lst)-1: k :-1]
print('a')
permutations.append(lst)
print(lst, 'b')
return permutations
print(permute([2,3,4,5])
I placed the print commands to check if for some reason the block is running twice but it doesnt. Instead of the final permutations being nested lists of all the permutations of [2,3,4,5] in order, it gives this:
[[2, 3, 5, 4], [2, 4, 5, 3], [2, 4, 5, 3], [2, 5, 4, 3], [2, 5, 4, 3], [3, 5, 4, 2], [3, 2, 5, 4], [3, 4, 5, 2], [3, 4, 5, 2], [3, 5, 4, 2], [3, 5, 4, 2], [4, 5, 3, 2], [4, 2, 5, 3], [4, 3, 5, 2], [4, 3, 5, 2], [4, 5, 3, 2], [4, 5, 3, 2], [5, 4, 3, 2], [5, 2, 4, 3], [5, 3, 4, 2], [5, 3, 4, 2], [5, 4, 3, 2], [5, 4, 3, 2], [5, 4, 3, 2]]

This is because when you are appending lst to permutations, you're actually appending a reference to an object, rather than just the values of the object. As such, when you then change the values of lst later, permutations also changes.
list.copy() returns just the values of a list, decoupled from the original object. As such, if you replace your permutations.append(lst) lines with permutations.append(lst.copy()), you should get the desired output.
Demonstration of list reference effect vs copied list:
>>> x = [1]
>>> y = []
>>> y.append(x) # Directly append x to y
>>> y
[[1]]
>>> x[0] = 2
>>> y # Changing x changed y
[[2]]
>>> y = []
>>> y.append(x.copy()) # Append copy of x to y
>>> y
[[2]]
>>> x[0] = 3
>>> y # Changing x had no effect on y
[[2]]

Related

How to print in Python 3.10 a series of lists [1], [1, 2], [1, 2, 3], [1, 2, 3, ..., n] in one liner list comprehension

I have a list [1, 2, 3, 4, ..., n]. In a one-liner list comprehension, how can I print the progression [1], [1, 2], [1, 2, 3], [1, 2, 3, 4], ..., [1, 2, 3, 4, ..., n-1], where n represents an arbitrary natural number?
Now if you want this principle to operate on any starting list (not necessarily [1,2,...]), you can do this:
rawlist=['a','b','c','d']
newlist=[rawlist[:i] for i in range(1,len(rawlist)+1)]
Try this:
n = 10
res = [list(range(1,i)) for i in range(2,n+1)]
print(res)
Output:
[
[1], # range(1, 2)
[1, 2], # range(1, 3)
[1, 2, 3],
[1, 2, 3, 4],
[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, 8],
[1, 2, 3, 4, 5, 6, 7, 8, 9] # range(1, 10)
]
How about a for loop in a for loop?
n = 10
print([[x for x in range(1,i+1)] for i in range(1,n+1)])
This prints an array of the numbers 1 to i for each value of i in the range 1 to n.
This should give you every list from [1] to [1,...n-1]
[print([x for x in range(1, y+1)]) for y in range(1, n)]
Starting with our [1, ..., n] list, where n in this case is 5:
>>> a_list = list(range(1, 6))
>>> a_list
[1, 2, 3, 4, 5]
we can just iterate over the list and create another list for each number, with the last one ending in n-1:
>>> print(*[list(range(1, x)) for x in a_list[1:]], sep=", ")
[1], [1, 2], [1, 2, 3], [1, 2, 3, 4]
Or, doing it based on n directly rather than a_list:
>>> n = 5
>>> print(*[list(range(1, x)) for x in range(2, n+1)], sep=", ")
[1], [1, 2], [1, 2, 3], [1, 2, 3, 4]
Note that using a list comprehension is not necessary; you can replace the outer [] in both examples with () (i.e. a parenthesized generator expression rather than a list comprehension) and it will print exactly the same thing.

Iteratively multiply elements of two lists in python

I have two lists (X and Y). I want to first scale the first element of X with all the elements of Y before moving to the second element of X which is then again scaled by all the elements of Y. How can I do this? Ideally, I would also like to append it to different lists (L) when a new element of X is started, but I am not sure if that is possible.
Y = [2, 3, 4, 5]
X = [1, 2, 3, 4]
L = []
for i in range(len(X)):
for j in range(len(Y)):
X_scale = X[i] * Y[j]
L.append(X_scale)
Preferred outcome:
# First element in X
X_scale = [2, 2, 3, 4]
X_scale = [3, 2, 3, 4]
X_scale = [4, 2, 3, 4]
X_scale = [5, 2, 3, 4]
# Second element in X
X_scale = [1, 4, 3, 4]
X_scale = [1, 6, 3, 4]
#etc
This seems to follow your pattern:
Y = [2, 3, 4, 5]
X = [1, 2, 3, 4]
L = []
for i,x in enumerate(X):
for y in Y:
X_scale = X.copy()
X_scale[i] = x * y
L.append(X_scale)
for row in L:
print(row)
Output:
[2, 2, 3, 4]
[3, 2, 3, 4]
[4, 2, 3, 4]
[5, 2, 3, 4]
[1, 4, 3, 4]
[1, 6, 3, 4]
[1, 8, 3, 4]
[1, 10, 3, 4]
[1, 2, 6, 4]
[1, 2, 9, 4]
[1, 2, 12, 4]
[1, 2, 15, 4]
[1, 2, 3, 8]
[1, 2, 3, 12]
[1, 2, 3, 16]
[1, 2, 3, 20]
Per OP's comment to group the indices:
Y = [2, 3, 4, 5]
X = [1, 2, 3, 4]
L = []
for i,x in enumerate(X):
L2 = []
for y in Y:
X_scale = X.copy()
X_scale[i] = x * y
L2.append(X_scale)
L.append(L2)
for row in L:
print(row)
Output:
[[2, 2, 3, 4], [3, 2, 3, 4], [4, 2, 3, 4], [5, 2, 3, 4]]
[[1, 4, 3, 4], [1, 6, 3, 4], [1, 8, 3, 4], [1, 10, 3, 4]]
[[1, 2, 6, 4], [1, 2, 9, 4], [1, 2, 12, 4], [1, 2, 15, 4]]
[[1, 2, 3, 8], [1, 2, 3, 12], [1, 2, 3, 16], [1, 2, 3, 20]]
First you can simply you loops by accessing the items directly, without an index. Then you can transform the inner loop into a comprehension list to make it more compact:
Y = [2, 3, 4, 5]
X = [1, 2, 3, 4]
L = []
for x_item in X:
L += [x_item * y_item for y_item in Y]

Rank elements in nested list without sorting list

Let's say I have a nested list:
list = [[10, 2, 8, 4], [12, 6, 4, 1], [8, 4, 3, 2], [9, 3, 4, 6]]
I want to rank the elements in the sublist against each other to create a new nested list with the rankings.
result = [[1, 4, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4], [1, 4, 3, 2]]
in the first sublist 10 would be 1st, 8 2nd, etc.
There are already some good solutions. Here just another one - functional approach for reference:
No 3rd library used.
lst = # your lists - don't use builtin "list"
def ranking(nums):
ranks = {x:i for i, x in enumerate(sorted(nums, reverse=True),1)}
return [ranks[x] for x in nums] # quick mapping back: O(1)
Calling it:
result = list(map(ranking, lst))
As already mentioned in the comment, you can use numpy.argsort, using it twice gives you the rank for the values, which need to be subtracted from len of the sub list to rank from highest to lowest, you can use List-Comprehension to do it for all the sub lists.
>>> import numpy as np
>>> lst = [[10, 2, 8, 4], [12, 6, 4, 1], [8, 4, 3, 2], [9, 3, 4, 6]]
>>> [(len(sub)-np.argsort(sub).argsort()).tolist() for sub in lst]
[[1, 4, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4], [1, 4, 3, 2]]
You can even use 2D numpy array and negate the values, then directly call argsort twice on the resulting array, and finally add 1:
>>> (-np.array(lst)).argsort().argsort()+1
array([[1, 4, 2, 3],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 4, 3, 2]], dtype=int64)
You can use scipy.stats.rankdata:
my_list = [[10, 2, 8, 4], [12, 6, 4, 1], [8, 4, 3, 2], [9, 3, 4, 6]]
from scipy.stats import rankdata
[list(len(l)+1-rankdata(l).astype(int)) for l in my_list]
output:
[[1, 4, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4], [1, 4, 3, 2]]
Without numpy/scipy:
[[sorted(li, reverse=True).index(x)+1 for x in li] for li in data]
[[1, 4, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4], [1, 4, 3, 2]]
Another solution with no external libraries, and with a better time complexity, just in case your sublists are a bit longer than 4 items (this has some overhead but I presume it is O(n log n) because of the call to sorted).
def rank_all(ls):
result = []
for subls in ls:
pairs = sorted([(subls[j],j) for j in range(len(subls))], reverse=True)
ranked = [0] * len(subls)
for j,p in enumerate(pairs):
ranked[p[1]]=j+1
result.append(ranked)
return result

How to get sliding window of a values for each element in both direction (forward, backward)?

I have a list of values like this,
lst = [1, 2, 3, 4, 5, 6, 7, 8]
Desired Output:
window size = 3
1 # first element in the list
forward = [2, 3, 4]
backward = []
2 # second element in the list
forward = [3, 4, 5]
backward = [1]
3 # third element in the list
forward = [4, 5, 6]
backward = [1, 2]
4 # fourth element in the list
forward = [5, 6, 7]
backward = [1, 2, 3]
5 # fifth element in the list
forward = [6, 7, 8]
backward = [2, 3, 4]
6 # sixth element in the list
forward = [7, 8]
backward = [3, 4, 5]
7 # seventh element in the list
forward = [8]
backward = [4, 5, 6]
8 # eight element in the list
forward = []
backward = [5, 6, 7]
Lets assume a window size of 4, now my desired output:
for each_element in the list, I want 4 values in-front and 4 values backward ignoring the current value.
I was able to use this to get sliding window of values but this also not giving me the correct required output.
import more_itertools
list(more_itertools.windowed([1, 2, 3, 4, 5, 6, 7, 8], n=3))
Code:
arr = [1, 2, 3, 4, 5, 6, 7, 8]
window = 3
for backward, current in enumerate(range(len(arr)), start = 0-window):
if backward < 0:
backward = 0
print(arr[current+1:current+1+window], arr[backward:current])
Output:
[2, 3, 4], []
[3, 4, 5], [1]
[4, 5, 6], [1, 2]
[5, 6, 7], [1, 2, 3]
[6, 7, 8], [2, 3, 4]
[7, 8], [3, 4, 5]
[8], [4, 5, 6]
[], [5, 6, 7]
One Liner:
print(dict([(e, (lst[i+1:i+4], lst[max(i-3,0):i])) for i,e in enumerate(last)]))
Output:
{1: ([2, 3, 4], []),
2: ([3, 4, 5], [1]),
3: ([4, 5, 6], [1, 2]),
4: ([5, 6, 7], [1, 2, 3]),
5: ([6, 7, 8], [2, 3, 4]),
6: ([7, 8], [3, 4, 5]),
7: ([8], [4, 5, 6]),
8: ([], [5, 6, 7])}
Credit: thanks to suggestions from #FeRD and #Androbin, the solution now looks better
This should get you started:
from dataclasses import dataclass
from typing import List
#dataclass
class Window:
index: int
backward: List[int]
forward: List[int]
def window(iterable, window_size, index):
backward = iterable[max(0, index - window_size):index]
forward = iterable[index + 1:index + 1 + window_size]
return Window(index, backward, forward)
>>> window([1,2,3,4,5,6], 3, 0)
Window(index=0, backward=[], forward=[2, 3, 4])
>>> window([1,2,3,4,5,6], 3, 5)
Window(index=5, backward=[3, 4, 5], forward=[])
I would also suggest adding some checks whether the index and window size make sense.
If you are stuck with an older Python version that doesn't have dataclasses yet, you can use Named Tuples instead.
This will work with more_itertools.windowed if you adjust the window size. Since you want 7 items (3 backward, 1 current, 3 forward), set the window size to 7.
from itertools import chain
from more_itertools import windowed
n = 3
iterable = [1, 2, 3, 4, 5, 6, 7, 8]
# pad the iterable so you start with an empty backward window
it = chain([None] * n, iterable, [None] * n)
for window in windowed(it, n * 2 + 1):
print(window[n])
print('forward =', [x for x in window[n + 1:] if x is not None])
print('backward =', [x for x in window[:n] if x is not None])
The output is:
1
forward = [2, 3, 4]
backward = []
2
forward = [3, 4, 5]
backward = [1]
3
forward = [4, 5, 6]
backward = [1, 2]
4
forward = [5, 6, 7]
backward = [1, 2, 3]
5
forward = [6, 7, 8]
backward = [2, 3, 4]
6
forward = [7, 8]
backward = [3, 4, 5]
7
forward = [8]
backward = [4, 5, 6]
8
forward = []
backward = [5, 6, 7]
Your sliding window reminds me of another data structure: fixed-size stacks. If you think about it, what you want is actually a fixed-size stack of 7 elements where the right three are the forward window elements and the back three are the back window elements. The 4th element is the current element. Here's how I would do it:
import collections
my_list = [1, 2, 3, 4, 5, 6, 7, 8]
window = collections.deque([], 7)
for i in my_list:
window.append(i)
# Get the back three elements
forward_window = list(window)[-3:]
# Get the front three elements
backward_window = list(window)[:len(window)-4]
print()
print(list(forward_window))
print(list(backward_window))
Of course, the code is not exactly what you want as the stack needs to be primed with some starting elements but that can be done with a bit more work:
import collections
my_list = [1, 2, 3, 4, 5, 6, 7, 8]
# Start with the first three elements
window = collections.deque(my_list[:3], 7)
# Iterate from the fourth element
for i in my_list[3:]:
window.append(i)
forward_window = list(window)[-3:]
backward_window = list(window)[:len(window)-4]
print()
print(list(forward_window))
print(list(backward_window))
After that you just need to clear the stack by adding some empty elements:
while len(window) != 4:
window.popleft()
forward_window = list(window)[4:]
backward_window = list(window)[:3]
print()
print(list(forward_window))
print(list(backward_window))
Here is the quick code I wrote
lst = [1, 2, 3, 4, 5, 6, 7, 8]
sliding_window_size = 3
def get_sliding_list(l, index):
l_list = []
r_list = []
min_range = 0
if index > sliding_window_size:
min_range = index - sliding_window_size
max_range = len(l)
if index + sliding_window_size < len(l):
max_range = index + sliding_window_size + 1
return (l[min_range:index], l[index + 1:max_range])
print(get_sliding_list(lst, 0))
print(get_sliding_list(lst, 1))
print(get_sliding_list(lst, 2))
print(get_sliding_list(lst, 3))
print(get_sliding_list(lst, 4))
print(get_sliding_list(lst, 5))
print(get_sliding_list(lst, 6))
print(get_sliding_list(lst, 7))
Output
([], [2, 3, 4])
([1], [3, 4, 5])
([1, 2], [4, 5, 6])
([1, 2, 3], [5, 6, 7])
([2, 3, 4], [6, 7, 8])
([3, 4, 5], [7, 8])
([4, 5, 6], [8])
([5, 6, 7], [])
Pass index of the element for which you want to retrieve sliding window
You can just use min and max to make sure you stay within the list (no loops needed) .
lst = [1, 2, 3, 4, 5, 6, 7, 8]
ws = 3 # window
st = 3 # starting point
mn = max(st-ws-1, 0)
mx = min(st+ws, len(lst))
print('Forward = ',lst[st:mx])
print('Backward = ', lst[mn:st-1])
Output:
Forward = [4, 5, 6]
Backward = [1, 2]
Here's a short and neat code based on list comprehension.
forward = [lst[i+1:i+1+window] for i in range(len(lst)]
backward = [lst[::-1][i+1:i+1+window] for i in range(len(lst)] # first reverse the input list and do same as did in forward
out = zip(forward,backward[::-1]) # first reverse the backward list and zip two list into one
Output
>>> forward
[[2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8], [8], []]
>>> backward
[[7, 6, 5], [6, 5, 4], [5, 4, 3], [4, 3, 2], [3, 2, 1], [2, 1], [1], []]
>>> out
[([2, 3, 4], []), ([3, 4, 5], [1]), ([4, 5, 6], [2, 1]), ([5, 6, 7], [3, 2, 1]), ([6, 7, 8], [4, 3, 2]), ([7, 8], [5, 4, 3]), ([8], [6, 5, 4]), ([], [7, 6, 5])]
[ll[i-4:i+4] for i in range(4, len(ll)-4)]
does the trick, I should think.

I am trying to make a big list by using a present list but it's not working, can anybody explain why is this happening?

list1=[0,1,2]
mega_list=[]
for x in range(4):
print(list1)
mega_list.append(list1)
list1.pop(0)
list1.insert(2,list1[1]+1)
print(mega_list)
This is because you are adding a reference to list1 to mega_list. That is to say, at the end of the for loop you really have this:
mega_list = [list1, list1, list1, list1]
But, after the loop, list1 = [4, 5, 6], so that's why you get the output:
[[4, 5, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]]
To fix this, make a copy of the list before iterating further:
list1=[0,1,2]
mega_list=[]
for x in range(4):
print(list1)
mega_list.append(list1.copy())
list1.pop(0)
list1.insert(2, list1[1]+1)
print(mega_list)
Which outputs:
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]]
list1=[0,1,2]
mega_list=[]
for x in range(4):
# Adds list1 as an element
mega_list.append(list1)
# Increments each element of list1
list1 = [x+1 for x in list1]
print(mega_list)
>>>[[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]]
You need to create a copy of the list, rather use the same instance in each append, e.g.:
list1=[0,1,2]
mega_list=[]
for x in range(5):
mega_list.append(list1[:])
list1.pop(0)
list1.insert(2,list1[1]+1)
print(mega_list) # [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]]
An alternative, is just use indexes into a full list of the input values, e.g.:
list1=list(range(7)) # [0,1,2,3,4,5,6]
mega_list=[]
for i in range(len(list1)-3+1):
mega_list.append(list1[i:i+3])
print(mega_list) # [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]]
One alternative is to create a generalized nwise() function using itertools:
import itertools as it
def nwise(iterable, n):
ts = it.tee(list1, n)
for c, t in enumerate(ts):
next(it.islice(t, c, c), None)
return zip(*ts)
list1 = list(range(7))
mega_list = [x for x in nwise(list1, 3)]
print(mega_list) # [(0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6)]

Categories