I'm having trouble with this lambdas syntax.
I'm trying to translate a list of booleans to an integer value, but I'm getting an error, I don't understand why.
Here is the code:
from functools import reduce
binary = [True, True, False, True]
L = list(range(len(binary))) #Step 1
print(L) #[0, 1, 2, 3]
L = map(lambda a: 2**a, L) #Step 2
print(L) #[1, 2, 4, 8]
L = zip(binary, L) #Step 3
print(L) #[(True, 1),(True, 2),(False, 4),(True, 8)]
L = filter(lambda a, b: a, L) #Step 4
print(L) #[(True, 1),(True, 2),(True, 8)] not sure
L = map(lambda a, b: b, L) #Step 5
print(L) #?????
L = reduce(lambda a, b: a + b, L) #Final step
print(L) #11
Output:
Traceback (most recent call last):
File "C:/Users/axel_/PycharmProjects/Python_Subject_Exam/19_new_exam_binary_list_translation.py", line 27, in <module>
L = reduce(lambda a, b: a + b, L)
TypeError: <lambda>() missing 1 required positional argument: 'b'
[0, 1, 2, 3]
<map object at 0x00000267FAFE5388>
<zip object at 0x00000267FAFE50C8>
<filter object at 0x00000267FAFE5248>
<map object at 0x00000267FAFE4EC8>
Process finished with exit code 1
Could anyone help me to debug?
I think this will solve your problem. I will write some comments in the code to help you understand:
from functools import reduce
binary = [True, True, False, True]
L = list(range(len(binary))) #[0, 1, 2, 3]
L = map(lambda a: 2**a, L) #[1, 2, 4, 8]
L = zip(binary, L) #[(True, 1), (True, 2), (False, 4), (True, 8)]
L = filter(lambda x: x[0], L) #<--- an item from zip() is an ((unpacked)) tuple
L = map(lambda x: x[1], L)
L = reduce(lambda a, b: a + b, L)
print(L) #11
This is one of the challenges if you are new to using iterators.
The iterator is evaluated as each item is pulled off the iterator, this means that when you call reduce to combine the result all of the lambda expressions are evaluated at that time. In your example this is equivalent to the following expression:
L = list(range(len(binary)))
L = reduce(lambda a, b: a + b,
map(lambda a, b: b,
filter(lambda a, b: a,
zip(binary,
map(lambda a: 2**a, L)
)
)
)
)
print(L)
Confusing?
In your example the line L = filter(lambda a, b: a, L) #Step 4 has an error. A filter expression takes a callable expression that takes a single argument along with an iterable but because it is not evaluated to the end you receive a confusing error.
One approach (and a good one when debugging) is to wrap each step in a call to list to force the evaluation to occur on each line eg L = list(filter(lambda a, b: a, L)). This will make the location of the error more apparent.
Alternatively use a generator expression rather than filter/map eg:
# filter (return odd numbers between 1 and 10)
result_a = filter(lambda a: a % 2, range(1, 11))
result b = (a for a in range(1, 11) if a % 2)
# map (return powers of 2)
result_a = map(lambda a: 2**a, range(11))
result_b = (2**a for a in range(11))
All of the results are the same but generator expressions have another trick, you can use tuple unpacking allowing for:
result = (b for a, b in zip(binary, range(len(binary))) if a)
Further, Python has other builtins that can simplify the code even more.
Enumerate will return a counter plus each element from an iterable allowing the first 3 steps to be simplified to:
# 1 step 1, 2 and 3 can be simplified with enumerate
L = ((i**2, b) for i, b in enumerate(L))
Next sum can be used to replace reduce, sum adds together all numerical values in an iterable:
L = reduce(lambda a, b: a + b, L)
# is equivalent to
L = sum(L)
And putting it all together the entire sequence can be simplified to:
L = sum(2**i for i, b in enumerate(binary) if b)
Hope that helps.
Related
I have the following code snippet but it is giving me the type error above. What I basically wanted to do is add two to every element and reduct it to a single sum, but it didnt work. How can we achieve this??
import functools
list1 = [1,2,3,4]
result = functools.reduce(lambda x:x+2, list1)
print(result)
Update:
It works when I do the following though:
list1 = [1,2,3,4]
result = functools.reduce(lambda x,y:x+y, list1)
print(result)
reduce() requires a lambda function which takes 2 arguments.
The first argument is the collector.
But here, so that +2 is applied also on the first argument, you have to give the initial value 0.
import functools
list1 = [1,2,3,4]
result = functools.reduce(lambda acc, el: acc + el + 2, list1, 0)
print(result) ## sum([3, 4, 5, 6]) = 18
You basically want to do a normal summing reduce(lambda x, y: x + y, lst) => sum(lst)
and a map(lambda x: x + 2, l) which is expressed in a more pythonic way as a list comprehension:
l = [1, 2, 3, 4]
sum([x + 2 for x in l]) # most pythonic!
## 18
sum(map(lambda x: x + 2, l))
## 18
or:
import functools
l = [1, 2, 3 ,4]
result = functools.reduce(lambda x, y: x + y, [x + 2 for x in l])
result ## 18
or:
import functools
l = [1, 2, 3 ,4]
result = functools.reduce(lambda x, y: x + y, map(lambda x: x + 2, l))
result ## 18
The callable passed to the reduce function needs to take two arguments to aggregate the current result with the next item from the given iterable:
import functools
list1 = [1, 2, 3, 4]
result = functools.reduce(lambda x, y: x + y + 2, list1, 0)
print(result)
This outputs:
18
I'm trying to get product of list where every element is multiplied by its index + 1. I tried with reduce and enumerate
value = reduce(lambda a, b: (a[0]+1)*a[1]*b[1], enumerate(list))
but it gives TypeError: 'int' object is not subscriptable.
Is it possible to do it in one line?
Editv2
example of list [1,2,3]
desire output list[0]*1 * list[1]*2 * list[2]*3 = 36
Simplest:
lst = [1,2,3] # do not shadow `list`
sum(i*v for i, v in enumerate(lst, 1))
# 14
Your reduce approach fails as it returns an int which is not the same type that it expects as its two inputs (tuple). You could do it with:
reduce(lambda a, b: (a[0]*a[1] + b[0]*b[1], 1), enumerate(lst, 1))[0]
# 14
Note the (..., 1) structure of the lambda expression where the 1 serves as an auxiliary factor for the next step.
Update: As you actually want the homogenous product of all the elements of your nested iterable, the following is simpler:
from itertools import chain
from operator import mul
reduce(mul, chain(*enumerate(lst, 1)))
# 36
The trick is to make a new list and multiply through it.
Create a new list where a number at index i is i*list[i], indexing starting with 1:
>>> new_list = [a*b for a, b in enumerate(list, 1)]
>>> new_list
[1, 4, 9]
and multiply over your new list:
>>> reduce((lambda x, y: x*y), new_list)
36
In one line:
>>> reduce((lambda x, y: x*y), [a*b for a, b in enumerate(list, 1)])
36
Hope it helped :)
Note: Answer edited to meet OP's changes.
ans = sum(i * x for i, x in enumerate(list, 1))
Not exactly what you are asking for, but it gives the desired result.
If I have a list containing a lambda function, for example:
a = [lambda x: x * 2]
how could I call this lambda function to a list:
b = [1,2,3,4]
so that it could have the result as:
result = list(map(lambda x: x * 2, b))
and get:
[2,4,6,8]
Further, what if a contains two lambda functions, for example:
a = [lambda x:x * 2, lambda x: x-1]
How could I call these functions to list b without using for loop or list comprehension but use map()?
I hope someone could help me with it!
You can simply do
result = list(map(a[0], b))
# [2,4,6,8]
since the lambda function is the first element of a
For the second function it's the same thing
result = list(map(a[1], b))
If you want to apply all functions in one line
[list(map(a[i],b)) for i in range(len(a))]
#[[2, 4, 6, 8], [0, 1, 2, 3]]
You can iterate over the lambda functions in the list no matter how many elements stored in it:
a = [lambda x:x * 2, lambda x: x-1]
b = [1, 2, 3, 4]
for func in a:
# apply each function on b and turn the results to list:
print(list(map(func, b)))
If you want to store each result:
a = [lambda x:x * 2, lambda x: x-1]
b = [1, 2, 3, 4]
results = []
for func in a:
results.append(list(map(func, b)))
In the special case of a single lambda wrapped in a list, do
result = list(map(a[0], b))
But there are no special cases in python. So for the general case, you can either have a flat list:
result = [f(x) for f in a for x in b]
or a nested one:
result = [[f(x) for f in a] for x in b]
You can reverse the sort order of the lists by swapping the loops over a and b.
You can even go as far as using itertools.product to get the flat case (again with a and b interchangeable to change the order of the result):
result = [f(x) for f, x in itertools.product(a, b)]
I need to add an undetermined number of lists (sets), but i only want to add members if they are unique
(1,2,3)
(3,4,5)
(4,7,8,9)
(5,3,9)
would need to give: (1,2,7,8)
from collections import Counter
from itertools import chain
lst = [(1,2,3), (3,4,5), (4,7,8,9), (5,3,9)]
[k for k, v in Counter(chain.from_iterable(lst)).items() if v==1]
# [1, 2, 7, 8]
For minimal cost, you can also return values with count 2, 3, etc.
Although Counter is the better solution, here is another approach:
import functools as ft
def get_disjoints(iterables):
"""Return a list of disjoint elements."""
def local_symdiff(a, b):
"""Return a symmetric difference of all observations."""
a, b = set(a), set(b)
intersections.update(a & b)
return {x for x in a | b if x not in intersections}
intersections = set()
return ft.reduce(local_symdiff, iterables)
iterables = [(1,2,3), (3,4,5), (4,7,8,9), (5,3,9)]
get_disjoints(iterables)
# {1, 2, 7, 8}
Pairs of iterables (via functools.reduced) are merged and filtered of all observed intersections.
def findSingles(t):
l = [n for t in tuples for n in t]
return tuple([i for i in l if l.count(i) == 1])
tuples = ((1,2,3), (3,4,5), (4,7,8,9), (5,3,9))
print(findSingles(tuples))
# (1, 2, 7, 8)
Try this:
def build_list(listnums):
nummap = {}
for n in listnums:
for num in n:
if not num in nummap:
nummap[num] = True
else:
nummap.pop(num, None)
return list(nummap.keys())
Here is a better* solution that uses only set operations and reduce():
all_sets = [{1,2,3}, {3,4,5}, {4,7,8,9}, {5,3,9}]
combined = reduce(
lambda a, b: a.union(b),
[
s - reduce(
lambda x, y: x.union(y),
[other for j, other in enumerate(all_sets) if j!=i]
) for i, s in enumerate(all_sets)
]
)
print(combined)
#set([8, 1, 2, 7])
Go go through each set in all_sets and get the distinct elements across all other sets using the set difference operator (-). Then we reduce the result of this operator using the set union() function to combine all sets.
To illustrate what's going on, here is the output of the inner list comprehension:
# this gets the elements in each set that are not in any of the others
inner = [
s - reduce(
lambda x, y: x.union(y),
[other for j, other in enumerate(all_sets) if j!=i]
) for i, s in enumerate(all_sets)
]
print(inner)
# [set([1, 2]), set([]), set([8, 7]), set([])]
*Better than my original solution below.
Original Solution
Here's a solution using list comprehensions and without counting occurrences.
all_sets = [{1,2,3}, {3,4,5}, {4,7,8,9}, {5,3,9}]
combined = [x for i, a in enumerate(all_sets) for x in a
if all(x not in b for j, b in enumerate(all_sets) if i!=j)]
print combined
#[1, 2, 7, 8]
Iterate though each value in each list and only add it to the combined list if it doesn't exist in any of the other lists. We use enumerate twice over all of the lists so we can keep track of what the "other" lists are.
You can flatten the tuples and then caste as a set:
s = [(1,2,3), (3,4,5), (4,7,8,9), (5,3,9)]
full_set = [i for b in s for i in b]
final_s = tuple(i for i in full_set if full_set.count(i) == 1)
Output:
(1, 2, 7, 8)
I need to check a condition for all the items in two 1-dimensional lists
For example:
L = [12,23,56,123,13,15]
B = [45,89,12,45,19,89]
For the above two lists, how do I need to check the condition if(L[i] > (float(B[i]*1.1))) where 'i' is the index's starting from index 0 to all the items in the lists (in this case index is 0 to 5).I also need to print the items of list1(L) which fails the condition?
If I understood you right, you can do this with generator expression and zip function
L = [12,23,56,123,13,15]
B = [45,89,12,45,19,89]
all(x[0] > (x[1]*1.1) for x in zip(L, B))
or, as Ashwini Chaudhary suggested in comments, with values unpacking:
L = [12,23,56,123,13,15]
B = [45,89,12,45,19,89]
all(l > (b * 1.1) for l, b in zip(L, B))
To get items from list L which fails the condition:
[l for l, b in zip(L, B) if l <= (b * 1.1)]
not sure this is what you want but its a cool numpy thing
>>> L = numpy.array(L)
>>> B = numpy.array(B)
>>> B < L
array([False, False, True, True, False, False], dtype=bool)
>>> L[L > B* 1.1]
array([ 56, 123])
>>> all(L > B*1.1)
In order to print the ones who are matching, you could use
matching = (l > float(b * 1.1) for l, b in zip(L, B))
This gives you a generator which you can use as you want. E.g.:
for m, l in zip(matching, L):
if m:
print l
But you could as well directly generate an iterator of matching ones:
matching = (l for l, b in zip(L, B) if l > float(b * 1.1))
and then print them or just check for emptiness.
Depending on what you want to do, it might be appropriate to change the generator expression to a list comprehension:
matching = [l for l, b in zip(L, B) if l > float(b * 1.1)]