Slicing flat list into multi-level nested list efficiently - python

For example, I have a flat list
[1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D', 'E', 'F', 'G']
I want to transform it into 4-deep list
[[[[1, 2], [3, 4]], [[5, 6], [7, 8]]], [[[9, 'A'], ['B', 'C']], [['D', 'E'] ['F', 'G']]]]
Is there a way to do it without creating a separate variable for every level? What is the most memory- and performance-efficient way?
UPDATE:
Also, is there a way to do it in a non-symmetrical fashion?
[[[[1, 2, 3], 4], [[5, 6, 7], 8]]], [[[9, 'A', 'B'], 'C']], [['D', 'E', 'F'], 'G']]]]

Note that your first list has 15 elements instead of 16. Also, what should A be? Is it a constant you've defined somewhere else? I'll just assume it's a string : 'A'.
If you work with np.arrays, you could simply reshape your array:
import numpy as np
r = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D', 'E', 'F', 'G'])
r.reshape(2,2,2,2)
It outputs:
array([[[['1', '2'],
['3', '4']],
[['5', '6'],
['7', '8']]]
[[['9', 'A'],
['B', 'C']],
[['D', 'E'],
['F', 'G']]]
dtype='<U11')
This should be really efficient because numpy doesn't change the underlying data format. It's still a flat array, displayed differently.
Numpy doesn't support irregular shapes. You'll have to work with standard python lists then:
i = iter([1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D', 'E', 'F', 'G'])
l1 = []
for _ in range(2):
l2 = []
for _ in range(2):
l3 = []
l4 = []
for _ in range(3):
l4.append(next(i))
l3.append(l4)
l3.append(next(i))
l2.append(l3)
l1.append(l2)
print(l1)
# [[[[1, 2, 3], 4], [[5, 6, 7], 8]], [[[9, 'A', 'B'], 'C'], [['D', 'E', 'F'], 'G']]]
As you said, you'll have to define a temporary variable for each level. I guess you could use list comprehensions, but they wouldn't be pretty.

Related

Possible sets of different sub-list items with one element of each sub-list

I am looking for a way to obtain combinations of single elements of all sub-lists contained in a list without knowing in advance the length of the list and the sub-lists. Let me illustrate what I mean via two examples below. I have two lists (myList1 and myList2) and would like to obtain the two combination sets (setsCombo1 and setsCombo1):
myList1 = [['a'], [1, 2, 3], ['X', 'Y']]
setsCombo1 = [['a', 1, 'X'],
['a', 1, 'Y'],
['a', 2, 'X'],
['a', 2, 'Y'],
['a', 3, 'X'],
['a', 3, 'Y']]
myList2 = [['a'], [1, 2, 3], ['X', 'Y'], [8, 9]]
setsCombo2 = [['a', 1, 'X', 8],
['a', 1, 'X', 9],
['a', 1, 'Y', 8],
['a', 1, 'Y', 9],
['a', 2, 'X', 8],
['a', 2, 'X', 9],
['a', 2, 'Y', 8],
['a', 2, 'Y', 9],
['a', 3, 'X', 8],
['a', 3, 'X', 9],
['a', 3, 'Y', 8],
['a', 3, 'Y', 9]]
I looked a bit into itertools but couldn't really find anything quickly that is appropriate...
itertools.product with unpacking * (almost) does that:
>>> from itertools import product
>>> list(product(*myList1))
[('a', 1, 'X'),
('a', 1, 'Y'),
('a', 2, 'X'),
('a', 2, 'Y'),
('a', 3, 'X'),
('a', 3, 'Y')]
To cast the inner elements to lists, we map:
>>> list(map(list, product(*myList1)))
[['a', 1, 'X'],
['a', 1, 'Y'],
['a', 2, 'X'],
['a', 2, 'Y'],
['a', 3, 'X'],
['a', 3, 'Y']]

Python: how to replicate the same row of a matrix?

How can I copy each row of an array n times?
So if I have a 2x3 array, and I copy each row 3 times, I will have a 6x3 array. For example, I need to convert A to B below:
A = np.array([[1, 2, 3],
[4, 5, 6]])
B = np.array([[1, 2, 3],
[1, 2, 3],
[1, 2, 3],
[4, 5, 6],
[4, 5, 6],
[4, 5, 6]])
If possible, I would like to avoid a for loop.
If I read correctly, this is probably what you want assuming you started with mat:
transformed = np.concatenate([np.vstack([mat[:, i]] * 3).T for i in range(mat.shape[1])], axis=1)
Here's a verifiable example:
# mocking a starting array
import string
mat = np.random.choice(list(string.ascii_lowercase), size=(5,3))
>>> mat
array([['s', 'r', 'e'],
['g', 'v', 'c'],
['i', 'b', 'd'],
['f', 'g', 's'],
['o', 'm', 'w']], dtype='<U1')
Transform it:
# this repeats it 3 times for sake of displaying
transformed = np.concatenate([np.vstack([mat[i, :]] * 3).T for i in range(mat.shape[0])], axis=1).T
>>> transformed
array([['s', 'r', 'e'],
['s', 'r', 'e'],
['s', 'r', 'e'],
['g', 'v', 'c'],
['g', 'v', 'c'],
['g', 'v', 'c'],
['i', 'b', 'd'],
['i', 'b', 'd'],
['i', 'b', 'd'],
['f', 'g', 's'],
['f', 'g', 's'],
['f', 'g', 's'],
['o', 'm', 'w'],
['o', 'm', 'w'],
['o', 'm', 'w']], dtype='<U1')
The idea of this is to use vstack to concatenate each column to itself multiple time, and then concatenate the result of that to get the final array.
You can use np.repeat with integer positional indexing:
B = A[np.repeat(np.arange(A.shape[0]), 3)]
array([[1, 2, 3],
[1, 2, 3],
[1, 2, 3],
[4, 5, 6],
[4, 5, 6],
[4, 5, 6]])
v1=[3,2]
v3=v1[:]*10
print(v3)
np.repeat is exactly what you are looking for. You can use the axis option to specify that you want to duplicate rows.
B = np.repeat(A, 3, axis=0)

How to generate transpose-like matrix without using any built-in function and without using loops?

Is not exactly like a matrix transpose. I'm using python and trying using matrix transformations but I can't without loops, I'm using numpy, is there any solution just using matrix operations or vectorized functions?.
For example:
To this
Looks like you want to rotate this 180 degrees then transpose. How about:
x = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
>>> array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
np.rot90(x, 2).T
>>> array([[9, 6, 3],
[8, 5, 2],
[7, 4, 1]])
Here are is a way that only uses indexing:
>>> import numpy as np
>>> a = np.array(['abcdefghi']).view('U1').reshape(3, 3)
>>> a
array([['a', 'b', 'c'],
['d', 'e', 'f'],
['g', 'h', 'i']], dtype='<U1')
>>>
>>> a[[2,1,0],[[2],[1],[0]]]
array([['i', 'f', 'c'],
['h', 'e', 'b'],
['g', 'd', 'a']], dtype='<U1')
If you do not want to hardcode the indices you'll have to use some kind of builtin. Either Python builtins:
>>> a[list(reversed(range(3))), list(zip(reversed(range(3))))]
array([['i', 'f', 'c'],
['h', 'e', 'b'],
['g', 'd', 'a']], dtype='<U1')
or numpy
>>> a[np.ogrid[2:-1:-1,2:-1:-1][::-1]]
array([['i', 'f', 'c'],
['h', 'e', 'b'],
['g', 'd', 'a']], dtype='<U1')
Note that all these methods do a non-lazy transpose, meaning that the resulting array is C contiguous.

Appending to a list of lists sequentially

I have two list of lists:
my_list = [[1,2,3,4], [5,6,7,8]]
my_list2 = [['a', 'b', 'c'], ['d', 'e', 'f']]
I want my output to look like this:
my_list = [[1,2,3,4,'a','b','c'], [5,6,7,8,'d','e','f']]
I wrote the following code to do this but I end up getting more lists in my result.
my_list = map(list, (zip(my_list, my_list2)))
this produces the result as:
[[[1, 2, 3, 4], ['a', 'b', 'c']], [[5, 6, 7, 8], ['d', 'e', 'f']]]
Is there a way that I can remove the redundant lists.
Thanks
Using zip is the right approach. You just need to add the elements from the tuples zip produces.
>>> my_list = [[1,2,3,4], [5,6,7,8]]
>>> my_list2 = [['a', 'b', 'c'], ['d', 'e', 'f']]
>>> [x+y for x,y in zip(my_list, my_list2)]
[[1, 2, 3, 4, 'a', 'b', 'c'], [5, 6, 7, 8, 'd', 'e', 'f']]
You can use zip in a list comprehension:
my_list = [[1,2,3,4], [5,6,7,8]]
my_list2 = [['a', 'b', 'c'], ['d', 'e', 'f']]
new_list = [i+b for i, b in zip(my_list, my_list2)]
As an alternative you may also use map with sum and lambda function to achieve this (but list comprehension approach as mentioned in other answer is better):
>>> map(lambda x: sum(x, []), zip(my_list, my_list2))
[[1, 2, 3, 4, 'a', 'b', 'c'], [5, 6, 7, 8, 'd', 'e', 'f']]

Organize list of lists in Python

Let's say a have a list of lists in Python:
list_of_values = [[a, b, c], [d, e, f], [g, h, i], [j, k, l]]
And I want to convert automatically to independent lists like:
list1 = [[a, b, c],[d + g + j, e + h + k, f + i + l]]
list2 = [[d, e, f], [g + j, h + k, i + l]]
list3 = [[g, h, i], [j, k, l]]
Let's say I have a list of lists of integers in Python:
list_of_values = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
And I want to convert automatically to independent lists like:
list1 = [[1, 2, 3],[4 + 7 + 10, 5 + 8 + 11, 6 + 9 + l2]]
list2 = [[4, 5, 6], [7 + 10, 8 + 11, 9 + 12]]
list3 = [[7, 8, 9], [10, 11, l2]]
Performing the math:
list1 = [[1, 2, 3], [21, 24, 27]]
list2 = [[4, 5, 6], [17, 19, 21]]
list3 = [[7, 8, 9], [10, 11, l2]]
For your updated Question:
Suppose you have "list of lists of strings" like below:
s = [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i'], ['j', 'k', 'l']]
Then you can use: join to concatenate:
>>> for i in range(len(s)):
... [s[i], map(lambda t: ''.join(t), zip(*s[i + 1:]))]
...
[['a', 'b', 'c'], ['dgj', 'ehk', 'fil']]
[['d', 'e', 'f'], ['gj', 'hk', 'il']]
[['g', 'h', 'i'], ['j', 'k', 'l']]
[['j', 'k', 'l'], []]
If you don't need last line in output then just use range argument less then one of length:
>>> for i in range(len(s)-1):
... [s[i], map(''.join, zip(*s[i + 1:]))] # remove lambda function
...
[['a', 'b', 'c'], ['dgj', 'ehk', 'fil']]
[['d', 'e', 'f'], ['gj', 'hk', 'il']]
[['g', 'h', 'i'], ['j', 'k', 'l']]
But suppose if you have "list of lists of numbers" e.g.:
l = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
Then you can use sum function:
>>> for i in range(len(l) - 1):
... [l[i], map(sum, zip(*l[i + 1:]))]
...
[[1, 2, 3], [21, 24, 27]]
[[4, 5, 6], [17, 19, 21]]
[[7, 8, 9], [10, 11, 12]]
Edit:..
If you wants to make single function for both strings and number then you canmake use of add() operator from operator library.
Check add() function:
>>> from operator import add
>>> add(1, 2)
3
>>> add('1', '2') # this is like + works
'12'
Now, using it make a new my_add() that add all elements in a sequence, check following codes:
>>> def my_add(t):
... return reduce(add, t)
...
>>> my_add(('a', 'b'))
'ab'
>>> my_add((2, 1))
3
Now, write a function using my_add() function that will so your work:
def do_my_work(s):
for i in range(len(s)-1):
print [s[i], map(my_add, zip(*s[i + 1:]))]
Now, see how this works for you:
>>> s
[['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i'], ['j', 'k', 'l']]
>>> do_my_work(s)
[['a', 'b', 'c'], ['dgj', 'ehk', 'fil']]
[['d', 'e', 'f'], ['gj', 'hk', 'il']]
[['g', 'h', 'i'], ['j', 'k', 'l']]
>>> l
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
>>> do_my_work(l) # so same function for str and int both!
[[1, 2, 3], [21, 24, 27]]
[[4, 5, 6], [17, 19, 21]]
[[7, 8, 9], [10, 11, 12]]
>>> import itertools
>>> lst = [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i'], ['j', 'k', 'l']]
>>> for i, item in enumerate(lst):
print [item, itertools.chain.from_iterable(lst[i+1:])]
[['a', 'b', 'c'], ['d', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l']]
[['d', 'e', 'f'], ['g', 'h', 'i', 'j', 'k', 'l']]
[['g', 'h', 'i'], ['j', 'k', 'l']]
[['j', 'k', 'l'], []]
for i in range(len(list_of_values) - 1):
print [list_of_values[i]] + [map(list, zip(*list_of_values[i+1:]))]
Output
[['a', 'b', 'c'], [['d', 'g', 'j'], ['e', 'h', 'k'], ['f', 'i', 'l']]]
[['d', 'e', 'f'], [['g', 'j'], ['h', 'k'], ['i', 'l']]]
[['g', 'h', 'i'], [['j'], ['k'], ['l']]]
For the numbers, you can simply do
list_of_values = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
for i in range(len(list_of_values) - 1):
print [list_of_values[i]] + [map(sum, zip(*list_of_values[i+1:]))]
Output
[[1, 2, 3], [21, 24, 27]]
[[4, 5, 6], [17, 19, 21]]
[[7, 8, 9], [10, 11, 12]]

Categories

Resources