How do I use modular arithmetic on indices? - python

I am trying to create a function that, for each member of a list, returns the value of that member and the number of values either side of it. The only trick is that it has to "wrap around" when it is at the start or end of the list
For example:
a = [0,1,2,3,4,5,6,7,8,9]
myfunc(a,2) # 2 indicates 2 either side
[8,9,0,1,2]
[9,0,1,2,3]
[0,1,2,3,4]
...
...
[6,7,8,9,0]
[7,8,9,0,1]
I can work out how to do it from index 2 until 7:
def myfunc(vals, rnge):
for i in range(0+rnge, len(vals)-rnge):
print vals[i-rnge:i+rnge+1]
But I can't work out how to handle when it needs to wrap around.

you could try this (extend vals in both directions first). there may be something in collections that allows for this to be done more efficiently:
def myfunc(vals, rnge):
vals_ext = vals[-rnge:] + vals + vals[:rnge]
for i in range(len(vals)):
print( vals_ext[i:i+2*rnge+1] )
output:
[8, 9, 0, 1, 2]
[9, 0, 1, 2, 3]
[0, 1, 2, 3, 4]
[1, 2, 3, 4, 5]
[2, 3, 4, 5, 6]
[3, 4, 5, 6, 7]
[4, 5, 6, 7, 8]
[5, 6, 7, 8, 9]
[6, 7, 8, 9, 0]
[7, 8, 9, 0, 1]

How about:
def myfunc(vals, rnge):
valsX3 = vals*3;
for i in range(len(vals)):
print valsX3[i:i+2*rnge+1]
You could use something like this to avoid the duplication of arrays:
wrapping around slices in Python / numpy
Not sure how it does it internally though.

Here is an alternative approach you might also find useful:
def myfunc(alist, offset):
adeque = collections.deque(a)
adeque.rotate(offset)
for i in xrange(len(alist)):
print list(itertools.islice(adeque, 0, 2*offset+1))
adeque.rotate(-1)
a = [0,1,2,3,4,5,6,7,8,9]
myfunc(a,2) # 2 indicates 2 either side
It makes use of the deque collections object which has an efficient rotate function.

Generate your lists using the numpy element-wise modulo of an (unshifted) array.
You want 5 elements that wrap around 10, so use modulo 10. For example if the list starts with 8:
np.mod(np.arange(8,8+5,1),10) returns [8, 9, 0, 1, 2]
To get all 10 possible lists, evaluate list(np.mod(np.arange(s,s+5,1),10))for each start s=1,2,..,10
Or, in a single list comprehension without resorting to numpy,
[[t%10 for t in range(s,s+5)] for s in range(10)]
returns
[[0, 1, 2, 3, 4],
[1, 2, 3, 4, 5],
[2, 3, 4, 5, 6],
[3, 4, 5, 6, 7],
[4, 5, 6, 7, 8],
[5, 6, 7, 8, 9],
[6, 7, 8, 9, 0],
[7, 8, 9, 0, 1],
[8, 9, 0, 1, 2],
[9, 0, 1, 2, 3]]

Here is the solution :
def defunc(vals, start, rnge):
n = len(vals)
new = []
for i in xrange((start - rnge), (start + rnge)+ 1):
new.append(vals[i%n])
return new
def myfunc(vals, rnge):
n = len(vals)
for i in range(n):
print defunc(vals,i,rnge)
I am not slicing the list to make the logic clearer.
I hope this works

Related

Python: How to split a list into ordered chunks

If I have the following list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Then
np.array_split([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 3)
Returns
[array([0, 1, 2, 3]), array([4, 5, 6]), array([7, 8, 9])]
Is there a way to get the sub-arrays in the following order?
[array([0, 3, 6, 9]), array([1, 4, 7]), array([2, 5, 8])]
As the lists are of differing lengths, a numpy.ndarray isn't possible without a bit of fiddling, as all sub-arrays must be the same length.
However, if a simple list meets your requirement, you can use:
l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
l2 = []
for i in range(3):
l2.append(l[i::3])
Output:
[[0, 3, 6, 9], [1, 4, 7], [2, 5, 8]]
Or more concisely, giving the same output:
[l[i::3] for i in range(3)]
Let's look into source code refactor of np.array_split:
def array_split(arr, Nsections):
Neach_section, extras = divmod(len(arr), Nsections)
section_sizes = ([0] + extras * [Neach_section + 1] + (Nsections - extras) * [Neach_section])
div_points = np.array(section_sizes).cumsum()
sub_arrs = []
for i in range(Nsections):
st = div_points[i]
end = div_points[i + 1]
sub_arrs.append(arr[st:end])
return sub_arrs
Taking into account your example arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] and Nsections = 3 it will construct section sizes [0, 4, 3, 3] and dividing points [0, 4, 7, 10]. Then do something like this:
[arr[div_points[i]:div_points[i + 1]] for i in range(3)]
Trying to mimic behaviour of numpy, indeed,
def array_split_withswap(arr, N):
sub_arrs = []
for i in range(N):
sub_arrs.append(arr[i::N])
Is the best option to go with (like in #S3DEV solution).

How to duplicate elements in a nested list?

I have a nested list that looks like this:
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
I want to duplicate each element in each inner list by 3 and place them side to side as such:
[[1, 1, 1, 2, 2, 2, 3, 3, 3], [4, 4, 4, 5, 5, 5, 6, 6, 6], [7, 7, 7, 8, 8, 8, 9, 9, 9]]
This is my current solution:
from itertools import repeat
x2 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
ls = []
for i in x2:
ls.append([x for item in i for x in repeat(item, 3)])
ls
>>> [[1, 1, 1, 2, 2, 2, 3, 3, 3],
[5, 5, 5, 6, 6, 6, 7, 7, 7],
[9, 9, 9, 10, 10, 10, 11, 11, 11],
[4, 4, 4, 8, 8, 8, 3, 3, 3]]
Is there any way to make it faster?
Here is one solution:
ls=[sum([[i]*3 for i in k], []) for k in x2]
>>>print(ls)
[[1, 1, 1, 2, 2, 2, 3, 3, 3], [4, 4, 4, 5, 5, 5, 6, 6, 6], [7, 7, 7, 8, 8, 8, 9, 9, 9]]
As you asked for faster, here are some comparisons between the different possible ways presented here and other places.
I used your method using repeat and added a list-comp version of it just to match the others.
I used #IoaTzimas version using sum.
I used the version using zip of #Steven Rumbalski from Duplicate elements in a list
I used the "pure" list-comp solution by #Wololo from the comments.
The results (on my machine) are (ordered ascending):
using_zip 5.630
using_repeat_comp 5.818
using_sum 5.905
using_repeat 6.421
using_pure 6.878
Surprisingly (to me), the zip solution is actually fastest, and the list-comp version of your repeat solution second. I would assume that the "pure" list-comp will be fastest but it was actually the worst. I guess TIL...
There is another result (proposed by #Henry Tjhia):
using_sort 3.219
Significantly better as you can see, but I put it separately as it relies heavily on the above specific example. So if you want to duplicate a list of ascending numbers that would be your best choice.
The code to produce them is:
import timeit
from itertools import repeat
x2 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
def using_repeat():
ls = []
for i in x2:
ls.append([x for item in i for x in repeat(item, 3)])
return ls
def using_repeat_comp():
return [[x for item in i for x in repeat(item, 3)] for i in x2]
def using_zip():
return [[x for triple in zip(l, l, l) for x in triple] for l in x2]
def using_sum():
return [sum([[i]*3 for i in k], []) for k in x2]
def using_pure():
return [[val for val in sublist for _ in range(3)] for sublist in x2]
def using_sort():
return [sorted(sub[:]*3) for sub in x2]
for func in [using_repeat, using_repeat_comp, using_zip, using_sum, using_pure, using_sort]:
print(func.__name__, timeit.timeit(f"{func.__name__}()", f"from __main__ import {func.__name__}"))

loop through a list and create arrays showing all sequences of n in a row, followed by the next iteration in the list

If I have a list, I'm attempting to create a function that will go through the list and create arrays of size n.
For example:
list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] with n = 3
Running the function would generate [0, 1, 2], [1, 2, 3], [2, 3, 4] etc.
If you want to write it as a function, you have two inputs:
input_list and n
Then you should iterate over the list, and make sub-lists with length n, i've done this using list slices.
def print_list(input_list,n):
for i in range(len(input_list)):
if len(input_list[i:i+n])==n:
print(input_list[i:i+n])
Here you can find the output of this program with an example :
>>> print_list([1,2,3,4,5,6],3)
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
You can use zip with lists with incremented offsets:
lst = list(range(11))
n = 3
out = []
for sub in zip(*[lst[i:] for i in range(n)]):
out.append(list(sub))
Since zip stops at the end of the final list, you won’t have any empty values at the end. And in a function:
def func(list, n):
return [[*x] for x in zip(*[list[i:] for i in range(n)])]
See if this works for you:
x=[0,1,2,3,4,5,6,7,8,9,10,11]
def lst(lst, n):
for x in range(len(lst)-(n-1)):
print(lst[x:x+n])
lst(x,3)
Output:
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]
[8, 9, 10]
[9, 10, 11]

Looking backward and forward in a circular loop in python

I would like to generate a list of single digits based on user input. In a circular iterative way, the list should contain the user input, the two digits before that, and the two digits after that. The order of the digits isn't important.
user_input = "1"
output = [9, 0, 1, 2, 3]
user_input = "9"
output = [7, 8, 9, 0, 1]
Using itertools.cycle I was able to get the next two digits, but I couldn't find an answer that can help me get the previous two digits. Is there a simple way to get those previous two digits?
from itertools import cycle
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
user_input = "139"
for i in user_input:
s = int(i)
lst = [s]
itr = cycle(numbers)
if s in itr:
#how can I get the two digits before s?
lst.append(next(itr)) #getting the next digit
lst.append(next(itr))
print(lst)
Could use a list comprehension and % 10:
>>> for s in range(10):
print([i % 10 for i in range(s-2, s+3)])
[8, 9, 0, 1, 2]
[9, 0, 1, 2, 3]
[0, 1, 2, 3, 4]
[1, 2, 3, 4, 5]
[2, 3, 4, 5, 6]
[3, 4, 5, 6, 7]
[4, 5, 6, 7, 8]
[5, 6, 7, 8, 9]
[6, 7, 8, 9, 0]
[7, 8, 9, 0, 1]
You can implement like this.
def backward_list(n):
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
if n == 0 or n == 1:
x = numbers.index(n)
else:
x = (numbers.index(n)-10)
return [numbers[x-2],numbers[x-1],numbers[x],numbers[x+1],numbers[x+2]]
Execution
In [1]: for i in range(10):
.....: print backward_list(i)
.....:
[8, 9, 0, 1, 2]
[9, 0, 1, 2, 3]
[0, 1, 2, 3, 4]
[1, 2, 3, 4, 5]
[2, 3, 4, 5, 6]
[3, 4, 5, 6, 7]
[4, 5, 6, 7, 8]
[5, 6, 7, 8, 9]
[6, 7, 8, 9, 0]
[7, 8, 9, 0, 1]
Modify your statements in the iff to this:
if s in itr and len(str) == 2:
lst.append(next(itr)) #getting the next digit
lst = [s - 1] + lst # prepend the first value
lst.append(next(itr))
lst = [s - 2] + lst # prepend the second value
Or you can also do
if s in itr and len(str) == 2:
lst.append(next(itr)) #getting the next digit
lst.insert(0, s-1) # prepend the first value
lst.append(next(itr))
lst.insert(0, s-2) # prepend the second value
You could take a range from the input and use that range to slice a numpy array
edit: my bad for writing code and not testing it... thanks #Stefan Pochmann for pointing that out...
import numpy as np
def cycle(x): #x is user input
indices = np.array(range(x-2, x+3))%10
numbers = np.array(range(10))
return numbers[indices]

How to perform multiple list slices at the same time?

Say I have a list:
L1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
and I have a list of indices:
indices = (2, 5, 7)
What I would like to be able to do is slice the list at all 3 indices at the same time. In other words, id like to perform the following lines of code in one line:
sub1 = L1[:2]
sub2 = L1[2:5]
sub3 = L1[5:7]
sub4 = L1[7:]
I could fairly easily write a function that does this, but I was wondering if it is possible to do this in one expression.
You could use operator.itemgetter with slice objects:
>>> from operator import itemgetter
>>> get = itemgetter(slice(0, 2), slice(2, 5), slice(5, 7), slice(7, None))
>>> values = range(1, 10)
>>> values
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> get(values)
([1, 2], [3, 4, 5], [6, 7], [8, 9])
Are you looking for a one liner? We could give you one, but I would just keep it simple:
L1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
indices = (2, 5, 7)
start = 0
sections = []
for end in indices:
sections.append(L1[start:end])
start = end
sections.append(L1[start:])
for part in sections:
print part
# [1, 2]
# [3, 4, 5]
# [6, 7]
# [8, 9]
Yes, it can be done in one expression, but it's not very readable:
seq = [1, 2, 3, 4, 5, 6, 7, 8, 9]
ind = (2, 5, 7)
print([seq[p:q] for p,q in zip((0,)+ind, ind+(len(seq),))])
output
[[1, 2], [3, 4, 5], [6, 7], [8, 9]]
If you really want to assign the sublists to separate names you can do it like this:
s1, s2, s3, s4 = [seq[p:q] for p,q in zip((0,)+ind, ind+(len(seq),))]
but it's probably better to just keep them in a list of lists.
Well, with the help of some built-in functionality you could do it in one line. The 'biggest' problem is transforming the list of end points to a list of (start,end) tuples or slice objects.
The code below extends the endpoints list with 0 on one end and None at the other end and uses zip() to combine pairs of them. Then use the operator.itemgetter() trick to extract the slices:
import operator
ends = [2,5,7]
values = range(20)
operator.itemgetter(*[slice(*z) for z in zip([0]+ends, ends+[None])])(values)
Output:
([0, 1], [2, 3, 4], [5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19])
List comprehensions are amazing.
Starting with your list:
>>> L1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
Pad your indices with None:
>>> indices = (None, 2, 5, 7, None)
Now comprehend away:
>>> [L1[start:stop] for start, stop in zip(indices[:-1], indices[1:])]
[[1, 2], [3, 4, 5], [6, 7], [8, 9]]

Categories

Resources