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
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')))
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')
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')]
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']]
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.