Looking backward and forward in a circular loop in python - 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]

Related

sum multiple list elements at the same time(python)

How can I sum multiple list elements at the same time?
For example, something like this in Python:
Our lists (input):
[3, 3, 1, 1, 1, 1, 1]
[1, 1, 4, 5, 6, 7, 8]
Output:
[4, 4, 5, 6, 7, 8, 9]
Note: we don't know how many list will be given to us.
This should do the job
l1 = [3, 3, 1, 1, 1, 1, 1]
l2 = [1, 1, 4, 5, 6, 7, 8]
l3 = [sum(t) for t in zip(l1, l2)]
print(l3)
As we don't know how many lists there will be, and I assume their lengths could be different, using the zip_longest function from itertools is the perfect tool:
from itertools import zip_longest
l1 = [3, 3, 1, 1, 1, 1, 1]
l2 = [1, 1, 4, 5, 6, 7, 8]
l3 = [1, 1, 4, 5, 6, 7, 8, -1]
l4 = [-105]
lists = [l1,l2,l3,l4]
summed = list(map(sum,zip_longest(*lists,fillvalue=0)))
print(summed)
Output:
[-100, 5, 9, 11, 13, 15, 17, -1]
This can be done very easily and efficiently using pandas.
lists = [[3, 3, 1, 1, 1, 1, 1], [1, 1, 4, 5, 6, 7, 8]]
df = pd.DataFrame(data=lists)
out = df.sum().tolist()
print(out):
[4, 4, 5, 6, 7, 8, 9]
It should work with any number of lists.
If you have varying number of list as input , you can try the below.,
note : input the numbers in list as comma-seperated values
Console
1,2,3
2,3,1
[3, 5, 4]
import pandas as pd
ls = []
while True:
inp = input()
if inp == "":
break
ls.append(list(map(int,inp.split(","))))
df = pd.DataFrame(data=ls)
out = df.astype(int).sum().tolist()
print(out)

checking if list contains same element in straight row

so,I have a list [5. 5. 5. 5. 0. 0.] I want to check if it contains same elements in straight row (atleast 4 )
I came up with this
for i in list:
w = []
for x in range(len(i)-4):
if (i[x] == i[x+1] == i[x+2] == i[x+3] != 0) :
print(i[x])
break
I gives me desired output,
but , what would the efficient way to achieving the same result ,without much looping ?
In numpy it's worth to find indices of value changes and return every index that preceeds another index that differs not less than 4.
x = np.array([6,6,6,6,7,7,8,8,4,4,4,4,4,4,4,3,3,3,3])
div_points = np.flatnonzero(np.diff(x, prepend=x[0]+1, append=x[-1]+1))
idx = np.r_[np.diff(div_points)>=4, False]
x[div_points[idx]]
>>> array([6, 4, 3])
And if you're quite lazy, you could just 'slide' all the comparisons:
view = np.lib.stride_tricks.sliding_window_view(x, 4)
view
array([[6, 6, 6, 6],
[6, 6, 6, 7],
[6, 6, 7, 7],
[6, 7, 7, 8],
[7, 7, 8, 8],
[7, 8, 8, 4],
[8, 8, 4, 4],
[8, 4, 4, 4],
[4, 4, 4, 4],
[4, 4, 4, 4],
[4, 4, 4, 4],
[4, 4, 4, 4],
[4, 4, 4, 3],
[4, 4, 3, 3],
[4, 3, 3, 3],
[3, 3, 3, 3]])
r = np.all(x[:-3, None] == view, axis=1)
x[:-3][r]
>>> array([6, 4, 4, 4, 4, 3])
Here's a one-liner that will return the indexes of all items in the specified 1D array that are identical to the next N items. Note that it requires the array be of floats because it uses NaN.
import functools as ft
N = 3
indexes = np.where(ft.reduce(lambda prev, cur: prev.astype(bool) & (cur == a), [np.pad(a, (0,i), constant_values=np.nan)[i:] for i in range(1, N+1)]))[0]
Example:
>>> import functools as ft
>>> a = np.array([1, 2, 3, 3, 3, 3, 4, 5, 10, 10, 10, 10, 10, 2, 4], dtype=float)
>>> N = 3
>>> indexes = np.where(ft.reduce(lambda prev, cur: prev.astype(bool) & (cur == a), [np.pad(a, (0,i), constant_values=np.nan)[i:] for i in range(1, N+1)]))[0]
>>> indexes
array([2, 8, 9])
Now, if we look at the array and the indexes:
[1, 2, 3, 3, 3, 3, 4, 5, 10, 10, 10, 10, 10, 2, 4]
^ 2 ^^ 8
^^ 9
Try this:
lst = [3, 5, 5, 5, 5, 0, 0]
for i in range(4, len(lst)):
if len(set(lst[i-4:i]))==1:
print(lst[i-4])
If you want to solve this problem without any library, you can iterate the list and count the Continuous elements:
a = [
[5, 5, 5, 5, 0, 0],
[1, 2, 3, 4, 5, 6],
[0, 1, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 0],
[0, 1, 2, 2, 2, 2],
[1, 1, 1, 1]
]
def contains_n(n, l):
c = 0
last = ""
for v in l:
if v == last:
c += 1
else:
c = 1
last = v
if c == n:
return True
return False
for v in a:
print(contains_n(4, v))
The output will be:
True
False
False
True
True
True

How to shuffle a python list such that the occurrence of some elements are preserved?

I start with a list of integers:
A = [ 1, 2, 5, 3, 4, 6, 7, 8 ]
After a shuffle, I would like some elements (say 3, 4 and 5) to preserve their order of occurrence in A while the rest of the elements are free to be randomly shuffled. Something like:
Outcome #1:
A = [ 5, 2, 3, 1, 8, 4, 6, 7 ]
-or-
Outcome #2:
A = [ 7, 5, 6, 1, 3, 4, 8, 2 ]
-but not-
Outcome #3 (invalid outcome)
A = [7, 4, 6, 1, 3, 5, 8, 2]
Appreciate all suggestions!
Extract the elements you want to maintain relative ordering among, shuffle as normal, then glue the lists back together by randomly picking indexes for the "kept" elements to be inserted. All operations are linear if you use sets for speeding up in operations, which I didn't bother with. Ordering of the keep list matters.
>>> import random
>>> L = [1, 2, 5, 3, 4, 6, 7, 8]
>>> keep = [2, 3, 4]
>>> kept = [L[i] for i in keep][::-1]
>>> unkept = [x for i, x in enumerate(L) if i not in keep]
>>> random.shuffle(unkept)
>>> idxes = random.sample(list(range(len(L))), k=len(keep))
>>> result = [kept.pop() if i in idxes else unkept.pop() for i in range(len(L))]
>>> result
[6, 5, 3, 8, 4, 1, 7, 2]
Random tests:
import random
def shuffle_with_fixed_order(L, keep):
kept = [L[i] for i in keep][::-1]
unkept = [x for i, x in enumerate(L) if i not in keep]
random.shuffle(unkept)
idxes = random.sample(list(range(len(L))), k=len(keep))
return [kept.pop() if i in idxes else unkept.pop() for i in range(len(L))]
if __name__ == "__main__":
for _ in range(5000):
L = list(range(50))
random.shuffle(L)
keep = sorted(random.sample(L, k=20))
shuffled = shuffle_with_fixed_order(L, keep)
new_locs = [shuffled.index(L[i]) for i in keep]
assert all(x < y for x, y in zip(new_locs, new_locs[1:]))
This is one approach using random module
Ex:
import random
A = [ 1, 2, 5, 3, 4, 6, 7, 8 ]
result = A[2:5]
del A[2:5]
while A:
l = len(A)
result.insert(random.randint(0, l), A.pop(random.randrange(l)))
print(result)
Demo
def rand_shfl(lst):
result = lst[2:5]
del lst[2:5]
while A:
l = len(A)
result.insert(random.randint(0, l), A.pop(random.randrange(l)))
return result
for _ in range(10):
A = [ 1, 2, 5, 3, 4, 6, 7, 8 ]
print((rand_shfl(A)))
Output:
[1, 7, 6, 5, 8, 3, 4, 2]
[8, 1, 2, 7, 5, 3, 6, 4]
[8, 6, 7, 2, 1, 5, 3, 4]
[1, 7, 2, 8, 5, 3, 4, 6]
[6, 8, 7, 5, 1, 3, 4, 2]
[7, 2, 5, 6, 1, 3, 4, 8]
[1, 8, 5, 2, 3, 4, 6, 7]
[5, 7, 1, 6, 3, 8, 4, 2]
[1, 5, 7, 3, 2, 4, 8, 6]
[8, 2, 7, 6, 5, 3, 4, 1]
Probably below will work for you
import random
lst=[1, 2, 5, 3, 4, 6, 7, 8]
s_lst = {5:0,3:0,4:0}
idx_lst = [random.randint(0, len(lst)) for i in range(len(s_lst))]
idx_lst.sort()
for i, s in enumerate(s_lst.keys()):
s_lst[s] = idx_lst[i]
lst.remove(s)
random.shuffle(lst)
for k, v in s_lst.items():
lst.insert(v, k)
print(lst)

Function to take a list of integers and count the highest repeated value

Need a function to take a list of integers and return the highest repeated value.
For example: take [4, 2, 2, 2, 8, 5, 4, 2, 9, 6, 3, 2] and return (2,5) because there are 5 copies of the value 2.
You can do this pretty easily using Counter.
from collections import Counter
a = [4, 2, 2, 2, 8, 5, 4, 2, 9, 6, 3, 2]
def count_duplicates(list_of_integers):
a = Counter(list_of_integers)
return a.most_common()[0]
count = count_duplicates(a)
print(count)
The output from that gives you (2, 5)
Inbuilt max, set and count methods:
def countMax(arr):
item = max(set(arr), key=arr.count)
return (item, arr.count(item))
print(countMax([4, 2, 2, 2, 8, 5, 4, 2, 9, 6, 3, 2]))
Prints (2, 5).
We can utilize tuple to keep track the highest repeated value, like so:
def find_highest_repeated(input_list):
counter = {}
highest_repeated = (0, 0, )
for item in input_list:
counter[item] = counter[item] + 1 if counter.get(item) is not None else 1
if counter[item] > highest_repeated[1]:
highest_repeated = (item, counter[item], )
return highest_repeated
find_highest_repeated([4, 2, 2, 2, 8, 5, 4, 2, 9, 6, 3, 2]) # (2, 5)
Hope that helps!
You can use defaultdict to achieve this:
from collections import defaultdict
def highest_repeated_value(nums):
cache = defaultdict(int)
for i in nums:
cache[i] += 1
return max(cache.items(), key=lambda x: x[1])
nums = [4, 2, 2, 2, 8, 5, 4, 2, 9, 6, 3, 2]
print(highest_repeated_value(nums))
Please note that if nums = [4, 4, 4, 4, 2, 2, 2, 8, 5, 4, 2, 9, 6, 3, 2] then there are five 4s and five 2s. However, the result will be (4, 5) i.e. five 4s.
If your using numpy and list contains all non-negative ints, you can use numpy.bincounts:
import numpy
def highest_repeated_value(nums):
counts = numpy.bincount(nums)
num = numpy.argmax(counts)
val = numpy.max(counts)
return num, val
nums = [4, 2, 2, 2, 8, 5, 4, 2, 9, 6, 3, 2]
print(highest_repeated_value(nums))
If you just want to work in python without using numpy, collections.Counter is a good way to handle it.
from collections import Counter
def highest_repeated_value(nums):
return Counter(nums).most_common()[0]
nums = [4, 2, 2, 2, 8, 5, 4, 2, 9, 6, 3, 2]
print(highest_repeated_value(nums))

How do I use modular arithmetic on indices?

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

Categories

Resources