Generate all possible lists by replacing elements with 0 - python

I would like to create from a list all the different list were 0,1,2,3...all element are replaced by an other
For example, if the replacement item is 0:
L=[1,2,3]
->[1,2,3],[0,2,3],[1,0,3],[1,2,0],[0,0,3],[0,2,0],[1,0,0],[0,0,0]
So far, I've tried I managed to do what I whant using Itertools but only in the case where 1 value is replaced by 0
Does anyone know how to do this ?

Everyone's trying too hard here. We want each value to be either the original value or 0 -- we want pairs like (1,0), (2,0), and (3,0):
>>> from itertools import product, repeat
>>> L = [1, 2, 3]
>>> zip(L, repeat(0))
<zip object at 0x7f931ad1bf08>
>>> list(zip(L, repeat(0)))
[(1, 0), (2, 0), (3, 0)]
and then we can just pass that into product:
>>> list(product(*zip(L, repeat(0))))
[(1, 2, 3), (1, 2, 0), (1, 0, 3), (1, 0, 0), (0, 2, 3), (0, 2, 0), (0, 0, 3), (0, 0, 0)]

This is one way using itertools. The benefit of this method is that it is lazy.
A new list is produced on every __next__ call of the generator transformer.
Alternatively, as below, you can output all combinations by calling list on the generator function.
from itertools import combinations, chain
A = [1, 2, 3]
def transformer(x):
idx = chain.from_iterable(combinations(range(len(x)), i) for i in range(len(x)+1))
for indices in idx:
y = x.copy()
for j in indices:
y[j] = 0
yield y
res = list(transformer(A))
print(res)
[[1, 2, 3], [0, 2, 3], [1, 0, 3], [1, 2, 0], [0, 0, 3], [0, 2, 0], [1, 0, 0], [0, 0, 0]]

You can use recursion. First, create a function that can generate a full combinations for each index of the input:
def full_combinations(d, current = []):
if len(d) == len(current):
yield current
else:
yield current
for i in range(len(d)):
if len(set(current+[i])) == len(current)+1:
yield from full_combinations(d, current+[i])
combination_list = list(full_combinations([1, 2, 3]))
new_results = [[0 if c in i else a for c, a in enumerate([1, 2, 3])] for i in combination_list]
full = [a for i, a in enumerate(new_results) if a not in new_results[:i]]
Output:
[[1, 2, 3], [0, 2, 3], [0, 0, 3], [0, 0, 0], [0, 2, 0], [1, 0, 3], [1, 0, 0], [1, 2, 0]]

It's not pretty, but I'm sure you can get this idea to work.
The idea is to use itertools.combinations to get all combinations of indices for every length, then we flatten this list with itertools.chain().
Then we loop through this list of lists, setting those indices to the replace character.
import itertools
l = [1,2,3]
replace = 0
indices = list(itertools.chain(*[list(itertools.combinations(list(range(len(l))),z+1)) for z in range(len(l))]))
allcombs = [[l]]
for i in indices:
l2 = l[:]
for j in i:
l2[j] = replace
allcombs.append(l2)
print(allcombs)
[[[1, 2, 3]], [0, 2, 3], [1, 0, 3], [1, 2, 0], [0, 0, 3], [0, 2, 0], [1, 0, 0], [0, 0, 0]]

Related

Generate a list based on the index of another list

I have a list:
hash_table = [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
I want to change this to:
result = [[0, 0], [1, 2], [4, 5]]
How to generate:
array: [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
map: 0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0
# start to end, generate the result like `[int(start), int(end)]`
combine them:[[0, 0], [1, 2], [4, 5]]
0 and 1 wouldn't appear in pairs. So the numbers in result must be an integer.
What I have tried:
hash_table = [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
output = [[]]
for pre, next_ in zip(hash_table, hash_table[1:]):
output[-1].append(pre)
if {next_, pre} == {0, 1}:
output.append([])
output[-1].append(hash_table[-1])
# the output is [[1], [0], [1, 1, 1], [0, 0, 0], [1, 1, 1]]
start = index = 0
result = []
while index < len(output):
# output[index]
if output[0] != 0:
res.append([start, math.ceil(len(output[index]))])
# I don't know how to handle the list "output".
# I couldn't know it. My mind has gone blank
start += len(output[index])/2
Any good ideas? I thought I made it too complicated.
You can use itertools.groupby to group the 0s and 1s:
import itertools
hash_table = [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
result = []
cur_ind = 0
for (val, vals) in itertools.groupby(hash_table):
vals = list(vals) # itertools doesn't make it a list by default
old_ind = cur_ind
cur_ind += len(vals)
if val == 0:
continue
result.append([old_ind // 2, (cur_ind - 1) // 2])
print(result)
Essentially, itertools.groupby will give an iterator of [(1, [1]), (0, [0]), (1, [1, 1, 1]), (0, [0, 0, 0]), (1, [1, 1, 1])] (more or less). We can iterate through this iterator and keep track if the index we're on by adding the length of the sublist to the current index. If the value is 1, then we have a run of ones so we append it to the results. The old_ind // 2 is integer division and is equivalent to int(old_ind / 2).
You could use groupby from itertools library:
import itertools
hash_table = [1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1]
s = "".join(map(str, hash_table)) # s = "10111000111"
gs = [(i, list(g)) for i, g in itertools.groupby(s)]
idx, result = 0, []
for i, g in gs: # i can be '1' or '0' (i.e, if the group consist in 1's or 0's)
if i == '1':
result.append([idx/2, (idx + len(g) - 1)/2])
idx += len(g)
return result

Python 3: Get diagonal elements of matrix (list of lists) about a point in that matrix without NumPy

I have a matrix (formed of a list of lists) that would look something like:
matrix = [[0, 0, 0, 0, 5],
[0, 0, 0, 4, 0],
[2, 0, 3, 0, 0],
[3, 2, 0, 2, 0],
[1, 0, 2, 0, 1]]
What I am struggling to create is a function that will take this matrix as an input, along with a position in the matrix - represented by a tuple - and return the two diagonals that intersect that point (without using NumPy). For example,
def getDiagonal(matrix, pos)
(row, col) = pos
# Smart diagonal finder code #
return (diag1, diag2)
diagonals = getDiagonals(matrix, (1, 1))
print(diagnonal[0])
print(diagnonal[1])
print(' ')
diagonals = getDiagonals(matrix, (1, 3))
print(diagnonal[0])
print(diagnonal[1])
Expected output:
OUT: [5, 4, 3, 2, 1]
OUT: [2, 2, 2]
OUT:
OUT: [0, 2, 2]
OUT: [0, 0, 3, 2, 1]
It is worth pointing out that I don't mind from which direction (bottom-to-top or top-to-bottom) the returned elements of the diagonals are. They could easily be done one way and revered using reverse() if need be.
I have looked at similar questions such as this one but this mainly deals with acquring the leading diagonals of a matrix and provides less information on getting the diagonals about a point.
Many thanks for your help and comments in advance!
A bit confusing, but I think this does it:
def getDiagonals(matrix, pos):
row, col = pos
nrows = len(matrix)
ncols = len(matrix[0]) if nrows > 0 else 0
# First diagonal
d1_i, d1_j = nrows - 1 - max(row - col, 0), max(col - row, 0)
d1_len = min(d1_i + 1, ncols - d1_j)
diag1 = [matrix[d1_i - k][d1_j + k] for k in range(d1_len)]
# Second diagonal
t = min(row, ncols - col - 1)
d2_i, d2_j = nrows - 1 - row + t, col + t
d2_len = min(d2_i, d2_j) + 1
diag2 = [matrix[d2_i - k][d2_j - k] for k in range(d2_len)]
return (diag1, diag2)
# Test
matrix = [[0, 0, 0, 0, 5],
[0, 0, 0, 4, 0],
[2, 0, 3, 0, 0],
[3, 2, 0, 2, 0],
[1, 0, 2, 0, 1]]
diagonals = getDiagonals(matrix, (1, 1))
print(diagonals[0])
# [1, 2, 3, 4, 5]
print(diagonals[1])
# [2, 2, 2]
diagonals = getDiagonals(matrix, (1, 3))
print(diagonals[0])
# [2, 2, 0]
print(diagonals[1])
# [1, 2, 3, 0, 0]
diagonals = getDiagonals(matrix, (2, 2))
print(diagonals[0])
# [1, 2, 3, 4, 5]
print(diagonals[1])
# [1, 2, 3, 0, 0]

Python: how to splice a list into sublists of given lengths?

x = [2, 1, 2, 0, 1, 2, 2]
I want to splice the above list into sublists of length = [1, 2, 3, 1]. In other words, I want my output to look something like this:
[[2], [1, 2], [0, 1, 2], [2]]
where my first sublist is of length 1, the second sublist is of length 2, and so forth.
You can use itertools.islice here to consume N many elements of the source list each iteration, eg:
from itertools import islice
x = [2, 1, 2, 0, 1, 2, 2]
length = [1, 2, 3, 1]
# get an iterable to consume x
it = iter(x)
new_list = [list(islice(it, n)) for n in length]
Gives you:
[[2], [1, 2], [0, 1, 2], [2]]
Basically we want to extract certain lengths of substrings.
For that we need a start_index and an end_index. The end_index is your start_index + the current length which we want to extract:
x = [2, 1, 2, 0, 1, 2, 2]
lengths = [1,2,3,1]
res = []
start_index = 0
for length in lengths:
res.append(x[start_index:start_index+length])
start_index += length
print(res) # [[2], [1, 2], [0, 1, 2], [2]]
Added this solution to the other answer as it does not need any imported modules.
You can use the following listcomp:
from itertools import accumulate
x = [2, 1, 2, 0, 1, 2, 2]
length = [1, 2, 3, 1]
[x[i - j: i] for i, j in zip(accumulate(length), length)]
# [[2], [1, 2], [0, 1, 2], [2]]

Add element into list at even indexes [duplicate]

This question already has answers here:
python: most elegant way to intersperse a list with an element
(15 answers)
Closed 6 years ago.
I have a list like
[1, 2, 3, 4, 5]
and I want to add zeroes at odd indexes:
[1, 0, 2, 0, 3, 0, 4, 0, 5]
My first thought was to create a list with zeroes and replace them with the values from the original list.
listOfZeros = [0] * (2*len(list)-1)
j = 0
for i in range(0, len(listOfZeros)):
if (i%2 == 0):
listOfZeros[i] = h_temp[j]
j += 1
This actually works, but I do dislike for loops and adding another counter j. Isn't there a better way by using slicing?
You can use insert(). Looking at your output, assuming you are not counting index 0 as even.
a = [1,2,3,4,5]
for x in range(len(a)):
a.insert(2*x+1, 0)
one way is by using zip:
a = [1, 2, 3, 4, 5]
d = [x for t in zip (a, [0] * len(a)) for x in t][:-1]
When you use zip, you create list of tuples.
a = [1,2,3,4,5]
b = [0,0,0,0,0]
c = zip(a,b)
#zip (a,b) creates [(1,0),(2,0),(3,0),(4,0),(5,0)]
Then you loop over the set of tuples to arrange them into list:
d = [x for t in c for x in t] #creates [1,0,2,0,3,0,4,0,5,0]
and cut the last element (since you end with 5)
[x for t in c for x in t][:-1] #take out the last 0
#resulting in [1,0,2,0,3,0,4,0,5]
then you are done.
You can do it with a generator:
def zero_on_odd(mylist):
for i in mylist:
yield i
yield 0
a = [1, 2, 3]
with_zeros = list(zero_on_odd(a))[:-1]
If you want to go functional...
from itertools import chain, repeat
_list = [1,2,3,4,5]
list(chain(*zip(_list, repeat(0))))[:-1]
# [1, 0, 2, 0, 3, 0, 4, 0, 5]
If you want to be silly...
[int(i) for i in '0'.join(str(i) for i in _list)]
# still [1, 0, 2, 0, 3, 0, 4, 0, 5]
Or, if you want to be functional AND silly...
map(int, '0'.join(map(str, _list)))
# really, it's still [1, 0, 2, 0, 3, 0, 4, 0, 5]
# except in Python 3.X, there it's a map object...
But, you should probably opt for one of the custom generator solutions.
For the fun of it, here is an itertools solution:
from itertools import islice, chain
data = [1,2,3,4,5]
print list(islice(chain.from_iterable((x, 0) for x in data), 0, 2 * len(data)-1))
Giving:
[1, 0, 2, 0, 3, 0, 4, 0, 5]
Another zip way:
>>> li
[1, 2, 3, 4, 5]
>>> [e for t in zip(li,[0]*(len(li)-1)) for e in t]+[li[-1]]
[1, 0, 2, 0, 3, 0, 4, 0, 5]
You can also use range and slice assignment:
>>> li=[1,2,3,4,5]
>>> for i in range(1,len(li)+len(li)-1, 2): li[i:i]=[0]
...
>>> li
[1, 0, 2, 0, 3, 0, 4, 0, 5]
And, a list comprehension:
>>> [li[i/2] if not i%2 else 0 for i in range(len(li)*2-1)]
[1, 0, 2, 0, 3, 0, 4, 0, 5]
A hacky way:
>>> ls1 = [1, 2, 3, 4, 5]
>>> ls2 = []
>>> list(ls2.extend([n, 0]) for n in ls1)
[None, None, None, None, None]
>>> ls2
[1, 0, 2, 0, 3, 0, 4, 0, 5, 0]

repeating a numpy array a specified number of times for itertools

I am trying to write some code that will give me the itertools product, for a varying number of inputs. For example, this works for me.
test = np.array([x for x in itertools.product([0,2],[0,2],[0,2])])
this gives me my desired result:
>>> test
array([[0, 0, 0],
[0, 0, 2],
[0, 2, 0],
[0, 2, 2],
[2, 0, 0],
[2, 0, 2],
[2, 2, 0],
[2, 2, 2]])
However, I'd like to be able to be able to pass to the product function a varying number of lists. For example:
test = np.array([x for x in itertools.product([0,2],[0,2],[0,2],[0,2])])
or
test = np.array([x for x in itertools.product([0,2],[0,2])])
I have tried
test = np.array([x for x in itertools.product(([0,2],) * 3)])
and
test = np.array([x for x in itertools.product([[0,2]]*3)])
but neither gives me the desired result. Surely there is an easy way to do this. I would appreciate any help.
It looks to me like you were grasping for the splat-unpack syntax:
>>> n = 3
>>> L = [0, 2]
>>> np.array([x for x in itertools.product(*([L] * n))])
array([[0, 0, 0],
[0, 0, 2],
[0, 2, 0],
[0, 2, 2],
[2, 0, 0],
[2, 0, 2],
[2, 2, 0],
[2, 2, 2]])
It may be easier to use the second argument repeat to itertools.product though.
>>> np.array(list(itertools.product(L, repeat=3)))
array([[0, 0, 0],
[0, 0, 2],
[0, 2, 0],
[0, 2, 2],
[2, 0, 0],
[2, 0, 2],
[2, 2, 0],
[2, 2, 2]])
itertools.product supports another argument called repeat as in itertools.product(*iterables[, repeat]) through which you can manipulate the dimensions of cross product. Note, this argument should be specified explicitly in order to disambiguate from the list content.
So your example extends to
test = np.array([x for x in itertools.product([0,2],[0,2],[0,2],[0,2])])
to
test = np.array([x for x in itertools.product([0,2], repeat = 4)])
you can try this
for 3 times:
test = np.array([x for x in itertools.product(*itertools.repeat([0,2],3))])
for n times:
test = np.array([x for x in itertools.product(*itertools.repeat([0,2],n))])
itertools.repeat([0,2],n) this will repeat elem, elem, elem, ... endlessly or up to n times and * in front of itertools is to unpack all element
You need to add * to expand the list of lists:
In [244]: list(itertools.product(*[[0,2]]*2))
Out[244]: [(0, 0), (0, 2), (2, 0), (2, 2)]
This expansion, and the use of repeat are equal in timing tests.

Categories