Python combination and permuation code - python

I have following code to generate the set of combination, append the combination in the list, and return list.
def make_combination():
import itertools
max_range = 5
indexes = combinations_plus = []
for i in range(0, max_range):
indexes.append(i)
for i in xrange(2, max_range):
each_combination = [list(x) for x in itertools.combinations(indexes, i)]
combinations_plus.append(each_combination)
retrun combinations_plus
It generates so many combinations that I don't want (hard to display). But, I want the following combination:
1) [[0, 1], [0, 2], [0, 3], [0, 4], [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
2) [[0, 1, 2], [0, 1, 3], [0, 1, 4], [0, 2, 3], [0, 2, 4], [0, 3, 4], [1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]
3) [[0, 1, 2, 3], [0, 1, 2, 4], [0, 1, 3, 4], [0, 2, 3, 4], [1, 2, 3, 4]]
I think problem in the following line but I don't know what it is. Any idea about what the mistake is.
combinations_plus.append(each_combination)

An easier way of doing what you want is the following:
list(list(itertools.combinations(list(range(5)), i)) for i in range(2, 5))

To fix your original code, there were two problems:
indexes = combinations_plus = []
The above creates two names for the exact same list. Appending to either appends to both which is not what you want.
The two for statements shouldn't be nested, or the list of indexes is incomplete:
for i in range(0, max_range):
indexes.append(i)
for i in xrange(2, max_range):
each_combination = [list(x) for x in itertools.combinations(indexes, i)]
combinations_plus.append(each_combination)
In fact, initialize indexes with range and skip the first for loop:
indexes = range(max_range) # becomes [0,1,2,3,4]
combinations_plus = []
With these fixes (and fixing the spelling of return, you have:
def make_combination():
import itertools
max_range = 5
indexes = range(max_range)
combinations_plus = []
for i in xrange(2, max_range):
each_combination = [list(x) for x in itertools.combinations(indexes, i)]
combinations_plus.append(each_combination)
return combinations_plus
Which returns (newlines added for readability):
[[[0, 1], [0, 2], [0, 3], [0, 4], [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]],
[[0, 1, 2], [0, 1, 3], [0, 1, 4], [0, 2, 3], [0, 2, 4], [0, 3, 4], [1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]],
[[0, 1, 2, 3], [0, 1, 2, 4], [0, 1, 3, 4], [0, 2, 3, 4], [1, 2, 3, 4]]]

Related

PyTorch indexing: select complement of indices

Say I have a tensor and index:
x = torch.tensor([1,2,3,4,5])
idx = torch.tensor([0,2,4])
If I want to select all elements not in the index, I can manually define a Boolean mask like so:
mask = torch.ones_like(x)
mask[idx] = 0
x[mask]
is there a more elegant way of doing this?
i.e. a syntax where I can directly pass the indices as opposed to creating a mask e.g. something like:
x[~idx]
I couldn't find a satisfactory solution to finding the complement of a multi-dimensional tensor of indices and finally implemented my own. It can work on cuda and enjoys fast parallel computation.
def complement_idx(idx, dim):
"""
Compute the complement: set(range(dim)) - set(idx).
idx is a multi-dimensional tensor, find the complement for its trailing dimension,
all other dimension is considered batched.
Args:
idx: input index, shape: [N, *, K]
dim: the max index for complement
"""
a = torch.arange(dim, device=idx.device)
ndim = idx.ndim
dims = idx.shape
n_idx = dims[-1]
dims = dims[:-1] + (-1, )
for i in range(1, ndim):
a = a.unsqueeze(0)
a = a.expand(*dims)
masked = torch.scatter(a, -1, idx, 0)
compl, _ = torch.sort(masked, dim=-1, descending=False)
compl = compl.permute(-1, *tuple(range(ndim - 1)))
compl = compl[n_idx:].permute(*(tuple(range(1, ndim)) + (0,)))
return compl
Example:
>>> import torch
>>> a = torch.rand(3, 4, 5)
>>> a
tensor([[[0.7849, 0.7404, 0.4112, 0.9873, 0.2937],
[0.2113, 0.9923, 0.6895, 0.1360, 0.2952],
[0.9644, 0.9577, 0.2021, 0.6050, 0.7143],
[0.0239, 0.7297, 0.3731, 0.8403, 0.5984]],
[[0.9089, 0.0945, 0.9573, 0.9475, 0.6485],
[0.7132, 0.4858, 0.0155, 0.3899, 0.8407],
[0.2327, 0.8023, 0.6278, 0.0653, 0.2215],
[0.9597, 0.5524, 0.2327, 0.1864, 0.1028]],
[[0.2334, 0.9821, 0.4420, 0.1389, 0.2663],
[0.6905, 0.2956, 0.8669, 0.6926, 0.9757],
[0.8897, 0.4707, 0.5909, 0.6522, 0.9137],
[0.6240, 0.1081, 0.6404, 0.1050, 0.6413]]])
>>> b, c = torch.topk(a, 2, dim=-1)
>>> b
tensor([[[0.9873, 0.7849],
[0.9923, 0.6895],
[0.9644, 0.9577],
[0.8403, 0.7297]],
[[0.9573, 0.9475],
[0.8407, 0.7132],
[0.8023, 0.6278],
[0.9597, 0.5524]],
[[0.9821, 0.4420],
[0.9757, 0.8669],
[0.9137, 0.8897],
[0.6413, 0.6404]]])
>>> c
tensor([[[3, 0],
[1, 2],
[0, 1],
[3, 1]],
[[2, 3],
[4, 0],
[1, 2],
[0, 1]],
[[1, 2],
[4, 2],
[4, 0],
[4, 2]]])
>>> compl = complement_idx(c, 5)
>>> compl
tensor([[[1, 2, 4],
[0, 3, 4],
[2, 3, 4],
[0, 2, 4]],
[[0, 1, 4],
[1, 2, 3],
[0, 3, 4],
[2, 3, 4]],
[[0, 3, 4],
[0, 1, 3],
[1, 2, 3],
[0, 1, 3]]])
>>> al = torch.cat([c, compl], dim=-1)
>>> al
tensor([[[3, 0, 1, 2, 4],
[1, 2, 0, 3, 4],
[0, 1, 2, 3, 4],
[3, 1, 0, 2, 4]],
[[2, 3, 0, 1, 4],
[4, 0, 1, 2, 3],
[1, 2, 0, 3, 4],
[0, 1, 2, 3, 4]],
[[1, 2, 0, 3, 4],
[4, 2, 0, 1, 3],
[4, 0, 1, 2, 3],
[4, 2, 0, 1, 3]]])
>>> al, _ = al.sort(dim=-1)
>>> al
tensor([[[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4]],
[[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4]],
[[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4]]])
You may want to try the single-line expression:
x[np.setdiff1d(range(len(x)), idx)]
Though it seems also not elegant:).

Expanding a vector to a new size

Given a vector, for example
my_list=[1, 2, 3]
how to expand each entry to a new matrix (seen as a multidimensional list or, more likely, a numpy array)?
For example, in the case of matrix being a numpy array of size 2x2 matrix, the output, expanded_my_list, would be:
[[[1, 1], [1, 1]], [[2, 2], [2, 2]], [[3, 3], [3, 3]]]
or as a numpy array:
array([[[1, 1],
[1, 1]],
[[2, 2],
[2, 2]],
[[3, 3],
[3, 3]]])
where expanded_my_list.shape is (3,2,2).
One solution may be:
for i in range(len(my_list)):
expanded[:, i] = my_list[i].expand_as(matrix[:, i])
my_list = [1, 2, 3]
[[[e] * 2 for _ in range(2)] for e in my_list]
output:
[[[1, 1], [1, 1]], [[2, 2], [2, 2]], [[3, 3], [3, 3]]]
You could use numpy.tile().
By creating additional axis via np.newaxis and repeating the elements of the list along these axis you can create your wanted result:
import numpy as np
lst = [1, 2, 3]
arr = np.array(lst)
arr2 = np.tile(arr[:, np.newaxis, np.newaxis], reps=(1, 2, 2))
# Output:
# array([[[1, 1],
# [1, 1]],
# [[2, 2],
# [2, 2]],
# [[3, 3],
# [3, 3]]])
lst2 = arr2.tolist() # If a nested list is required
Or more general:
arr = np.array([[1, 2],
[3, 4]])
expanded_shape = (3, 4)
arr2 = np.tile(arr.reshape(arr.shape + (1,)*len(expanded_shape)),
reps=(1,)*arr.ndim + expanded_shape)
# Output, shape (2, 2, 3, 4)
# array([[[[1, 1, 1, 1],
# [1, 1, 1, 1],
# [1, 1, 1, 1]],
# [[2, 2, 2, 2],
# [2, 2, 2, 2],
# [2, 2, 2, 2]]],
# [[[3, 3, 3, 3],
# [3, 3, 3, 3],
# [3, 3, 3, 3]],
# [[4, 4, 4, 4],
# [4, 4, 4, 4],
# [4, 4, 4, 4]]]])

Matrix COUNT + GROUP BY in python

I have a matrix of vectors filled with integers. For example:
[[1, 2, 3],
[2, 3, 1],
[1, 2, 3],
[2, 3, 1],
[2, 3, 1]]
and I want to count all distinguish vectors in order to obtain something like this:
[[2, [1, 2, 3]],
[3, [2, 3, 1]]]
where first I have the number of number of occurrences and then the vector.
In SQL it can be done with COUNT + GROUP BY.
However, how can I 'smartly' compute it with python?
With Python only, you can use a Counter:
from collections import Counter
matrix = [[1, 2, 3],
[2, 3, 1],
[1, 2, 3],
[2, 3, 1],
[2, 3, 1]]
c = Counter(map(tuple, matrix))
result = [[count, list(row)] for row, count in c.items()]
print(result)
# [[2, [1, 2, 3]], [3, [2, 3, 1]]]
With NumPy, you can use np.unique:
import numpy as np
matrix = np.array([[1, 2, 3],
[2, 3, 1],
[1, 2, 3],
[2, 3, 1],
[2, 3, 1]])
rows, counts = np.unique(matrix, axis=0, return_counts=True)
result = [[count, list(row)] for row, count in zip(rows, counts)]
print(result)
# [[2, [1, 2, 3]], [3, [2, 3, 1]]]
First convert every sub-list of m to tuple. Then use collections.Counter to count the occurances of the tuple in the main list. Now loop through this counter object with keys (no of counts) and values (tuples) and append them into a new list like this :
from collections import Counter
m = [[1, 2, 3],
[2, 3, 1],
[1, 2, 3],
[2, 3, 1],
[2, 3, 1]]
m = map(tuple, m)
l = []
for k,v in Counter(m).items():
l.append([v, list(k)])
Output :
[[2, [1, 2, 3]], [3, [2, 3, 1]]]
Note : Counter(m) produces this counter object :
Counter({(2, 3, 1): 3, (1, 2, 3): 2})

Subset Sum Problem: Returning the indices of the values instead of the values

would like to know how to put a twist on the subset sum problem.
Given a list of integers and a target integer, I want to compute all the possible groups (consisting of 2 or 3 members) from the list that sum up to the target.
The output will be a 2D list of groups with the indices of the 2 or 3 numbers.
So for example,
nums = [3, 0, 1, 0, -1, -2, 0]
t = 0
ttsum(nums, t) returns [[1, 3], [1, 6], [2, 4], [3, 6], [0, 4, 5], [1, 3, 6], [1, 2, 4], [2, 3, 4], [2, 4, 6]]
Thank you!!!
Also find the combinations of a list that represents the indices. Check the sum, then take the indices.
from itertools import combinations
l = [3, 0, 1, 0, -1, -2, 0]
[list(idx) for i in range(2, 4, 1) for seq,idx in zip(combinations(l, i), combinations(range(0, len(l), 1), i)) if sum(seq) == 0]
Output:
[[1, 3],
[1, 6],
[2, 4],
[3, 6],
[0, 4, 5],
[1, 2, 4],
[1, 3, 6],
[2, 3, 4],
[2, 4, 6]]

How to write a lot of nested 'for' loops (Recursively)

word = "word"
# Splitting word into its characters
newword = []
for char in word:
newword.append(char)
print newword
#getting all permutations
test= []
for i in newword:
for j in newword:
if i != j:
for k in newword:
if j != k and i!= k:
for l in newword:
if i != l and j != l and k != l:
test.append(i+j+k+l)
print test
print type(test)
print len(test)
These 4 nested loops work nicely for 'word' because it has exactly 4 letters in it.
If I wanted as many 'for' loops as there are letters in any given word, how can I do this?
Any nice tricks?
In [10]: import itertools
In [11]: word = "word"
In [12]: test = [''.join(perm) for perm in itertools.permutations(word)]
In [13]: test
Out[13]:
['word',
'wodr',
'wrod',
'wrdo',
'wdor',
'wdro',
'owrd',
'owdr',
'orwd',
'ordw',
'odwr',
'odrw',
'rwod',
'rwdo',
'rowd',
'rodw',
'rdwo',
'rdow',
'dwor',
'dwro',
'dowr',
'dorw',
'drwo',
'drow']
This is a general recursive problem you are trying to solve. itertools already contains functions for practically all implementations you could possible need. However, if you want something to learn about, this is one way of doing it. I will permute a list of numbers. In this case, Ill find permutations for:
[0,1,2, ... ,N-1]
Note that once you have the permutations for the above, you can simply use these as the indices for permuting anything. So what is the general way of doing this?
Let us first look at the result for a specific case. For the case of say [0,1,2,3]. The result we are looking for is the list of lists:
[[0, 1, 2, 3], [0, 1, 3, 2], [0, 2, 1, 3], [0, 2, 3, 1], [0, 3, 1, 2],
[0, 3, 2, 1], [1, 0, 2, 3], [1, 0, 3, 2], [1, 2, 0, 3], [1, 2, 3, 0],
[1, 3, 0, 2], [1, 3, 2, 0], [2, 0, 1, 3], [2, 0, 3, 1], [2, 1, 0, 3],
[2, 1, 3, 0], [2, 3, 0, 1], [2, 3, 1, 0], [3, 0, 1, 2], [3, 0, 2, 1],
[3, 1, 0, 2], [3, 1, 2, 0], [3, 2, 0, 1], [3, 2, 1, 0]]
The idea is to write a function that takes a single list of lists, and increment it. Consider the simple function:
def permNums(inp, N=4):
newInp = []
for i in inp:
for j in range(N):
if j not in i: newInp.append( i+[j] )
return newInp
Now execute this funciton with an empty list of lists ...
In [22]: permNums([[]])
Out[22]: [[0], [1], [2], [3]]
What happens when you run it again with its output?
In [23]: permNums(_)
Out[23]:
[[0, 1],
[0, 2],
[0, 3],
[1, 0],
[1, 2],
[1, 3],
[2, 0],
[2, 1],
[2, 3],
[3, 0],
[3, 1],
[3, 2]]
and repeat it again?
In [24]: permNums(_)
Out[24]:
[[0, 1, 2],
[0, 1, 3],
[0, 2, 1],
[0, 2, 3],
[0, 3, 1],
[0, 3, 2],
[1, 0, 2],
[1, 0, 3],
[1, 2, 0],
[1, 2, 3],
[1, 3, 0],
[1, 3, 2],
[2, 0, 1],
[2, 0, 3],
[2, 1, 0],
[2, 1, 3],
[2, 3, 0],
[2, 3, 1],
[3, 0, 1],
[3, 0, 2],
[3, 1, 0],
[3, 1, 2],
[3, 2, 0],
[3, 2, 1]]
Do it another time, and you will get the result you want.
Now you can consider the simple implementation:
result = [[]]
for i in range(N): result = permNums(_)
This will solve your problem (you just need to map the indices to your string, and join the result). However, this is not classical recursion. For recursion, there are two additional steps you need the perform.
Call the function within itself
Figure out when this calling-itself business is going to stop ...
Calling the function within itself is simple. Just replace
return newInp
with
return permNums(newInp, N)
This step should not be surprising because this is exactly what you did manually on the iPython console. However, you will need to stop at some point. In this specific case, the stopping criterion should be simple. If the number of elements in one of the inner lists == N, then stop.
So the modified program has two simple additions:
def permNums(inp, N=4):
if len(inp[0]) == N: return inp # stopping criterion
newInp = []
for i in inp:
for j in range(N):
if j not in i: newInp.append( i+[j] )
return permNums(newInp, N) # keep calling itself
print permNums([[]])
Hope this helps ...

Categories