Replace when n or less consecutive values are found - python

this is perhaps a very simple question
I have a list that looks like this:
a=[0,1,1,2,3,2,1,2,0,3,4,1,1,1,1,0,0,0,0,4,5,1,1,1,3,2,0,2,1,1,3,4,1]
I am struggling to find a simple python code that replaces when n or less consecutive 1s are found to 0s and creates a new list with the new values
So if
n = 2
b = [0,0,0,2,3,2,0,2,0,3,4,1,1,1,1,0,0,0,0,4,5,1,1,1,3,2,0,2,0,0,3,4,0]
if
n = 3
b = [0,0,0,2,3,2,0,2,0,3,4,1,1,1,1,0,0,0,0,4,5,0,0,0,3,2,0,2,0,0,3,4,0]
I have highlighted the new replaces values in each example

You can try this:
import itertools
a=[0,1,1,2,3,2,1,2,0,3,4,1,1,1,1,0,0,0,0,4,5,1,1,1,3,2,0,2,1,1,3,4,1]
n = 3
new_list = list(itertools.chain(*[[0]*len(b) if a == 1 and len(b) <= n else b for a, b in [(c, list(d)) for c, d in itertools.groupby(a)]]))
Output:
[0, 0, 0, 2, 3, 2, 0, 2, 0, 3, 4, 1, 1, 1, 1, 0, 0, 0, 0, 4, 5, 0, 0, 0, 3, 2, 0, 2, 0, 0, 3, 4, 0]

"One"-liner, using some itertools:
from itertools import groupby, chain
a=[0,1,1,2,3,2,1,2,0,3,4,1,1,1,1,0,0,0,0,4,5,1,1,1,3,2,0,2,1,1,3,4,1]
list(
chain.from_iterable(
([0] * len(lst) if x == 1 and len(lst) <= n else lst
for x, lst in ((k, list(g)) for k, g in groupby(a)))
)
)
# [0,0,0,2,3,2,0,2,0,3,4,1,1,1,1,0,0,0, 0,4,5,1,1,1,3,2,0,2,0,0,3,4,0]
groupby groups the initial list into groups of identical objects. Its output is an iterator of pairs (k, g) where k is the element that is the grouping key and g is an iterator producing the actual elements in the group.
Since you cannot call len on an iterator, this listifies the groups and chains the resulting lists except lists of 1 of the appropriate lengthes. Those are replaced by lists of 0 of the same length.
In single steps (using intermediate lists instead of generators):
grouped_lists_by_key = [k, list(g)) for k, g in groupby(a)]
# [(0, [0]), (1, [1, 1]), ...]
grouped_lists = [[0] * len(lst) if x == 1 and len(lst) <= n else lst for x, lst in grouped]
# [[0], [0, 0], [2], [3], ...]
flattened = chain.from_iterable(grouped_lists)
# [0, 0, 0, 2, 3, ...]

Non-oneliner using itertools.groupby():
a = [0,1,1,2,3,2,1,2,0,3,4,1,1,1,1,0,0,0,0,4,5,1,1,1,3,2,0,2,1,1,3,4,1]
n = 2
b = []
for k, g in groupby(a):
l = list(g)
if k == 1 and len(l) <= n:
b.extend([0]*len(l))
else:
b.extend(l)
print(b)

Try this:
def replacer(array, n):
i, consec = 0, 0
while i < len(array):
if array[i] == 1:
consec += 1
else:
if consec >= n:
for x in range(i-consec, i):
array[x] = 0
consec = 0
i += 1
return array

Longer than others, but arguably straightforward:
a = [0,1,1,2,3,2,1,2,0,3,4,1,1,1,1,0,0,0,0,4,5,1,1,1,3,2,0,2,1,1,3,4,1]
def suppress_consecutive_generator(consecutive=2, toreplace=1, replacement=0):
def gen(l):
length = len(l)
i = 0
while i < length:
if l[i] != toreplace:
yield l[i]
i += 1
continue
j = i
count = 0
while j < length:
if l[j] != toreplace:
break
count += 1
j += 1
i += count
if count <= consecutive:
for _ in range(count):
yield replacement
else:
for _ in range(count):
yield toreplace
return gen
print(list(suppress_consecutive_generator()(a)))

Related

check if key is present in every segment of size k in array?

def findxinkindowSize(arr, x, k, n) :
i = 0
while i < n :
j = 0
# Search x in segment
# starting from index i
while j < k :
if arr[i + j] == x :
break
j += 1
# If loop didn't break
if j == k :
return False
i += k
# If n is a multiple of k
if i == n :
return True
j = i - k
# Check in last segment if n
# is not multiple of k.
while j < n :
if arr[j] == x :
break
j += 1
if j == n :
return False
return True
# Driver Code
if __name__ == "__main__" :
arr = [ 3, 5, 2, 4, 9, 3,
1, 7, 3, 11, 12, 3 ]
x, k = 3, 3
n = len(arr)
if (findxinkindowSize(arr, x, k, n)) :
print("Yes")
else :
print("No")
Above code works good for the array of size 3, 6, 9, 12, ... i.e multiples of size of k (segment size), but for the array arr = [3, 5, 2, 4, 9, 3, 1, 7] which is of size 8 there is an error in the line if arr[i + j] == x : shows out of index.
Is there any better solution for this problem?
This is a simple approach using slicing.
Iterate through every slice of size k and check if x is present in that slice or not.
def findxinkindowSize(arr, x, k, n):
for i in range(0, n, k):
if x not in arr[i: i+k]:
return False
return True
This code doesn't give IndexError.

Algorithm to find shortest continuous subarray that contains all values from a set

I have the following problem to solve:
Given a set of integers, e.g. {1,3,2}, and an array of random integers, e.g.
[1, 2, 2, -5, -4, 0, 1, 1, 2, 2, 0, 3,3]
Find the shortest continuous subarray that contains all of the values from the set. If the subarray can not be found, return an empty array.
Result: [1, 2, 2, 0, 3]
Or
[1, 2, 2, -5, -4, 3, 1, 1, 2, 0], {1,3,2}.
Result: [3, 1, 1, 2]
I have tried the following put there seems to be something wrong with my second loop. I'm not sure what I need to change:
def find_sub(l, s):
i = 0
counts = dict()
end = 0
while i < len(s):
curr = l[end]
if curr in s:
if curr in counts:
counts[curr] = counts[curr] + 1
else:
counts[curr] = 1
i += 1
end += 1
curr_len = end
start = 0
for curr in l:
if curr in counts:
if counts[curr] == 1:
if end < len(l):
next_item = l[end]
if next_item in counts:
counts[next_item] += 1
end += 1
else:
counts[curr] -= 1
start += 1
else:
start += 1
if (end - start) < curr_len:
return l[start:end]
else:
return l[:curr_len]
You are using two-pointer approach, but move both indexes only once - until the first match found. You should repeat move right - move left pattern to get the best index interval.
def find_sub(l, s):
left = 0
right = 0
ac = 0
lens = len(s)
map = dict(zip(s, [0]*lens))
minlen = 100000
while left < len(l):
while right < len(l):
curr = l[right]
right += 1
if curr in s:
c = map[curr]
map[curr] = c + 1
if c==0:
ac+=1
if ac == lens:
break
if ac < lens:
break
while left < right:
curr = l[left]
left += 1
if curr in s:
c = map[curr]
map[curr] = c - 1
if c==1:
ac-=1
break
if right - left + 1 < minlen:
minlen = right - left + 1
bestleft = left - 1
bestright = right
return l[bestleft:bestright]
print(find_sub([1, 2, 2, -5, -4, 3, 1, 0, 1, 2, 2, 0, 3, 3], {1,3,2}))
print(find_sub([1, 2, 2, -5, -4, 3, 1, 0, 1, 2, 2, 1, 0, 3, 3], {1,3,2}))
>>[2, -5, -4, 3, 1]
>>[2, 1, 0, 3]
You can use a sliding window approach (using a generator), the idea is to generate all subsets of size n (size of the set) to size N (size of the list), and check if any of them exists, stopping when finding the first one:
from itertools import islice, chain
def window(seq, n=2):
"Returns a sliding window (of width n) over data from the iterable"
" s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ... "
it = iter(seq)
result = tuple(islice(it, n))
if len(result) == n:
yield result
for elem in it:
result = result[1:] + (elem,)
yield result
l = [1, 2, 2, -5, -4, 3, 1, 1, 2, 0]
s = {1,3,2}
def minimum_subset(l, s):
for w in chain.from_iterable(window(l, i) for i in range(len(s), len(l)+1)):
if s == set(w):
return w
return []
print(minimum_subset(l, s))
Result (3, 1, 1, 2)
Here you have the live example
This should be the most performant solution, running in O(n):
def find_sub(l, s):
if len(l) < len(s):
return None
# Keep track of how many elements are in the interval
counters = {e: 0 for e in s}
# Current and best interval
lo = hi = 0
best_lo = 0
best_hi = len(l)
# Increment hi until all elements are in the interval
missing_elements = set(s)
while hi < len(l) and missing_elements:
e = l[hi]
if e in counters:
counters[e] += 1
if e in missing_elements:
missing_elements.remove(e)
hi += 1
if missing_elements:
# Array does not contain all needed elements
return None
# Move the two pointers
missing_element = None
while hi < len(l):
if missing_element is None:
# We have all the elements
if hi - lo < best_hi - best_lo:
best_lo = lo
best_hi = hi
# Increment lo
e = l[lo]
if e in counters:
counters[e] -= 1
if counters[e] == 0:
missing_element = e
lo += 1
else:
# We need more elements, increment hi
e = l[hi]
if e in counters:
counters[e] += 1
if missing_element == e:
missing_element = None
hi += 1
return l[best_lo:best_hi]
assert find_sub([1, 2, 2, -5, -4, 3, 1, 0, 1, 2, 2, 0, 3, 3], {1, 3, 2}) == [2, -5, -4, 3, 1]
assert find_sub([1, 2, 2, -5, -4, 3, 1, 0, 1, 2, 2, 1, 0, 3, 3], {1, 3, 2}) == [2, 1, 0, 3]
assert find_sub([1, 2, 2, -5, -4, 3, 1, 0, 1, 2, 2, 1, 0, 3, 3], {1, 3, 7}) is None
Joining in the fun, here's my attempt. I'm not familiar with algorithm names, but this would seem like a sliding window approach based on #Netwave's description for his answer.
I = {1, 3, 2}
A = [1, 2, 2, -5, -4, 0, 1, 1, 2, 2, 0, 3, 3]
setcount = {i: 0 for i in I}
stage = []
shortest = A
for i in range(len(A)):
# Subset
stage.append(A[i])
# Update the count
if A[i] in I:
setcount[A[i]] += 1
while 0 not in setcount.values():
# Check if new subset is shorter than existing's
if len(stage) < len(shortest):
shortest = stage.copy()
# Consume the head to get progressively shorter subsets
if stage[0] in I:
setcount[stage[0]] -= 1
stage.pop(0)
>>>print(shortest)
[1, 2, 2, 0, 3]

How to find how many times a number occurs in a list without using any in-built functions

I am trying to find how many times a number occurs in a list without using any in-built functions. The below code won't work:
a = [1,1,2,3,4,4,5]
for i in a:
c = 0
if a[i] == a[i]+1:
c =+1
print(c)
Num is the number you are looking for. Not sure if that is what you are want.
a = [1,1,1,1,2,3,4,4,5]
c = 0
num = 1;
for i in a:
if i == num:
c += 1
print(c)
Or this
a = [1,1,1,1,2,3,4,4,5]
b = []
t = 0
x = 0
while t < len(a):
c = 0
temp = a
for i in temp:
if i == x:
c += 1
b.append(c)
t += c
x += 1
print(b)
outputs [0, 4, 1, 1, 2, 1]
I'm surprised to see 3 answers with no use of a dictionary to solve this problem.
l = [1, 1, 2, 3, 4, 4, 5]
counts = {}
for x in l:
if x in counts:
counts[x] += 1
else:
counts[x] = 1
After running the above code, counts stores the number of occurrences of each item in list l with the items themselves (numbers, in this case) as keys.
>>> l = [1, 1, 2, 3, 4, 4, 5]
>>> counts = {}
>>> for x in l:
... if x in counts:
... counts[x] += 1
... else:
... counts[x] = 1
...
>>> counts
{1: 2, 2: 1, 3: 1, 4: 2, 5: 1}
An ugly but almost fun way I have seen this done is to loop through your list, find the maximum, create a list of that size, and then reloop through and increment the index in your new list as you hit your values.
a = [1,1,2,3,4,4,5]
max = -1
for i in a:
if i > max:
max = i
long_list = [0] * (max + 1) #create the list of the max size
for i in a:
long_list[i] = long_list[i] + 1
print(long_list)
output here is: [0, 2, 1, 1, 2, 1]
Again this is not space efficient at all but I enjoy the implementation as I think it is fun.
The issue with this implementation is if you have a list such as [1,2,3,545543,34]. Then your output will be a little wild printed that way and a lot of space is wasted.
Surprisingly this is easy if you know the max. I assumed your min=0 and max=5 (you can change)
a = [1,1,2,3,4,4,5]
freq=[0]*6 # assume 5 is your max
for i in a:
freq[i] += 1
print(freq)
print(freq[num])
DEFAULTDICT
if you dont know the max
from collections import defaultdict
a = [1,1,2,3,4,4,5,5,5,5,5,5,5]
d=defaultdict(int)
for i in a:
d[i] +=1
print(d)

Find items and repetitions in list

I am working in Python and considering the following problem: given a list, such as [1, 0, -2, 0, 0, 4, 5, 0, 3] which contains the integer 0 multiple times, I would like to have the indices at of these 0 and for each one, the number of times it appears in the list until a different element appears or the list ends.
Given l = [1, 0, -2, 0, 0, 4, 5, 0], the function would return ((1, 1), (3, 2), (7, 1)). The result is a list of tuples. The first element of the tuple is the index (in the list) of the given element and the second is the number of times it is repeated until a different element appears or the list ends.
Naively, I would write something like this:
def myfun(l, x):
if x not in l:
print("The given element is not in list.")
else:
j = 0
n = len(l)
r = list()
while j <= (n-2):
count = 0
if l[j] == x:
while l[j + count] == x and j <= (n-1):
count +=1
r.append((j, count))
j += count
else:
j += 1
if l[-1] == x:
r.append((n-1, 1))
return r
But I was wondering whether there would be a nicer (shorter?) way of doing the same thing.
Not the prettiest, but a one-liner:
>>> import itertools
>>> l=[1, 0, -2, 0, 0, 4, 5, 0]
>>> [(k[0][0],len(k)) for k in [list(j) for i,j in itertools.groupby(enumerate(l), lambda x: x[1]) if i==0]]
[(1, 1), (3, 2), (7, 1)]
First, itertools.groupby(enumerate(l), lambda x: x[1]) will group by the second item of enumerate(l), but keep the index of the item.
Then [list(j) for i,j in itertools.groupby(enumerate(l), lambda x: x[1]) if i==0] will keep only the 0 values.
Finally, the last list comprehension is needed because list(j) consume the itertools object.
Another oneliner with groupby, without using intermediate lists:
>>> from itertools import groupby
>>> l = [1, 0, -2, 0, 0, 4, 5, 0, 3]
>>> [(next(g)[0], 1 + sum(1 for _ in g)) for k, g in groupby(enumerate(l), key=lambda x: x[1]) if k == 0]
[(1, 1), (3, 2), (7, 1)]
In above enumerate will return (index, value) tuples which are then grouped by the value. groupby returns (key, iterable) tuples and if key is nonzero the group is discarded. For kept groups next is used to pull out the first item in the group and take index from there while rest of the items are processed by generator expression given to sum in order to get the count.
This is how i would do this
l=[1, 0, -2, 0, 0, 4, 5, 0]
lis=[]
t=0
for m in range(len(l)):
if l[m]==0:
if t==0:
k=m
j=1
t=1
else:
j=j+1
t=1
if m==len(l)-1:
lis.append((k,j))
else:
if t==1:
t=0
lis.append((k,j))
Another solution, using itertools.takewhile:
from itertools import takewhile
L = [1, 0, -2, 0, 0, 4, 5, 0]
res = []
i = 0
while i < len(L):
if L[i] == 0:
t = len(list(takewhile(lambda k: k == 0, L[i:])))
res.append((i, t))
i += t
else:
i += 1
print(res)
The line
t = len(list(takewhile(lambda k: k == 0, L[i:])))
counts the number of zeroes there are from the current position to the right.
While clear enough, the disadvantage of this solution is that it needs the whole list before processing it.

convert a matrix(list of lists) into a square matrix in python

I want to convert the given matrices a, b into square matrices by inserting zeros wherever necessary
a = [[1,2],[3,4],[5,6],[7,8]]
b = [[1,2,3,4],[5,6,7,8]]
I want the output to be
a1 = [[1,2,0,0],[3,4,0,0],[5,6,0,0],[7,8,0,0]]
b1 = [[1,2,3,4],[5,6,7,8][0,0,0,0],[0,0,0,0]]
I have trouble installing numpy package on my machine. Any solution without the use of numpy would greatly help.
Thanks
>>> a = [[1,2], [3,4], [5,6], [7,8]]
>>> b = [[1,2,3,4], [5,6,7,8]]
>>>
>>> def matrix(a, n):
... for row in a:
... yield row + [0] * (n - len(row))
... for i in range(len(a), n):
... yield [0] * n
...
>>> list(matrix(a, 4))
[[1, 2, 0, 0], [3, 4, 0, 0], [5, 6, 0, 0], [7, 8, 0, 0]]
>>> list(matrix(b, 4))
[[1, 2, 3, 4], [5, 6, 7, 8], [0, 0, 0, 0], [0, 0, 0, 0]]
This little function ought to do the trick:
def squarify(lst, n):
for sublist in lst:
while len(sublist) < n:
sublist.append(0)
while len(lst) < n:
lst.append([0]*n)
You need two pieces of information: the longest row, and the most number of columns. To get longest row, assuming that you can assume that each matrix has a set number of rows, you need to do something like
rowAlen = len(a[0])
rowBlen = len(b[0])
longestRow = 0;
if rowAlen > rowBlen:
longestRow = rowAlen
else:
longestRow = rowBlen
Then you'll need to get the number of columns of the matrix with the most columns.
colAlen = len(a)
colBlen = len(b)
maxCol = 0;
if colAlen > colBlen:
maxCol = colAlen
else:
maxCol = colBlen
Now that you have longestRow and maxCol, you can manipulate matrices.
for x in range(0,maxCol):
if colAlen <= x:
a.append([])
if colBlen <= x:
b.append([])
for y in range(0, longestRow):
if len(a[x]) <= y:
a[x].append(0)
if len(b[x]) <= y:
b[x].append(0)
This will return the output you are looking for, as well as alter the original list if it was stored in a variable.
def matrize(L):
msize = max(len(L), max([len(subL) for subL in L]))
for item in L:
while len(item)<msize: item.append(0)
zrow = [0]*msize
while len(L)< msize: L.append(zrow)
return L

Categories