I am looking for an idiomatic way to combine an n-dimensional vector (given as a list) with a list of offsets, that shall be applied to every dimensions.
I.e.: Given I have the following values and offsets:
v = [5, 6]
o = [-1, 2, 3]
I want to obtain the following list:
n = [[4, 5], [7, 5], [8, 5], [4, 8], [7, 8], [8, 8], [4, 9], [7, 9], [8, 9]]
originating from:
n = [[5-1, 6-1], [5+2, 6-1], [5+3, 6-1], [5-1, 6+2], [5+2, 6+2], [5+3, 6+2], [5-1, 6+3], [5+2, 6+3], [5+3, 6+3]]
Performance is not an issue here and the order of the resulting list also does not matter. Any suggestions on how this can be produced without ugly nested for loops? I guess itertools provides the tools for a solution, but I did not figure it out yet.
from itertools import product
[map(sum, zip(*[v, y])) for y in product(o, repeat=2)]
or, as falsetru and Dobi suggested in comments:
[map(sum, zip(v, y)) for y in product(o, repeat=len(v))]
itertools.product() gives you the desired combinations of o. Use that with a list comprehension to create n:
from itertools import product
n = [[v[0] + x, v[1] + y] for x, y in product(o, repeat=2)]
Demo:
>>> [[v[0] + x, v[1] + y] for x, y in product(o, repeat=2)]
[[4, 5], [4, 8], [4, 9], [7, 5], [7, 8], [7, 9], [8, 5], [8, 8], [8, 9]]
Use itertools.product:
>>> import itertools
>>>
>>> v = [5, 6]
>>> o = [-1, 2, 3]
>>>
>>> x, y = v
>>> [[x+dx, y+dy] for dx, dy in itertools.product(o, repeat=2)]
[[4, 5], [4, 8], [4, 9], [7, 5], [7, 8], [7, 9], [8, 5], [8, 8], [8, 9]]
originating from:
[[5-1, 6-1], [5-1, 6+2], [5-1, 6+3], [5+2, 6-1], [5+2, 6+2], [5+2, 6+3], [5+3, 6-1], [5+3, 6+2], [5+3, 6+3]]
You may use itertools module to get all permutations.
Related
I'm writing an algorithm that rotates a square matrix 90ยบ degrees in-place, without using a second matrix. It works, but I've got one small problem that is troubling me.
So the basic, working algorithm is:
def rotate(matrix):
n = len(matrix)
# reverse rows
matrix.reverse()
# reflect
start = 0
for row in range(n):
for col in range(start, n):
matrix[row][col], matrix[col][row] = matrix[col][row], matrix[row][col]
start = start + 1
The idea is to pass a matrix defined as a list of lists, like [[1, 2, 3], [4, 5, 6], [7, 8, 9]].
Example input/output:
>>> some_matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> rotate(some_matrix)
>>> print(some_matrix)
[[7, 4, 1], [8, 5, 2], [9, 6, 3]]
Great. So, I was wondering if I could replace matrix.reverse() with something maybe a little more intuitive, like simply using slice indexing. So I wrote a new rotate, like this:
def rotate2(matrix):
n = len(matrix)
# reverse rows
matrix = matrix[::-1]
# reflect
start = 0
for row in range(n):
for col in range(start, n):
matrix[row][col], matrix[col][row] = matrix[col][row], matrix[row][col]
start = start + 1
Which SHOULD give me the same results, based on:
>>> a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> b = a
>>> b = b[::-1]
>>> a.reverse()
>>> print(a)
[[7, 8, 9], [4, 5, 6], [1, 2, 3]]
>>> print(b)
[[7, 8, 9], [4, 5, 6], [1, 2, 3]]
>>> print(a==b)
True
However, when I use rotate2 on the same input/output example, I get:
>>> some_matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> rotate2(some_matrix)
>>> print(some_matrix)
[[9, 6, 3], [8, 5, 2], [7, 4, 1]]
So what am I missing here?
matrix = matrix[::-1] creates a new list and assigns it to the local name matrix; it does not modify the original list in-place.
matrix.reverse(), on the other hand, does modify the original list.
Consider these simple functions:
def r1(m):
m = m[::-1]
def r2(m):
m.reverse()
>>> x = [[1,2], [3,4]]
>>> r1()
>>> x
[[1,2], [3,4]]
>>> r2()
>>> x
[[3,4],[1,2]]
I have two arrays as the code shows below (A and B). I would like to take as output C = [[1, 2, 3, 11], [4, 5, 6, 12], [7, 8, 9, 13]]
I'm trying to, but the only thing I could reach was this:
A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
B = [11, 12, 13]
C = [[x, y] for x, y in zip(A, B)]
print(C)
# Output: [[[1, 2, 3], 11], [[4, 5, 6], 12], [[7, 8, 9], 13]]
You just need to make an array of a single element (y) and then add the two lists together
[x + [y] for x, y in zip(A, B)]
If it doesn't need to be a new list and modifying A suffices:
for a, b in zip(A, B):
a.append(b)
It's more efficient, at least for longer inner lists.
You didn't have the same level of nesting in both A and B, which is why you got a weird answer. A is a list of lists, while B is just a list.
Solutions
If you want to add inplace:
A.append(B)
# Output: A = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
Otherwise:
C = A + [B]
which leaves A and B untouched and creates a new joined list.
Timings
Conclusion: for this problem it really doesn't matter which way you do it, as the timings are about the same for each case. Some ways are more readable though.
Code for timing:
import timeit
A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
B = [10, 11, 12]
def append_arrs(arrs=(A, B)):
a, b = arrs
a.append(b)
return a
def plus_arrs(arrs=(A, B)):
return arrs[0] + [arrs[1]]
def loopadd_arrs(arrs=(A, B)):
return [x + [y] for x, y in zip(*arrs)]
def listadd_arrs(arrs=(A, B)):
a, b = arrs
return list((*a, b))
func_list = ["append_arrs", "plus_arrs", "loopadd_arrs", "listadd_arrs"]
for func in func_list:
t = timeit.timeit(stmt=f'{func}', setup=f'from __main__ import {func}')
print(f"Time for {func}: {t}")
Possible with numpy
import numpy as np
A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
B = np.array([[11, 12, 13]]).T
C = np.append(A,B,axis=1)
Possibly you will have to transpose C according to the shape you want.
Is there a way to do this without using a regular for loop to iterate through the main list?
>>> map(lambda x: x*2, [[1,2,3],[4,5,6]])
[[1, 2, 3, 1, 2, 3], [4, 5, 6, 4, 5, 6]]
# want [[2,4,6],[8,10,12]]
You have nested lists, and x represents just one of the lists. To process that, you need to actually map the multiplication function on to the individual elements of x, like this
>>> map(lambda x: map(lambda y: y * 2, x), [[1, 2, 3], [4, 5, 6]])
[[2, 4, 6], [8, 10, 12]]
But I would prefer list comprehension over this,
>>> [[y * 2 for y in x] for x in [[1, 2, 3], [4, 5, 6]]]
[[2, 4, 6], [8, 10, 12]]
Alternative solution would be to go for numpy vectorized operations:
import numpy as np
ll = [[1,2,3],[4,5,6]]
(2*np.array(ll)).tolist()
#Out[6]: [[2, 4, 6], [8, 10, 12]]
This is a bit overkill and not too practical for this particular example, but another stylistic option could be to use functools.partial to make it very clear what is happening and a combination of map and a list comprehension.
from functools import partial
from operator import mul
l = [[1, 2, 3], [4, 5, 6]]
double = partial(mul, 2)
dub_l = [map(double, sub) for sub in l]
How would you combine sublists within a list by pairs?
For example with:
list1 = [[1,2,3],[4,5],[6],[7,8],[9,10]]
the result would be:
[[1,2,3,4,5],[6,7,8],[9,10]]
You could use zip_longest with a fill value (in case your list has an odd number of sublists) to zip an iterator over list1. Running a list comprehension over the zip generator object allows you to concatenate the consecutive pairs of lists:
>>> from itertools import zip_longest # izip_longest in Python 2.x
>>> x = iter(list1)
>>> [a+b for a, b in zip_longest(x, x, fillvalue=[])]
[[1, 2, 3, 4, 5], [6, 7, 8], [9, 10]]
Try using a list comprehension (but be careful with the indexes!). It works for lists with an even or odd number of sublists:
list1 = [[1, 2, 3], [4, 5], [6], [7, 8], [9, 10]]
n = len(list1)
[list1[i] + (list1[i+1] if i+1 < n else []) for i in xrange(0, n, 2)]
=> [[1, 2, 3, 4, 5], [6, 7, 8], [9, 10]]
list1=[[1,2,3],[4,5],[6],[7,8],[9,10]]
length = len(list1)
new_list = [ list1[i]+list1[i+1] if i+1 < length
else [list1[i]] for i in range(0,length,2) ]
print(new_list)
>>> list1=[[1,2,3],[4,5],[6],[7,8],[9,10]]
>>> list1
[[1, 2, 3], [4, 5], [6], [7, 8], [9, 10]]
Now we can do:
>>> test = [list1[0]+list1[1]]+[list1[2]+list1[3]]+list1[4]
>>> test
[[1, 2, 3, 4, 5], [6, 7, 8], 9, 10]
>>>
I am sure there is a better way, but this is the way I can think of!
list1 = [[1, 2, 3], [4, 5], [6], [7, 8], [9, 10]]
from itertools import islice, chain
print([list(chain.from_iterable(islice(list1, i, i + 2)))
for i in range(0, len(list1), 2)])
[[1, 2, 3, 4, 5], [6, 7, 8], [9, 10]]
Or without islice:
print([list(chain.from_iterable(list1[i:i+2]))
for i in range(0, len(list1), 2)])
[[1, 2, 3, 4, 5], [6, 7, 8], [9, 10]]
Use a simple loop:
list1=[[1,2,3],[4,5],[6],[7,8],[9,10]]
newlist = []
for i in range(0, len(list1), 2):
newlist.append(list1[i] + list1[i+1])
if len(list1) % 2 > 0:
newlist.append(list1[-1])
print newlist
Here is (I hope) a correct solution:
def pair_up(ls):
new_list = []
every_other1 = ls[::2]
every_other2 = ls[1::2]
for i in range(len(every_other2)):
new_list.append(every_other1[i]+every_other2[i])
if len(ls) % 2 == 1:
new_list.append(ls[-1])
return new_list
Working on same list with removing n-ths[-1] odd sublists:
for i in range(len(l)/2):#here we go only to last even item
l[i]+=l[i+1]#adding odd sublist to even sublist
l.pop(i+1)#removing even sublist
I have three lists of lists, and I'm trying to write a generator function to help me package up values in the same index.
So my lists:
list1 = [[1, 2, 3], [2, 3, 4],...]
list2 = [[4, 5, 6], [5, 6, 7],...]
list3 = [[8, 9, 10], [9, 10, 11],...]
My desired output:
result1 = [[1, 4, 8], [2, 5, 9],...]
result2 = [[2, 5, 9], [3, 6, 10],...]
result3 = [[3, 6, 10], [4, 7, 11],...]
My attempt:
def bundle(x, y, z, index):
for row in x, y, z:
for item in row[index]:
yield list(item)
I keep getting float is not iterable errors. If i modify it slightly:
def bundle(x, y, z, index):
for row in x, y, z:
for item in row:
yield item[index]
I get the values I want as one large sequence, but I would prefer to keep them grouped in a nested style.
If you're dealing with large lists, a custom, fully lazy approach would be the following:
import itertools as it
def bundle(lists, index):
return ([b[index] for b in blocks] for blocks in it.izip(*lists))
print list(bundle([[[1, 2, 3], [2, 3, 4]],
[[4, 5, 6], [5, 6, 7]],
[[8, 9, 10], [9, 10, 11]]],
0))
# => [[1, 4, 8], [2, 5, 9]]
One way to do this is by a repeated application of zip():
res1, res2, res3 = zip(*(zip(*x) for x in zip(list1, list2, list3)))
This uses zip(list1, list2, list3) to create a sequence of matrices, zip(*x) to transpose each of this matrices, and a final zip() to unpack to the three resulting sequences. (I don't think this approach is very efficient.)
If you're dealing with numeric values then you could use numpy's transposition method to achieve what you want:
import numpy
numpy.array([list1,list2, list3]).T