Transpose a list - python

Suppose i have a nested list m = [[2, 3], [4, 7]] and I want to transpose it such that I get [[2, 4], [3, 7]] OR if m can be [[1,2,3],[4,5,6],[7,8,9]] then after passing through the transpose function i should get [[1,4,7],[2,5,8],[3,6,9]]
It should also work for [[1,2,3],[4,5,6]] which returns [[1,4],[2,5],[5,6]]
def transpose(m):
t = make_copy(m) ##[[2, 3], [4, 7]]
if len(t) == 1: ## if [[1]]
return t ##[[1]]
i = 0
while i <= len(t): #0 <= 2
t[0][i] = t[i][0] #t[0][0] = t[0]t[0] ## t[0][1] = t[1][0] so now 3 = 4
i += 1 #1
return t
I thought this should work but it didn't give me the answer I want. Whats wrong ?

Use zip function
print zip(*[[2, 3], [4, 7]])
# [(2, 4), (3, 7)]
So, your transpose function becomes like this
def transpose(m):
return zip(*m)
assert transpose([[2, 3], [4, 7]]) == [(2, 4), (3, 7)]
assert transpose([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) == [
(1, 4, 7), (2, 5, 8), (3, 6, 9)]
Remember, it just returns a list of tuples. They can be used almost interchangeably, except for the facts that the syntaxes for creating them is different and tuples are immutable.
If you really want to get only list of lists, you have throw in a comprehension or a map function, like this
def transpose(m):
return [list(item) in zip(*m)]
# return map(list, zip(*m))
assert transpose([[2, 3], [4, 7]]) == [[2, 4], [3, 7]]
assert transpose([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) == [
[1, 4, 7], [2, 5, 8], [3, 6, 9]]
assert transpose([[1, 2, 3], [4, 5, 6]]) == [[1, 4], [2, 5], [3, 6]]

Related

How to get all elements at a specific depth-level in a nested list?

I am looking for a method to get all the elements nested at a user-defined list depth level e.g.:
lst = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
# example 1
level = 1 # user defined level
output = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
# example 2
level = 2
output = [[1, 2], [3, 4], [5, 6], [7, 8]]
# example 3
level = 3
output = [1, 2, 3, 4, 5, 6, 7, 8]
You can just use a recursive algorithm, for example:
output = []
def extract(lists, d):
if d == 1:
return output.extend(lists)
for sub_list in lists:
extract(sub_list, d - 1)
For level 1:
extract(lst, 1)
print(output)
>>> [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
For level 2:
extract(lst, 2)
print(output)
>>> [[1, 2], [3, 4], [5, 6], [7, 8]]
For level 3
extract(lst, 3)
print(output)
>>> [1, 2, 3, 4, 5, 6, 7, 8]
You can use chain.from_iterable to go down one level every time:
from itertools import chain
def get_at_level(lst, level):
for _ in range(level-1):
lst = chain.from_iterable(lst)
return list(lst)
Examples:
>>> lst = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
>>> get_at_level(lst, 1)
[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
>>> get_at_level(lst, 2)
[[1, 2], [3, 4], [5, 6], [7, 8]]
>>> get_at_level(lst, 3)
[1, 2, 3, 4, 5, 6, 7, 8]
Please NOTE, that the function returns only a shallow copy of the original list. So assuming you call with any level that is not the lowest - you will have the same references to sub-lists from the original. This means that modifying the returned list might* change the original! If you don't care about the original list, that's fine. If you don't want it to be changed, create a deep copy of it in the first line of the function.
* Changing the first level of the returned will not be a problem because as explained, list returns a shallow copy. BUT, doing something like get_at_level(lst, 2)[0][0] = 0 will also affect the original.
A recursive function like func as below would work.
It is basically the same as marcos' answer.
func accepts a list as its first argument and depth as the second.
from functools import reduce
lst = [[[1,2],[3,4]],[[5,6],[7,8]]]
func = lambda x, d: x if d == 1 else func(reduce(lambda a,b: a+b, x), d-1)
func(lst, 3) # output [1,2,3,4,5,6,7,8]
This isn't a very clean solution for the problem, but it works!
lst = [[[1,2],[3,4]],[[5,6],[7,8]]]
def function(lst, level):
if level == 1:
print(lst)
elif level == 2:
l2 = []
for x in lst:
for i in x:
l2.append(i)
print(l2)
elif level == 3:
l3 = []
for x in lst:
for i in x:
for a in i:
l3.append(a)
print(l3)
else:
print("Invalid depth level")
function(lst, 1) #level 1, 2 or 3
The problem here is that it isn't dynamic.
You can use a recursive generator function:
def flatten(d, l=1):
for i in d:
yield from ([i] if l == 1 else flatten(i, l-1))
lst = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
print(list(flatten(lst, 3)))
print(list(flatten(lst, 2)))
print(list(flatten(lst, 1)))
Output:
[1, 2, 3, 4, 5, 6, 7, 8]
[[1, 2], [3, 4], [5, 6], [7, 8]]
[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]

Generalising different for-loop method in Python

I have this following code where I am trying to form an array 'opt'. Here, I am taking three possible values of 'pos_set' = [1, 2, 3] and in a similar manner, I can extend this. But, I just want a generalized code for any possible integer value of pos_set.
opt = []
if pos_set == 1:
for j in range(1, n):
opt.append([j])
elif pos_set == 2:
for j in range(1, n):
for k in range(j+1, n):
opt.append([j, k])
elif pos_set == 3:
for j in range(1, n):
for k in range(j+1, n):
for l in range(k+1, n):
opt.append([j, k, l])
For more clarity, I am doing this with the objective of collecting all possibilities if you roll an n-sided die and keep doing this as long as you keep rolling larger values.
For instance, if you roll a sequence 1-2-6-4, in this case after getting a 4 following a larger no. 6, you stop rolling. Similarly, if you roll a sequence 1-2-6-6, in this case you get a repeated 6 so you stop because it is not larger than your previous roll. I am considering cases before the smaller or same number occurs i.e., [1, 2, 6] in both cases.
If you guys can help me, I would be grateful.
You can use the following recursive function:
def f(p, n, i=1):
if p == 0:
return [[]]
return [[j, *l] for j in range(i, n) for l in f(p - 1, n, j + 1)]
so that:
print(f(1, 7))
print(f(2, 7))
print(f(3, 7))
outputs:
[[1], [2], [3], [4], [5], [6]]
[[1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [2, 3], [2, 4], [2, 5], [2, 6], [3, 4], [3, 5], [3, 6], [4, 5], [4, 6], [5, 6]]
[[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 2, 6], [1, 3, 4], [1, 3, 5], [1, 3, 6], [1, 4, 5], [1, 4, 6], [1, 5, 6], [2, 3, 4], [2, 3, 5], [2, 3, 6], [2, 4, 5], [2, 4, 6], [2, 5, 6], [3, 4, 5], [3, 4, 6], [3, 5, 6], [4, 5, 6]]
Was not sure by how you went about your question were you just concerned for constructing your qpos_set, or did you need help with the code to produce the actual output that will meet your objective.
Here is a code I wrote that will roll n- sided die (I just set it at 100 for demonstration) but this will continue to roll another die and append its value until that number is equal or less and then the previous roll.
I'm going to hold off on breaking down the code unless you needed this portion as well. Let me know!
import random
die = list(range(1, 100))
temp = [0, 1, 2]
winners = []
while temp[1] > temp[0]:
temp[0] = random.randint(1, len(die))
temp[1] = random.randint(1, len(die))
temp[0] = temp[2]
if temp[1] > temp[0]:
winners.append(temp[1])
temp[2] = temp[1]
else:
winners.append(temp[1])
break
print(winners)
There is a built-in solution for this, itertools.combinations:
In [5]: import itertools
In [6]: list(itertools.combinations(range(1,7), 3))
Out[6]:
[(1, 2, 3),
(1, 2, 4),
(1, 2, 5),
(1, 2, 6),
(1, 3, 4),
(1, 3, 5),
(1, 3, 6),
(1, 4, 5),
(1, 4, 6),
(1, 5, 6),
(2, 3, 4),
(2, 3, 5),
(2, 3, 6),
(2, 4, 5),
(2, 4, 6),
(2, 5, 6),
(3, 4, 5),
(3, 4, 6),
(3, 5, 6),
(4, 5, 6)]

How to create a list from tuples with sub-tuples?

I have two lists a and b. Then I create all combinations taking all two-length combinations of a plus one each of b:
import itertools as it
a = [1,2,3,4]
b = [5,6]
for i in it.product(it.combinations(a, 2), b):
print (i)
# output:
((1, 2), 5)
((1, 2), 6)
((1, 3), 5)
...
# expected output:
[1, 2, 5]
[1, 2, 6]
[1, 3, 5]
...
How can the tuples be transformed at the stage of the loop operation into lists?
The following comprehensions will work:
>>> [[*x, y] for x, y in it.product(it.combinations(a, 2), b)] # Py3
>>> [list(x) + [y] for x, y in it.product(it.combinations(a, 2), b)] # all Py versions
[[1, 2, 5],
[1, 2, 6],
[1, 3, 5],
[1, 3, 6],
[1, 4, 5],
[1, 4, 6],
[2, 3, 5],
[2, 3, 6],
[2, 4, 5],
[2, 4, 6],
[3, 4, 5],
[3, 4, 6]]
Simplified approach:
a = [1,2,3,4]
b = [5,6]
l = len(a)
print(sorted([a[i], a[i_n], j] for i in range(l) for j in b
for i_n in range(i+1, l) if i < l-1))
The output:
[[1, 2, 5], [1, 2, 6], [1, 3, 5], [1, 3, 6], [1, 4, 5], [1, 4, 6], [2, 3, 5], [2, 3, 6], [2, 4, 5], [2, 4, 6], [3, 4, 5], [3, 4, 6]]

Python, functional programming, mapping to a higher level

Does anybody know how to map in Python easily a function to a higher level in a nested list, i.e. the equivalent to Map[f, expr, levelspec] in Mathematica.
You can trivially roll your own
def map_level(f, item, level):
if level == 0:
return f(item)
else:
return [map_level(f, i, level - 1) for i in item]
>>> double = lambda x: x * 2
>>> data = [[1, 2, 3], [4, 5, 6]]
>>> map_level(double, data, 0)
[[1, 2, 3], [4, 5, 6], [1, 2, 3], [4, 5, 6]]
>>> map_level(double, data, 1)
[[1, 2, 3, 1, 2, 3], [4, 5, 6, 4, 5, 6]]
>>> map_level(double, data, 2)
[[2, 4, 6], [8, 10, 12]]

Zip as a list comprehension

I have a fairly last list of data like this:
data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
I'm trying to zip it so that that I get something like this:
zipped_data = [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
I know I could do that with
l = [(data[0]), (data[1]), (data[2])]
zipped_data = zip(*l)
But I would like to write a list comprehension to do that for any number of items in data. I tried this, but it didn't work.
s = [zip(i) for i in data]
s
[[(1,), (2,), (3,)], [(4,), (5,), (6,)], [(7,), (8,), (9,)]]
Can anyone identify where I've gone wrong here? Thanks.
Try the *:
In [2]: lis=[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
In [3]: zip(*lis)
Out[3]: [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
If you really want to rewrite zip as a list comprehension, then this is how I would do it:
In [25]: data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
In [26]: [tuple(lis[j] for lis in data) for j in range(min(len(l) for l in data))]
Out[26]: [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
In [27]: data = [[1, 2, 3], [4, 5, 6], [7, 8]]
In [28]: [tuple(lis[j] for lis in data) for j in range(min(len(l) for l in data))]
Out[28]: [(1, 4, 7), (2, 5, 8)]
Though, zip(*data) is definitely a better way to go about this
I would do it with zip but here is done with list comprehension
def zip_lists(lists):
"""
Assuming all lists have the same length
>>> zip_lists([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
>>> zip_lists([[1, 2], [3, 4], [5, 6], [7, 8]])
[[1, 3, 5, 7], [2, 4, 6, 8]]
"""
return [[l[x] for l in lists] for x in range(len(lists[0]))]

Categories

Resources