Sorting a list of multi-dimensional points - python

I have a list of D-dimensional points (where D is a constant), and I would like to sort them first based on the 1st dimension values, then by the 2nd dimesion values and so on, so if 2 points have the same values at the first x dimensions, they would be sorted based on the values of dimension x+1.
I know that if my number of dimension is final, I could use this solution: https://stackoverflow.com/a/37111840
But since I have D dimensions where D is a constant number in the code, I'm not sure how to define the sorting "key" values well.

As #iz_ points out, this is how python sorting works by default. Here is an example illustrating this point:
import itertools
import random
# generate all length 3 tuples of 0s 1s and 2s
foo = list(itertools.product(*([range(3)]*3)))
#mix them all up
random.shuffle(foo)
print(foo)
# this sorts by the first, then the second, then the last
foo.sort()
print(foo)
[(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 1, 0), (0, 1, 1), (0, 1, 2), (0, 2, 0), (0, 2, 1), (0, 2, 2), (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 1, 0), (1, 1, 1), (1, 1, 2), (1, 2, 0), (1, 2, 1), (1, 2, 2), (2, 0, 0), (2, 0, 1), (2, 0, 2), (2, 1, 0), (2, 1, 1), (2, 1, 2), (2, 2, 0), (2, 2, 1), (2, 2, 2)]

example list l1 with three dimensions:
l1 = [[0, 3, 0], [0, 0, 3], [1, 0, 3], [2, 2, 1], [2, 1, 1], [2, 1, 0], [1, 0, 0], [2, 0, 0], [1, 1, 2], [1, 2, 3]]
l2 = sorted(l1, key = lambda k: k[::-1])
print(l2)
[[1, 0, 0], [2, 0, 0], [2, 1, 0], [0, 3, 0], [2, 1, 1], [2, 2, 1], [1, 1, 2], [0, 0, 3], [1, 0, 3], [1, 2, 3]]
I'm not sure whether that's exactly the sort order that you wanted, but it keys on k values of arbitrary length (as long as all elements of l1 are the same length).

Related

how to make pythonic Nested List

I'd like to make nested list
given_list = [[0, 1, 2], [0, 1, 2], [0, 1, 2]] # each element : range(0, n), num of element : m
new_list = [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 1, 0], ..., [2, 2, 2]] # total num : n^m
How do I make it?
I tried to overlap the for statement m times, but I don't think it's pythonic.
Looks like you are trying to compute the product of the lists in given_list:
> from itertools import product
> list(product(*given_list))
[(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 1, 0), (0, 1, 1), (0, 1, 2), (0, 2, 0), (0, 2, 1), (0, 2, 2), (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 1, 0), (1, 1, 1), (1, 1, 2), (1, 2, 0), (1, 2, 1), (1, 2, 2), (2, 0, 0), (2, 0, 1), (2, 0, 2), (2, 1, 0), (2, 1, 1), (2, 1, 2), (2, 2, 0), (2, 2, 1), (2, 2, 2)]
If you really need a list of lists, rather than a list of tuples, you'll have to call list on each element.
[list(t) for t in product(*given_list)]

Get 26 nearest neighbors of a point in 3D space - vectorized

Say you have a point in 3D space with coordinate (2,2,2). How can you vectorize the operation with either numpy (I was thinking of using meshgrid, I just have not been able to get it to work) or scipy to find the 26 nearest neighbors in 3D space? There are 26 neighbors because I am considering the point as a cube, and thus the neighbors would be the 6 neighbors along the cube faces + 8 neighbors along the cube corners +12 neighbors connected to cube edges.
So for point (2,2,2), how can I get the following coordinates:
(1, 1, 1), (1, 1, 2), (1, 1, 3), (1, 2, 1), (1, 2, 2), (1, 2, 3), (1, 3, 1), (1, 3, 2), (1, 3, 3), (2, 1, 1), (2, 1, 2), (2, 1, 3), (2, 2, 1), (2, 2, 3), (2, 3, 1), (2, 3, 2), (2, 3, 3), (3, 1, 1), (3, 1, 2), (3, 1, 3), (3, 2, 1), (3, 2, 2), (3, 2, 3), (3, 3, 1), (3, 3, 2), (3, 3, 3)
I have already implemented this with a triple for loop, which works. However, speed is critical for my system and thus I need to vectorize this operation in order for my system not to fail. The triple for loop is as follows:
neighbors = [] # initialize the empty neighbor list
# Find the 26 neighboring voxels' coordinates
for i in [-1, 0, 1]: # i coordinate
for j in [-1, 0, 1]: # j coordinate
for k in [-1, 0, 1]: # k coordinate
if (i ==0 and j ==0 and k ==0): # if at the same point
pass # skip the current point
else:
neighbors.append((self.current_point[0]+i,self.current_point[1]+j,self.current_point[2]+k)) # add the neighbor to the neighbors list
This is my first post to StackOverflow, so apologies if I missed anything. This is for a path planning algorithm I wish to put on a drone, and thus time is critical so that I don't hit a wall or something.
You could create a motion directions in easy way using itertools.product:
from itertools import product
motion = np.array(list(product(m, repeat=3)))
motion
>>> array([[-1, -1, -1],
[-1, -1, 0],
[-1, -1, 1],
[-1, 0, -1],
[-1, 0, 0],
[-1, 0, 1],
[-1, 1, -1],
[-1, 1, 0],
[-1, 1, 1],
[ 0, -1, -1],
[ 0, -1, 0],
[ 0, -1, 1],
[ 0, 0, -1],
[ 0, 0, 0],
[ 0, 0, 1],
[ 0, 1, -1],
[ 0, 1, 0],
[ 0, 1, 1],
[ 1, -1, -1],
[ 1, -1, 0],
[ 1, -1, 1],
[ 1, 0, -1],
[ 1, 0, 0],
[ 1, 0, 1],
[ 1, 1, -1],
[ 1, 1, 0],
[ 1, 1, 1]])
This is quite easy but slower than pure numpy:
m = [-1, 0, 1]
motion = np.stack(np.meshgrid(m, m, m), axis=-1).reshape(-1, 3)
or:
motion = np.transpose(np.indices((3,3,3)) - 1).reshape(-1, 3)
Then remove origin (0, 0, 0) (it's in the middle):
motion = np.delete(motion, 13, axis=0)
And then you're able to capture all the surrounding points:
motion + [[2, 2, 2]]
>>> array([[1, 1, 1],
[1, 1, 2],
[1, 1, 3],
[1, 2, 1],
[1, 2, 2],
[1, 2, 3],
[1, 3, 1],
[1, 3, 2],
[1, 3, 3],
[2, 1, 1],
[2, 1, 2],
[2, 1, 3],
[2, 2, 1],
[2, 2, 3],
[2, 3, 1],
[2, 3, 2],
[2, 3, 3],
[3, 1, 1],
[3, 1, 2],
[3, 1, 3],
[3, 2, 1],
[3, 2, 2],
[3, 2, 3],
[3, 3, 1],
[3, 3, 2],
[3, 3, 3]])
If you already have the coordinates that you are comparing against as a numpy array, say it is x, then you can calculate the euclidean distance between (2, 2, 2) and x with
distances = np.power(x - (2, 2, 2), 2).sum(axis=1)
Now you just want the indices of the 26 smallest, which you can do with np.argpartition
indices = np.argpartition(distances, 26)[:26]
Now, to get the actual elements:
elements = x[indices, :]
As an example, if x is just the set of points that you provided, then
distances = np.power(x - (2, 2, 2), 2).sum(axis=1)
indices = np.argpartition(distances, 2)[:2]
elements = x[indices, :]
This returns
array([[1, 2, 2],
[2, 1, 2]])
As expected.
Note that this highly rated answer makes the claim that this function argpartition runs in linear time, as opposed to nlog(n) time that a regular sort would take to run.

How to create a list of indices from a 2D list?

If I have a 2 dimensional list, how can I generate a list of indices for a specific element in that 2D list?
For example, if I have the list
two_d_list = [[0, 1, 0, 0], [1, 1, 0, 0], [0, 0, 0, 1]]
How could I make a list with this format
index_list = [[0, 1], [1, 0], [1, 1], [2, 3]]
which is the list of 2 dimensional indexes of all 1s in two_d_list.
The format of
index_list = [(0, 1), (1, 0), (1, 1), (2, 3)]
would also work. I just need to retrieve the indices.
With list comprehension:
>>> [(r, c) for r, line in enumerate(two_d_list) for c, num in enumerate(line) if num==1]
[(0, 1), (1, 0), (1, 1), (2, 3)]
two_d_list = [[0, 1, 0, 0], [1, 1, 0, 0], [0, 0, 0, 1]]
result = []
for i in range(len(two_d_list)):
for j in range(len(two_d_list[i])):
if two_d_list[i][j] == 1:
result.append((i, j))
print(result)
Result:
[(0, 1), (1, 0), (1, 1), (2, 3)]
Working with list of lists can be cumbersome and quite slow. If you are able to use numpy arrays in your application, the solution becomes quite easy and fast.
import numpy as np
two_d_list = [[0, 1, 0, 0], [1, 1, 0, 0], [0, 0, 0, 1]]
# Create a numpy array
arr = np.array(two_d_list)
# np.where implements exactly the functionality you are after
# then you stack the two lists of indices and transpose
indices = np.stack(np.where(arr == 1)).T
print(indices)

assembling lists with common central element

I'm creating all the possible permutations composed of three elements from 0 to a given number using this:
for i in itertools.permutations(range(len(atoms)), 3):
if i[0] < i[-1]:
angles = list(i)
The condition avoids having (0, 1, 2) and (2, 1, 0) "angles" at the same time on my list, what is already great. Now, I need to separate this list into smaller groups being composed of "angles" which have the same central element.
In this way I'd have:
A = ([0, 1, 2], [0, 1, 3], [3, 1, 4])...
B = ([0, 2, 3], [0, 2, 4], [3, 2, 4])...
and so on.
Could you please help me out?
You could use defaultdict to group the permutations:
from collections import defaultdict
angles = defaultdict(list)
for i in itertools.permutations(range(len(atoms)), 3):
if i[0] < i[-1]:
angles[i[1]].append(i)
If len(atoms) is 4 then you'd get following result:
defaultdict(<type 'list'>, {
0: [(1, 0, 2), (1, 0, 3), (2, 0, 3)],
1: [(0, 1, 2), (0, 1, 3), (2, 1, 3)],
2: [(0, 2, 1), (0, 2, 3), (1, 2, 3)],
3: [(0, 3, 1), (0, 3, 2), (1, 3, 2)]
})
The itertools.groupby function can be used to create those lists containing the same central element, but you have to sort the list first so that permutations with the same central element are next to each other. To do that, you need to pass both sort and groupby a key function that looks at the central element. One way to do that is like this:
def keyfunc(s):
return s[1]
or, as a lambda:
keyfunc = lambda s: s[1]
Or you can just use itemgetter from the operator module, which is succinct and significantly faster than using a lambda or def function.
The code below is based on your code but it creates the initial list in a list comprehension. Then it puts the groups in a dictionary, with the central element as the dict key.
from itertools import permutations, groupby
from operator import itemgetter
atoms = 'abcd'
perms = permutations(range(len(atoms)), 3)
angles = [list(u) for u in perms if u[0] < u[-1]]
keyfunc = itemgetter(1)
angles.sort(key=keyfunc)
print(angles)
groups = {k: list(g) for k, g in groupby(angles, keyfunc)}
print(groups)
output
[[1, 0, 2], [1, 0, 3], [2, 0, 3], [0, 1, 2], [0, 1, 3], [2, 1, 3], [0, 2, 1], [0, 2, 3], [1, 2, 3], [0, 3, 1], [0, 3, 2], [1, 3, 2]]
{0: [[1, 0, 2], [1, 0, 3], [2, 0, 3]], 1: [[0, 1, 2], [0, 1, 3], [2, 1, 3]], 2: [[0, 2, 1], [0, 2, 3], [1, 2, 3]], 3: [[0, 3, 1], [0, 3, 2], [1, 3, 2]]}
While the above answers provide excellent solutions to a more general problem, with just 3 element to choose a list comprehension is sufficient.
atoms = [0, 1, 2, 3]
angles = [[(a, b, c) for i, a in enumerate(atoms, start=1) if a != b
for c in atoms[i:] if c != b] for b in atoms]
# [[(1, 0, 2), (1, 0, 3), (2, 0, 3)],
# [(0, 1, 2), (0, 1, 3), (2, 1, 3)],
# [(0, 2, 1), (0, 2, 3), (1, 2, 3)],
# [(0, 3, 1), (0, 3, 2), (1, 3, 2)]]
It is also about 40% faster than permutations + groupby approach on my machine.
You might consider itertools.groupby:
from itertools import groupby, permutations
perms = filter(lambda x: x[0] < x[-1], permutations(range(4), 3))
key = lambda x: x[1] # sort and group by second element
angles = [list(g) for k, g in groupby(sorted(perms, key=key), key=key)]
# here, any comprehension can be used, e.g.
# angles = {k: list(g) for k, g in groupby(sorted(perms, key=key), key=key)}
# will produce the dict from #niemmi's answer
>>> angles
[
[(1, 0, 2), (1, 0, 3), (2, 0, 3)],
[(0, 1, 2), (0, 1, 3), (2, 1, 3)],
[(0, 2, 1), (0, 2, 3), (1, 2, 3)],
[(0, 3, 1), (0, 3, 2), (1, 3, 2)]
]

Python: how do I create a list of combinations from a series of ranges of numbers

For a list of numerical values of n length, e. g. [1, 3, 1, 2, ...], I would like to create a list of the lists of all possible combinations of values from range[x+1] where x is a value from the list. The output might look something like this:
for list[1, 3, 2] return all possible lists of range[x+1] values:
# the sequence of the list is unimportant
[
[0,0,0],[1,0,0],[0,1,0],[0,2,0],[0,3,0],[0,0,1],[0,0,2],[1,1,0],
[1,2,0],[1,3,0],[1,0,1],[1,0,2],[0,1,1],[0,2,1],[0,3,1],[0,1,2],
[0,2,2],[0,3,2],[1,1,1],[1,2,1],[1,3,1],[1,1,2],[1,2,2],[1,3,2]
]
So in this example I am looking for all variations of [e1, e2, e3] from e1 in [0,1], e2 in [0,1,2,3] and e3 in [0,1,2]
Use itertools.product with a dynamically-specified list of iterators:
vals = [1,3,2]
for item in itertools.product(*[range(x+1) for x in vals]):
print item
Output:
(0, 0, 0)
(0, 0, 1)
(0, 0, 2)
(0, 1, 0)
(0, 1, 1)
(0, 1, 2)
(0, 2, 0)
(0, 2, 1)
(0, 2, 2)
(0, 3, 0)
(0, 3, 1)
(0, 3, 2)
(1, 0, 0)
(1, 0, 1)
(1, 0, 2)
(1, 1, 0)
(1, 1, 1)
(1, 1, 2)
(1, 2, 0)
(1, 2, 1)
(1, 2, 2)
(1, 3, 0)
(1, 3, 1)
(1, 3, 2)
Python's itertools module has a tool that does what you need:
import itertools
p = itertools.permutations([0, 1, 2, 3])
p_as_list = list(p)
Edit: As your needs are fairly specific you could benefit from having your own function that does something alike this one: (note I haven't got the implementation down just yet, maybe someone might refine this):
def magic_permutations (*args):
lists = []
larg = len(args)
for i in range(larg):
lists.append([])
i = 0
for nums in args:
for num in nums:
if i >= larg:
i = 0
lists[i].append(num)
i += 1
return lists
Edit: I misunderstood your question the first time, so I'll apologize for that. I'll however leave this be.
To obtain the exact sequence shown in the question (albeit in a different order, but that's not a problem) use this function:
import itertools as it
def combs(lst):
return [list(e) for e in it.product(*(range(x+1) for x in lst))]
The result is as expected:
combs([1, 3, 2])
=> [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 1, 0], [0, 1, 1], [0, 1, 2],
[0, 2, 0], [0, 2, 1], [0, 2, 2], [0, 3, 0], [0, 3, 1], [0, 3, 2],
[1, 0, 0], [1, 0, 1], [1, 0, 2], [1, 1, 0], [1, 1, 1], [1, 1, 2],
[1, 2, 0], [1, 2, 1], [1, 2, 2], [1, 3, 0], [1, 3, 1], [1, 3, 2]]
for ii in itertools.product(range(2),range(4),range(3):
print ii
(0, 0, 0)
(0, 0, 1)
(0, 0, 2)
(0, 1, 0)
(0, 1, 1)
(0, 1, 2)
(0, 2, 0)
(0, 2, 1)
(0, 2, 2)
(0, 3, 0)
(0, 3, 1)
(0, 3, 2)
(1, 0, 0)
(1, 0, 1)
(1, 0, 2)
(1, 1, 0)
(1, 1, 1)
(1, 1, 2)
(1, 2, 0)
(1, 2, 1)
(1, 2, 2)
(1, 3, 0)
(1, 3, 1)
(1, 3, 2)
It's not in the same order, but I think this is what you wanted:
def xrangeCombinations(input):
if len(input) > 1:
for i in xrange(input[-1] + 1):
for j in xrangeCombinations(input[:-1]):
yield j + [i]
else:
for i in xrange(input[-1] + 1):
yield [i]
for i in xrangeCombinations([1, 3, 2]):
print i
Produces the output:
[0, 0, 0]
[1, 0, 0]
[0, 1, 0]
[1, 1, 0]
[0, 2, 0]
[1, 2, 0]
[0, 3, 0]
[1, 3, 0]
[0, 0, 1]
[1, 0, 1]
[0, 1, 1]
[1, 1, 1]
[0, 2, 1]
[1, 2, 1]
[0, 3, 1]
[1, 3, 1]
[0, 0, 2]
[1, 0, 2]
[0, 1, 2]
[1, 1, 2]
[0, 2, 2]
[1, 2, 2]
[0, 3, 2]
[1, 3, 2]
This solution might be slower than alternatives so if speed is an issue you should probably improve it.
Using numpy if you don't mind getting tuples in the end:
>>> import numpy as np
>>> e1=np.array([0,1])
>>> e2=np.array([0,1,2])
>>> e3=np.array([0,1,2,3])
>>> g=np.meshgrid(e1,e2,e3) #you need numpy ver>1.7.0, change the order of final result by changing the order of e1, e2, e3
>>> zip(*[item.flatten() for item in g])
[(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3), (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 0, 3), (0, 1, 0), (0, 1, 1), (0, 1, 2), (0, 1, 3), (1, 1, 0), (1, 1, 1), (1, 1, 2), (1, 1, 3), (0, 2, 0), (0, 2, 1), (0, 2, 2), (0, 2, 3), (1, 2, 0), (1, 2, 1), (1, 2, 2), (1, 2, 3)]

Categories

Resources