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]
Related
I have two lists, one containing some unique elements (integers in my case) and the other containing indices that indicate into which sublist of a newly created nested list the elements should be inserted.
elements = [1, 2, 3, 4, 5, 6]
indices = [0, 0, 1, 2, 2, 1]
expected_result = [[1, 2], [3, 6], [4, 5]]
The list of elements contains only unique items, potentially not sorted.
The list of indices is 'normalized' such that the lower indices will always occur first.
The new nested list should use the indices to determine the sublist of the expected result to which the elements shall belong.
I have come up with the following function, but I have a feeling that there should be an easier way.
def indices_to_nested_lists(indices: Sequence[int], elements: Sequence):
result = []
for i in range(max(indices)+1):
sublist = []
for j in range(len(elements)):
if indices[j] == i:
sublist.append(elements[j])
result.append(sublist)
return result
Can anyone think of an easier, maybe more pythonic way of achieving the same result?
Try using this for loop with zip:
l = [[] for i in range(max(indices) + 1)]
for x, y in zip(elements, indices):
l[y].append(x)
print(l)
Output:
[[1, 2], [3, 6], [4, 5]]
A = [[1, 2, 4],
[3, 5, 6],
[7,8,9]]
def sumof(A,3):
We need to find the lowest sum of 3 elements
here 1 + 2 + 3 = 6 so output is (1,2,3)
Can we do by using zip
one basic solution, convert nested list into flat list, sort it,slice sorted list and sum:
A = [[1, 2, 4],
[3, 5, 6],
[7,8,9]]
def sumof(A, n):
# convert nested list into flat list
flat_list = [item for sublist in A for item in sublist]
return sum(sorted(flat_list)[:n])
print (sumof(A,3))
If you have a large array, you can do this without sorting the list,
which is a little faster like
from operator import add
from functools import reduce
A = [[1, 2, 4],
[3, 5, 6],
[7,8,9]]
addlists = lambda l: reduce(add, l)
list_A = addlists(A)
result = [list_A.pop(list_A.index(min(list_A))) for _ in range(3)]
It's a little more complicated, though the modules imported are really useful.
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]
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]
I'm trying to add all the elements of two unequal nested lists in parallel and append the result back to another new list, i've written a little hacky code that could add them but there's a lot of things wrong with the code, first i tried to make the pairs equal by appending 0's to the end of the list but the code still runs into the problems since the length of the first pair is 3 and the length of the second pair is 4, i also tried using map but i couldn't add an integer and a NoneType,
import pdb
import itertools
x = [[2,3,3], [5,0,3]]
y = [[0,3], [2,3,3,3]]
for idx, (a, b) in enumerate(itertools.zip_longest(x, y)):
while len(a) < len(b):
x[idx].append(0)
while len(b) < len(a):
y[idx].append(0)
print(x, y)
new_list = list()
for i in zip(x, y):
for idx, j in enumerate(i):
for ind, a in enumerate(j):
val = x[idx][ind] + y[idx][ind]
new_list.append(val)
print(new_list)
the final result should be like this
[2, 6, 3, 7, 3, 6, 3]
You can simply use itertools.zip_longest and fill-in with 0, like this
>>> from itertools import zip_longest as zip
>>> x = [[2, 3, 3], [5, 0, 3]]
>>> y = [[0, 3], [2, 3, 3, 3]]
>>> [k + l for i, j in zip(x, y, fillvalue=[0]) for k, l in zip(i, j, fillvalue=0)]
[2, 6, 3, 7, 3, 6, 3]
This would would work even if x and y have unequal number of elements,
>>> from itertools import zip_longest as zip
>>> x = [[2, 3, 3], [5, 0, 3], [1]]
>>> y = [[0, 3], [2, 3, 3, 3]]
>>> [k + l for i, j in zip(x, y, fillvalue=[0]) for k, l in zip(i, j, fillvalue=0)]
[2, 6, 3, 7, 3, 6, 3, 1]
Note that, when we zip x and y, we use [0] as fillvalue. And when we zip i and j we use 0 as the fillvalue.
So, if the number of lists in x and y are not equal, then [0] will be used fill-in and when the number of elements in i and j are not equal, 0 will be used as the fill-in.
You can use fillvalue=0 in izip_longest to get ride of checking for validations then use map function for sum the zipped items:
from itertools import chain,zip_longest
list(chain.from_iterable(map(sum,zip_longest(i,j,fillvalue=0)) for i,j in zip_longest(x, y)))
[2, 6, 3, 7, 3, 6, 3]
Note that if you want to iterate over the result you don't have to use list (its just for demonstrating the result).
zip_longest is very helpful here:
x = [[2,3,3], [5,0,3]]
y = [[0,3], [2,3,3,3]]
from itertools import zip_longest
res = []
for list1, list2 in zip_longest(x, y, fillvalue=[0]):
for value1, value2 in zip_longest(list1, list2, fillvalue=0):
res.append(value1 + value2)
The fill value pads the list or sublist with the given value. In our case a new list with [0] for the outer loop and 0 for the inner loop.
Writing this a nested list comprehension does the same but may take more time to read and understand the code. Using more lines can make reading and understanding faster. Of course, this depends very much on the person reading the code.
from itertools import zip_longest
new_list = [a + b
for listpair in zip(x, y)
for a, b in zip_longest(*listpair, fillvalue=0)]