How works python key=operator.itemgetter(1))? - python

I have a matrix and I need to find max element and its number. How to rewrite it without operator (with for)?
for j in range(size - 1):
i, val = max(enumerate(copy[j::, j]), key=operator.itemgetter(1))
copy = change_rows(copy, j, i)
P = change_rows(P, j, i)
And actually maybe you can explain what this string means?
i, val = max(enumerate(copy[j::, j]), key=operator.itemgetter(1))

Let's decompose this line.
i, val = max(enumerate(copy[j::, j]), key=operator.itemgetter(1))
First, enumerate() creates an iterator over copy[j::,j] that yields index-value pairs. For example,
>>> for i, val in enumerate("abcd"):
... print(i, val)
...
0 a
1 b
2 c
3 d
Next, the max() function is for finding the largest item in a sequence. But we want it to target the values of copy[j::,j], not the indices that we are also getting from enumerate(). Specifying key=operator.itemgetter(1) tells max() to look at the (i,val) pairs and find the one with the largest val.
This is probably better done with np.argmax(), especially because val goes unused.
>>> import numpy as np
>>> for j in range(size - 1):
... i = np.argmax(copy[j::, j]) # Changed this line.
copy = change_rows(copy, j, i)
P = change_rows(P, j, i)

Related

(python) Can you please tell me what is the problem in the code below

I just start to learn python and i have a problem:
arr = [1,3,3,3,0,1,1]
def solution(arr):
a=[]
for r in range(len(arr)-1):
if arr[r] == arr[r+1]:
a.append(r+1)
print(a)
for i in range(len(a)):
k = int(a[i])
arr[k] = -1
arr.remove(-1)
return arr
There's a message
IndexError: list index out of range for ''arr[k] = -1''
Can you please tell me the reason for the Error and correct it?
Of course, it results in a Runtime exception. The list a stores indices. For each element v in a, you are trying to remove the value arr[v]. Doing this will reduce the size of arr by one every time. So, in the next iteration, v can be greater than the size of arr. Hence, it results in List index out of bound exception.
Your code, corrected:
arr = [1,3,3,3,0,1,1]
def solution(arr):
a=[]
for r in range(len(arr)-1):
if arr[r] == arr[r+1]:
a.append(r+1)
print(a)
c = 0
for i in range(len(a)):
k = int(a[i])
arr[k - c] = -1
arr.remove(-1)
c += 1
return arr
print(solution(arr))
It looks like you are trying to remove consecutive duplicates from the list. This can be easily solved using the following code.
def remove_duplicates(arr):
stack = [arr[0]]
for i in range(1, len(arr)):
if stack[-1] != arr[i]:
stack.append(arr[i])
return stack
print(remove_duplicates([1,3,3,3,0,1,1]))
In short, you cannot modify the array shape when you have determined the indices based on the unmodified array to index into it.
Here is something that you might be looking for:
def solution(arr):
a = []
for r in range(len(arr) - 1):
if arr[r] == arr[r + 1]:
a.append(r + 1)
print(a)
for i in range(len(a)):
k = int(a[i])
arr[k] = -1
# In the following line, you cannot modify the array length
# when you have already computed the indices based on the unmodified array
# arr.remove(-1)
arr = [x for x in arr if x != -1] # This is a better way to deal with it
return arr
print(solution(arr=[1, 3, 3, 3, 0, 1, 1]))
You don’t want to mess with the original list. Otherwise you’ll run into index errors. Index errors mean the item you were looking for in the list no longer exists. Most likely this line was the culprit arr.remove(-1).
arr = [1,3,3,3,0,1,1]
solution = []
for i, v in enumerate(arr):
if i == 0 or v != arr[i -1]:
solution.append(v)
print(solution)
This should get you what you are after. enumerate tells you want index you are at when looping through the list. More information can be found here: https://realpython.com/python-enumerate/
Well, you've probably already know what wrong happened here, removing the element inside the loop:
for i in range(len(a)):
k = int(a[i])
arr[k] = -1
arr.remove(-1)
You can fix the whole thing just changing the line to this list filter+lambda implementation, well, not inside the loop, but after the completion of loop iterations, just like follows:
for i in range(len(a)):
k = int(a[i])
arr[k] = -1
arr = list(filter(lambda x: x != -1, arr))
And you'll get what you want just from your solution!

How can I reduce the number of variables on the bubble sorting?

I made a bubble sort code. It works fine but I want to reduce the number of variables. I would like to use only i in the code. How can I remove j?
def bubble(v):
l = len(v)
for i in range(l-1):
for j in range(l-i-1):
if v[j]>v[j+1]:
v[j+1],v[j] = v[j],v[j+1]
return v
One of the possible ways is to use a list of length two, one for storing i and the other for storing j, in this fashion:
def bubble(v):
l = len(v)
i = [0, 0] # use like [i, j]
while i[0] < (l-1):
i[1] = 0
while i[1] < (l-i[0]-1):
if v[i[1]]>v[i[1]+1]:
v[i[1]+1],v[i[1]] = v[i[1]],v[i[1]+1]
i[1] += 1
return v
i[0] += 1
Note that, here, we have to use while loops instead of for loops.

Python classes: find arrays whose sum equals a specific number

I am learning classes in Python and I am trying to do the exercise in which you are supposed to find the indices from the array of a list of numbers whose sum equals a target number. I am trying to do this way but I get "None" back.
class Find_arrays:
def find_two_sum(self,lst, num):
self.lst = lst
self.num = num
indices = {}
for indx, num in enumerate(lst):
indices.setdefault(num, []).append(idx)
for k, v in indices.items():
i = v.pop
if n-k in indices and indices[n-k]:
return i, indices[n-k].pop
a = Find_arrays()
print(a.find_two_sum([10,20,40],60))
Output: None
While if I try this way as simple function, it works well:
def find_two_sum(lst, n):
indices = {}
for idx, num in enumerate(lst):
indices.setdefault(num, []).append(idx)
for k, v in indices.items():
i = v.pop()
if n - k in indices and indices[n-k]:
return i, indices[n-k].pop()
print( find_two_sum([3, 1, 5, 7, 5, 9], 10) )
print( find_two_sum([2,5,6,2,6,7], 12))
Output:
(0, 3)
(1, 5)
Could you please give me a suggestion of what I am doing wrong? Thank you very much advance.
If you literally copy the "simple function" in your class, it works well:
class Find_arrays:
def find_two_sum(self, lst, n):
indices = {}
for idx, num in enumerate(lst):
indices.setdefault(num, []).append(idx)
for k, v in indices.items():
i = v.pop()
if n - k in indices and indices[n-k]:
return i, indices[n-k].pop()
this because in the class example you wrote i = v.pop instead of i = v.pop()
class Find_arrays:
def find_two_sum(self, lst, n):
indices = {}
for idx, num in enumerate(lst):
indices.setdefault(num, []).append(idx)
for k, v in indices.items():
i = v.pop()
if n - k in indices and indices[n - k]:
return i, indices[n - k].pop()
a = Find_arrays()
print(a.find_two_sum([10, 20, 40], 60))
In the class version the variables n and num are not used in the same way as in the function version. The curved brackets after pop are missing twice.
I used the Pycharm IDE and its Python Debugger to find that. See https://www.jetbrains.com/help/pycharm/part-1-debugging-python-code.html. I also googled the error message. The google search led me to Error when attempting to use the 'pop' method with a list in Python .

Going through a list and checking in Python

I'm currently looking for an algorithm to be able to go through a list such as the following one: [1,1,1,1,2,3,4,5,5,5,3,2]
I want, in this example, to be able to select the first "1" as there's a duplicate next to it, and keep going through the list until finding the next number having a duplicate next to it, and then select the last number of this one (ie. "5" in this example).
Eventually, make the difference between these 2 numbers (ie. 5-1)
I have this code at the moment:
i=0
for i in range(len(X)):
if (X[i] == X[i+1]):
first_number = X[i]
elif (X[i] != X[i+1]):
i+=1
I'd like to add a further condition to my question. Suppose you have the following list: lst=[1,1,1,1,2,3,4,5,5,5,3,3,3,3,2,2,2,4,3] In this case, I'll get the following differences according to your code = lst = [4,-2,-1] and then stops. However, I'd like "4-2" to be added to the list afterwards because "4" is followed by a number less than "4" (thus, going to the opposite direction - up - of what "2" followed "4" were following). I hope this is clear enough. Many thanks
You can use enumerate with a starting index of 1. Duplicates are detected if the current value is equal to the value at the previous index:
l = [1,1,1,1,2,3,4,5,5,5,3,2]
r = [v for i, v in enumerate(l, 1) if i < len(l) and v == l[i]]
result = r[-1] - r[0]
# 4
The list r is a list of all duplicates. r[-1] is the last item and r[0] is the first.
More trials:
>>> l= [1,1,5,5,5,2,2]
>>> r = [v for i, v in enumerate(l, 1) if i < len(l) and v == l[i]]
>>> r[-1] - r[0]
1
Solution:
def subDupeLimits( aList ):
dupList = []
prevX = None
for x in aList:
if x == prevX:
dupList.append(x) # track duplicates
prevX = x # update previous x
# return last duplicate minus first
return dupList[-1] - dupList[0]
# call it
y = subDupeLimits( [1,1,1,1,2,3,4,5,5,5,3,2] )
# y = 4
You can use itertools.groupby to find groups of repeating numbers, then find the difference between the first two of those:
>>> import itertools
>>> lst = [1,1,1,1,2,3,4,5,5,5,3,2]
>>> duplicates = [k for k, g in itertools.groupby(lst) if len(list(g)) > 1]
>>> duplicates[1] - duplicates[0]
4
Or use duplicates[-1] - duplicates[0] if you want the difference between the first and the last repeated number.
In the more general case, if you want the difference between all pairs of consecutive repeated numbers, you could combine that with zip:
>>> lst = [1,1,1,1,2,3,4,5,5,5,3,3,3,3,2,2,2]
>>> duplicates = [k for k, g in itertools.groupby(lst) if len(list(g)) > 1]
>>> duplicates
[1, 5, 3, 2]
>>> [x - y for x,y in zip(duplicates, duplicates[1:])]
[-4, 2, 1]
I think now I got what you want: You want the difference between any consecutive "plateaus" in the list, where a plateau is either a repeated value, or a local minimum or maximum. This is a bit more complicated and will take several steps:
>>> lst=[1,1,1,1,2,3,4,5,5,5,3,3,3,3,2,2,2,4,3]
>>> plateaus = [lst[i] for i in range(1, len(lst)-1) if lst[i] == lst[i-1]
... or lst[i-1] <= lst[i] >= lst[i+1]
... or lst[i-1] >= lst[i] <= lst[i+1]]
>>> condensed = [k for k, g in itertools.groupby(plateaus)]
>>> [y-x for x, y in zip(condensed, condensed[1:])]
[4, -2, -1, 2]

How can I prevent adding the same value (With another key) into a dictionary?

I need to fill a dictionary with pairs key-value given by the next code:
for i in range(1,n+1):
d = {}
Ri = Vector([#SomeCoordinates])
for k in range(1,n+1):
Rk = Vector([#SomeCoordinates])
if i != k:
d['R'+str(i)+str(k)] = (Rk-Ri).mod # Distance between Ri and Rk
else:
None
""" Since (Rk-Ri).mod gives me the distance between two points (i and k),
it's meaningless to calc the distance if i == k. """
Here's the problem:
'Rik' represents the same distance as 'Rki' and I don't want to add a distance twice.
Then, I tried with this code:
if i != k and ( ('R'+str(i)+str(k)) and ('R'+str(k)+str(i)) ) not in d:
d['R'+str(i)+str(k)] = (Rk-Ri).mod
else:
None
but the problem is still there.
When I "print d" I get R12 but also R21 (And the same with every pair of numbers " i k ").
What can I do?
You could use the following:
d = {}
for i in range(1, n + 1):
Ri = Vector([#SomeCoordinates]).
for k in range(i + 1, n + 1):
Rk = Vector([#SomeCoordinates])
d[i, k] = d[k, i] = (Rk - Ri).mod
This way we ensure we'll take only a pair (by enforcing k > i) and then we can assign to the dictionary the distance for both (i, k) and (k, i).
I used d[i, k] instead of d['R' + str(i) + str(k)] because the latter has the following disadvantage: We can't infer for example, if d['R123'] refers to (12, 3) or (1, 23).
Also, I moved dictionary initialisation (d = {}) outside both loops, because it's initialised for each i.
If I undertand you correctly, you are looking for all the combinations of two elements. You can use itertools.combinations to autoamtically generate all such combinations with no duplicates.
d = {}
for i, k in itertools.combinations(range(1, n+1), 2):
Ri = Vector([SomeCoordinates])
Rk = Vector([SomeCoordinates])
d['R'+str(i)+str(k)] = (Rk-Ri).mod
You could even make it a dict comprehension (although it may be a bit long):
d = {'R'+str(i)+str(k)] : (Vector([SomeCoordinates]) - Vector([SomeCoordinates])).mod
for i, k in itertools.combinations(range(1, n+1), 2)}
Or, to do the (possibly expensive) calculation of Vector([SomeCoordinates]) just once for each value of i or k, try this (thanks to JuniorCompressor for pointing this out):
R = {i: Vector([SomeCoordinates]) for i in range(1, n+1)}
d = {(i, k): (R[i] - R[k]).mod for i, k in itertools.combinations(range(1, n+1), 2)}
Also, as others have noted, 'R'+str(i)+str(k) is not a good key, as it will be impossible to distinguish between e.g. (1,23) and (12,3), as both end up as 'R123'. I suggest you just use the tuple (i,k) instead.
You might always put the smaller value first so that a previous entry is automatically overwritten:
if i != k:
key = str(i) + "," + str(k) if i < k else str(k) + "," + str(i)
d['R'+key] = (Rk-Ri).mod
(I assume that your script only needs the distance values, not information from the current keys.)

Categories

Resources