Usage of key in max function and argument-less count method - python

list_x = ['A', 'B', 'B', 'E']
x = max(set(list_x), key=list_x.count)
Can anyone explain what's happening here? I know max() returns the max value and the set() would return set of the given list but what is , key=list_x.count there? (also, why there is no input for count method? ie. list_x.count("B") )
Note: I got this line from someone else's submission online.

max() goes through the given iterable and returns the largest item.
The key parameter (also used in list.sort(), min(), and other similar functions) can be passed a function that's used to get what key to sort on. Basically, this key function is called once for each item in the iterable and that value is used for sorting instead.
The list_x.count count method is a reference to list.count(). This returns how many times the item is in the list.
Another way to write what you have would be:
max(set(list_x), key=lambda item: list_x.count(item))
It's just that instead of passing a function/lambda that calls list_x.count() to key, the list_x.count function itself was passed.
set() is being used here because a set can only contain unique elements. Since you are getting the count for each element, it is faster to use a set so you are not calling list_x.count('B') multiple times.

This computes the element in list_x that is repeated the most. set(list_x) is used to compute the max over, and for each (unique, of course) element in the set, the count of that element in the list is computed (as in list_x.count('B'), for example).
So:
list_x = ['A', 'B', 'B', 'E']
x = max(set(list_x), key=list_x.count)
# x = 'B'
list_x = ['A', 'B', 'B', 'E', 'A', 'A']
x = max(set(list_x), key=list_x.count)
# x = 'A'

Related

Finding all the elements in a list between two elements (not using index, and with wrap around)

I'm trying to figure out a way to find all the elements that appear between two list elements (inclusive) - but to do it without reference to position, and instead with reference to the elements themselves. It's easier to explain with code:
I have a list like this:
['a','b','c','d','e']
And I want a function that would take, two arguments corresponding to elements eg. f('a','d'), and return the following:
['a','b','c','d']
I'd also like it to wrap around, eg. f('d','b'):
['d','e','a','b']
I'm not sure how to go about coding this. One hacky way I've thought of is duplicating the list in question (['a','b','c','d','e','a','b','c','d','e']) and then looping through it and flagging when the first element appears and when the last element does and then discarding the rest - but it seems like there would be a better way. Any suggestions?
def foo(a, b):
s, e = [a.index(x) for x in b]
if s <= e:
return a[s:e+1]
else:
return a[s:] + a[:e+1]
print(foo(['a','b','c','d','e'], ['a', 'd'])) # --> ['a', 'b', 'c', 'd']
print(foo(['a','b','c','d','e'], ['d', 'b'])) # --> ['d', 'e', 'a', 'b']
So the following obviously needs error handling as indicated below, and also, note the the index() function only takes the index of the first occurrence. You have not specified how you want to handle duplicate elements in the list.
def f(mylist, elem1, elem2):
posn_first = mylist.index(elem1) # what if it's not in the list?
posn_second = mylist.index(elem2) # ditto
if (posn_first <= posn_second):
return mylist[posn_first:posn_second+1]
else:
return mylist[posn_first:] + mylist[:posn_second+1]
This would be a simple approach, given you always want to use the first appearence of the element in the list:
def get_wrapped_values(input_list, start_element, end_element):
return input_list[input_list.index(start_element): input_list.index(end_element)+1]

Why is Python function returning wrong values? [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 3 years ago.
My function returns wrong values when called.
First, iteration returns correct value but then if I call a new function. Why is it returning non-empty lists?
I ran the below code in my shell. The second last call should return an empty list but it returns the value return in the call before that.
>>> from solvers.algorithms.dancing_links import solve
>>> solve({}, {})
[]
>>> solve(ctor, rtoc)
['B', 'D', 'F']
>>> solve(ctor, rtoc)
['B', 'D', 'F']
>>> solve({}, {})
['B', 'D', 'F']
This is my python code.
# Solves the Sudoku using dancing links algorithm using implementation from:
# https://www.cs.mcgill.ca/~aassaf9/python/algorithm_x.html
def solve(columns_to_rows, rows_to_columns, solution=[]):
"""
This function solves the exact cover problem, i.e., given a matrix A in which each element is 0 or 1.
Is there a subset of rows which have exactly one 1 in each column?
The exact cover problem can also be stated as following,
"Given a set X and a set of subsets of X called Y, is there a subset of Y such that all of its elements
are disjoint and union of its elements is X."
:param columns_to_rows: It is a dictionary where value of key `i` is the set of columns in which ith row is 1.
:param rows_to_columns: It is a dictionary where value of key `i` is the set of rows in which ith column is 1.
:param solution: Currently selected rows
:return:
"""
if not columns_to_rows:
return list(solution)
else:
# Column with minimum 1's in it.
selected_column = min(columns_to_rows, key=lambda col: columns_to_rows[col])
# For each row which has a 1 in the `selected_column`.
for selected_row in columns_to_rows[selected_column]:
solution.append(selected_row)
# Select and remove all columns which have 1's in the `selected_row`
# Also remove all the rows which have 1's in the same column as the `selected_row`
removed_columns = select_and_remove(columns_to_rows, rows_to_columns, selected_row)
tmp = solve(columns_to_rows, rows_to_columns, solution)
if tmp is not None:
return tmp
deselect_and_insert(columns_to_rows, rows_to_columns, selected_row, removed_columns)
solution.pop()
Edit 1:
I added a print statement to print the "solution" variable and I found this.
>>> from solvers.algorithms.dancing_links import solve>>> solve({}, {})
[]
[]
>>> solve({}, {})
[]
[]
>>> solve(ctor, rtoc)
[]
['A']
['B']
['B', 'D']
['B', 'D', 'F']
['B', 'D', 'F']
>>> solve({}, {})
['B', 'D', 'F']
['B', 'D', 'F']
>>>
So the first time I call the function it returns empty list and solution is also empty.
Then when I call the function second time with actual parameters it works as expected and goes into a recursive call.
But the third time the solution variable is already set to the previous value but it should be empty.
Why is it happening? And how to fix it?
Don't pass solution=[] to function, its saves it reference and can affect you resulsts, pass a new list everytime the function is running
more inforamtion: here

Retrieving all but one value

I'm looking to retrieve all but one value from a list:
ll = ['a','b','c']
nob = [x for x in ll if x !='b']
Is there any simpler, more pythonic way to do this, with sets perhaps?
given that the element is unique in the list, you can use list.index
i = l.index('b')
l = ll[:i] +ll[i+1:]
another possibility is to use list.remove
ll.remove('b') #notice that ll will change underneath here
whatever you do, you'll always have to step through the list and compare each element, which gets slow for long lists. However, using the index, you'll get the index of the first matching element and can operate with this alone, thus avoiding to step through the remainder of the list.
list_ = ['a', 'b', 'c']
list_.pop(1)
You can also use .pop, and pass the index column, or name, that you want to pop from the list. When you print the list you will see that it stores ['a', 'c'] and 'b' has been "popped" from it.

Enforcing "no 2 same contiguous elements" in random list generation

I have a set of 4 strings and want to generate a list of 16 elements, but with enforcing the rule (or obtaining the same result as enforcing such rule) to never have the same element repeated in two contiguous positions in the resulting list.
Being almost a total newbie in Python I went to check the different methods in the random library and found many different and useful ways to do something similar (random.shuffle would almost do the trick), but no one of those addressed this my particular need.
What data format and what methods should I use?
Pseudocode algorithm:
For i in n (n being the amount of elements you want)
Generate the next element
If it's the same as the previous element, repeat 2
Use random.choice to pick an element from a list of elements randomly.
Here's a proof of concept Python code:
import random
sources = ['a', 'b', 'c', 'd'] # you said 4 strings
result = [random.choice(sources)]
while len(result) < 16: # you said you need 16 elements
elem = random.choice(sources)
if elem != result[-1]:
result.append(elem)
This code is optimized for clarity, not succinctness, cleverness or speed.
For a more general solution, you could turn to Python generators.
Given an arbitrary iterable of inputs (eg: your four input strings), the following generator will generate an infinite iterable of choices from that list, with no two side-by-side elements being the same:
import random
def noncontiguous(inputs):
last = random.choice(inputs)
yield last
while True:
next = random.choice(inputs)
if next != last:
last = next
yield next
You can then use list comprehensions or a basic for loop to obtain the 16 element subset of this infinite sequence:
>>> gen = noncontiguous(['a', 'b', 'c', 'd'])
>>> [gen.next() for i in range(16)]
['c', 'b', 'c', 'b', 'a', 'c', 'b', 'c', 'd', 'a', 'd', 'c', 'a', 'd', 'b', 'c']
More interestingly, you can continue to use the same generator object to create more noncontiguous elements
>>> for i in range(8):
... gen.next()
...
'b'
'c'
'd'
'c'
'b'
'd'
'a'
'c'
Zart's code modified to (a) work and (b) pre-calculate the set subtractions:
import random
def setsub():
# 4 strings
sources = ['a', 'b', 'c', 'd']
# convert them to set
input = set(sources)
subs = {}
for word in sources:
subs[word] = list(input - set([word]))
# choose first element
output = [random.choice(sources)]
# append random choices excluding previous element till required length
while len(output) < 16:
output.append(random.choice(subs[output[-1]]))
return output
A rather severe abuse of itertools:
import itertools
import random
print list(itertools.islice((x[0] for x in
itertools.groupby(random.randint(1, 10) for y in itertools.count())), 16))
It uses islice() to get the first 16 elements of an infinite generator based around count(), using groupby() to collapse equal adjacent elements.
This is revised Eli's version that doesn't brute-forces elements, and hopefully doesn't lack clarity:
import random
# 4 strings
sources = ['a', 'b', 'c', 'd']
# convert them to set
input = set(sources)
# choose first element
output = [random.choice(input)]
# append random choices excluding previous element till required length
while len(output) < 16:
output.append(random.choice(input - set(output[-1:])))

Using an index to get an item

I have a list in python ('A','B','C','D','E'), how do I get which item is under a particular index number?
Example:
Say it was given 0, it would return A.
Given 2, it would return C.
Given 4, it would return E.
What you show, ('A','B','C','D','E'), is not a list, it's a tuple (the round parentheses instead of square brackets show that). Nevertheless, whether it to index a list or a tuple (for getting one item at an index), in either case you append the index in square brackets.
So:
thetuple = ('A','B','C','D','E')
print thetuple[0]
prints A, and so forth.
Tuples (differently from lists) are immutable, so you couldn't assign to thetuple[0] etc (as you could assign to an indexing of a list). However you can definitely just access ("get") the item by indexing in either case.
values = ['A', 'B', 'C', 'D', 'E']
values[0] # returns 'A'
values[2] # returns 'C'
# etc.
You can use _ _getitem__(key) function.
>>> iterable = ('A', 'B', 'C', 'D', 'E')
>>> key = 4
>>> iterable.__getitem__(key)
'E'
Same as any other language, just pass index number of element that you want to retrieve.
#!/usr/bin/env python
x = [2,3,4,5,6,7]
print(x[5])
You can use pop():
x=[2,3,4,5,6,7]
print(x.pop(2))
output is 4

Categories