Permutations and indexes, python - python

I create a list of all permutations of lets say 0,1,2
perm = list(itertools.permutations([0,1,2]))
This is used for accessing indexes in another list in that specific order. Every time a index is accessed it is popped.
When an element is popped, the elements with indexes higher than the popped elements index will shift one position down. This means that if I want to pop from my list by indexes [0,1,2] it will result in an index error, since index 2 will not exist when I reach it. [0,1,2] should therefor be popped in order [0,0,0].
more examples is
[0,2,1] = [0,1,0]
[2,0,1] = [2,0,0]
[1,2,0] = [1,1,0]
right now this is being handled through a series of checks, my question is if anyone knows a smart way to turn the list of lists generated by itertools into the desired list:
[(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
[(0, 0, 0), (0, 1, 0), (1, 0, 0), (1, 1, 0), (2, 0, 0), (2, 1, 0)]

Simply iterate through each tuple, and decrement the indexes of each subsequent index that is greater than that element:
l=[(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
def lower_idxs(lst):
new_row = list(lst)
for i, val in enumerate(new_row):
for j in xrange(i+1, len(new_row)):
if new_row[i] < new_row[j]:
new_row[j] -= 1
return new_row
print [lower_idxs(x) for x in l]
will print out
[[0, 0, 0], [0, 1, 0], [1, 0, 0], [1, 1, 0], [2, 0, 0], [2, 1, 0]]
Here is a fancier one-liner based on Randy C's solution:
print [tuple(y-sum(v<y for v in x[:i]) for i,y in enumerate(x)) for x in l]

Here's a one-liner for it (assuming your list is l):
[v-sum(v>v2 for v2 in l[:k]) for k, v in enumerate(l)]

Related

Converting a vector of ranges into combination of sets in Python

In Python, I have a list of ranges like that:
A = [range(0,2),range(0,4),range(0,3),range(0,3)]
First I have to convert all of these ranges into sets. I can create an empty set and add the resulting list to it. I would have:
B = [[0, 1], [0, 1, 2, 3], [0, 1, 2], [0, 1, 2]]
But after that, I have to create all the possible combinations of elements between the lists. The set with the lowest values would be [0, 0, 0, 0] and the highest values would be: [1, 3, 2, 2]. It would be a combination of 2x4x3x3 = 72 sets. How can I achieve this result, starting with the list of ranges (A)?
You can use the built-in itertools module to take the cartesian product of all the range objects in A, and skip making B altogether:
import itertools
A = [range(2), range(4), range(3), range(3)]
list(itertools.product(*A))
Output (skipping some items for readability):
[(0, 0, 0, 0),
(0, 0, 0, 1),
(0, 0, 0, 2),
(0, 0, 1, 0),
(0, 0, 1, 1),
.
.
.
(1, 3, 2, 2)]
Verifying the length:
>>> len(list(itertools.product(*A)))
72
Note that itertools.product() yields tuple objects. If for whatever reason you'd prefer these to be lists, you can use a comprehension:
[[*p] for p in itertools.product(*A)]
Another approach, as #don'ttalkjustcode points out, is that you can avoid creating A entirely and skip directly to the cartesian product via the map() function:
list(itertools.product(*map(range, (2, 4, 3, 3))))
However, this assumes that all your ranges start at 0.
You could generalize this mapping technique by using a lambda which will create range objects from a list of tuples:
>>> list(map(lambda t: range(*t), ((6, -3, -1), (0, 3), (5,), (10, 1, -2))))
[range(6, -3, -1), range(0, 3), range(0, 5), range(10, 1, -2)]
to get the Cartesian product do the following :
A = []
for i in range(2):
for j in range(4):
for k in range(3):
for n in range(3):
combo = [i,j,k,n]
A.append(combo)

find the number of pairs that belong to a column but not a higher order column

I have an n x k binary numpy array, I am trying to find an efficient way to find the number of pairs of ones that belong to some column[j] but not to any higher column, in this case higher means in increasing index value.
For example in the array:
array([[1, 1, 1, 0, 1, 0],
[1, 0, 1, 1, 1, 0],
[1, 0, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 0],
[1, 0, 1, 0, 1, 1],
[1, 0, 1, 0, 1, 1],
[1, 1, 1, 1, 0, 0],
[1, 1, 1, 0, 1, 0]], dtype=int32)
the output should be array([ 0, 0, 11, 2, 14, 1], dtype=int32). This makes sense because we see column[2] has all ones, so any pair of ones will necessarily have a highest column in common of at least 2, because even though column[0] also has all ones, it's lower, so no pair of ones have it as their highest in common. In all cases I am considering, column[0] will always have all ones.
Here is some example code that works and I believe is something like O(n^2 k)
def hcc(i, j, k, bin_mat):
# hcc means highest common columns
# i: index i
# j: index j
# k: number of columns - 1
# bin_mat: binary matrix
for q in range(k, 0, -1):
if (bin_mat[i, q] and bin_mat[j, q]):
return q
return 0
def get_num_pairs_columns(bin_mat):
k = bin_mat.shape[1]-1
num_pairs_hcc = np.zeros(k+1, dtype=np.int32) # number of one-pairs in columns
for i in range(bin_mat.shape[0]):
for j in range(bin_mat.shape[0]):
if(i < j):
num_pairs_hcc[hcc(i, j, k, bin_mat)] += 1
return num_pairs_highest_column
Another way I've though of approaching the problem is through sets. So every column gets its own set, and the index of every row with a one gets added to such a set. So for the example above, this would look like:
set = [{0, 1, 2, 3, 4, 5, 6, 7},
{0, 3, 6, 7},
{0, 1, 2, 3, 4, 5, 6, 7},
{1, 3, 6},
{0, 1, 3, 4, 5, 7},
{4, 5}]
The idea is to find the number of pairs in set[j] that are in no higher set (it can be in a lower set, just not higher). Since, I mentioned before, all cases will have column zero with all ones, every set is a subset of set[0]. So a much worse performing code using this approach looks like this:
def generate_sets(bin_mat):
sets = []
for j in range(bin_mat.shape[1]):
column = set()
for i in range(bin_mat.shape[0]):
if bin_mat[i, j] == 1:
column.add(i)
sets.append(column)
return sets
def get_hcc_sets(bin_mat):
sets = generate_sets(bin_mat)
pairs_sets = []
num_pairs_hcc = np.zeros(len(sets), dtype=np.int32)
for subset in sets:
pairs_sets.append({p for p in itertools.combinations(sorted(subset), r = 2)})
for j in range(len(sets)-1):
intersections = [pairs_sets[j].intersection(pairs_sets[q]) for q in range(j+1, len(sets))]
num_pairs_hcc[j] = len(pairs_sets[j] - set.union(*intersections))
num_pairs_hcc[len(sets)-1]=len(pairs_sets[len(sets)-1])
return num_pairs_hcc
I haven't checked that this sets implementation always produces the same results as the previous one, but in the finitely many cases I tried, it works. However, I am 100% certain that my first implementation gives exactly the result I need.
another reference example:
input:
array([[1, 1, 0],
[1, 1, 0],
[1, 1, 0],
[1, 1, 0],
[1, 0, 1],
[1, 0, 1],
[1, 0, 1],
[1, 0, 1]], dtype=int32)
output:
array([16, 6, 6], dtype=int32)
Is there a way to beat my O(n^2 k) implementation. It seems rather brute force and like there should be something I can exploit to make this calculation faster. I always expect n to be greater than k, by a orders of magnitude in many cases. So I'd rather the k have a higher exponent than the n.
If you are going for the O(n² k) approach in python, you can do it with much shorter code using itertools and set; the code might be more efficient too.
import itertools
t = [[1, 1, 1, 0, 1, 0],
[1, 0, 1, 1, 1, 0],
[1, 0, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 0],
[1, 0, 1, 0, 1, 1],
[1, 0, 1, 0, 1, 1],
[1, 1, 1, 1, 0, 0],
[1, 1, 1, 0, 1, 0]]
n,k = len(t),len(t[0])
# build set of pairs of 1 in column j
def candidates(j):
return {(i1, i2) for (i1, i2) in itertools.combinations(range(n), 2) if 1 == t[i1][j] == t[i2][j]}
# build set of pairs of 1 in higher columns
def badpairs(j):
return {(i1, i2) for (i1, i2) in itertools.combinations(range(n), 2) if any(1 == t[i1][j0] == t[i2][j0] for j0 in range(j+1, k))}
# set difference
def finalpairs(j):
return candidates(j) - badpairs(j)
# print pairs
for j in range(k):
print(j, finalpairs(j))
# 0 set()
# 1 set()
# 2 {(2, 4), (1, 2), (2, 7), (4, 6), (0, 6), (2, 3), (6, 7), (0, 2), (2, 6), (5, 6), (2, 5)}
# 3 {(1, 6), (3, 6)}
# 4 {(0, 1), (0, 7), (0, 4), (3, 4), (1, 5), (3, 7), (0, 3), (1, 4), (5, 7), (1, 7), (0, 5), (1, 3), (4, 7), (3, 5)}
# 5 {(4, 5)}
# print number of pairs
for j in range(k):
print(j, len(finalpairs(j)))
# 0 0
# 1 0
# 2 11
# 3 2
# 4 14
# 5 1
Alternate definition for badpairs:
def badpairs(j):
return set().union(*(candidates(j0) for j0 in range(j+1, k)))
Slightly different approach: avoid building badpairs
def finalpairs(j):
return {(i1, i2) for (i1, i2) in itertools.combinations(range(n), 2) if 1 == t[i1][j] == t[i2][j] and not any(1 == t[i1][j0] == t[i2][j0] for j0 in range(j+1, k))}

Find all subsequences in list

I have a list of 1s and 0s as follows:
lst = [1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1]
I'm looking for a method that finds all the sequences of 0s within this list and returns their indices, i.e.:
[1, 3]
[8, 9]
[13, 13]
[15, 16]
This answer shows a method of getting the longest sequence, but I can't think of a way to work from it to get all the sequences.
def f(l):
_1to0 = [ i+1 for i, (x, y) in enumerate(zip(l[:-1], l[1:])) if y == 0 and x != y ]
_0to1 = [ i for i, (x, y) in enumerate(zip(l[:-1], l[1:])) if x == 0 and x != y ]
if l[0] == 0:
_1to0.insert(0,0)
if l[-1] == 0:
_0to1.append(len(l))
return zip(_1to0, _0to1)
Detect changes 1 -> 0 (starts) and 0 -> 1 (ends)
If start with 0, add a start at indice 0
If ends with 0, add an end at the last indice
Combine starts and ends in pairs
In [1]: list(f([1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1]))
Out[1]: [(1, 3), (8, 9), (13, 13), (15, 16)]
For Python 3.8 you can modify the first answer in referenced code by using the Walrus operator
Code
from itertools import groupby
import operator
lst = [1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1]
r = [(p[1][0][0], p[1][-1][0]) for (x,y) in groupby(enumerate(lst), operator.itemgetter(1)) if (p := (x, list(y)))[0] == 0]
print(r)
Output
[(1, 3), (8, 9), (13, 13), (15, 16)]
Explanation
Adding a Walrus operator to OP code reference we have:
r = [p for (x,y) in groupby(enumerate(lst), operator.itemgetter(1)) if (p := (x, list(y)))[0] == 0]
# Outputs: [(0, [(1, 0), (2, 0), (3, 0)]), (0, [(8, 0), (9, 0)]), (0, [(13, 0)]), (0, [(15, 0), (16, 0)])]
Conditional in the list comprehension:
(p := (x, list(y)))[0] # is a check for x == 0
Need to capture the right terms in p
First p[1] for instance is:
[(1, 0), (2, 0), (3, 0)]
We want the (1, 3) which index 0 of the first and last term of the list
p[1][0][0] # index zero of first tuple -> 1
p[1][-1][0] # index zero of last tuple -> 3
So in general we have the tuple:
(p[1][0][0], p[1][-1][0])
list = [1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1]
indexes_counts = []
start_zero_index = -1
is_inside_zero_sequence = False
zero_length = 0
# Use enumerate to loop on the lists and indexes
for i, x in enumerate(list):
# If inside a zeroes sequence
if is_inside_zero_sequence:
# If current item is zero too
if 0 == x:
# Increase the zro_length counter
zero_length += 1
# Else, current element is not zero
else:
# Handle end of zeroes sequence
indexes_counts.append([start_zero_index, zero_length])
is_inside_zero_sequence = False
zero_length = 0
# If not in zeroes sequence and current number is not zero
elif 0 == x:
# Handle not zero
is_inside_zero_sequence = True
start_zero_index = i
zero_length = 1
# [[1, 3], [8, 2], [13, 1], [15, 2]]
print(indexes_counts)

Python How to repeat a list's elements in another list content until the length of the second list fulfilled?

How to repeat a list's elements in another list content until the length of the second list fulfilled?
For example:
LA = [0,1,2]
LB = [(0,0),(1,0),(2,0),(3,0),(4,0),(5,0),(6,0)]
the end result should be:
LC = [(0,0,0),(1,0,1),(2,0,2),(3,0,0),(4,0,1),(5,0,2),(6,0,0)]
Hopefully it can be done in one line
You can use itertools.cycle:
from itertools import cycle
LA = [0,1,2]
LB = [(0,0),(1,0),(2,0),(3,0),(4,0),(5,0),(6,0)]
LC = [(i, j, k) for (i, j), k in zip(LB, cycle(LA))]
print LC
# [(0, 0, 0), (1, 0, 1), (2, 0, 2), (3, 0, 0), (4, 0, 1), (5, 0, 2), (6, 0, 0)]
This works because zip generates items until one of the iterables is exhausted...but a cycle object is inexhaustible, so we'll keep padding items from LA until LB runs out.
#use list comprehension and get the element from LA by using the index from LB %3.
[v+(LA[k%3],) for k,v in enumerate(LB)]
Out[718]: [[0, 0, 0], [1, 0, 1], [2, 0, 2], [3, 0, 0], [4, 0, 1], [5, 0, 2], [6, 0, 0]]
Try enumerate() like this along with list comprehension -
[elem + (LA[i % len(LA)],) for i, elem in enumerate(LB)]
Here a more "explicit" version that works with any length of LA.
LA = [0,1,2]
LB = [(0,0),(1,0),(2,0),(3,0),(4,0),(5,0),(6,0)]
i = 0
LC = []
for x,y in LB:
try:
z = LA[i]
except IndexError:
i = 0
z = LA[i]
LC.append((x,y,z))
i += 1
print LC
[(0, 0, 0), (1, 0, 1), (2, 0, 2), (3, 0, 0), (4, 0, 1), (5, 0, 2), (6, 0, 0)]

How to list X numbers in base of Y? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I have to create a list in Python of all the numbers of length X where each digit is lower than 3. for example, for length 4:
[[0000],[0001],[0002],[0010],[0011],...] and so on..
I have some ideas. but I can't think of any good, performant solution.
I thought about doing the following:
Create a function "Is each number's digits < 2"
Loop over 9999 numbers and run the functions on them. Then add to the list.
To summerize, I want to list all numbers that < x in base of 3
edit:
this can help: [(x,y,z) for x in xrange(3) for y in xrange(3) for z in xrange(3)].
It is even better for me that the outpot is in generator. but this answer isn't dynamic. i can't change its length.
Using itertools.product:
[''.join(map(str,tup)) for tup in product(range(3),repeat=4)]
I joined them into strings since [0000] will just display as [0]. You could just leave them as tuples and get rid of all the join(map(str... mumbo jumbo. In that case you don't even need the list comp, it's just
list(product(range(4),repeat=3))
The following in not exactly what you asked for, but it comes close:
from itertools import product
def create_list(x):
return list(product(range(3), repeat=x))
print create_list(3)
This will print:
[(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)]
Come up with a rule that orders all the responses so that each one comes either before or after each other one.
Write code to find the first response.
Write code to determine if a response is the last response.
Write code to convert a response into the next response.
Now the algorithm is trivial:
Set an indicator to the first response from 2 above.
Output the current value of the indicator.
If the indicator is the last response from 3 above, stop.
Increment the indicator using 4 above.
Go to step 2.
I would suggest you order them numerically, so four four digits, it would be 0000, 0001, 0002, 0010, 0011, and so on. The first response is then all zeroes. The last response is all twos. This just leaves the issue of writing the code to increment to the next response.
inputBase, expectedSize = 2, 3
def convertToBase(num, base):
result, current = [], 0
if not num: result.append(0)
while num:
result.append((num % base))
current += 1
num /= base
result.reverse()
return result, current
currentNum, result = 0, []
while True:
based, size = convertToBase(currentNum, inputBase)
if size > expectedSize: break
while len(based) < expectedSize:
based.insert(0, 0)
result.append(based)
currentNum += 1
print result
Output:
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
Just change
inputBase, expectedSize = 2, 3
to any base and the number of digits you want.

Categories

Resources