Related
I have simple tensor
a = tf.constant([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]])
and want to slice it, but I need to do it differently for each of the rows. This slice operation is described by another tensor
b = tf.constant([[0, 1], [2, 4], [2, 5]])
It means that from the first row of tensor a I need elements from 0 to 1, from the second row from 2 to 4 and so on. So the final final result will be
[
[1],
[8, 9],
[13, 14, 15]
]
My first idea was to fill ranges between begin and end of a slice, but unfortunately, doing it with map_fn is not possible because result's rows have different lengths.
Does anyone know how to do such operation?
Basically we have two arrays to iterate. One with actual data, other with range to return.
Therefore, zip function can help iterate over elements from multiple arrays one by one.
import tensorflow as tf
a = tf.constant([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]])
b = tf.constant([[0, 1], [2, 4], [2, 5]])
# As you iterate, provided a and b have same length
# [1, 2, 3, 4, 5] sliced as [0:1]
# [6, 7, 8, 9, 10] sliced as [2:4]
# [11, 12, 13, 14, 15] sliced as [2:5]
[data.numpy().tolist()[start:end] for data, (start, end) in zip(a,b)]
Output:
[[1], [8, 9], [13, 14, 15]]
If the size of b is known at graph compile time, then you can slice each row separately.
import tensorflow as tf
a = tf.constant([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]])
b = tf.constant([[0, 1], [2, 4], [2, 5]])
r = []
for i in range(3):
bi = b[i]
r.append(a[i][bi[0]: bi[1]])
print(r)
I have a list of arrays, where each array is a list of lists. I want to turn this into a single array with all the columns. I've tried using for loops to get this done, but it feels like it should be doable in list comprehension. Is there a nice one-liner that will do this?
Example Input: [[[1,2],[3,4],[5,6]],[[7,8],[9,10],[11,12]]]
Desired Output: [[1,2,7,8],[3,4,9,10],[5,6,11,12]]
Note: Example only has two arrays in the main list, but my actual data has much more, so I'm looking for something that works for N subarrays.
Edit:
Example trying to solve this
Works for two but doesn't generalize:
[input[0][i]+input[1][i] for i in range(len(input[0]))]
These don't work, but show the idea:
[[element for table in input for element in row] for row in table]
[[*imput[j][i] for j in range(len(input))] for i in range(len(input[0]))]
Edit: Selected answer that uses only list comprehension and zip, but all answers (as of now) work, so use whichever fits your style/use case best.
You can generalize this from the standard list flattening pattern and zip:
>>> L = [[[1,2],[3,4],[5,6]],[[7,8],[9,10],[11,12]]]
>>> list([y for z in x for y in z] for x in zip(*L))
[[1, 2, 7, 8], [3, 4, 9, 10], [5, 6, 11, 12]]
>>> L = [[[1,2],[3,4],[5,6]],[[7,8],[9,10],[11,12]],[[13,14],[15,16],[17,18]]]
>>> list([y for z in x for y in z] for x in zip(*L))
[[1, 2, 7, 8, 13, 14], [3, 4, 9, 10, 15, 16], [5, 6, 11, 12, 17, 18]]
Here is one way of doing it:
initial = [[[1,2],[3,4],[5,6]],[[7,8],[9,10],[11,12]]]
output = [a+b for a, b in zip(*initial)]
print(output)
If you have more lists, this also works:
import itertools
initial = [[[1,2],[3,4],[5,6]],[[7,8],[9,10],[11,12]],[[13,14],[15,16],[17,18]]]
output = [list(itertools.chain.from_iterable(values)) for values in zip(*initial)]
print(output)
If you don't mind it is a tuple in the list.You could also try:
from itertools import chain
a = [[[1, 2], [3, 4], [5, 6]], [[7, 8], [9, 10], [11, 12]], [[13, 14], [15, 16], [17, 18]]]
output = list(map(list, map(chain.from_iterable, zip(*a))))
# [[1, 2, 7, 8, 13, 14], [3, 4, 9, 10, 15, 16], [5, 6, 11, 12, 17, 18]]
This would do it, I named your input first:
[*map(lambda x: list(i for s in x for i in s), zip(*first))]
[[1, 2, 7, 8], [3, 4, 9, 10], [5, 6, 11, 12]]
The 2D list should be like this:
matrix = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]
]
I am unable to find the logic for this. I have done this using numpy's reshape function. But unable to do without numpy.
Here is one simple way:
res = [list(range(i, i+4)) for i in range(1, 14, 4)]
print(res)
[[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]]
You can also wrap in a generic function:
def square(n):
return [list(range(i, i+n)) for i in range(1, n**2, n)]
res = square(4)
Explanation
The syntax for range construction is range(start, end, [step]). step is optional; if it is not specified, it is assumed to be 1.
The first part range(i, i+n) creates a range object from i to i+n-1, inclusive.
The second part range(1, n**2, n) iterates in steps of n to n*n, not including the final term. Since end is non-inclusive, squaring n provides the desired cap.
This is one approach
l = range(1, 17) #Create a list using range
print([l[i:i+4] for i in range(0, len(l), 4)]) #Divide the list into 4 equal chunks
Output:
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
I like this one too:
>> [[i+4*j for i in range(1,5)] for j in range(4)]
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
Use a list-comprehension like this with tuple unpacking:
>>> [[*range(i, i+4)] for i in range(1, 14, 4)]
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
or you could use the grouper from the itertools recipes to split a given list (or iterable) into an 2d matrix. this is something along those lines:
def to2d(items, width):
return list(list(i) for i in zip(*(iter(items_1d),) * width))
items_1d = list(range(1, 17)) # or just items_1d = range(1, 17)
print(to2d(items=items_1d, width=4))
I have the following code for a list of lists with the intention of creating a matrix of numbers:
grid=[[1,2,3,4,5,6,7],[8,9,10,11,12],[13,14,15,16,17],[18,19,20,21,22]]
On using the following code which i figured out would reverse the list, it produces a matrix ...
for i in reversed(grid):
print(i)
The output is:
[18, 19, 20, 21, 22]
[13, 14, 15, 16, 17]
[8, 9, 10, 11, 12]
[1, 2, 3, 4, 5, 6, 7]
I want however, the output to be as below, so that the numbers "connect" as they go up:
[22,21,20,19,18]
[13,14,15,16,17]
[12,11,10,9,8]
[1,2,3,4,5,6,7]
Also, for an upvote, I'd be interested in more efficient ways of generating the matrix in the first place. For instance, to generate a 7x7 array - can it be done using a variable, for instance 7, or 49. Or for a 10x10 matrix, 10, or 100?
UPDATE:
Yes, sorry - the sublists should all be of the same size. Typo above
UPDATE BASED ON ANSWER BELOW
These two lines:
>>> grid=[[1,2,3,4,5,6,7],[8,9,10,11,12],[13,14,15,16,17],[18,18,20,21,22]]
>>> [lst[::-1] for lst in grid[::-1]]
produce the following output:
[[22, 21, 20, 18, 18], [17, 16, 15, 14, 13], [12, 11, 10, 9, 8], [7, 6, 5, 4, 3, 2, 1]]
but I want them to print one line after the other, like a matrix ....also, so I can check the output is as I specified. That's all I need essentially, for the answer to be the answer!
You need to reverse the list and also the sub-lists:
[lst[::-1] for lst in grid[::-1]]
Note that lst[::-1] reverses the list via list slicing, see here.
You can visualize the resulting nested lists across multiples lines with pprint:
>>> from pprint import pprint
>>> pprint([lst[::-1] for lst in grid[::-1]])
[[22, 21, 20, 19, 18],
[17, 16, 15, 14, 13],
[12, 11, 10, 9, 8],
[7, 6, 5, 4, 3, 2, 1]]
usually 2D matrices are created, manipulated with numpy
then index slicing can reorder rows, columns
import numpy as np
def SnakeMatrx(n):
Sq, Sq.shape = np.arange(n * n), (n, n) # Sq matrix filled with a range
Sq[1::2,:] = Sq[1::2,::-1] # reverse odd row's columns
return Sq[::-1,:] + 1 # reverse order of rows, add 1 to every entry
SnakeMatrx(5)
Out[33]:
array([[21, 22, 23, 24, 25],
[20, 19, 18, 17, 16],
[11, 12, 13, 14, 15],
[10, 9, 8, 7, 6],
[ 1, 2, 3, 4, 5]])
SnakeMatrx(4)
Out[34]:
array([[16, 15, 14, 13],
[ 9, 10, 11, 12],
[ 8, 7, 6, 5],
[ 1, 2, 3, 4]])
if you really want a list of lists:
SnakeMatrx(4).tolist()
Out[39]: [[16, 15, 14, 13], [9, 10, 11, 12], [8, 7, 6, 5], [1, 2, 3, 4]]
numpy is popular but not a official Standard Library in Python distributions
of course it can be done with list manipulation
def SnakeLoL(n):
Sq = [[1 + i + n * j for i in range(n)] for j in range(n)] # Sq LoL filled with a range
for row in Sq[1::2]:
row.reverse() # reverse odd row's columns
return Sq[::-1][:] # reverse order of rows
# or maybe more Pythonic for return Sq[::-1][:]
# Sq.reverse() # reverse order of rows
# return Sq
SnakeLoL(4)
Out[91]: [[16, 15, 14, 13], [9, 10, 11, 12], [8, 7, 6, 5], [1, 2, 3, 4]]
SnakeLoL(5)
Out[92]:
[[21, 22, 23, 24, 25],
[20, 19, 18, 17, 16],
[11, 12, 13, 14, 15],
[10, 9, 8, 7, 6],
[1, 2, 3, 4, 5]]
print(*SnakeLoL(4), sep='\n')
[16, 15, 14, 13]
[9, 10, 11, 12]
[8, 7, 6, 5]
[1, 2, 3, 4]
Simple way of python:
list(map(lambda i: print(i), [lst[::-1] for lst in grid[::-1]]))
If I have a square matrix as a nested list in python I can split it up into several equal sized boxes and create a new list where each element is a list of the numbers in one of those boxes. E.g.
a = [[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15 ,16]]
b = [[a[0][0], a[0][1], a[1][0], a[1][1]],
[a[0][2], a[0][3], a[1][2], a[1][3]],
[a[2][0], a[2][1], a[3][0], a[3][1]],
[a[2][2], a[2][3], a[3][2], a[3][3]]]
Is there an easier way to do this? Is there a way to set this up as a function which I can apply to matrices of different sizes and also specify the size of the boxes?
The following is equivalent to what you have and a bit more concise:
b = [a[0][:2] + a[1][:2],
a[0][2:] + a[1][2:],
a[2][:2] + a[3][:2],
a[2][2:] + a[3][2:]]
Or an equivalent list comprehension:
b = [a[i][s] + a[i+1][s] for i in (0,2) for s in (slice(None,2), slice(2,None))]
Using NumPy:
In [31]: import numpy as np
In [32]: a = np.arange(1,17).reshape(4,4)
In [33]: a
Out[33]:
array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15, 16]])
In [34]: b = a.reshape(-1,2,2,2).swapaxes(1,2).reshape(4,4)
In [35]: b
Out[35]:
array([[ 1, 2, 5, 6],
[ 3, 4, 7, 8],
[ 9, 10, 13, 14],
[11, 12, 15, 16]])