I have this exercise that I fail to understand
Suppose we are given a list X of integers. We need to construct a sequence of indices (positions) of the elements in this list equal to the maximal element. The indicies in the sequence are in the ascending order.
Hint use the enumerator function
from typing import Iterator
X = [1,10,3,4,10,5]
S : Iterator[int] = YOUR_EXPRESSION
assert list(S)==[1,4]
This is the only thing I could come up with, but for sure it does not return [1,4]
If you wondering what I don't understand, it is not clear from reading the description how it could return [1,4].
Maybe you want to try to explain that to me first...
This is my (wrong) solution
my_enumerate=enumerate (X)
my_enumerate=(list(my_enumerate))
my_enumerate.sort(reverse=True)
So you have the list X containing [1,10,3,4,10,5]. The maximal, or largest, element is 10. Which means we should return a list of all the indices where we find 10. There are two 10s at index 1 and 4 respectively.
Using enumerate you get at each iteration the index and element. You can use that to filter out the elements you don't need. List comprehensions are useful in this case, allowing for filtering with the if syntax i.e. [val for val in items if some_condition]
you can use a generator like this
max_val=max(X)
s = (i for i, v in enumerate(X) if v==max_val)
This is my solution
( x[0] for x in enumerate (X) if x[1] == max(X) )
this is the book solution
(i for (i, n) in enumerate(X) if n == max(X))
This requires two steps:
Determine the maximum value with max
Iterate the indices of your list and retain those that have this maximum value
To avoid a bad time complexity, it is necessary to not repeat the first step:
S : Iterator[int] = (lambda mx:
(i for i, x in enumerate(X) if x == mx)
)(max(X))
The reason for presenting the code in such ugly expression, is that in the question it seems a requirement to follow the template, and only alter the part that is marked with "YOUR_EXPRESSION".
This is not how you would write it without such artificial constraints. You would just do mx = max(X) and then assign the iterator to S in the next statement without the need for this inline lambda.
Related
I would like to improve my understanding regarding the key argument of the builtin max function. Please consider the following example:
I have a list of lists, and I want to find the one with the biggest length.
I can write the straight forward solution:
maximum = 0
for l in lists:
maximum = max(maximum, len(l))
However I would prefer to avoid an explicit for loop using the key argument of max. As I understand it, the key will apply its argument to each element in the input and then compare those outputs. So this should work:
maximum = max(lists, key=len)
I believe it should work because according to my (apparently false) understanding the above statement would be equivalent to applying length to each element and then invoking max:
max([len(l) for l in text_tokenized])
What am I missing?
I believe it should work because according to my (apparently false) understanding the above statement would be equivalent to applying length to each element and then invoking max:
No, The code
maximum = max(lists, key=len)
Means, find an "element" from lists which having maximum value of len(element)
Whereas, what you intended to find value of len(element). one line quvilent of your code:
maximum = 0
for l in lists:
maximum = max(maximum, len(l))
would be
max(map(len, lists))
or may be using len(max(lists, key=len))
I am building a function to extract all negatives from a list called xs and I need it to add those extracted numbers into another list called new_home. I have come up with a code that I believe should work, however; it is only showing an empty list.
Example input/output:
xs=[1,2,3,4,0,-1,-2,-3,-4] ---> new_home=[1,2,3,4,0]
Here is my code that returns an empty list:
def extract_negatives(xs):
new_home=[]
for num in range(len(xs)):
if num <0:
new_home= new_home+ xs.pop(num)
return
return new_home
Why not use
[v for v in xs if v >= 0]
def extract_negatives(xs):
new_home=[]
for num in range(len(xs)):
if xs[num] < 0:
new_home.append(xs[num])
return new_home
for your code
But the Chuancong Gao solution is better:
def extract_negative(xs):
return [v for v in xs if v >= 0]
helper function filter could also help. Your function actually is
new_home = filter(lambda x: x>=0, xs)
Inside the loop of your code, the num variable doesn't really store the value of the list as you expect. The loop just iterates for len(xs) times and passes the current iteration number to num variable.
To access the list elements using loop, you should construct loop in a different fashion like this:
for element in list_name:
print element #prints all element.
To achieve your goal, you should do something like this:
another_list=[]
for element in list_name:
if(element<0): #only works for elements less than zero
another_list.append(element) #appends all negative element to another_list
Fortunately (or unfortunately, depending on how you look at it) you aren't examining the numbers in the list (xs[num]), you are examining the indexes (num). This in turn is because as a Python beginner you probably nobody haven't yet learned that there are typically easier ways to iterate over lists in Python.
This is a good (or bad, depending on how you look at it) thing, because had your code taken that branch you would have seen an exception occurring when you attempted to add a number to a list - though I agree the way you attempt it seems natural in English. Lists have an append method to put new elements o the end, and + is reserved for adding two lists together.
Fortunately ignorance is curable. I've recast your code a bit to show you how you might have written it:
def extract_negatives(xs):
out_list = []
for elmt in xs:
if elmt < 0:
out_list.append(elmt)
return out_list
As #ChuangongGoa suggests with his rather terse but correct answer, a list comprehension such as he uses is a much better way to perform simple operations of this type.
I am using build-in function zip() to compare 2 element in List.
myList =[11,12,93,14,15,45,56,67,78,11]
z = 0;
final = 0 ;
for x, y in zip(myList, myList[1:]):
if x > y :
z = x
if (final<x):
final = x;
print final;
For this code I get valid answer as: 93
Now if I add another element in last index as 333
myList =[11,12,93,14,15,45,56,67,78,11,333]
z = 0;
final = 0 ;
for x, y in zip(myList, myList[1:]):
if x > y :
z = x
if (final<x):
final = x;
print final;
I get still get output as :93 , but the correct answer in 333.
Can someone explain the logic as where I am wrong.
zip stops at the end of its shortest argument. myList[1:] is always one shorter than myList (provided the list isn't empty), and so the last pair you get from zip is x,y = 11,333:
>>> myList =[11,12,93,14,15,45,56,67,78,11,333]
>>> pairs = zip(myList, myList[1:])
>>> list(pairs)[-1]
(11, 333)
But you only ever assign final and z to the current value of x, which can never be the last element. You could add a phantom element to the end of the list that can't trigger the conditions: you're using mixed greater and lesser comparisons, so your best option is the special value "not a number" float('nan'), which is neither bigger nor smaller than any number (all comparisons return False). For the special case of pairs, you can put the dummy element in like this since the second argument is known to be exactly one element shorter than the longer one:
>>> pairs = zip(myList, myList[1:]+[float('nan')])
>>> list(pairs)[-1]
(333, nan)
But for a more general solution, you will want to use zip_longest from itertools, which effectively pads the end of the shorter arguments like this until they are the length of the longest one:
>>> pairs = it.zip_longest(myList, myList[1:], fillvalue=float('nan'))
>>> list(pairs)[-1]
(333, nan)
Take a look at itertools.izip_longest, as you are attempting to zip together iterables that are different in length. izip_longest will allow you to add a fill value for the iterables that are shorter than the longest one.
The reason this happens is because zip has length equal to the shortest sequence. Note that in the first case your list is of even length (so all pairs are returned), but in the second, it's odd, so you don't get the last one. If you wanted different behavior, take a look at itertools.izip_longest.
I'm trying to create a function that returns the largest element of an array, I feel I have the correct code but my syntax is in the wrong order, I'm trying to use a for/while loop in order to do so. So far I have the following:
def manindex(arg):
ans = 0
while True:
for i in range (len(arg)):
if arg[i] > arg[ans]:
pass
ans = i
return ans
Not sure where I'm going wrong if anyone could provide some guidance, thanks
EDIT: So it's been pointing out I'm causing an infinite loop so if I take out the while statement I'm left with
def manindex(arg):
ans = 0
for i in range (len(arg)):
if arg[i] > arg[ans]:
ans = i
return ans
But I have a feeling it's still not correct
When you say array I think you mean list in Python, you don't need a for/loop or while/loop to achieve this at all.
You can also use index with max, like so:
xs.index(max(xs))
sample:
xs = [1,123,12,234,34,23,42,34]
xs.index(max(xs))
3
You could use max with the key parameter set to seq.__getitem__:
def argmax(seq):
return max(range(len(seq)), key=seq.__getitem__)
print(argmax([0,1,2,3,100,4,5]))
yields
4
The idea behind finding the largest index is always the same, iterating over the elements of the array, compare to the max value we have at the moment, if it's better, the index of the current element is the maximum now, if it's not, we keep looking for it.
enumerate approach:
def max_element_index(items):
max_index, max_value = None, None
for index, item in enumerate(items):
if item > max_value:
max_index, max_value = index, item
return max_index
functional approach:
def max_element_index(items):
return reduce(lambda x,y: x[1] > y[1] and x or y,
enumerate(items), (None, None))[0]
At the risk of looking cryptic, the functional approach uses the reduce function which takes two elements and decides what is the reduction. Those elements are tuples (index, element), which are the result of the enumerate function.
The reduce function, defined on the lambda body takes two elements and return the tuple of the largest. As the reduce function reduces until only one element in the result is encountered, the champion is the tuple containing the index of the largest and the largest element, so we only need to access the 0-index of the tuple to get the element.
On the other hand if the list is empty, None object is returned, which is granted on the third parameter of the reduce function.
Before I write a long winded explanation, let me give you the solution:
index, value = max(enumerate(list1), key=lambda x: x[1])
One line, efficient (single pass O(n)), and readable (I think).
Explanation
In general, it's a good idea to use as much of python's incredibly powerful built-in functions as possible.
In this instance, the two key functions are enumerate() and max().
enumerate() converts a list (or actually any iterable) into a sequence of indices and values. e.g.
>>> list1 = ['apple', 'banana', 'cherry']
>>> for tup in enumerate(list1):
... print tup
...
(0, 'apple')
(1, 'banana')
(2, 'cherry')
max() takes an iterable and returns the maximum element. Unfortunately, max(enumerate(list1)) doesn't work, because max() will sort based on the first element of the tuple created by enumerate(), which sadly is the index.
One lesser-known feature of max() is that it can take a second argument in the form max(list1, key=something). The key is a function that can be applied to each value in the list, and the output of that function is what gets used to determine the maximum. We can use this feature to tell max() that it should be ranking items by the second item of each tuple, which is the value contained in the list.
Combining enumerate() and max() with key (plus a little help from lambda to create a function that returns the second element of a tuple) gives you this solution.
index, value = max(enumerate(list1), key=lambda x: x[1])
I came up with this recently (and am sprinkling it everywhere in my code) after watching Raymond Hettinger's talk on Transforming Code into Beautiful, Idiomatic Python, where he suggests exorcising the for i in xrange(len(list1)): pattern from your code.
Alternatively, without resorting to lambda (Thanks #sweeneyrod!):
from operator import itemgetter
index, value = max(enumerate(list1), key=itemgetter(1))
I believe if you change your for loop to....
for i in range (len(arg)):
if arg[i] > ans:
ans = arg[i]
it should work.
You could try something like this. If the list is empty, then the function will return an error.
m is set to the first element of the list, we then iterate over the list comparing the value at ever step.
def findMax(xs):
m = xs[0]
for x in xs:
if x > m:
m = x
return m
findMax([]) # error
findMax([1]) # 1
findMax([2,1]) # 2
if you wanted to use a for loop and make it more generic, then:
def findGeneric(pred, xs):
m = xs[0]
for x in xs:
if pred(x,m):
m = x
return m
findGeneric(lambda a,b: len(a) > len(b), [[1],[1,1,1,1],[1,1]]) # [1,1,1,1]
How can I update the upper limit of a loop in each iteration? In the following code, List is shortened in each loop. However, the lenList in the for, in loop is not, even though I defined lenList as global. Any ideas how to solve this? (I'm using Python 2.sthg)
Thanks!
def similarity(List):
import difflib
lenList = len(List)
for i in range(1,lenList):
import numpy as np
global lenList
a = List[i]
idx = [difflib.SequenceMatcher(None, a, x).ratio() for x in List]
z = idx > .9
del List[z]
lenList = len(List)
X = ['jim','jimmy','luke','john','jake','matt','steve','tj','pat','chad','don']
similarity(X)
Looping over indices is bad practice in python. You may be able to accomplish what you want like this though (edited for comments):
def similarity(alist):
position = 0
while position < len(alist):
item = alist[position]
position += 1
# code here that modifies alist
A list will evaluate True if it has any entries, or False when it is empty. In this way you can consume a list that may grow during the manipulation of its items.
Additionally, if you absolutely have to have indices, you can get those as well:
for idx, item in enumerate(alist):
# code here, where items are actual list entries, and
# idx is the 0-based index of the item in the list.
In ... 3.x (I believe) you can even pass an optional parameter to enumerate to control the starting value of idx.
The issue here is that range() is only evaluated once at the start of the loop and produces a range generator (or list in 2.x) at that time. You can't then change the range. Not to mention that numbers and immutable, so you are assigning a new value to lenList, but that wouldn't affect any uses of it.
The best solution is to change the way your algorithm works not to rely on this behaviour.
The range is an object which is constructed before the first iteration of your loop, so you are iterating over the values in that object. You would instead need to use a while loop, although as Lattyware and g.d.d.c point out, it would not be very Pythonic.
What you are effectively looping on in the above code is a list which got generated in the first iteration itself.
You could have as well written the above as
li = range(1,lenList)
for i in li:
... your code ...
Changing lenList after li has been created has no effect on li
This problem will become quite a lot easier with one small modification to how your function works: instead of removing similar items from the existing list, create and return a new one with those items omitted.
For the specific case of just removing similarities to the first item, this simplifies down quite a bit, and removes the need to involve Numpy's fancy indexing (which you weren't actually using anyway, because of a missing call to np.array):
import difflib
def similarity(lst):
a = lst[0]
return [a] + \
[x for x in lst[1:] if difflib.SequenceMatcher(None, a, x).ratio() > .9]
From this basis, repeating it for every item in the list can be done recursively - you need to pass the list comprehension at the end back into similarity, and deal with receiving an empty list:
def similarity(lst):
if not lst:
return []
a = lst[0]
return [a] + similarity(
[x for x in lst[1:] if difflib.SequenceMatcher(None, a, x).ratio() > .9])
Also note that importing inside a function, and naming a variable list (shadowing the built-in list) are both practices worth avoiding, since they can make your code harder to follow.