Python permutation as list comprehension - python

I've got a list of strings and a permutation. I'm trying to apply the permutation to the list, but I'm trying to keep my code clean and concise. At the moment I have a working solution, and it looks like this:
mylist = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
permutation = [5,2,6,3,7,9,1,4,8]
mynewlist = ['']*9
for i in range(9):
mynewlist[permutation[i]-1] = mylist[i]
print mynewlist
What I don't like about it is that I have to initialize the list to an empty list first, and then loop through it in an odd manner. I was just wondering if someone could come up with a cleaner way to write this, perhaps using a list comprehension? Or by applying a map?
For reference purposes - the result of the above is:
['g', 'b', 'd', 'h', 'a', 'c', 'e', 'i', 'f']

Your algorithm, but cleaner:
mynewlist = mylist[:]
for pos, elem in zip(permutation, mylist):
mynewlist[pos - 1] = elem

You could use sorted():
sorted(mylist, key=lambda v, i=iter(permutation): next(i))
This outputs:
>>> sorted(mylist, key=lambda v, i=iter(permutation): next(i))
['g', 'b', 'd', 'h', 'a', 'c', 'e', 'i', 'f']
This sorts the input mylist according to the indices taken from the permutation list; the key function is called for each element in the input sequence in order, once, before sorting.
This does not compare that favorably with your version though; you have a O(n) algorithm, this takes O(n lg n).

Just a crazy thought, but how about...
>>> mylist = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
>>> permutation = [5,2,6,3,7,9,1,4,8]
>>> {permutation[i]: v for i, v in enumerate(mylist)}.values()
['g', 'b', 'd', 'h', 'a', 'c', 'e', 'i', 'f']
...exploiting the fact that dictionaries are sorted by hash values, and integers hash to their own value.
I think this is an O(n) algorithm.

Here is my attempt:
mynewlist = [list_item for p, list_item in sorted(zip(permutation, mylist))]
The result is:
['g', 'b', 'd', 'h', 'a', 'c', 'e', 'i', 'f']
Discussion
Let's start from the right. The zip() function couples the two lists together:
>>> zip(permutation, mylist)
[(5, 'a'), (2, 'b'), (6, 'c'), (3, 'd'), (7, 'e'), (9, 'f'), (1, 'g'), (4, 'h'), (8, 'i')]
We can then sort the result of the zip() function, which will produce a list of tuples sorted by permutation index:
>>> sorted(zip(permutation, mylist))
[(1, 'g'), (2, 'b'), (3, 'd'), (4, 'h'), (5, 'a'), (6, 'c'), (7, 'e'), (8, 'i'), (9, 'f')]
This is almost what we want: the characters are in order. The next step is to eliminate the permutation index, leaving just the characters. That's the final form I presented above.

Related

How to generate random binary tree

I am given a list of labels L and I wish to recursively generate a random binary tree from L.
The desired behavior is like this:
generate(['A', 'B', 'C', 'D', 'E', 'F'])
could give:
((('A', ('B', 'C')), ('D', 'E')), 'F')
Note that the list of leaf labels from left to right should equal L.
I am in doubt how to randomly construct the tree. This is what I have so far (I split the list of labels at a random index.
def generate_tree(L):
split = randint(1, len(L)-1)
left = L[:split]
right = L[split:]
# call generate(left) and generate(right) based on some conditions
I am stuck. I would be grateful for a couple of hints or help.
You weren' too far off. All you needed was a base case and building the resulting tuple from the results of the recursive calls:
def generate_tree(L):
# base case
if len(L) == 1:
return L[0]
split = randint(1, len(L)-1)
left = L[:split]
right = L[split:]
# recursion
return (generate_tree(left), generate_tree(right))
>>> generate_tree(['A', 'B', 'C', 'D', 'E', 'F'])
(('A', 'B'), (('C', 'D'), ('E', 'F')))
>>> generate_tree(['A', 'B', 'C', 'D', 'E', 'F'])
((('A', 'B'), 'C'), (('D', 'E'), 'F'))
>>> generate_tree(['A', 'B', 'C', 'D', 'E', 'F'])
('A', (('B', 'C'), (('D', 'E'), 'F')))
And if you are code golfing and looking for a fancy (>=3.8 only) one-liner:
def gt(L):
return (gt(L[:(s:=randint(1, len(L)-1))]), gt(L[s:])) if len(L) > 1 else L[0]
You can randomly slice the list and then recursively apply the tree construction:
import random
def r_tree(d):
_l, _r = tuple(d[:(_n:=random.randint(0, len(d)-1))]), tuple(d[_n+1:])
l, r = _l if len(_l) < 3 else r_tree(_l), _r if len(_r) < 3 else r_tree(_r)
return (d[_n], n[0] if len(n:=tuple(filter(None, [l, r]))) == 1 else n)
print(r_tree(['A', 'B', 'C', 'D', 'E', 'F']))
Output:
('D', (('A', ('B', 'C')), ('E', 'F')))

How to return ordened array names?

I have an array I want to sort from low to high, but I want it to return the array names (G, F, H...) instead of just the numbers. How do I do this?
A=12.74087388
B=12.48817861
C=12.31249807
D=12.95688859
E=12.49693343
F=11.51090636
G=10.16505019
H=11.99872655
Array=np.array([A,B,C,D,E,F,G,H])
sort=np.sort(Array)
Use a dictionary:
d = dict(A=12.74087388,
B=12.48817861,
C=12.31249807,
D=12.95688859,
E=12.49693343,
F=11.51090636,
G=10.16505019,
H=11.99872655)
and sort by value:
>>> sorted(d, key=d.get)
['G', 'F', 'H', 'C', 'B', 'E', 'A', 'D']
or keep the numbers and sort by value:
from operator import itemgetter
print(sorted(d.items(), key=itemgetter(1)))
Output:
[('G', 10.16505019),
('F', 11.51090636),
('H', 11.99872655),
('C', 12.31249807),
('B', 12.48817861),
('E', 12.49693343),
('A', 12.74087388),
('D', 12.95688859)]
As #jonrsharpe pointed out, the variable name isn't a property of those values, so you'll have to attach it differently. I think the easiest way is to go about it like this:
my_array = [[12.74087388, 'A'], [12.48817861, 'B'], etc]
my_sorted_array = sorted(my_array)
my_sorted_named_array = [i[1] for i in my_sorted_array]
This is a dictionary-based solution, as suggested in the comments.
lst = [12.74087388, 12.48817861, 12.31249807, 12.95688859,
12.49693343, 11.51090636, 10.16505019, 11.99872655]
d = dict(zip(list('ABCDEFGH'), lst))
sorted_names = list(zip(*sorted(d.items(), key=lambda x: x[1])))[0]
# ('G', 'F', 'H', 'C', 'B', 'E', 'A', 'D')
I suppose you prefer a list of tuples instead of a dictionary in case you have duplicated letters.
data = [
('A', 12.74087388),
('B', 12.48817861),
('C', 12.31249807),
('D', 12.95688859),
('E', 12.49693343),
('F', 11.51090636),
('G', 10.16505019),
('H', 11.99872655)
]
dt = np.dtype([('letter', np.unicode_, 1), ('num', np.float64)])
arr = np.array(data, dtype=dt)
arr.sort(order='num')

Zip lists of tuples

I'm doing some stuff with data from files, and I have already zipped every column with its info, but now i want to combine info from other files (where i have zipped the info too) and i don't know how to unzip and get it together.
EDIT:
I have a couple of zip objects:
l1 = [('a', 'b'), ('c', 'd')] # list(zippedl1)
l2 = [('e', 'f'), ('g', 'h')] # list(zippedl1)
l3 = [('i', 'j'), ('k', 'm')] # list(zippedl1)
and i want to unzip like:
unzipped = [('a', 'c', 'e', 'g', 'i', 'k'), ('b', 'd', 'f', 'h', 'j', 'm')]
I wouldn't like to transform the zipped structures to a list, just for memory reasons. I searched and i didn't find something that helps me. Hope you can help me please!.
[sorry about my bad english]
I believe you want to zip an unpacked chain:
# Leaving these as zip objects as per your edit
l1 = zip(('a', 'c'), ('b', 'd'))
l2 = zip(('e', 'g'), ('f', 'h'))
l3 = zip(('i', 'k'), ('j', 'm'))
unzipped = [('a', 'c', 'e', 'g', 'i', 'k'), ('b', 'd', 'f', 'h', 'j', 'm')]
You can simply do
from itertools import chain
result = list(zip(*chain(l1, l2, l3)))
# You can also skip list creation if all you need to do is iterate over result:
# for x in zip(chain(l1, l2, l3)):
# print(x)
print(result)
print(result == unzipped)
This prints:
[('a', 'c', 'e', 'g', 'i', 'k'), ('b', 'd', 'f', 'h', 'j', 'm')]
True
You need to concatenate the lists first:
>>> l1 = [('a', 'b'), ('c', 'd')]
>>> l2 = [('e', 'f'), ('g', 'h')]
>>> l3 = [('i', 'j'), ('k', 'm')]
>>> zip(*(l1 + l2 + l3))
[('a', 'c', 'e', 'g', 'i', 'k'), ('b', 'd', 'f', 'h', 'j', 'm')]

Equally distribute a list in python

Suppose I have the following list in python:
a = ['a','b','c','d','e','f','g','h','i','j']
How do I distribute the list like this:
['a','f']
['b','g']
['c','h']
['d','i']
['e','j']
And how do I achieve this if I have a list of unequal length and putting the 'superfluous' items into a separate list?
I want to be able to distribute the elements of the original list into n parts in the indicated manner.
So if n=3 that would be:
['a','d','g']
['b','e','h']
['c','f','i']
and the 'superfluous' element in a separate list
['j']
You can use zip with a list comprehension:
def distribute(seq):
n = len(seq)//2 #Will work in both Python 2 and 3
return [list(x) for x in zip(seq[:n], seq[n:])]
print distribute(['a','b','c','d','e','f','g','h','i','j'])
#[['a', 'f'], ['b', 'g'], ['c', 'h'], ['d', 'i'], ['e', 'j']]
Not exceedingly elegant, but here goes:
In [5]: a = ['a','b','c','d','e','f','g','h','i','j']
In [6]: [[a[i], a[len(a)//2+i]] for i in range(len(a)//2)]
Out[6]: [['a', 'f'], ['b', 'g'], ['c', 'h'], ['d', 'i'], ['e', 'j']]
If you're happy with a list of tuples, you could use zip():
In [7]: zip(a[:len(a)//2], a[len(a)//2:])
Out[7]: [('a', 'f'), ('b', 'g'), ('c', 'h'), ('d', 'i'), ('e', 'j')]
To convert this into a list of lists:
In [8]: map(list, zip(a[:len(a)//2], a[len(a)//2:]))
Out[8]: [['a', 'f'], ['b', 'g'], ['c', 'h'], ['d', 'i'], ['e', 'j']]

Trying to turn inner and out tuples into inner and outer lists

Ok, so I've got information in the form of
(('A', 'B', 'C'), ('D', 'E', 'F'), ('H', 'I', 'J'))
and I would like to convert this to
[['A', 'B', 'C'], ['D', 'E', 'F'], ['H', 'I', 'J']]
What is the best/easiest way to do this?
List comprehension:
tpl = (('A', 'B', 'C'), ('D', 'E', 'F'), ('H', 'I', 'J'))
lst = [list(x) for x in tpl]
a = (('A', 'B', 'C'), ('D', 'E', 'F'), ('H', 'I', 'J'))
print map(list, a)
prints
[['A', 'B', 'C'], ['D', 'E', 'F'], ['H', 'I', 'J']]
>>> data = (('A', 'B', 'C'), ('D', 'E', 'F'), ('H', 'I', 'J'))
>>> [list(tup) for tup in data]
[['A', 'B', 'C'], ['D', 'E', 'F'], ['H', 'I', 'J']]
Here is a simple recursive solution for any number of nested tuples:
>>> tup = (('A', ('B', 'C')), ('D', 'E', 'F', ('H', 'I', 'J')))
>>> listify = lambda x: map(listify, x) if isinstance(x, tuple) else x
>>> listify(tup)
[['A', ['B', 'C']], ['D', 'E', 'F', ['H', 'I', 'J']]]
For Python 3 replace map(listify, x) with list(map(listify, x)).
If you know the structure is only two levels, try:
x = (('A', 'B', 'C'), ('D', 'E', 'F'), ('H', 'I', 'J'))
y = [ list(t) for t in x ]
If there might be deeper nesting, you'll want recursion -- see F.J's answer.

Categories