Iterating with lambda and map python - python

Here is my code:
map(lambda i: 3*i, map(lambda x: x, [[1, 2],[3, 4]]))
It outputs: [[1, 2, 1, 2, 1, 2], [3, 4, 3, 4, 3, 4]]
How do I modify it to print 3, 6, 9, 12?
I do not want to use append. Just map and lambda.

The problem you observe arises from the fact that the inner map returns a sequence of lists, not a sequence of numbers. This is because that map is iterating over a list containing smaller lists. So, if you use itertools.chain.from_iterable to flatten that list, you'll get a sequence of numbers. You can then use the outer map to triplicate them
In [67]: L = [[1, 2],[3, 4]]
In [68]: for i in map(lambda i: 3*i, itertools.chain.from_iterable(L)): print(i)
3
6
9
12

Your code didn't work as expected because in Python the * operator is polymorphic. Thus if x and n are numbers, x*n returns the product of both numbers, but if one of the operands (say n) is an integer and the other is a sequence (.e. x is a list, a tuple or a string) the expression x*n returns the sequence which results of concatenating x with itself n times. Examples:
>>> .1 * 2.5
0.25
>>> [1, 2] * 3
[1, 2, 1, 2, 1, 2]
>>> "beep" * 2
beepbeep
In order to get the expected result you need to flatten out your list before applying map():
>>> your_list = [[1, 2], [3, 4]]
>>> flat_list = [item for sublist in your_list for item in sublist]
>>> flat_list
[1, 2, 3, 4]
>>> triples = map(lambda x: 3*x, flat_list)
>>> triples
[3, 6, 9, 12]
Actually you don't need to use map() and lambda. I think using a one-liner list comprehension would be more pythonic:
>>> triples = [3*item for sublist in your_list for item in sublist]
>>> triples
[3, 6, 9, 12]

Related

Multiply a list by the elements of other list

I have this list of list, with a series of values:
factors = [1,2,3]
values = [[1,2,3],[3,1,4],[5,5,2]]
I want to multiply each list1 in values, but the corresponding element of list1. I am trying with this:
factors = [1,2,3]
values = [[1,2,3],[3,1,4],[5,5,2]]
multiply = []
for i in factors:
multiply = [values[i]*i]
But it does not work. The expected value would be:
[[1, 2, 3], [6, 2, 8], [15, 15, 6]]
Try this:
factors = [1, 2, 3]
values = [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
multiply = []
for idx, lst in enumerate(values):
multiply.append([factors[idx] * x for x in lst])
print(multiply)
For a list comprehension version of the above code, see #Hommes answer
Update: Given more general setup of the problem I changed the comprehension
Solution with list comprehension:
factors = [1, 2, 3]
values = [[1, 2, 3], [3, 1, 4], [5, 5, 2]]
multiply = [[factors[idx] * elem for elem in lst] for idx, lst in enumerate(values)]
Out[39]: [[1, 2, 3], [6, 2, 8], [15, 15, 6]]
There is a few problems with the code as it is:
Python uses zero-indexing
The first element of a list in Python has the index 0. In your for loop:
for i in factor:
multiply = [values[i]*i]
The last iteration will try to access values[3]. But values has only 3 elements, so the last element is values[2], not values[3].
Multiplication * of a list and an integer doesn't actually multiply
Multiplying a list by an Int, say n gives you a new list that concatenates the original n times. For example:
>>> [1, 2, 3] * 3
[1, 2, 3, 1, 2, 3, 1, 2, 3]
The most straightforward way of actually broadcasting the multiplication over the list is to use a 'list comprehension'. For example:
>>> [3*x for x in [1,2,3]]
[3, 6, 9]
Applying this into your example would look something like:
for i in factors:
multiply = [i*x for x in values[i-1]]
You are only keeping the last calculation in multiply
Each go around your for loop you assign a new value to multiply, overwriting whatever was there previously. If you want to collect all your results, then you should append to the multiply list.
multiply.append([i*x for x in values[i-1]])
All together, your example is fixed as:
factors = [1,2,3]
values = [[1,2,3],[3,1,4],[5,5,2]]
multiply = []
for i in factors:
multiply.append([i*x for x in values[i-1]])
Improvements
However, there are still ways to improve the code in terms of concision and readability.
The root of the problem is to multiply a list's elements by a number. * can't do it, but you can write your own function which can:
def multiply_list(X, n):
return [n*x for x in X]
Then you can use this function, and a list comprehension, to remove the for loop:
multiply = [multiply_list(x, i) for (x, i) in zip(values, factors)]
Or, if you think it is readable, then you can just use the nested list comprehension, which is much more concise:
multiply = [[factor * x for x in value] for (value, factor) in zip(values, factors)]
You can use list comprehension for a one liner. Try this
multiply = [[x * y for y in list1] for x in list1]

Using zip_longest to sum different length lists and fill different lengths from start rather than end

I have two lists [1,2,3,4] and [1,2,3]
I would like to sum these to give me the following: [1,3,5,7].
This was done by doing 1+0=1, 2+1=3, 3+2=5 and 4+3=7.
I understand that itertools.zip_longest would do this, but it would fill the mismatch in length with 0 at the end, giving me [2,3,6,4] and not the value I want.
I would like the mismatch in length to be solved by filling the first length with zero.
The built-in reversed() function could be used to do it like this:
from itertools import zip_longest
def sum_lists(*iterables):
iterables = (reversed(it) for it in iterables)
return list(reversed([a+b for a, b in zip_longest(*iterables, fillvalue=0)]))
if __name__ == '__main__':
result = sum_lists([1, 2, 3, 4], [1, 2, 3])
print(result) # -> [1, 3, 5, 7]
result = sum_lists([1, 2, 3], [1, 2, 3, 4])
print(result) # -> [1, 3, 5, 7] # Order of args doesn't matter.
You can pad the second list with zeros and use zip:
s1, s2 = [1,2,3,4], [1, 2, 3]
new_result = [a+b for a, b in zip(s1, ([0]*(len(s1)-len(s2)))+s2)]
Output:
[1, 3, 5, 7]
You could build a shift by using repeat, then concatenate the shift with the shorter one using chain:
from itertools import repeat, chain
first = [1, 2, 3, 4]
second = [1, 2, 3]
shift = repeat(0, abs(len(first) - len(second)))
result = [a + b for a, b in zip(first, chain(shift, second))]
print(result)
Output
[1, 3, 5, 7]
You can use the reversed function to generate the two lists in reverse order so that zip_longest would align the zipping from the other end, and then reverse the result afterwards:
from itertools import zip_longest
lists = [1,2,3,4], [1, 2, 3]
print(list(map(sum, zip_longest(*map(reversed, lists), fillvalue=0)))[::-1])
This outputs:
[1, 3, 5, 7]
You could go around this problem be reversing your lists befor ziping with zip_longest.
from itertools import zip_longest
s1, s2 = [1,2,3,4], [1, 2, 3]
res = [a+b for a, b in zip_longest(reversed(s1), reversed(s2), fillvalue=0)]
and finally, reverse again, to produce the desired result:
res = res[::-1]
print(res) # [1, 3, 5, 7]
The main advantage of this method as #CoryKramer says in his comment is that you do not have to know beforehand which list is the longest.

pythonic way of iterating over variable number of sequences [duplicate]

Say I have list1 = [1,2,3,4] and list2 = [5,6,7,8]. How would I compare the first element, 1, in list1 with the first element, 5, in list2? And 2 with 6, 3 with 7, and so on.
I'm trying to use a for loop for this, but I'm not sure how to do it. I understand that doing for x in list1 just checks an element x to all elements in list1, but I don't know how to do it when comparing two lists the way I described.
You can traverse both lists simultaneously using zip:
for (x, y) in zip(list1, list2): do_something
The 'zip' function gives you [(1,5), (2,6), (3,7), (4,8)], so in loop iteration N you get the Nth element of each list.
The default comparison operators compare lists in lexicographical order. So you can say things like:
>>> [1, 2, 3, 4] < [5, 6, 7, 8]
True
If instead you want to compute the elementwise comparison, you can use map and cmp (or any other operator:
>>> map(cmp, [1, 2, 3, 4], [5, 6, 7, 8])
[-1, -1, -1, -1]
If your result is going to be a new list, then you can use a list comprehension:
new_list = [ some_function(i, j) for i, j in zip(list1, list2) ]
Here's a real example of the above code:
>>> list1 = [1, 2, 3, 4]
>>> list2 = [1, 3, 4, 4]
>>> like_nums = [ i == j for i, j in zip(list1, list2) ]
>>> print like_nums
[True, False, False, True]
This will make a list of bools that show whether items of the same index in two lists are equal to each other.
Furthermore, if you use the zip function, there is a way to unzip the result when you are done operating on it. Here's how:
>>> list1 = [1, 2, 3, 4]
>>> list2 = [1, 3, 4, 4]
>>> new_list = zip(list1, list2) # zip
>>> print new_list
[(1, 1), (2, 3), (3, 4), (4, 4)]
>>> newlist1, newlist2 = zip(*new_list) # unzip
>>> print list(newlist1)
[1, 2, 3, 4]
>>> print list(newlist2)
[1, 3, 4, 5]
This could be useful if you need to modify the original lists, while also comparing the elements of the same index in some way.

function of difference between value

Is there a function in Python to get the difference between two or more values in a list? So, in those two lists:
list1 = [1, 5, 3, 7]
list2 = [4, 2, 6, 4]
I need to calculate the difference between every value in list1 and list2.
for i in list1:
for ii in list2:
print i -ii
This gives negative values, but I want the subtraction between the values of the two lists only from highest value to lowest value for not getting negative values.
For the above lists, I expect the output to be [3, 3, 3, 3].
Thanks.
Assuming you expect [3, 3, 3, 3] as the answer in your question, you can use abs and zip:
[abs(i-j) for i,j in zip(list1, list2)]
Either zip the lists, or use numpy:
>>> list1 = [1, 5, 3, 7]
>>> list2 = [4, 2, 6, 4]
>>> [a-b for a,b in zip(list1, list2)]
[-3, 3, -3, 3]
>>> import numpy as np
>>> np.array(list1) - np.array(list2)
array([-3, 3, -3, 3])
Remember to cast the array back to a list as needed.
edit:
In response to the new requirement that the absolute values are needed: you can add abs in the list comprehension:
>>> [abs(a-b) for a,b in zip(list1, list2)]
[3, 3, 3, 3]
and the numpy solution would change to:
>>> map(abs, np.array(list1) - np.array(list2))
[3, 3, 3, 3]
You may also do if else condition inside list comprehension.
>>> [i-j if i>j else j-i for i,j in zip(list1, list2)]
[3, 3, 3, 3]
You can use zip method in order combine these two lists. See the tutorials for zip method https://docs.python.org/2/library/functions.html#zip
>>> list1 = [1, 5, 3, 7]
>>> list2 = [4, 2, 6, 4]
>>> [abs(x-y) for x, y in zip(list1, list2)]
[3, 3, 3, 3]
Avinash Raj's answer is correct, or alternatively, using map():
from operator import sub
C = map(sub, A, B)

Repeat each item in a list a number of times specified in another list

I have two lists, x and y:
>>> x = [2, 3, 4]
>>> y = [1, 2, 3]
I want to use these to create a new list. The new list will have each element in x repeated the number of times specified by the corresponding element in y. Hence, the desired output is
>>> new_list
[2, 3, 3, 4, 4, 4]
The order of the elements in new_list doesn't matter to me. It's also not crucial that it be a list -- any sequence type is fine.
What is the fastest, most efficient, most Pythonic way to achieve this?
numpy's repeat function gets the job done:
>>> import numpy as np
>>> x = [2, 3, 4]
>>> y = [1, 2, 3]
>>> np.repeat(x, y)
array([2, 3, 3, 4, 4, 4])
You can use list comprehension, like this
>>> x = [2, 3, 4]
>>> y = [1, 2, 3]
>>> [item for item, count in zip(x, y) for i in range(count)]
[2, 3, 3, 4, 4, 4]
Here, we zip the x and y so that the element from x and its corresponding count from y are grouped as a single tuple. Then, we iterate count number of items to produce the same item.
If your objects in x are immutables, then you can create count copies of the same and put them together in a list, like this
>>> [i for item, count in zip(x, y) for i in [item] * count]
[2, 3, 3, 4, 4, 4]
You can do the same lazily, with itertools.repeat, like this
>>> from itertools import chain, repeat
>>> chain.from_iterable((repeat(item, count) for item, count in zip(x,y)))
<itertools.chain object at 0x7fabe40b5320>
>>> list(chain.from_iterable((repeat(item, cnt) for item, cnt in zip(x,y))))
[2, 3, 3, 4, 4, 4]
Please note that the chain returns an iterable, not a list. So, if you don't want all the elements at once, you can get the items one by one from it. This will be highly memory efficient if the count is going to be a very big number, as we don't create the entire list in the memory immediately. We generate the values on-demand.
Thanks ShadowRanger. You can actually apply repeat over x and y and get the result like this
>>> list(chain.from_iterable(map(repeat, x, y)))
[2, 3, 3, 4, 4, 4]
here, map function will apply the values from x and y to repeat one by one. So, the result of map will be
>>> list(map(repeat, x, y))
[repeat(2, 1), repeat(3, 2), repeat(4, 3)]
Now, we use chain.from_iterable to consume values from each and every iterable from the iterable returned by map.
Simple using for loop.
>>> x = [2, 3, 4]
>>> y = [1, 2, 3]
>>> final = []
>>> for index, item in enumerate(y):
final.extend([x[index]]*item)
One way to achieve this is via using .elements() function of collections.Counter() along with zip. For example:
>>> from collections import Counter
>>> x = [2, 3, 4]
>>> y = [1, 2, 3]
# `.elements()` returns an object of `itertool.chain` type, which is an iterator.
# in order to display it's content, here type-casting it to `list`
>>> list(Counter(dict(zip(x,y))).elements())
[2, 3, 3, 4, 4, 4]

Categories

Resources