Sliding window of width n over the given iterable - python

I have a sequence, window size and step:
seq = [0,1,2,3,4]
n=4
step=2
from more_itertools import windowed
list(windowed([0,1,2,3,4], n, fillvalue=0, step=step))
result:
[(0, 1, 2, 3), (2, 3, 4, 0)]
but I need:
[(0, 1, 2, 3), (2, 3, 4, 0), (4, 0, 0, 0)]
Please help me find a solution

Just write your own windowed function:
def windowed(iterable, size, fillvalue=None, step=1):
for i in range(0, len(iterable), step):
window = iterable[i:i+size]
window += [fillvalue] * (size - len(window))
yield window
>>> list(windowed([0,1,2,3,4], 4, fillvalue=0, step=2))
[[0, 1, 2, 3], [2, 3, 4, 0], [4, 0, 0, 0]]

this should also work with iterables and not just sequences:
from itertools import islice
def sliding_window(seq, n, step, fillvalue=None):
it = iter(seq)
values = tuple(islice(it, n))
while values:
yield values + (n-len(values)) * (fillvalue, )
values = values[step:] + tuple(islice(it, step))
the function outputs:
print(list(sliding_window(seq, n, step, fillvalue=0)))
# [(0, 1, 2, 3), (2, 3, 4, 0), (4, 0, 0, 0)]
most of it is borrowed from the original itertools recipe for a sliding window.

How about using padded?
seq = [0,1,2,3,4]
n=4
step=2
from more_itertools import windowed, padded
list(windowed(padded(seq, 0, n=n, next_multiple=True), n, step=step))

Consider more_itertools.stagger:
Given
import itertools as it
import more_itertools as mit
iterable = [0, 1, 2, 3, 5]
Code
Get all results from sliding windows:
windows = list(mit.stagger(iterable, offsets=(0, 1, 2, 3), longest=True, fillvalue=0))
windows
# [(0, 1, 2, 3), (1, 2, 3, 5), (2, 3, 5, 0), (3, 5, 0, 0), (5, 0, 0, 0)]
Next, filter out the desired results:
[w for i, w in enumerate(windows) if not (i % 2)]
# [(0, 1, 2, 3), (2, 3, 5, 0), (5, 0, 0, 0)]
or slice the iterable:
list(it.islice(windows, 0, None, 2))
# [(0, 1, 2, 3), (2, 3, 5, 0), (5, 0, 0, 0)]

Related

How can I get the permutations for every number within range 1 to lst?

So I wrote this code that attempts to return all the permutations of a number in range (1, lst).
def permutation(lst):
if isinstance(lst, int):
lst = list(range(1, lst + 1))
if len(lst) == 0:
return []
if len(lst) == 1:
return ({(1,)})
if len(lst) == 2:
return ({(1,2),(2,1)})
l = []
for i in range(len(lst)):
m = lst[i]
remLst = lst[:i] + lst[i + 1:]
for p in permutation(remLst):
l.append(tuple([m] + list(p)))
return set(l)
However, the output that I got seems to be incorrect.. because when I put in
permutation(3)
I get...
{(1, 2, 1), (1, 1, 2), (3, 2, 1), (2, 1, 2), (2, 2, 1), (3, 1, 2)}
but I'm supposed to get
{(1,2,3), (1,3,2), (2,1,3), (2,3,1), (3,1,2), (3,2,1)}
and when my input is permutation(4)
my output is
{(4, 1, 2, 1), (1, 4, 1, 2), (4, 1, 1, 2), (3, 1, 2, 1), (4, 3, 2, 1), (3, 2, 2, 1), (4, 3, 1, 2), (3, 4, 2, 1), (3, 2, 1, 2), (4, 2, 2, 1), (3, 1, 1, 2), (3, 4, 1, 2), (2, 3, 2, 1), (4, 2, 1, 2), (2, 4, 1, 2), (2, 4, 2, 1), (2, 3, 1, 2), (1, 2, 1, 2), (1, 2, 2, 1), (1, 3, 2, 1), (2, 1, 1, 2), (2, 1, 2, 1), (1, 4, 2, 1), (1, 3, 1, 2)}
but I'm supposed to get this...
{(4, 3, 1, 2), (3,4, 1, 2), (3, 1, 4, 2), (3, 1, 2,4),
(4, 1, 3, 2), (1, 4, 3, 2), (1, 3, 4, 2), (1, 3, 2, 4),
(4, 1, 2, 3), (1, 4, 2, 3), (1, 2, 4, 3), (1, 2, 3, 4),
(4, 3, 2, 1), (3, 4, 2, 1), (3, 2, 4, 1), (3, 2, 1, 4),
(4, 2, 3, 1), (2, 4, 3, 1), (2, 3, 4, 1), (2, 3, 1, 4),
(4, 2, 1, 3), (2, 4, 1, 3), (2, 1, 4, 3), (2, 1, 3, 4)}
What changes should I make so that my code returns the correct output??
You can use itertools.permutations: it does exactly what you want to do.
from itertools import permutations
lst = 3
p = permutations(range(lst))
print(p)
See the docs here
I don't think you can do this
if len(lst) == 1:
return ({(1,)})
if len(lst) == 2:
return ({(1,2),(2,1)})
If the length of the list is 1, it doesn't necessarily mean that all its permutations will be 1. I think this messes up your recursion, try this instead:
if len(lst) == 1:
return ({(lst[0],)})
if len(lst) == 2:
return ({(lst[0],lst[1]),(lst[1],lst[0])})
Full code becomes:
def permutation(lst):
if isinstance(lst, int):
lst = list(range(1, lst + 1))
if len(lst) == 0:
return []
if len(lst) == 1:
return ({(lst[0],)})
if len(lst) == 2:
return ({(lst[0],lst[1]),(lst[1],lst[0])})
l = []
for i in range(len(lst)):
m = lst[i]
remLst = lst[:i] + lst[i + 1:]
for p in permutation(remLst):
l.append(tuple([m] + list(p)))
return set(l)
Here is an alternative implementation of the permutations function:
def permutations(lst):
if len(lst) <= 1:
return [lst]
results = []
for idx, item in enumerate(lst):
sub_perms = permutations(lst[:idx] + lst[idx+1:])
results.extend([item] + sub_perm for sub_perm in sub_perms)
return results
Output for permutations([1,2,3]):
>>> permutations([1,2,3])
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
You can use simple list comprehension, which might be a bit more pythonic.
def permutation(M):
def permutation_set(S):
if not S:
return [[]]
else:
return [[x] + p for x in S for p in permutation_set(S - {x})]
return permutation_set(set(range(1, M+1)))
permutation(3)
# ==> [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
The interpretation of this code is:
Given a number M, compose a set S = {1, 2, ..., M} -- set(range(1, M+1))
For each item x in set S, recursively generate permutations of a subset S - {x}
Adjoin x to the front of each one of the permutations of the subset -- [x] + p for p in permutation_set(S - {x})
This yields for each x in S, the sequence of permutations of S that begin with x.
Traversing through all x gives all permutations of S -- [x] + p for x in S

python permutations with more randomization

I'm trying to generate permutation from a list of indices, currently, I am using itertools.permutation. It's okay, except I need a really random nature of the indices as I will not be able to select all the permutations, but a very short subset of the total set (initial ones) for simulation.
For itertools.permutation:
The permutation tuples are emitted in lexicographic ordering according to the order of the input iterable. So, if the input iterable is sorted, the combination tuples will be produced in sorted order.
import itertools
for ind, idxs in enumerate(itertools.permutations(range(5))):
print(ind)
print(idxs)
print('--------')
0
(0, 1, 2, 3, 4)
--------
1
(0, 1, 2, 4, 3)
--------
2
(0, 1, 3, 2, 4)
--------
3
(0, 1, 3, 4, 2)
--------
4
(0, 1, 4, 2, 3)
--------
5
(0, 1, 4, 3, 2)
--------
6
(0, 2, 1, 3, 4)
--------
7
(0, 2, 1, 4, 3)
--------
8
(0, 2, 3, 1, 4)
--------
9
(0, 2, 3, 4, 1)
--------
10
(0, 2, 4, 1, 3)
--------
11
(0, 2, 4, 3, 1)
--------
12
(0, 3, 1, 2, 4)
--------
13
(0, 3, 1, 4, 2)
--------
One solution definitely comes to my mind is to shuffle the list each time to get a random order, but that makes the idea of permutation obsolete, which is not desired as there is a chance that the same sample will be generated more than once. The permutation should be generated iteratively, so I can not just do list(itertools.permutation..) as this will make a really unnecessary long list.
One way would be to shuffle before and/or after you generate the permutations.
For reference:
import itertools
import random
a = list(range(3))
print("original =",a)
random.shuffle(a)
print("shuffled =",a)
permutations = list(itertools.permutations(a))
print("permutations of shuffled array =",permutations)
random.shuffle(permutations)
print("shuffled permutations of shuffled array =",permutations)
original = [0, 1, 2]
shuffled = [1, 0, 2]
permutations of shuffled array = [(1, 0, 2), (1, 2, 0), (0, 1, 2), (0, 2, 1), (2, 1, 0), (2, 0, 1)]
shuffled permutations of shuffled array = [(0, 1, 2), (2, 0, 1), (2, 1, 0), (1, 0, 2), (1, 2, 0), (0, 2, 1)]
Generate random permutations : if you only use a small number k of them, the chance you take twice the same is k/n!.
Use random.sample:
permutations = list(itertools.permutations(range(5)))
permutation = random.sample(permutations, k=4)
# run 1
>>> random.sample(permutations, k=4)
[(0, 4, 1, 2, 3), (4, 0, 1, 3, 2), (3, 2, 0, 4, 1), (1, 2, 3, 4, 0)]
# run 2
>>> random.sample(permutations, k=4)
[(2, 1, 4, 0, 3), (0, 3, 4, 1, 2), (3, 1, 4, 0, 2), (0, 3, 4, 2, 1)]
# run 3
>>> random.sample(permutations, k=4)
[(3, 4, 1, 0, 2), (3, 0, 1, 2, 4), (0, 4, 1, 2, 3), (3, 4, 2, 0, 1)]
# and so on

I have a list of tuples that have different length so I need to iterate over list

The tuple that length is equal to 6 is correct one while the tuples with shorter length are artefacts that should be joined to give length of 6.
For example:
I have a list of tuples as below:
foo = [(3, 1, 0, 1, 1, 1), (3, 1), (1, 1), (3, 1), (3, 1, 0, 1), (1, 2), (3, 3, 3, 1, 2, 2)]
len(foo[0]) = 6
len(foo[1]) = 2
len(foo[2]) = 2
len(foo[3]) = 2
len(foo[4]) = 4
len(foo[5]) = 2
len(foo[6]) = 6
So it means that I want to have a list with the following output:
foo_1 = [(3, 1, 0, 1, 1, 1), (3, 1, 1, 1, 3, 1), (3, 1, 0, 1, 1, 2), (3, 3, 3, 1, 2, 2)]
where:
foo_1[1] = foo[1] + foo[2] + foo[3],
foo_1[2] = foo[4] + foo[5]
Basically, I need to iterate over list of tuples and compare the length of each with 6. Then if the length of tuple is not equal to six I have to join tuples till their length will be 6.
You can create a function that flatten's the list of tuples, and then use generators and zip to group them into proper number of length.
>>> def flatten(lst):
for tup in lst:
yield from tup
>>> list(zip(*[flatten(foo)]*6))
[(3, 1, 0, 1, 1, 1),
(3, 1, 1, 1, 3, 1),
(3, 1, 0, 1, 1, 2),
(3, 3, 3, 1, 2, 2)]
You can find more about how zip(*[iter(iterable)]*n) works here.
Or you can use the itertools.chain.from_iterable function to accomplish the flattening part:
>>> flat = chain.from_iterable(foo)
>>> list(zip(*[flat]*6))
[(3, 1, 0, 1, 1, 1),
(3, 1, 1, 1, 3, 1),
(3, 1, 0, 1, 1, 2),
(3, 3, 3, 1, 2, 2)]
import itertools
foo=[(3, 1, 0, 1, 1, 1), (3, 1), (1, 1), (3, 1), (3, 1, 0, 1), (1, 2), (3, 3, 3, 1, 2, 2)]
foo1=[i for t in foo for i in t]
list(itertools.zip_longest(*[iter(foo1)]*6))
Output:
[(3, 1, 0, 1, 1, 1),
(3, 1, 1, 1, 3, 1),
(3, 1, 0, 1, 1, 2),
(3, 3, 3, 1, 2, 2)]
Or just iterate over and use slice
[foo1[i:i+6] for i in range(0,len(foo1),6)]
foo1 is list of all elements..and after that we can use slicing or zip_longest from itertools to get the desired result.
itertools.zip_longest
Make an iterator that aggregates elements from each of the iterables.
If the iterables are of uneven length, missing values are filled-in
with fillvalue. Iteration continues until the longest iterable is
exhausted. Roughly equivalent to:
If the total length is not in multiples of 6
foo=[(3, 1, 0, 1, 1, 1), (3, 1), (1, 1), (3, 1), (3, 1, 0, 1), (1, 2), (3, 3, 3, 1, 2, 2),(1,)
I've added an extra (1,)
list(itertools.zip_longest(*[iter(foo1)]*6))
(1, None, None, None, None, None)]
If we need some fill value instead of None then
list(itertools.zip_longest(*[iter(foo1)]*6,fillvalue=2))
(1, 2, 2, 2, 2, 2)
An easy way to get the results could use more_itertools.chunked
from more_itertools import chunked
from itertools import chain
foo = [(3, 1, 0, 1, 1, 1), (3, 1), (1, 1), (3, 1),
(3, 1, 0, 1), (1, 2), (3, 3, 3, 1, 2, 2)]
for chunk in chunked(chain.from_iterable(foo), 6):
print(chunk)
Prints:
[3, 1, 0, 1, 1, 1]
[3, 1, 1, 1, 3, 1]
[3, 1, 0, 1, 1, 2]
[3, 3, 3, 1, 2, 2]

Generate all permutations with separate limits for each index

Let's say we have a list, e.g., [3, 2, 1]. I would like to generate all permutations of that list in the form:
[1, 1, 1], [2, 1, 1], [3, 1, 1], [1, 2, 1], [2, 2, 1] , [3, 2, 1]
for any list of length n. This way, the value of the ith element of the original list is the upper limit for the value of the ith element of all the permutations.
I would also like to use a generator using yield, since the input list may be rather large large (e.g., n = 30).
So far, I have been using something like this:
itertools.product(range(1, 5), repeat=5)
Which has the following output when used in a for loop:
(1, 1, 1, 1, 1), (1, 1, 1, 1, 2), (1, 1, 1, 1, 3), (1, 1, 1, 1, 4), (1, 1, 1, 2, 1), (1, 1, 1, 2, 2), (1, 1, 1, 2, 3), ...
However, I don't think it allows specifying custom limits for each element of the permutations.
Also, please note that the elements of the input list do not necessarily need to be consecutive numbers, so [25, 17, 10, 4] is a valid input.
This recursive function returns a generator in the desired order:
def f(limits):
if not limits:
yield ()
return
for l in f(limits[1:]):
for i in range(1, limits[0]+1):
yield (i,) + l
>>> print(list(f([3, 2, 1])))
[(1, 1, 1), (2, 1, 1), (3, 1, 1), (1, 2, 1), (2, 2, 1), (3, 2, 1)]
You can filter the results of itertools.product
>>> from itertools import product
>>> l = [3,2,1]
>>> list(filter(lambda t: all(x<=y for x,y in zip(t,l)), product(l, repeat=len(l))))
[(3, 2, 1), (3, 1, 1), (2, 2, 1), (2, 1, 1), (1, 2, 1), (1, 1, 1)]
You're looking for the Cartesian product of a bunch of ranges the argument of which is defined by your input list:
from itertools import product
lims = [3, 2, 1]
gen = product(*(range(1,lim+1) for lim in lims))
print(list(gen))
The result is
[(1, 1, 1), (1, 2, 1), (2, 1, 1), (2, 2, 1), (3, 1, 1), (3, 2, 1)]

Enumerating three variables in python list comprehension

I am trying to print all the possible enumerations of a list for three variables. For example if my input is:
x = 1
y = 1
z = 1
I want the output to be like:
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [1, 0, 0], [1, 1, 0], [1, 0, 1], [0, 1, 1], [1, 1, 1]]
If any of the x,y,z variables are higher than 1, it would enumerate all the integers from 0 to the variable value. For example, if x=3 then 0, 1, 2, or 3 would be possible in the first slot of the 3-element lists.
Right now I am creating the list comprehension like this:
output = [ [x,y,z] for x,y,z in range(x,y,z)]
I think something is wrong with the range function?
You could use the product() function from itertools as follows:
from itertools import product
answer = list(list(x) for x in product([0, 1], repeat=3))
print(answer)
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]]
Complementary to the solutions using product, you could also use a triple list comprehension.
>>> x, y, z = 1, 2, 3
>>> [(a, b, c) for a in range(x+1) for b in range(y+1) for c in range(z+1)]
[(0, 0, 0),
(0, 0, 1),
(0, 0, 2),
(some more),
(1, 2, 2),
(1, 2, 3)]
The +1 is necessary since range does not include the upper bound.
If you want the output to be a list of lists, you can just do [[a, b, c] for ...].
Note, however, that this will obviously only work is you always have three variables (x, y, z), while product would work with an arbitrary number of lists/upper limits.
You can use range() function within a list comprehension and itertools.product function:
>>> x = 1
>>> y = 1
>>> z = 1
>>> from itertools import product
>>> list(product(*[range(i+1) for i in [x,y,z]]))
[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)]
This approach will work for different numbers too:
>>> x = 2
>>> y = 2
>>> z = 2
>>>
>>> list(product(*[range(i+1) for i in [x,y,z]]))
[(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 need it in form of a list of lists (instead of list of tuples) you can use map over the output of the answer by Kasramvd, that is:
map(list,list(product(*[range(i+1) for i in [x,y,z]])))

Categories