Modifying an array in python using nested loops - python

I have an array, p=(1,2,4,5,7,10...) and I would like to input a 0 in where the array deviates from f=(1,2,3,4,5,6...). I have tried using nested for loops but I can't work out how to make the desired output=(1,2,0,4,5,0,7,0,0,10...) using python.
This is all I have really got so far, but it iterates for p[0] with all the elements of f before moving on to p[1] and I don't know how to prevent this:
for x in f:
for y in p:
if x==y:
print(x)
break
else:
print('0')
Thank you!

I'd suggest to make p a set so that checking membership is fast:
>>> p = (1,2,4,5,7,10)
>>> p_set = set(p)
>>> tuple([i if i in p_set else 0 for i in range(11)])
(0, 1, 2, 0, 4, 5, 0, 7, 0, 0, 10)

code:
p =(1,2,4,5,7,10)
f =tuple(range(1,11))
for x in f:
for y in p:
if x == y:
print(x)
break
else:
print('0')
result:
1
2
0
4
5
0
7
0
0
10

You don't need a nested loop, just iterate through the full numbers' array and insert a zero. Example
p = [1,2,3,4,5,6,7,8,9,10,11,12,13,14, 15]
f = [1,2,4,5,6,7,10,14,15]
for index,i in enumerate(p):
if i == f[index]:
pass
else:
f.insert(index, 0)
Result
[1, 2, 0, 4, 5, 6, 7, 0, 0, 10, 0, 0, 0, 14, 15]

Related

Python loops with break/continue

Given a set of integers numbers = [10, 0, 11, 5, 3, 0, 6, 0, 2, 0, 6, 9] containing at least two zeros. Print the sum of numbers from the given set located between the last two zeros (if the last zeros are in a row, then print 0).
My attempt:
numbers = [10, 0, 11, 5, 3, 0, 6, 0, 2, 0, 6, 9]
index_of_first_zero = 0
i = 0
while i < len(numbers):
if numbers[i] == 0:
index_first_zero = 1
break
i += 1
index_of_last_zero = len(numbers) - 1
i = len(numbers) - 1
while i >= 0:
if numbers[i] == 0:
index_of_last_zero = i
break
i -= 1
summa = 0
while i in range(index_of_first_zero+1, index_of_last_zero):
summa += numbers[i]
print("Summa =", summa)
But unput is Summa = 0
Can you help me please?
It's much easier to reverse the list and look for the first two zeros.
>>> numbers = [10, 0, 11, 5, 3, 0, 6, 0, 2, 0, 6, 9]
>>> numbers_rev = reversed(numbers)
>>> sum_ = 0
>>>
>>> for x in numbers_rev:
... if x == 0:
... break
>>>
>>> for x in numbers_rev:
... if x == 0:
... break
... sum_ += x
>>>
>>> sum_
2
Alternative:
numbers = [10, 0, 11, 5, 3, 0, 6, 0, 2, 5, 6, 0, 6, 9]
numbers_rev = numbers[::-1]
sum_ = 0
for x in numbers_rev[numbers_rev.index(0)+1:]:
if x == 0:
break
sum_ += x
This should do the trick...
a = [10, 0, 11, 5, 3, 0, 6, 0, 2, 0, 6, 9]
i1 = a[::-1].index(0)
i2 = a[::-1].index(0,i1+1)
print("Summa =",sum(a[len(a)-i2:len(a)-i1]))
I hope this makes it clear :)
#take your original list
numbers = [10, 0, 11, 5, 3, 0, 6, 0, 2, 0, 6, 9]
#reverse the list
numbes = numbers.reverse()
#make clear that the first zero has not been encountered
encountered_zero = False
#set the temporary sum to 0
cur_sum = 0
#for every number in your list
for number in numbers:
#if it's a zero, and you haven't passed any yet
if number == 0 and not encountered_zero:
#mark it
encountered_zero = True
#skip the rest of the iteration
continue
#if you have already encountered a zero
if encountered_zero == True:
#add every number to the sum
cur_sum += number
#if you encounter another zero
if encountered_zero == True and number == 0:
#break out of the loop, you're done
break
#here you have your answer
summa = cur_sum
print("Summa =", summa)
There are a few mistakes in your program...
One mistake in your program is that in the final part you're telling the script: "While the variable i is in the iterator range do [...]"
However, you should put a for loop there, not a while, changing it to:
summa = 0
for i in range(index_of_first_zero+1, index_of_last_zero):
summa += numbers[i]
print("Summa = ", summa)
Now that part of the program should work properly, because i in the for loop will be replaced with the values in the range iterator.
Instead, with the while, i takes index_of_last_zero as value, so it will never be in the range iterator.
However, the second error is a logical one: you have to sum the numbers between the last two zeros, not between the first and the last one, so the best thing to do is to reverse the list as other users already answered, so the entire program has to change:
original_list = [10, 0, 11, 5, 3, 0, 6, 0, 2, 0, 6, 9]
reversed_list = reversed(original_list)
my_sum = 0
for num in reversed_list:
if num == 0:
# it breaks here because it found the first zero,
# and then it will continue the cycle from the next element
break
# Now it won't loop again from the beginning, but from where it broke before.
for num in reversed_list:
if num == 0:
break
my_sum += num
print(my_sum) # -> 2
This program will work, thanks to #timgeb, and works with reversed() which is a built-in function that returns an Iterator object.
Here will be clarified to you what is an Iterator and how to work with it.
Anyway, I'll put here another solution that won't use that function.
original_list = [10, 0, 11, 5, 3, 0, 6, 0, 2, 0, 6, 9]
reversed_list = [] # we'll create it manually
my_sum = 0
# let's create here the reversed_list
for i in range(len(original_list) - 1, 0, -1):
reversed_list.append(original_list[i])
while i < len(reversed_list):
if reversed_list[i] == 0:
# it breaks here because it found the first zero,
# and we store the index of the first zero.
index_first_zero = i
break
i += 1
# Now it won't loop again from the beginning, but from where it broke before.
for i in range(index_first_zero, len(reversed_list)):
if reversed_list[i] == 0:
break
my_sum += reversed_list[i]
print(my_sum) # -> 2

How to filter Python list while keeping filtered values zero

With
input = [0,0,5,9,0,4,10,3,0]
as list
I need an output, which is going to be two highest values in input while setting other list elements to zero.
output = [0,0,0,9,0,0,10,0,0]
The closest I got:
from itertools import compress
import numpy as np
import operator
input= [0,0,5,9,0,4,10,3,0]
top_2_idx = np.argsort(test)[-2:]
test[top_2_idx[0]]
test[top_2_idx[1]]
Can you please help?
You can sort, find the two largest values, and then use a list comprehension:
input = [0,0,5,9,0,4,10,3,0]
*_, c1, c2 = sorted(input)
result = [0 if i not in {c1, c2} else i for i in input]
Output:
[0, 0, 0, 9, 0, 0, 10, 0, 0]
Not as pretty as Ajax's solution but a O(n) solution and a little more dynamic:
from collections import deque
def zero_non_max(lst, keep_top_n):
"""
Returns a list with all numbers zeroed out
except the keep_top_n.
>>> zero_non_max([0, 0, 5, 9, 0, 4, 10, 3, 0], 3)
>>> [0, 0, 5, 9, 0, 0, 10, 0, 0]
"""
lst = lst.copy()
top_n = deque(maxlen=keep_top_n)
for index, x in enumerate(lst):
if len(top_n) < top_n.maxlen or x > top_n[-1][0]:
top_n.append((x, index))
lst[index] = 0
for val, index in top_n:
lst[index] = val
return lst
lst = [0, 0, 5, 9, 0, 4, 10, 3, 0]
print(zero_non_max(lst, 2))
Output:
[0, 0, 0, 9, 0, 0, 10, 0, 0]
Pure numpy approach:
import numpy as np
arr = np.array([0, 0, 5, 9, 0, 4, 10, 3, 0])
top_2_idx = np.argsort(arr)[-2:]
np.put(arr, np.argwhere(~np.isin(arr, arr[top_2_idx])), 0)
print(arr)
The output:
[ 0 0 0 9 0 0 10 0 0]
Numpy.put
It's possible to achieve this with a single list traversal, making the algorithm O(n):
First find the two highest values with a single traversal;
Then create a list of zeros and add in the found maxima.
Code
def two_max(lst):
# Find two highest values in a single traversal
max_i, max_j = 0, 1
for i in range(len(lst)):
_, max_i, max_j = sorted((max_i, max_j, i), key=lst.__getitem__)
# Make a new list with zeros and replace both maxima
new_lst = [0] * len(lst)
new_lst[max_i], new_lst[max_j] = lst[max_i], lst[max_j]
return new_lst
lst = [0, 0, 5, 9, 0, 4, 10, 3, 0]
print(two_max(lst)) # [0, 0, 0, 9, 0, 0, 10, 0, 0]
Note that if the maximum value in the list appears more than twice, only the two left-most values will appear.
As a sidenote, do not use names such as input in your code as this overshadows the built-in function of the same name.
Here is another numpy-based solution that avoids sorting the entire array, which takes O(nlogn) time.
import numpy as np
arr = np.array([0,0,5,9,0,4,10,3,0])
arr[np.argpartition(arr,-2)[:-2]] = 0
If you want to create a new array as output:
result = np.zeros_like(arr)
idx = np.argpartition(arr,-2)[-2:]
result[idx] = arr[idx]
A corresponding Python-native solution is to use heap.nlargest, which also avoids sorting the entire array.
import heapq
arr = [0,0,5,9,0,4,10,3,0]
l = len(arr)
idx1, idx2 = heapq.nlargest(2, range(l), key=arr.__getitem__)
result = [0] * l
result[idx1] = arr[idx1]
result[idx2] = arr[idx2]

Python: How to group elements in the list and get sum of groups

I want to group list digits by their value and calculate sum of each group. Basically, it looks this way:
input:
list = [0,1,1,0,0,0,1,0,1,1,1,1,0]
OUT (which i want to get):
newList = [0,2,0,1,0,4,0]
Any ideas how to acheive this in Python?
Thanks
You can use itertools.groupby:
import itertools
s = [0,1,1,0,0,0,1,0,1,1,1,1,0]
final_s = [sum(b) for _, b in itertools.groupby(s)]
Output:
[0, 2, 0, 1, 0, 4, 0]
Here is recursive approach with one loop.
list1 = [0,1,1,0,0,0,1,0,1,1,1,1,0]
final_=[]
def recursive(lst):
track = []
if not lst:
return 0
else:
for i,j in enumerate(lst):
try:
if lst[i]==lst[i+1]:
track.append((lst[i],lst[i+1]))
else:
track.append(lst[i])
final_.append(track)
return recursive(lst[i+1:])
except IndexError:
final_.append([i])
recursive(list1)
print(list(map(lambda x:0 if 0 in x else len(x),final_)))
output:
[0, 2, 0, 1, 0, 4, 0]
Try this:
ist = [0,1,1,0,0,0,1,0,1,1,1,1,0]
a=[[]]
[a[-1].append(e) if e==ist[c-1] else a.append([e]) for c,e in enumerate(ist)]
new_list=[sum(l) for l in a]
Doesn't need libraries. Output:
[0, 2, 0, 1, 0, 4, 0]

Count number of tails since the last head

Consider a sequence of coin tosses: 1, 0, 0, 1, 0, 1 where tail = 0 and head = 1.
The desired output is the sequence: 0, 1, 2, 0, 1, 0
Each element of the output sequence counts the number of tails since the last head.
I have tried a naive method:
def timer(seq):
if seq[0] == 1: time = [0]
if seq[0] == 0: time = [1]
for x in seq[1:]:
if x == 0: time.append(time[-1] + 1)
if x == 1: time.append(0)
return time
Question: Is there a better method?
Using NumPy:
import numpy as np
seq = np.array([1,0,0,1,0,1,0,0,0,0,1,0])
arr = np.arange(len(seq))
result = arr - np.maximum.accumulate(arr * seq)
print(result)
yields
[0 1 2 0 1 0 1 2 3 4 0 1]
Why arr - np.maximum.accumulate(arr * seq)? The desired output seemed related to a simple progression of integers:
arr = np.arange(len(seq))
So the natural question is, if seq = np.array([1, 0, 0, 1, 0, 1]) and the expected result is expected = np.array([0, 1, 2, 0, 1, 0]), then what value of x makes
arr + x = expected
Since
In [220]: expected - arr
Out[220]: array([ 0, 0, 0, -3, -3, -5])
it looks like x should be the cumulative max of arr * seq:
In [234]: arr * seq
Out[234]: array([0, 0, 0, 3, 0, 5])
In [235]: np.maximum.accumulate(arr * seq)
Out[235]: array([0, 0, 0, 3, 3, 5])
Step 1: Invert l:
In [311]: l = [1, 0, 0, 1, 0, 1]
In [312]: out = [int(not i) for i in l]; out
Out[312]: [0, 1, 1, 0, 1, 0]
Step 2: List comp; add previous value to current value if current value is 1.
In [319]: [out[0]] + [x + y if y else y for x, y in zip(out[:-1], out[1:])]
Out[319]: [0, 1, 2, 0, 1, 0]
This gets rid of windy ifs by zipping adjacent elements.
Using itertools.accumulate:
>>> a = [1, 0, 0, 1, 0, 1]
>>> b = [1 - x for x in a]
>>> list(accumulate(b, lambda total,e: total+1 if e==1 else 0))
[0, 1, 2, 0, 1, 0]
accumulate is only defined in Python 3. There's the equivalent Python code in the above documentation, though, if you want to use it in Python 2.
It's required to invert a because the first element returned by accumulate is the first list element, independently from the accumulator function:
>>> list(accumulate(a, lambda total,e: 0))
[1, 0, 0, 0, 0, 0]
The required output is an array with the same length as the input and none of the values are equal to the input. Therefore, the algorithm must be at least O(n) to form the new output array. Furthermore for this specific problem, you would also need to scan all the values for the input array. All these operations are O(n) and it will not get any more efficient. Constants may differ but your method is already in O(n) and will not go any lower.
Using reduce:
time = reduce(lambda l, r: l + [(l[-1]+1)*(not r)], seq, [0])[1:]
I try to be clear in the following code and differ from the original in using an explicit accumulator.
>>> s = [1,0,0,1,0,1,0,0,0,0,1,0]
>>> def zero_run_length_or_zero(seq):
"Return the run length of zeroes so far in the sequnece or zero"
accumulator, answer = 0, []
for item in seq:
accumulator = 0 if item == 1 else accumulator + 1
answer.append(accumulator)
return answer
>>> zero_run_length_or_zero(s)
[0, 1, 2, 0, 1, 0, 1, 2, 3, 4, 0, 1]
>>>

Count number of clusters of non-zero values in Python?

My data looks something like this:
a=[0,0,0,0,0,0,10,15,16,12,11,9,10,0,0,0,0,0,6,9,3,7,5,4,0,0,0,0,0,0,4,3,9,7,1]
Essentially, there's a bunch of zeroes before non-zero numbers and I am looking to count the number of groups of non-zero numbers separated by zeros. In the example data above, there are 3 groups of non-zero data so the code should return 3.
Number of zeros between groups of non-zeros is variable
Any good ways to do this in python? (Also using Pandas and Numpy to help parse the data)
With a as the input array, we could have a vectorized solution -
m = a!=0
out = (m[1:] > m[:-1]).sum() + m[0]
Alternatively for performance, we might use np.count_nonzero which is very efficient to count bools as is the case here, like so -
out = np.count_nonzero(m[1:] > m[:-1]) + m[0]
Basically, we get a mask of non-zeros and count rising edges. To account for the first element that could be non-zero too and would not have any rising edge, we need to check it and add to the total sum.
Also, please note that if input a is a list, we need to use m = np.asarray(a)!=0 instead.
Sample runs for three cases -
In [92]: a # Case1 :Given sample
Out[92]:
array([ 0, 0, 0, 0, 0, 0, 10, 15, 16, 12, 11, 9, 10, 0, 0, 0, 0,
0, 6, 9, 3, 7, 5, 4, 0, 0, 0, 0, 0, 0, 4, 3, 9, 7,
1])
In [93]: m = a!=0
In [94]: (m[1:] > m[:-1]).sum() + m[0]
Out[94]: 3
In [95]: a[0] = 7 # Case2 :Add a non-zero elem/group at the start
In [96]: m = a!=0
In [97]: (m[1:] > m[:-1]).sum() + m[0]
Out[97]: 4
In [99]: a[-2:] = [0,4] # Case3 :Add a non-zero group at the end
In [100]: m = a!=0
In [101]: (m[1:] > m[:-1]).sum() + m[0]
Out[101]: 5
You may achieve it via using itertools.groupby() with list comprehension expression as:
>>> from itertools import groupby
>>> len([is_true for is_true, _ in groupby(a, lambda x: x!=0) if is_true])
3
simple python solution, just count changes from 0 to non-zero, by keeping track of the previous value (rising edge detection):
a=[0,0,0,0,0,0,10,15,16,12,11,9,10,0,0,0,0,0,6,9,3,7,5,4,0,0,0,0,0,0,4,3,9,7,1]
previous = 0
count = 0
for c in a:
if previous==0 and c!=0:
count+=1
previous = c
print(count) # 3
pad array with a zero on both sides with np.concatenate
find where zero with a == 0
find boundaries with np.diff
sum up boundaries found with sum
divide by two because we will have found twice as many as we want
def nonzero_clusters(a):
return int(np.diff(np.concatenate([[0], a, [0]]) == 0).sum() / 2)
demonstration
nonzero_clusters(
[0,0,0,0,0,0,10,15,16,12,11,9,10,0,0,0,0,0,6,9,3,7,5,4,0,0,0,0,0,0,4,3,9,7,1]
)
3
nonzero_clusters([0, 1, 2, 0, 1, 2])
2
nonzero_clusters([0, 1, 2, 0, 1, 2, 0])
2
nonzero_clusters([1, 2, 0, 1, 2, 0, 1, 2])
3
timing
a = np.random.choice((0, 1), 100000)
code
from itertools import groupby
def div(a):
m = a != 0
return (m[1:] > m[:-1]).sum() + m[0]
def pir(a):
return int(np.diff(np.concatenate([[0], a, [0]]) == 0).sum() / 2)
def jean(a):
previous = 0
count = 0
for c in a:
if previous==0 and c!=0:
count+=1
previous = c
return count
def moin(a):
return len([is_true for is_true, _ in groupby(a, lambda x: x!=0) if is_true])
def user(a):
return sum([1 for n in range (len (a) - 1) if not a[n] and a[n + 1]])
sum ([1 for n in range (len (a) - 1) if not a[n] and a[n + 1]])

Categories